ukernel_sys/submit_ring.rs
1//! Submission ring for batched kernel operations.
2//!
3//! Userspace queues [`SubmitEntry`] operations into the ring and flushes
4//! them with a single `SYS_SUBMIT` syscall. The kernel processes all entries,
5//! capability-checks each one, and writes [`CompleteEntry`] results.
6//!
7//! One ring transition for N operations. Security enforcement is per-operation
8//! (every capability is checked), but the transition cost is amortized.
9//!
10//! # Memory layout
11//!
12//! The ring is a shared page mapped into both kernel and domain address space.
13//! Synchronization uses atomic indices with acquire/release ordering.
14
15/// Number of entries in the ring. Sized to fit in a page with completions.
16pub const RING_SIZE: usize = 64;
17
18/// Fixed virtual address for the ring in domain address space.
19pub const RING_VADDR: u64 = 0x7FFF_0000_0000;
20
21/// Submission entry — a single kernel operation request.
22///
23/// Written by userspace, read by kernel. Each entry is cache-line aligned
24/// to prevent false sharing.
25#[repr(C, align(64))]
26#[derive(Clone, Copy)]
27pub struct SubmitEntry {
28 /// Operation code ([`KernelOp`](crate::KernelOp) discriminant).
29 pub opcode: u16,
30 /// Per-operation flags (see [`submit_flags`]).
31 pub flags: u16,
32 /// Reserved.
33 pub _reserved: u32,
34 /// Operation-specific arguments. Meaning depends on opcode.
35 pub args: [u64; 5],
36 /// Opaque user token, returned in the corresponding [`CompleteEntry`].
37 pub user_data: u64,
38}
39
40impl Default for SubmitEntry {
41 fn default() -> Self {
42 Self {
43 opcode: 0,
44 flags: 0,
45 _reserved: 0,
46 args: [0; 5],
47 user_data: 0,
48 }
49 }
50}
51
52impl SubmitEntry {
53 /// Create a submission entry.
54 #[inline]
55 pub const fn new(opcode: u16, args: [u64; 5], user_data: u64) -> Self {
56 Self {
57 opcode,
58 flags: 0,
59 _reserved: 0,
60 args,
61 user_data,
62 }
63 }
64}
65
66/// Submission entry flags.
67pub mod submit_flags {
68 /// Request completion notification even on success.
69 pub const NEED_COMPLETION: u16 = 1 << 0;
70 /// Barrier — flush all pending operations before this one.
71 pub const BARRIER: u16 = 1 << 1;
72 /// Hint that more operations follow.
73 pub const MORE_COMING: u16 = 1 << 2;
74}
75
76/// Completion entry — kernel's response to a submission.
77///
78/// Written by kernel, read by userspace. The `user_data` field matches
79/// the value from the corresponding [`SubmitEntry`].
80#[repr(C, align(16))]
81#[derive(Clone, Copy, Default)]
82pub struct CompleteEntry {
83 /// Result value. Positive/zero = success, negative = -errno.
84 pub result: i64,
85 /// User token from the corresponding [`SubmitEntry`].
86 pub user_data: u64,
87 /// Completion flags (see [`complete_flags`]).
88 pub flags: u16,
89 /// Reserved.
90 pub _reserved: [u8; 6],
91}
92
93impl CompleteEntry {
94 /// Create a completion entry.
95 #[inline]
96 pub const fn new(result: i64, user_data: u64) -> Self {
97 Self {
98 result,
99 user_data,
100 flags: 0,
101 _reserved: [0; 6],
102 }
103 }
104
105 /// Whether the operation succeeded.
106 #[inline]
107 pub fn is_ok(&self) -> bool {
108 self.result >= 0
109 }
110
111 /// Error code if the operation failed.
112 #[inline]
113 pub fn error(&self) -> Option<i32> {
114 if self.result < 0 {
115 Some((-self.result) as i32)
116 } else {
117 None
118 }
119 }
120}
121
122/// Completion flags (kernel → domain notifications).
123pub mod complete_flags {
124 /// A signal is pending — check signal state after processing.
125 pub const SIGNAL_PENDING: u16 = 1 << 0;
126 /// A timer expired — check timer state.
127 pub const TIMER_EXPIRED: u16 = 1 << 1;
128}
129
130/// Shared-memory ring buffer mapped into both kernel and domain.
131///
132/// This struct defines the memory layout. The actual ring is initialized
133/// by the kernel and mapped at [`RING_VADDR`] in the domain's address space.
134///
135/// Synchronization protocol:
136/// 1. Domain writes entries, advances `submit_head` (Release)
137/// 2. Domain calls `SYS_SUBMIT`
138/// 3. Kernel reads entries from `submit_tail` to `submit_head` (Acquire)
139/// 4. Kernel writes completions, advances `complete_head` (Release)
140/// 5. Domain reads completions from `complete_tail` to `complete_head` (Acquire)
141#[repr(C)]
142pub struct SubmitRing {
143 /// Next slot for userspace to write (producer).
144 pub submit_head: u32,
145 /// Next slot for kernel to process (consumer).
146 pub submit_tail: u32,
147 /// Next completion slot for kernel to write (producer).
148 pub complete_head: u32,
149 /// Next completion slot for userspace to read (consumer).
150 pub complete_tail: u32,
151 /// Padding to cache-line boundary.
152 pub _pad: [u8; 48],
153 // Followed by:
154 // [SubmitEntry; RING_SIZE] — submission entries
155 // [CompleteEntry; RING_SIZE] — completion entries
156}