Skip to main content

strat9_abi/
data.rs

1use zerocopy::{FromBytes, IntoBytes};
2
3#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
4#[repr(C)]
5pub struct TimeSpec {
6    pub tv_sec: i64,
7    pub tv_nsec: i64,
8}
9
10impl TimeSpec {
11    /// Return a zero-initialized timestamp.
12    pub const fn zero() -> Self {
13        Self {
14            tv_sec: 0,
15            tv_nsec: 0,
16        }
17    }
18
19    /// Convert the timestamp to nanoseconds with saturating arithmetic.
20    pub fn to_nanos(&self) -> u64 {
21        (self.tv_sec as u64)
22            .saturating_mul(1_000_000_000)
23            .saturating_add(self.tv_nsec as u64)
24    }
25
26    /// Build a timestamp from a nanoseconds value.
27    pub fn from_nanos(nanos: u64) -> Self {
28        Self {
29            tv_sec: (nanos / 1_000_000_000) as i64,
30            tv_nsec: (nanos % 1_000_000_000) as i64,
31        }
32    }
33}
34
35#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
36#[repr(C)]
37pub struct Stat {
38    pub st_dev: u64,
39    pub st_ino: u64,
40    pub st_nlink: u64,
41    pub st_mode: u32,
42    pub st_uid: u32,
43    pub st_gid: u32,
44    pub _padding0: u32,
45    pub st_rdev: u64,
46    pub st_size: u64,
47    pub st_blksize: u64,
48    pub st_blocks: u64,
49    pub st_atime: TimeSpec,
50    pub st_mtime: TimeSpec,
51    pub st_ctime: TimeSpec,
52}
53
54#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
55#[repr(C)]
56pub struct StatVfs {
57    pub f_bsize: u64,
58    pub f_frsize: u64,
59    pub f_blocks: u64,
60    pub f_bfree: u64,
61    pub f_bavail: u64,
62    pub f_files: u64,
63    pub f_ffree: u64,
64    pub f_favail: u64,
65    pub f_fsid: u64,
66    pub f_flag: u64,
67    pub f_namemax: u64,
68}
69
70#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
71#[repr(C)]
72pub struct Map {
73    pub offset: usize,
74    pub size: usize,
75    pub flags: u32,
76    pub _reserved: u32,
77    pub addr: usize,
78}
79
80#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
81#[repr(C)]
82pub struct HandleInfo {
83    pub resource_type: u32,
84    pub permissions: u32,
85    pub resource: u64,
86}
87
88#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
89#[repr(C)]
90pub struct MemoryRegionInfo {
91    pub size: u64,
92    pub page_size: u64,
93    pub flags: u32,
94    pub _reserved: u32,
95}
96
97pub const PCI_MATCH_VENDOR_ID: u32 = 1 << 0;
98pub const PCI_MATCH_DEVICE_ID: u32 = 1 << 1;
99pub const PCI_MATCH_CLASS_CODE: u32 = 1 << 2;
100pub const PCI_MATCH_SUBCLASS: u32 = 1 << 3;
101pub const PCI_MATCH_PROG_IF: u32 = 1 << 4;
102
103#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
104#[repr(C, align(4))]
105pub struct PciAddress {
106    pub bus: u8,
107    pub device: u8,
108    pub function: u8,
109    pub _reserved: u8,
110}
111
112#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
113#[repr(C)]
114pub struct PciProbeCriteria {
115    pub match_flags: u32,
116    pub vendor_id: u16,
117    pub device_id: u16,
118    pub class_code: u8,
119    pub subclass: u8,
120    pub prog_if: u8,
121    pub _reserved: u8,
122}
123
124#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
125#[repr(C)]
126pub struct PciDeviceInfo {
127    pub address: PciAddress,
128    pub vendor_id: u16,
129    pub device_id: u16,
130    pub class_code: u8,
131    pub subclass: u8,
132    pub prog_if: u8,
133    pub revision: u8,
134    pub header_type: u8,
135    pub interrupt_line: u8,
136    pub interrupt_pin: u8,
137    pub _reserved: u8,
138}
139
140#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
141#[repr(C)]
142pub struct FileStat {
143    pub st_dev: u64,
144    pub st_ino: u64,
145    pub st_mode: u32,
146    pub st_nlink: u32,
147    pub st_uid: u32,
148    pub st_gid: u32,
149    pub st_rdev: u64,
150    pub st_size: u64,
151    pub st_blksize: u64,
152    pub st_blocks: u64,
153    pub st_atime: TimeSpec,
154    pub st_mtime: TimeSpec,
155    pub st_ctime: TimeSpec,
156}
157
158impl FileStat {
159    /// Return a fully zeroed `FileStat`.
160    pub const fn zeroed() -> Self {
161        FileStat {
162            st_dev: 0,
163            st_ino: 0,
164            st_mode: 0,
165            st_nlink: 0,
166            st_uid: 0,
167            st_gid: 0,
168            st_rdev: 0,
169            st_size: 0,
170            st_blksize: 0,
171            st_blocks: 0,
172            st_atime: TimeSpec::zero(),
173            st_mtime: TimeSpec::zero(),
174            st_ctime: TimeSpec::zero(),
175        }
176    }
177
178    /// Return true when the mode encodes a directory.
179    pub fn is_dir(&self) -> bool {
180        (self.st_mode & 0o170000) == 0o040000
181    }
182
183    /// Return true when the mode encodes a regular file.
184    pub fn is_file(&self) -> bool {
185        (self.st_mode & 0o170000) == 0o100000
186    }
187}
188
189#[derive(Clone, Copy, FromBytes, IntoBytes)]
190#[repr(C, align(64))]
191pub struct IpcMessage {
192    pub sender: u64,
193    pub msg_type: u32,
194    pub flags: u32,
195    pub payload: [u8; 48],
196}
197
198impl IpcMessage {
199    /// Create an empty IPC message for `msg_type`.
200    pub const fn new(msg_type: u32) -> Self {
201        IpcMessage {
202            sender: 0,
203            msg_type,
204            flags: 0,
205            payload: [0u8; 48],
206        }
207    }
208
209    /// Build a standard error reply carrying a status code in payload.
210    pub fn error_reply(sender: u64, status: i32) -> Self {
211        let mut msg = IpcMessage::new(0x80);
212        msg.sender = sender;
213        msg.payload[0..4].copy_from_slice(&(status as u32).to_le_bytes());
214        msg
215    }
216}
217
218impl core::fmt::Debug for IpcMessage {
219    /// Implements fmt.
220    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
221        f.debug_struct("IpcMessage")
222            .field("sender", &self.sender)
223            .field("msg_type", &format_args!("0x{:02x}", self.msg_type))
224            .field("flags", &self.flags)
225            .finish()
226    }
227}
228
229pub const SEEK_SET: usize = 0;
230pub const SEEK_CUR: usize = 1;
231pub const SEEK_END: usize = 2;
232
233// File type constants (matching Linux DT_* values)
234pub const DT_UNKNOWN: u8 = 0;
235pub const DT_FIFO: u8 = 1;
236pub const DT_CHR: u8 = 2;
237pub const DT_DIR: u8 = 4;
238pub const DT_BLK: u8 = 6;
239pub const DT_REG: u8 = 8;
240pub const DT_LNK: u8 = 10;
241pub const DT_SOCK: u8 = 12;
242
243/// Fixed-size header for each directory entry in the SYS_GETDENTS wire format.
244///
245/// Wire layout per entry: `DirentHeader` (12 bytes) followed by `name_len`
246/// bytes of filename data and a trailing NUL byte.
247#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
248#[repr(C, packed)]
249pub struct DirentHeader {
250    pub ino: u64,
251    pub file_type: u8,
252    pub name_len: u16,
253    pub _padding: u8,
254}
255
256impl DirentHeader {
257    pub const SIZE: usize = 12; // 8 + 1 + 2 + 1
258
259    /// Return the total packed entry size (header + name + trailing NUL).
260    pub const fn entry_size(&self) -> usize {
261        Self::SIZE + self.name_len as usize + 1
262    }
263}
264
265macro_rules! assert_abi_struct {
266    ($t:ty, $size:expr, $align:expr) => {
267        static_assertions::assert_eq_size!($t, [u8; $size]);
268        static_assertions::const_assert_eq!(core::mem::align_of::<$t>(), $align);
269    };
270}
271
272assert_abi_struct!(DirentHeader, 12, 1);
273assert_abi_struct!(Stat, 120, 8);
274assert_abi_struct!(StatVfs, 88, 8);
275assert_abi_struct!(Map, 32, 8);
276assert_abi_struct!(FileStat, 112, 8);
277assert_abi_struct!(IpcMessage, 64, 64);
278assert_abi_struct!(TimeSpec, 16, 8);
279assert_abi_struct!(HandleInfo, 16, 8);
280assert_abi_struct!(MemoryRegionInfo, 24, 8);
281assert_abi_struct!(PciAddress, 4, 4);
282assert_abi_struct!(PciProbeCriteria, 12, 4);
283assert_abi_struct!(PciDeviceInfo, 16, 4);