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);