Nothing to see here, move along
1use core::sync::atomic::Ordering;
2
3use crate::cap::cnode;
4use crate::cap::object::ObjectTag;
5use crate::cap::ops;
6use crate::cap::pool::POOL;
7use crate::mem::phys::BitmapFrameAllocator;
8use crate::proc::PROCESSES;
9use crate::ring::{
10 CompletionEntry, RING_OP_CAP_CREATE, RING_OP_NOP, RING_OP_NOTIFY_POLL, RING_OP_NOTIFY_SIGNAL,
11 RingHeader, RingIndex, SubmissionEntry, ring_cq_offset, ring_sq_offset,
12};
13use crate::types::Pid;
14
15fn setup_ring_page() -> (x86_64::PhysAddr, *mut u8) {
16 let allocator = BitmapFrameAllocator;
17 let frame = allocator.allocate().expect("alloc ring frame");
18 let phys = frame.phys_addr();
19 crate::mem::addr::zero_frame(phys);
20 let virt = crate::mem::addr::phys_to_virt(phys);
21 let _ = frame.inner();
22 (phys, virt.as_mut_ptr::<u8>())
23}
24
25fn teardown_ring_page(phys: x86_64::PhysAddr) {
26 BitmapFrameAllocator::free_frame_by_addr(phys);
27}
28
29unsafe fn write_sqe(ring_base: *mut u8, index: u32, sqe: SubmissionEntry) {
30 let sq_base = unsafe { ring_base.add(ring_sq_offset()) };
31 let entry_ptr =
32 unsafe { sq_base.add((index as usize) * core::mem::size_of::<SubmissionEntry>()) };
33 unsafe { core::ptr::write_volatile(entry_ptr as *mut SubmissionEntry, sqe) };
34}
35
36unsafe fn read_cqe(ring_base: *const u8, index: u32) -> CompletionEntry {
37 let cq_base = unsafe { ring_base.add(ring_cq_offset()) };
38 let entry_ptr =
39 unsafe { cq_base.add((index as usize) * core::mem::size_of::<CompletionEntry>()) };
40 unsafe { core::ptr::read_volatile(entry_ptr as *const CompletionEntry) }
41}
42
43unsafe fn set_sq_tail(ring_base: *mut u8, val: u32) {
44 let header = unsafe { &*(ring_base as *const RingHeader) };
45 header.sq_tail.store(val, Ordering::Release);
46}
47
48fn alloc_test_process() -> (
49 crate::types::Pid,
50 crate::sync::IrqMutexGuard<'static, crate::proc::ProcessManager, 0>,
51) {
52 let mut allocator = BitmapFrameAllocator;
53 let mut ptable = PROCESSES.lock();
54 let created = ptable.allocate(&mut allocator).expect("alloc process");
55 ptable.start(created).expect("start");
56 let pid = created.pid();
57 bootstrap_test_cnode(pid, &mut ptable);
58 (pid, ptable)
59}
60
61fn bootstrap_test_cnode(pid: Pid, ptable: &mut crate::proc::ProcessManager) {
62 crate::tests::helpers::bootstrap_test_cnode(pid, ptable);
63}
64
65crate::kernel_test!(
66 fn ring_nop_returns_zero() {
67 let (phys, ring_base) = setup_ring_page();
68 let (pid, ptable) = alloc_test_process();
69 drop(ptable);
70
71 unsafe {
72 write_sqe(
73 ring_base,
74 0,
75 SubmissionEntry {
76 opcode: RING_OP_NOP,
77 user_data: 42,
78 ..SubmissionEntry::zeroed()
79 },
80 );
81 set_sq_tail(ring_base, 1);
82 }
83
84 let result = crate::ring::process::ring_enter(phys, pid, 0);
85 assert!(result == Ok(1), "should process 1 entry");
86
87 let cqe = unsafe { read_cqe(ring_base, 0) };
88 assert!(cqe.result == 0, "NOP should return 0");
89 assert!(cqe.user_data == 42, "user_data should be preserved");
90
91 let mut ptable = PROCESSES.lock();
92 ptable.destroy(pid, &mut BitmapFrameAllocator);
93 drop(ptable);
94 teardown_ring_page(phys);
95 }
96);
97
98crate::kernel_test!(
99 fn ring_cap_create_via_ring() {
100 let (phys, ring_base) = setup_ring_page();
101 let (pid, ptable) = alloc_test_process();
102
103 let (cnode_id, cnode_gen, depth, gv, gb) =
104 cnode::cnode_coords(pid, &ptable).expect("cnode coords");
105 {
106 let mut pool = POOL.lock_after(&ptable);
107 ops::create_via_cnode(
108 &mut pool,
109 cnode_id,
110 cnode_gen,
111 10,
112 depth,
113 gv,
114 gb,
115 ObjectTag::Endpoint,
116 )
117 .expect("pre-create endpoint cap");
118 }
119
120 drop(ptable);
121
122 unsafe {
123 write_sqe(
124 ring_base,
125 0,
126 SubmissionEntry {
127 opcode: RING_OP_CAP_CREATE,
128 arg0: ObjectTag::Endpoint as u8 as u64,
129 cap_slot: 20,
130 user_data: 100,
131 ..SubmissionEntry::zeroed()
132 },
133 );
134 set_sq_tail(ring_base, 1);
135 }
136
137 let result = crate::ring::process::ring_enter(phys, pid, 0);
138 assert!(result == Ok(1), "should process 1 entry");
139
140 let cqe = unsafe { read_cqe(ring_base, 0) };
141 assert!(
142 cqe.result >= 0,
143 "cap_create should succeed, got {}",
144 cqe.result
145 );
146 assert!(cqe.user_data == 100, "user_data should be preserved");
147
148 let mut ptable = PROCESSES.lock();
149 ptable.destroy(pid, &mut BitmapFrameAllocator);
150 drop(ptable);
151 teardown_ring_page(phys);
152 }
153);
154
155crate::kernel_test!(
156 fn ring_invalid_opcode_returns_error() {
157 let (phys, ring_base) = setup_ring_page();
158 let (pid, ptable) = alloc_test_process();
159 drop(ptable);
160
161 unsafe {
162 write_sqe(
163 ring_base,
164 0,
165 SubmissionEntry {
166 opcode: 0xFF,
167 user_data: 7,
168 ..SubmissionEntry::zeroed()
169 },
170 );
171 set_sq_tail(ring_base, 1);
172 }
173
174 let result = crate::ring::process::ring_enter(phys, pid, 0);
175 assert!(result == Ok(1), "should still process the entry");
176
177 let cqe = unsafe { read_cqe(ring_base, 0) };
178 assert!(cqe.result < 0, "invalid opcode should return error");
179 assert!(cqe.user_data == 7, "user_data should be preserved");
180
181 let mut ptable = PROCESSES.lock();
182 ptable.destroy(pid, &mut BitmapFrameAllocator);
183 drop(ptable);
184 teardown_ring_page(phys);
185 }
186);
187
188crate::kernel_test!(
189 fn ring_empty_sq_returns_zero() {
190 let (phys, _ring_base) = setup_ring_page();
191 let (pid, ptable) = alloc_test_process();
192 drop(ptable);
193
194 let result = crate::ring::process::ring_enter(phys, pid, 0);
195 assert!(result == Ok(0), "empty SQ should return 0 completions");
196
197 let mut ptable = PROCESSES.lock();
198 ptable.destroy(pid, &mut BitmapFrameAllocator);
199 drop(ptable);
200 teardown_ring_page(phys);
201 }
202);
203
204crate::kernel_test!(
205 fn ring_cq_full_limits_processing() {
206 let (phys, ring_base) = setup_ring_page();
207 let (pid, ptable) = alloc_test_process();
208 drop(ptable);
209
210 let header = unsafe { &*(ring_base as *const RingHeader) };
211 header.cq_head.store(0, Ordering::Release);
212 header
213 .cq_tail
214 .store(crate::ring::MAX_CQ_ENTRIES, Ordering::Release);
215
216 {
217 let mut ptable = PROCESSES.lock();
218 let exec = ptable.exec_mut(pid).expect("get exec");
219 exec.ring_cq_tail = RingIndex::new(crate::ring::MAX_CQ_ENTRIES);
220 }
221
222 unsafe {
223 write_sqe(
224 ring_base,
225 0,
226 SubmissionEntry {
227 opcode: RING_OP_NOP,
228 user_data: 1,
229 ..SubmissionEntry::zeroed()
230 },
231 );
232 set_sq_tail(ring_base, 1);
233 }
234
235 let result = crate::ring::process::ring_enter(phys, pid, 0);
236 assert!(result == Ok(0), "should process 0 entries when CQ is full");
237
238 let mut ptable = PROCESSES.lock();
239 ptable.destroy(pid, &mut BitmapFrameAllocator);
240 drop(ptable);
241 teardown_ring_page(phys);
242 }
243);
244
245crate::kernel_test!(
246 fn ring_batch_cap_limits_processing() {
247 let (phys, ring_base) = setup_ring_page();
248 let (pid, ptable) = alloc_test_process();
249 drop(ptable);
250
251 (0..32u32).for_each(|i| unsafe {
252 write_sqe(
253 ring_base,
254 i,
255 SubmissionEntry {
256 opcode: RING_OP_NOP,
257 user_data: i,
258 ..SubmissionEntry::zeroed()
259 },
260 );
261 });
262 unsafe { set_sq_tail(ring_base, 32) };
263
264 let result = crate::ring::process::ring_enter(phys, pid, 0).expect("ring_enter");
265 assert!(
266 result <= 16,
267 "batch cap should limit to MAX_RING_BATCH (16), got {}",
268 result
269 );
270
271 let mut ptable = PROCESSES.lock();
272 ptable.destroy(pid, &mut BitmapFrameAllocator);
273 drop(ptable);
274 teardown_ring_page(phys);
275 }
276);
277
278crate::kernel_test!(
279 fn ring_notify_signal_and_poll_via_ring() {
280 let (phys, ring_base) = setup_ring_page();
281 let (pid, ptable) = alloc_test_process();
282
283 let (cnode_id, cnode_gen, depth, gv, gb) =
284 cnode::cnode_coords(pid, &ptable).expect("cnode coords");
285 {
286 let mut pool = POOL.lock_after(&ptable);
287 ops::create_via_cnode(
288 &mut pool,
289 cnode_id,
290 cnode_gen,
291 5,
292 depth,
293 gv,
294 gb,
295 ObjectTag::Notification,
296 )
297 .expect("create notification");
298 }
299 drop(ptable);
300
301 unsafe {
302 write_sqe(
303 ring_base,
304 0,
305 SubmissionEntry {
306 opcode: RING_OP_NOTIFY_SIGNAL,
307 cap_slot: 5,
308 arg0: 0xFF,
309 user_data: 200,
310 ..SubmissionEntry::zeroed()
311 },
312 );
313 write_sqe(
314 ring_base,
315 1,
316 SubmissionEntry {
317 opcode: RING_OP_NOTIFY_POLL,
318 cap_slot: 5,
319 user_data: 201,
320 ..SubmissionEntry::zeroed()
321 },
322 );
323 set_sq_tail(ring_base, 2);
324 }
325
326 let result = crate::ring::process::ring_enter(phys, pid, 0);
327 assert!(result == Ok(2), "should process 2 entries");
328
329 let cqe_signal = unsafe { read_cqe(ring_base, 0) };
330 assert!(cqe_signal.result == 0, "signal should succeed");
331 assert!(cqe_signal.user_data == 200);
332
333 let cqe_poll = unsafe { read_cqe(ring_base, 1) };
334 assert!(cqe_poll.result == 0, "poll should succeed");
335 assert!(
336 cqe_poll.extra == 0xFF,
337 "poll should return signaled bits, got {:#x}",
338 cqe_poll.extra
339 );
340 assert!(cqe_poll.user_data == 201);
341
342 let mut ptable = PROCESSES.lock();
343 ptable.destroy(pid, &mut BitmapFrameAllocator);
344 drop(ptable);
345 teardown_ring_page(phys);
346 }
347);