Skip to main content

strat9_abi/
data.rs

1//! ABI data structures shared between kernel and userspace.
2//!
3//! These types define the wire format for syscalls, IPC messages, and
4//! file system operations. Both kernel and userspace must agree on the
5//! exact memory layout (size, alignment, field ordering).
6
7use zerocopy::{FromBytes, IntoBytes};
8
9// ── IPC Message Constants ───────────────────────────────────────────────────
10
11/// Total size of an IPC message in bytes (including header and payload).
12pub const IPC_MESSAGE_SIZE: usize = 256;
13
14/// Alignment requirement for IPC messages (64-byte aligned for cache line).
15pub const IPC_MESSAGE_ALIGN: usize = 64;
16
17/// Size of the IPC message header (sender + msg_type + flags).
18pub const IPC_MESSAGE_HEADER_SIZE: usize = 16;
19
20/// Maximum payload size in an IPC message (256 - 16 = 240 bytes).
21pub const IPC_PAYLOAD_CAPACITY: usize = IPC_MESSAGE_SIZE - IPC_MESSAGE_HEADER_SIZE;
22
23// ── IPC File Flags ──────────────────────────────────────────────────────────
24
25/// Directory entry flag: this entry is a directory.
26pub const IPC_FILE_FLAG_DIRECTORY: u32 = 1 << 0;
27
28/// Directory entry flag: this entry is a device file.
29pub const IPC_FILE_FLAG_DEVICE: u32 = 1 << 1;
30
31/// Directory entry flag: this entry is a pipe.
32pub const IPC_FILE_FLAG_PIPE: u32 = 1 << 2;
33
34/// Directory entry flag: file is opened in append mode.
35pub const IPC_FILE_FLAG_APPEND: u32 = 1 << 3;
36
37/// Directory entry flag: supports chunked reads.
38pub const IPC_FILE_FLAG_CHUNK_READ: u32 = 1 << 4;
39
40/// Directory entry flag: supports chunked writes.
41pub const IPC_FILE_FLAG_CHUNK_WRITE: u32 = 1 << 5;
42
43// ── SiloMode ────────────────────────────────────────────────────────────────
44
45/// 9-bit octal silo permission mode (3 control + 3 hardware + 3 registry).
46///
47/// Shared ABI type used by both kernel and userspace init.
48/// The kernel's richer `OctalMode` (with typed bitflag fields)
49/// is built from this via `OctalMode::from_octal(val.0)`.
50///
51/// Bit layout: `[control:3][hardware:3][registry:3]` (LSB = registry).
52///
53/// Example: `0o777` = full permissions in all groups.
54#[derive(Debug, Clone, Copy, PartialEq, Eq, FromBytes, IntoBytes)]
55#[repr(transparent)]
56pub struct SiloMode(pub u16);
57
58impl SiloMode {
59    /// Return true if `self` bits are a subset of `other`'s bits
60    /// (i.e. `self` does not request any permission `other` lacks).
61    pub const fn is_subset_of(&self, other: &SiloMode) -> bool {
62        let (s_c, s_h, s_r) = ((self.0 >> 6) & 0o7, (self.0 >> 3) & 0o7, self.0 & 0o7);
63        let (o_c, o_h, o_r) = ((other.0 >> 6) & 0o7, (other.0 >> 3) & 0o7, other.0 & 0o7);
64        (s_c & !o_c) == 0 && (s_h & !o_h) == 0 && (s_r & !o_r) == 0
65    }
66}
67
68// ── TimeSpec ───────────────── saturating multiplication─────────────────────
69
70/// POSIX-style timestamp: seconds + nanoseconds.
71///
72/// Used for `SYS_CLOCK_GETTIME`, `SYS_NANOSLEEP`, and file timestamps.
73#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
74#[repr(C)]
75pub struct TimeSpec {
76    /// Seconds since Unix epoch (or relative time for sleep).
77    pub tv_sec: i64,
78    /// Nanoseconds (0..999_999_999).
79    pub tv_nsec: i64,
80}
81
82impl TimeSpec {
83    /// Return a zero-initialized timestamp.
84    pub const fn zero() -> Self {
85        Self {
86            tv_sec: 0,
87            tv_nsec: 0,
88        }
89    }
90
91    /// Convert the timestamp to nanoseconds with saturating arithmetic.
92    pub fn to_nanos(&self) -> u64 {
93        (self.tv_sec as u64)
94            .saturating_mul(1_000_000_000)
95            .saturating_add(self.tv_nsec as u64)
96    }
97
98    /// Build a timestamp from a nanoseconds value.
99    pub fn from_nanos(nanos: u64) -> Self {
100        Self {
101            tv_sec: (nanos / 1_000_000_000) as i64,
102            tv_nsec: (nanos % 1_000_000_000) as i64,
103        }
104    }
105}
106
107// ── Stat (legacy, kept for compat) ─────────────────────────────────────────
108
109/// Legacy stat structure (120 bytes). Prefer [`FileStat`] for new code.
110#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
111#[repr(C)]
112pub struct Stat {
113    pub st_dev: u64,
114    pub st_ino: u64,
115    pub st_nlink: u64,
116    pub st_mode: u32,
117    pub st_uid: u32,
118    pub st_gid: u32,
119    pub _padding0: u32,
120    pub st_rdev: u64,
121    pub st_size: u64,
122    pub st_blksize: u64,
123    pub st_blocks: u64,
124    pub st_atime: TimeSpec,
125    pub st_mtime: TimeSpec,
126    pub st_ctime: TimeSpec,
127}
128
129// ── StatVfs ────────────────────────────────────────────────────────────────
130
131/// Filesystem statistics (for `SYS_STAT` on directories).
132///
133/// Similar to Linux `struct statfs`.
134#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
135#[repr(C)]
136pub struct StatVfs {
137    /// Preferred file system block size.
138    pub f_bsize: u64,
139    /// File system fragment size.
140    pub f_frsize: u64,
141    /// Total data blocks in the file system.
142    pub f_blocks: u64,
143    /// Free blocks available to unprivileged users.
144    pub f_bfree: u64,
145    /// Free blocks available to unprivileged users.
146    pub f_bavail: u64,
147    /// Total file nodes (inodes).
148    pub f_files: u64,
149    /// Free file nodes.
150    pub f_ffree: u64,
151    /// Free file nodes available to unprivileged users.
152    pub f_favail: u64,
153    /// File system ID.
154    pub f_fsid: u64,
155    /// File system flags (read-only, etc.).
156    pub f_flag: u64,
157    /// Maximum filename length.
158    pub f_namemax: u64,
159}
160
161// ── Map (mmap) ─────────────────────────────────────────────────────────────
162
163/// Memory mapping descriptor for `SYS_MMAP`.
164///
165/// Describes a region of virtual memory mapped into a process.
166#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
167#[repr(C)]
168pub struct Map {
169    /// Offset into the backing file (must be page-aligned).
170    pub offset: usize,
171    /// Size of the mapping in bytes.
172    pub size: usize,
173    /// Protection flags (`PROT_READ`, `PROT_WRITE`, `PROT_EXEC`).
174    pub flags: u32,
175    pub _reserved: u32,
176    /// Virtual address of the mapping.
177    pub addr: usize,
178}
179
180// ── HandleInfo ──────────────────────────────────────────────────────────────
181
182/// Information about a capability handle (returned by `SYS_HANDLE_INFO`).
183#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
184#[repr(C)]
185pub struct HandleInfo {
186    /// Resource type (file, memory, IPC, etc.).
187    pub resource_type: u32,
188    /// Permission bits (read, write, execute, grant, revoke).
189    pub permissions: u32,
190    /// Underlying resource identifier (fd, memory region ID, etc.).
191    pub resource: u64,
192}
193
194// ── MemoryRegionInfo ────────────────────────────────────────────────────────
195
196/// Information about an exported memory region (returned by `SYS_MEM_REGION_INFO`).
197#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
198#[repr(C)]
199pub struct MemoryRegionInfo {
200    /// Total size of the region in bytes.
201    pub size: u64,
202    /// Page size used for mapping.
203    pub page_size: u64,
204    /// Region flags (read, write, execute).
205    pub flags: u32,
206    pub _reserved: u32,
207}
208
209// ── AsyncRingLayout ─────────────────────────────────────────────────────────
210
211/// Layout descriptor for async I/O ring buffers.
212///
213/// Describes the submission and completion queue regions within
214/// a shared memory page used by the async I/O subsystem.
215#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
216#[repr(C)]
217pub struct AsyncRingLayout {
218    /// Physical address of the submission queue base.
219    pub sq_base: u64,
220    /// Physical address of the completion queue base.
221    pub cq_base: u64,
222    /// Size of the submission queue in bytes.
223    pub sq_size: u64,
224    /// Size of the completion queue in bytes.
225    pub cq_size: u64,
226    /// Number of entries in each queue.
227    pub entries: u32,
228    pub _reserved: u32,
229}
230
231// ── PCI ─────────────────────────────────────────────────────────────────────
232
233/// PCI match flag: match by vendor ID.
234pub const PCI_MATCH_VENDOR_ID: u32 = 1 << 0;
235
236/// PCI match flag: match by device ID.
237pub const PCI_MATCH_DEVICE_ID: u32 = 1 << 1;
238
239/// PCI match flag: match by class code.
240pub const PCI_MATCH_CLASS_CODE: u32 = 1 << 2;
241
242/// PCI match flag: match by subclass.
243pub const PCI_MATCH_SUBCLASS: u32 = 1 << 3;
244
245/// PCI match flag: match by programming interface.
246pub const PCI_MATCH_PROG_IF: u32 = 1 << 4;
247
248/// PCI device address (bus/device/function).
249#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
250#[repr(C, align(4))]
251pub struct PciAddress {
252    /// PCI bus number (0-255).
253    pub bus: u8,
254    /// PCI device number (0-31).
255    pub device: u8,
256    /// PCI function number (0-7).
257    pub function: u8,
258    pub _reserved: u8,
259}
260
261/// PCI device search criteria for `SYS_PCI_ENUM`.
262///
263/// Set `match_flags` to indicate which fields to match. Fields not
264/// flagged are ignored.
265#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
266#[repr(C)]
267pub struct PciProbeCriteria {
268    /// Bitmask of fields to match (see `PCI_MATCH_*` constants).
269    pub match_flags: u32,
270    /// Vendor ID to match (if `PCI_MATCH_VENDOR_ID` is set).
271    pub vendor_id: u16,
272    /// Device ID to match (if `PCI_MATCH_DEVICE_ID` is set).
273    pub device_id: u16,
274    /// Class code to match (if `PCI_MATCH_CLASS_CODE` is set).
275    pub class_code: u8,
276    /// Subclass to match (if `PCI_MATCH_SUBCLASS` is set).
277    pub subclass: u8,
278    /// Programming interface to match (if `PCI_MATCH_PROG_IF` is set).
279    pub prog_if: u8,
280    pub _reserved: u8,
281}
282
283/// PCI device information returned by `SYS_PCI_ENUM`.
284#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
285#[repr(C)]
286pub struct PciDeviceInfo {
287    /// PCI bus/device/function address.
288    pub address: PciAddress,
289    /// Vendor ID (e.g., 0x8086 for Intel).
290    pub vendor_id: u16,
291    /// Device ID (e.g., 0x100E for Intel E1000).
292    pub device_id: u16,
293    /// PCI class code (e.g., 0x02 for network controller).
294    pub class_code: u8,
295    /// PCI subclass (e.g., 0x00 for Ethernet controller).
296    pub subclass: u8,
297    /// Programming interface (e.g., 0x00 for E1000).
298    pub prog_if: u8,
299    /// PCI revision ID.
300    pub revision: u8,
301    /// Header type (0 = standard, 1 = PCI-to-PCI bridge).
302    pub header_type: u8,
303    /// Interrupt line (IRQ number, 0 = none).
304    pub interrupt_line: u8,
305    /// Interrupt pin (A=1, B=2, C=3, D=4, 0 = none).
306    pub interrupt_pin: u8,
307    pub _reserved: u8,
308}
309
310// ── FileStat ────────────────────────────────────────────────────────────────
311
312/// File status information (returned by `SYS_FSTAT`, `SYS_STAT`, `SYS_FSTATAT`).
313///
314/// Equivalent to POSIX `struct stat` with 64-bit fields.
315#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
316#[repr(C)]
317pub struct FileStat {
318    /// Device ID containing this file.
319    pub st_dev: u64,
320    /// Inode number.
321    pub st_ino: u64,
322    /// File type and permissions (see `DT_*` and `0o7777` masks).
323    pub st_mode: u32,
324    /// Number of hard links.
325    pub st_nlink: u32,
326    /// Owner user ID.
327    pub st_uid: u32,
328    /// Owner group ID.
329    pub st_gid: u32,
330    /// Device ID (for special files).
331    pub st_rdev: u64,
332    /// Total size in bytes.
333    pub st_size: u64,
334    /// Preferred block size for I/O.
335    pub st_blksize: u64,
336    /// Number of 512-byte blocks allocated.
337    pub st_blocks: u64,
338    /// Last access time.
339    pub st_atime: TimeSpec,
340    /// Last modification time.
341    pub st_mtime: TimeSpec,
342    /// Last status change time.
343    pub st_ctime: TimeSpec,
344}
345
346impl FileStat {
347    /// Return a fully zeroed `FileStat`.
348    pub const fn zeroed() -> Self {
349        FileStat {
350            st_dev: 0,
351            st_ino: 0,
352            st_mode: 0,
353            st_nlink: 0,
354            st_uid: 0,
355            st_gid: 0,
356            st_rdev: 0,
357            st_size: 0,
358            st_blksize: 0,
359            st_blocks: 0,
360            st_atime: TimeSpec::zero(),
361            st_mtime: TimeSpec::zero(),
362            st_ctime: TimeSpec::zero(),
363        }
364    }
365
366    /// Return true when the mode encodes a directory.
367    pub fn is_dir(&self) -> bool {
368        (self.st_mode & 0o170000) == 0o040000
369    }
370
371    /// Return true when the mode encodes a regular file.
372    pub fn is_file(&self) -> bool {
373        (self.st_mode & 0o170000) == 0o100000
374    }
375}
376
377// ── IpcMessage ─────────────────────────────────────────────────────────────
378
379/// Fixed-size IPC message (256 bytes, 64-byte aligned).
380///
381/// Used by IPC ports, channels, and the transport layer.
382/// The header occupies 16 bytes; the payload is 240 bytes.
383#[derive(Clone, Copy, FromBytes, IntoBytes)]
384#[repr(C, align(64))]
385pub struct IpcMessage {
386    /// PID/TID of the sending process.
387    pub sender: u64,
388    /// Message type identifier (protocol-specific).
389    pub msg_type: u32,
390    /// Message flags (protocol-specific).
391    pub flags: u32,
392    /// Payload data (up to 240 bytes).
393    pub payload: [u8; IPC_PAYLOAD_CAPACITY],
394}
395
396impl IpcMessage {
397    /// Total wire size of the message (including header).
398    pub const WIRE_SIZE: usize = IPC_MESSAGE_SIZE;
399    /// Alignment requirement.
400    pub const ALIGN: usize = IPC_MESSAGE_ALIGN;
401    /// Maximum payload bytes.
402    pub const PAYLOAD_CAPACITY: usize = IPC_PAYLOAD_CAPACITY;
403
404    /// Usable payload capacity for `OPEN` inline path (240 - 6 bytes overhead).
405    pub const OPEN_INLINE_CAPACITY: usize = IPC_PAYLOAD_CAPACITY - 6;
406    /// Usable payload capacity for `UNLINK` inline path (240 - 2 bytes overhead).
407    pub const UNLINK_INLINE_CAPACITY: usize = IPC_PAYLOAD_CAPACITY - 2;
408    /// Usable payload capacity for `READ` inline path (240 - 8 bytes overhead).
409    pub const READ_INLINE_CAPACITY: usize = IPC_PAYLOAD_CAPACITY - 8;
410    /// Usable payload capacity for `WRITE` inline path (240 - 18 bytes overhead).
411    pub const WRITE_INLINE_CAPACITY: usize = IPC_PAYLOAD_CAPACITY - 18;
412
413    /// Create an empty IPC message for `msg_type`.
414    pub const fn new(msg_type: u32) -> Self {
415        IpcMessage {
416            sender: 0,
417            msg_type,
418            flags: 0,
419            payload: [0u8; IPC_PAYLOAD_CAPACITY],
420        }
421    }
422
423    /// Build a standard error reply carrying a status code in payload.
424    ///
425    /// The `msg_type` is set to `0x80` (error reply marker).
426    /// The payload contains the error code as a little-endian `i32`.
427    pub fn error_reply(sender: u64, status: i32) -> Self {
428        let mut msg = IpcMessage::new(0x80);
429        msg.sender = sender;
430        msg.payload[0..4].copy_from_slice(&(status as u32).to_le_bytes());
431        msg
432    }
433}
434
435impl core::fmt::Debug for IpcMessage {
436    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
437        f.debug_struct("IpcMessage")
438            .field("sender", &self.sender)
439            .field("msg_type", &format_args!("0x{:02x}", self.msg_type))
440            .field("flags", &self.flags)
441            .finish()
442    }
443}
444
445// ── Seek constants ──────────────────────────────────────────────────────────
446
447/// Seek relative to the beginning of the file.
448pub const SEEK_SET: usize = 0;
449
450/// Seek relative to the current file position.
451pub const SEEK_CUR: usize = 1;
452
453/// Seek relative to the end of the file.
454pub const SEEK_END: usize = 2;
455
456// ── File type constants ─────────────────────────────────────────────────────
457
458/// Unknown file type.
459pub const DT_UNKNOWN: u8 = 0;
460
461/// FIFO (named pipe).
462pub const DT_FIFO: u8 = 1;
463
464/// Character device.
465pub const DT_CHR: u8 = 2;
466
467/// Directory.
468pub const DT_DIR: u8 = 4;
469
470/// Block device.
471pub const DT_BLK: u8 = 6;
472
473/// Regular file.
474pub const DT_REG: u8 = 8;
475
476/// Symbolic link.
477pub const DT_LNK: u8 = 10;
478
479/// Unix domain socket.
480pub const DT_SOCK: u8 = 12;
481
482// ── DirentHeader ────────────────────────────────────────────────────────────
483
484/// Fixed-size header for each directory entry in the `SYS_GETDENTS` wire format.
485///
486/// Wire layout per entry: `DirentHeader` (12 bytes) followed by `name_len`
487/// bytes of filename data and a trailing NUL byte.
488///
489/// Total entry size = 12 + name_len + 1 bytes.
490#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
491#[repr(C, packed)]
492pub struct DirentHeader {
493    /// Inode number of the entry.
494    pub ino: u64,
495    /// File type (see `DT_*` constants).
496    pub file_type: u8,
497    /// Length of the filename in bytes (excluding NUL).
498    pub name_len: u16,
499    pub _padding: u8,
500}
501
502impl DirentHeader {
503    /// Size of the fixed header portion (12 bytes).
504    pub const SIZE: usize = 12; // 8 + 1 + 2 + 1
505
506    /// Return the total packed entry size (header + name + trailing NUL).
507    pub const fn entry_size(&self) -> usize {
508        Self::SIZE + self.name_len as usize + 1
509    }
510}
511
512// ── SiloConfig ──────────────────────────────────────────────────────────────
513
514/// Silo configuration block passed from init to `SYS_SILO_CREATE`.
515///
516/// Defines resource limits, capabilities, and scheduling parameters
517/// for a new silo (process isolation container).
518///
519/// Layout must match the kernel's definition (ABI contract).
520#[derive(Debug, Clone, Copy)]
521#[repr(C)]
522pub struct SiloConfig {
523    /// Minimum memory reservation in bytes.
524    pub mem_min: u64,
525    /// Maximum memory limit in bytes (0 = unlimited).
526    pub mem_max: u64,
527    /// CPU scheduling weight (relative priority).
528    pub cpu_shares: u32,
529    /// CPU time quota per period in microseconds (0 = unlimited).
530    pub cpu_quota_us: u64,
531    /// CPU scheduling period in microseconds.
532    pub cpu_period_us: u64,
533    /// CPU affinity bitmask (0 = any CPU).
534    pub cpu_affinity_mask: u64,
535    /// Maximum number of threads/tasks in the silo.
536    pub max_tasks: u32,
537    /// Read bandwidth limit in bytes/sec (0 = unlimited).
538    pub io_bw_read: u64,
539    /// Write bandwidth limit in bytes/sec (0 = unlimited).
540    pub io_bw_write: u64,
541    /// Pointer to the initial capability list.
542    pub caps_ptr: u64,
543    /// Length of the capability list in bytes.
544    pub caps_len: u64,
545    /// Silo behavior flags.
546    pub flags: u64,
547    /// Silo ID (assigned by kernel, 0 = auto-assign).
548    pub sid: u32,
549    /// Octal permission mode (see `SiloMode`).
550    pub mode: u16,
551    /// Silo family/type identifier.
552    pub family: u8,
553    /// Required CPU features (bitmask from CPUID).
554    pub cpu_features_required: u64,
555    /// Allowed CPU features (bitmask, 0 = all allowed).
556    pub cpu_features_allowed: u64,
557    /// XCR0 register mask for FPU/SSE/AVX state.
558    pub xcr0_mask: u64,
559    /// Maximum concurrent graphics sessions (0 = no graphics).
560    pub graphics_max_sessions: u16,
561    /// Graphics session time-to-live in seconds.
562    pub graphics_session_ttl_sec: u32,
563    pub graphics_reserved: u16,
564}
565
566impl SiloConfig {
567    /// Return a zero-initialized silo configuration.
568    pub const fn zero() -> Self {
569        Self {
570            mem_min: 0,
571            mem_max: 0,
572            cpu_shares: 0,
573            cpu_quota_us: 0,
574            cpu_period_us: 0,
575            cpu_affinity_mask: 0,
576            max_tasks: 0,
577            io_bw_read: 0,
578            io_bw_write: 0,
579            caps_ptr: 0,
580            caps_len: 0,
581            flags: 0,
582            sid: 0,
583            mode: 0,
584            family: 0,
585            cpu_features_required: 0,
586            cpu_features_allowed: u64::MAX,
587            xcr0_mask: 0,
588            graphics_max_sessions: 0,
589            graphics_session_ttl_sec: 0,
590            graphics_reserved: 0,
591        }
592    }
593}
594
595// ── Static assertions ──────────────────────────────────────────────────────
596
597macro_rules! assert_abi_struct {
598    ($t:ty, $size:expr, $align:expr) => {
599        static_assertions::assert_eq_size!($t, [u8; $size]);
600        static_assertions::const_assert_eq!(core::mem::align_of::<$t>(), $align);
601    };
602}
603
604assert_abi_struct!(DirentHeader, 12, 1);
605assert_abi_struct!(Stat, 120, 8);
606assert_abi_struct!(StatVfs, 88, 8);
607assert_abi_struct!(Map, 32, 8);
608assert_abi_struct!(FileStat, 112, 8);
609assert_abi_struct!(IpcMessage, IPC_MESSAGE_SIZE, IPC_MESSAGE_ALIGN);
610assert_abi_struct!(TimeSpec, 16, 8);
611assert_abi_struct!(HandleInfo, 16, 8);
612assert_abi_struct!(MemoryRegionInfo, 24, 8);
613assert_abi_struct!(PciAddress, 4, 4);
614assert_abi_struct!(PciProbeCriteria, 12, 4);
615assert_abi_struct!(PciDeviceInfo, 16, 4);