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