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
88pub const PCI_MATCH_VENDOR_ID: u32 = 1 << 0;
89pub const PCI_MATCH_DEVICE_ID: u32 = 1 << 1;
90pub const PCI_MATCH_CLASS_CODE: u32 = 1 << 2;
91pub const PCI_MATCH_SUBCLASS: u32 = 1 << 3;
92pub const PCI_MATCH_PROG_IF: u32 = 1 << 4;
93
94#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
95#[repr(C, align(4))]
96pub struct PciAddress {
97    pub bus: u8,
98    pub device: u8,
99    pub function: u8,
100    pub _reserved: u8,
101}
102
103#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
104#[repr(C)]
105pub struct PciProbeCriteria {
106    pub match_flags: u32,
107    pub vendor_id: u16,
108    pub device_id: u16,
109    pub class_code: u8,
110    pub subclass: u8,
111    pub prog_if: u8,
112    pub _reserved: u8,
113}
114
115#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
116#[repr(C)]
117pub struct PciDeviceInfo {
118    pub address: PciAddress,
119    pub vendor_id: u16,
120    pub device_id: u16,
121    pub class_code: u8,
122    pub subclass: u8,
123    pub prog_if: u8,
124    pub revision: u8,
125    pub header_type: u8,
126    pub interrupt_line: u8,
127    pub interrupt_pin: u8,
128    pub _reserved: u8,
129}
130
131#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
132#[repr(C)]
133pub struct FileStat {
134    pub st_dev: u64,
135    pub st_ino: u64,
136    pub st_mode: u32,
137    pub st_nlink: u32,
138    pub st_uid: u32,
139    pub st_gid: u32,
140    pub st_rdev: u64,
141    pub st_size: u64,
142    pub st_blksize: u64,
143    pub st_blocks: u64,
144    pub st_atime: TimeSpec,
145    pub st_mtime: TimeSpec,
146    pub st_ctime: TimeSpec,
147}
148
149impl FileStat {
150    /// Return a fully zeroed `FileStat`.
151    pub const fn zeroed() -> Self {
152        FileStat {
153            st_dev: 0,
154            st_ino: 0,
155            st_mode: 0,
156            st_nlink: 0,
157            st_uid: 0,
158            st_gid: 0,
159            st_rdev: 0,
160            st_size: 0,
161            st_blksize: 0,
162            st_blocks: 0,
163            st_atime: TimeSpec::zero(),
164            st_mtime: TimeSpec::zero(),
165            st_ctime: TimeSpec::zero(),
166        }
167    }
168
169    /// Return true when the mode encodes a directory.
170    pub fn is_dir(&self) -> bool {
171        (self.st_mode & 0o170000) == 0o040000
172    }
173
174    /// Return true when the mode encodes a regular file.
175    pub fn is_file(&self) -> bool {
176        (self.st_mode & 0o170000) == 0o100000
177    }
178}
179
180#[derive(Clone, Copy, FromBytes, IntoBytes)]
181#[repr(C, align(64))]
182pub struct IpcMessage {
183    pub sender: u64,
184    pub msg_type: u32,
185    pub flags: u32,
186    pub payload: [u8; 48],
187}
188
189impl IpcMessage {
190    /// Create an empty IPC message for `msg_type`.
191    pub const fn new(msg_type: u32) -> Self {
192        IpcMessage {
193            sender: 0,
194            msg_type,
195            flags: 0,
196            payload: [0u8; 48],
197        }
198    }
199
200    /// Build a standard error reply carrying a status code in payload.
201    pub fn error_reply(sender: u64, status: i32) -> Self {
202        let mut msg = IpcMessage::new(0x80);
203        msg.sender = sender;
204        msg.payload[0..4].copy_from_slice(&(status as u32).to_le_bytes());
205        msg
206    }
207}
208
209impl core::fmt::Debug for IpcMessage {
210    /// Implements fmt.
211    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
212        f.debug_struct("IpcMessage")
213            .field("sender", &self.sender)
214            .field("msg_type", &format_args!("0x{:02x}", self.msg_type))
215            .field("flags", &self.flags)
216            .finish()
217    }
218}
219
220pub const SEEK_SET: usize = 0;
221pub const SEEK_CUR: usize = 1;
222pub const SEEK_END: usize = 2;
223
224// File type constants (matching Linux DT_* values)
225pub const DT_UNKNOWN: u8 = 0;
226pub const DT_FIFO: u8 = 1;
227pub const DT_CHR: u8 = 2;
228pub const DT_DIR: u8 = 4;
229pub const DT_BLK: u8 = 6;
230pub const DT_REG: u8 = 8;
231pub const DT_LNK: u8 = 10;
232pub const DT_SOCK: u8 = 12;
233
234/// Fixed-size header for each directory entry in the SYS_GETDENTS wire format.
235///
236/// Wire layout per entry: `DirentHeader` (12 bytes) followed by `name_len`
237/// bytes of filename data and a trailing NUL byte.
238#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
239#[repr(C, packed)]
240pub struct DirentHeader {
241    pub ino: u64,
242    pub file_type: u8,
243    pub name_len: u16,
244    pub _padding: u8,
245}
246
247impl DirentHeader {
248    pub const SIZE: usize = 12; // 8 + 1 + 2 + 1
249
250    /// Return the total packed entry size (header + name + trailing NUL).
251    pub const fn entry_size(&self) -> usize {
252        Self::SIZE + self.name_len as usize + 1
253    }
254}
255
256macro_rules! assert_abi_struct {
257    ($t:ty, $size:expr, $align:expr) => {
258        static_assertions::assert_eq_size!($t, [u8; $size]);
259        static_assertions::const_assert_eq!(core::mem::align_of::<$t>(), $align);
260    };
261}
262
263assert_abi_struct!(DirentHeader, 12, 1);
264assert_abi_struct!(Stat, 120, 8);
265assert_abi_struct!(StatVfs, 88, 8);
266assert_abi_struct!(Map, 32, 8);
267assert_abi_struct!(FileStat, 112, 8);
268assert_abi_struct!(IpcMessage, 64, 64);
269assert_abi_struct!(TimeSpec, 16, 8);
270assert_abi_struct!(HandleInfo, 16, 8);
271assert_abi_struct!(PciAddress, 4, 4);
272assert_abi_struct!(PciProbeCriteria, 12, 4);
273assert_abi_struct!(PciDeviceInfo, 16, 4);