Nothing to see here, move along
at main 347 lines 11 kB view raw
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);