Nothing to see here, move along meow
0

Configure Feed

Select the types of activity you want to include in your feed.

at main 22 kB View raw
1use crate::cap::cnode; 2use crate::cap::object::ObjectTag; 3use crate::cap::pool::POOL; 4use crate::cap::table::{CapRef, Rights}; 5use crate::error::KernelError; 6use crate::mem::phys::BitmapFrameAllocator; 7use crate::pci::{BarInfo, DEVICE_TABLE}; 8use crate::proc::PROCESSES; 9use crate::types::Pid; 10use lancer_core::header::KernelObjectHeader; 11use lancer_core::object_layout::{KernelObject, PciDeviceObject}; 12use x86_64::structures::paging::{PhysFrame, Size4KiB}; 13use x86_64::{PhysAddr, VirtAddr}; 14 15use crate::proc::address_space::{map_fb_page_inner, unmap_user_page}; 16 17fn find_virtio_net_idx() -> Option<usize> { 18 let table = DEVICE_TABLE.lock(); 19 table 20 .iter() 21 .enumerate() 22 .find(|(_, dev)| dev.vendor_id == 0x1AF4 && dev.class_code == 0x02) 23 .map(|(i, _)| i) 24} 25 26fn find_first_memory_bar(idx: usize) -> Option<(usize, u64, u64)> { 27 let table = DEVICE_TABLE.lock(); 28 let dev = table.get(idx)?; 29 dev.bars 30 .iter() 31 .enumerate() 32 .find_map(|(bar_idx, bar)| match bar { 33 BarInfo::Memory { 34 phys_base, size, .. 35 } => Some((bar_idx, *phys_base, *size)), 36 _ => None, 37 }) 38} 39 40fn bootstrap_test_cnode(pid: Pid, ptable: &mut crate::proc::ProcessManager) { 41 crate::tests::helpers::bootstrap_test_cnode(pid, ptable); 42} 43 44fn setup_process_with_pci_cap(device_table_idx: u8) -> (Pid, u64) { 45 let mut allocator = BitmapFrameAllocator; 46 let mut ptable = PROCESSES.lock(); 47 let created = ptable.allocate(&mut allocator).expect("alloc process"); 48 ptable.start(created).expect("start"); 49 let pid = created.pid(); 50 bootstrap_test_cnode(pid, &mut ptable); 51 52 let (cnode_id, cnode_gen, depth, gv, gb) = 53 cnode::cnode_coords(pid, &ptable).expect("cnode coords"); 54 let address = 0u64; 55 { 56 let mut pool = POOL.lock_after(&ptable); 57 let header = KernelObjectHeader::new(ObjectTag::PciDevice, 0, 64); 58 let mut pci_obj = PciDeviceObject::init_default(header); 59 pci_obj.device_table_idx = device_table_idx; 60 let (obj_id, obj_gen) = 61 crate::tests::helpers::alloc_typed(&mut pool, ObjectTag::PciDevice, pci_obj) 62 .expect("alloc pci"); 63 let cap = CapRef::new(ObjectTag::PciDevice, obj_id, Rights::ALL, obj_gen); 64 cnode::resolve_and_insert(&pool, cnode_id, cnode_gen, address, depth, gv, gb, cap) 65 .expect("insert pci cap"); 66 } 67 68 (pid, address) 69} 70 71crate::kernel_test!( 72 fn pci_discovers_devices() { 73 let table = DEVICE_TABLE.lock(); 74 assert!( 75 !table.is_empty(), 76 "PCI enumeration found 0 devices (Q35 has built-in devices)" 77 ); 78 } 79); 80 81crate::kernel_test!( 82 fn pci_finds_virtio_net() { 83 let table = DEVICE_TABLE.lock(); 84 let found = table 85 .iter() 86 .any(|dev| dev.vendor_id == 0x1AF4 && dev.class_code == 0x02); 87 assert!( 88 found, 89 "virtio-net device (vendor=1AF4 class=02) not found in PCI device table" 90 ); 91 } 92); 93 94crate::kernel_test!( 95 fn pci_virtio_net_has_io_bar() { 96 let table = DEVICE_TABLE.lock(); 97 let dev = table 98 .iter() 99 .find(|d| d.vendor_id == 0x1AF4 && d.class_code == 0x02) 100 .expect("virtio-net device must exist"); 101 102 let has_io = dev.bars.iter().any(|bar| matches!(bar, BarInfo::Io { .. })); 103 assert!( 104 has_io, 105 "transitional virtio-net (device_id=0x1000) should have an IO BAR for legacy interface" 106 ); 107 } 108); 109 110crate::kernel_test!( 111 fn pci_virtio_net_has_memory_bar() { 112 let idx = find_virtio_net_idx().expect("virtio-net must exist"); 113 let (bar_idx, phys_base, size) = 114 find_first_memory_bar(idx).expect("virtio-net should have at least one Memory BAR"); 115 116 assert!( 117 phys_base != 0, 118 "BAR{} phys_base should be non-zero", 119 bar_idx 120 ); 121 assert!(size > 0, "BAR{} size should be non-zero", bar_idx); 122 assert!( 123 phys_base & 0xF == 0, 124 "BAR{} phys_base should be 16-byte aligned", 125 bar_idx 126 ); 127 } 128); 129 130crate::kernel_test!( 131 fn pci_device_cap_stores_table_idx() { 132 let idx = find_virtio_net_idx().expect("virtio-net must exist") as u8; 133 let (pid, address) = setup_process_with_pci_cap(idx); 134 135 let ptable = PROCESSES.lock(); 136 let pool = POOL.lock_after(&ptable); 137 let (cnode_id, cnode_gen, depth, gv, gb) = 138 cnode::cnode_coords(pid, &ptable).expect("cnode coords"); 139 let cap = cnode::resolve_and_validate( 140 &pool, 141 cnode_id, 142 cnode_gen, 143 address, 144 depth, 145 gv, 146 gb, 147 ObjectTag::PciDevice, 148 Rights::READ, 149 ) 150 .expect("validate pci cap"); 151 152 let pci_data = pool 153 .read_as::<PciDeviceObject>(cap.phys(), cap.generation()) 154 .expect("retrieve pci data"); 155 156 assert!( 157 pci_data.device_table_idx == idx, 158 "stored idx {} != expected {}", 159 pci_data.device_table_idx, 160 idx 161 ); 162 drop(pool); 163 drop(ptable); 164 165 let mut ptable = PROCESSES.lock(); 166 ptable.destroy(pid, &mut BitmapFrameAllocator); 167 } 168); 169 170crate::kernel_test!( 171 fn pci_bar_map_mmio_pages() { 172 let idx = find_virtio_net_idx().expect("virtio-net must exist"); 173 let (phys_base, size) = find_first_memory_bar(idx) 174 .map(|(_, base, sz)| (base, sz)) 175 .expect("need a Memory BAR"); 176 let page_count = size.div_ceil(4096) as usize; 177 let (pid, _address) = setup_process_with_pci_cap(idx as u8); 178 179 let pml4_phys = { 180 let ptable = PROCESSES.lock(); 181 ptable.exec(pid).unwrap().pml4_phys 182 }; 183 let mut allocator = BitmapFrameAllocator; 184 let base_vaddr = 0x0000_2000_0000_0000u64; 185 186 let mapped = (0..page_count).try_fold(0usize, |count, i| { 187 let phys = PhysAddr::new(phys_base + (i as u64) * 4096); 188 let virt = VirtAddr::new(base_vaddr + (i as u64) * 4096); 189 let frame = PhysFrame::<Size4KiB>::containing_address(phys); 190 match map_fb_page_inner(pml4_phys, virt, frame, &mut allocator) { 191 Ok(()) => Ok(count + 1), 192 Err(e) => Err((e, count)), 193 } 194 }); 195 196 assert!( 197 mapped.is_ok(), 198 "mapping Memory BAR pages should succeed, got {:?}", 199 mapped 200 ); 201 assert!( 202 mapped.unwrap() == page_count, 203 "should have mapped {} pages", 204 page_count 205 ); 206 207 (0..page_count).for_each(|i| { 208 let virt = VirtAddr::new(base_vaddr + (i as u64) * 4096); 209 let _ = unmap_user_page(pml4_phys, virt); 210 }); 211 212 let mut ptable = PROCESSES.lock(); 213 ptable.destroy(pid, &mut BitmapFrameAllocator); 214 } 215); 216 217crate::kernel_test!( 218 fn pci_bar_unmap_returns_correct_frames() { 219 let idx = find_virtio_net_idx().expect("virtio-net must exist"); 220 let (phys_base, size) = find_first_memory_bar(idx) 221 .map(|(_, base, sz)| (base, sz)) 222 .expect("need a Memory BAR"); 223 let page_count = size.div_ceil(4096) as usize; 224 let (pid, _address) = setup_process_with_pci_cap(idx as u8); 225 226 let pml4_phys = { 227 let ptable = PROCESSES.lock(); 228 ptable.exec(pid).unwrap().pml4_phys 229 }; 230 let mut allocator = BitmapFrameAllocator; 231 let base_vaddr = 0x0000_3000_0000_0000u64; 232 233 (0..page_count).for_each(|i| { 234 let phys = PhysAddr::new(phys_base + (i as u64) * 4096); 235 let virt = VirtAddr::new(base_vaddr + (i as u64) * 4096); 236 let frame = PhysFrame::<Size4KiB>::containing_address(phys); 237 map_fb_page_inner(pml4_phys, virt, frame, &mut allocator).expect("map should succeed"); 238 }); 239 240 (0..page_count).for_each(|i| { 241 let expected_phys = PhysAddr::new(phys_base + (i as u64) * 4096); 242 let virt = VirtAddr::new(base_vaddr + (i as u64) * 4096); 243 let returned_frame = unmap_user_page(pml4_phys, virt).expect("unmap should succeed"); 244 assert!( 245 returned_frame.start_address() == expected_phys, 246 "unmap page {} returned phys {:#x}, expected {:#x}", 247 i, 248 returned_frame.start_address().as_u64(), 249 expected_phys.as_u64(), 250 ); 251 }); 252 253 let mut ptable = PROCESSES.lock(); 254 ptable.destroy(pid, &mut BitmapFrameAllocator); 255 } 256); 257 258crate::kernel_test!( 259 fn pci_bar_unmap_nonexistent_fails() { 260 let mut allocator = BitmapFrameAllocator; 261 let mut ptable = PROCESSES.lock(); 262 let created = ptable.allocate(&mut allocator).expect("alloc process"); 263 ptable.start(created).expect("start"); 264 let pid = created.pid(); 265 let pml4_phys = ptable.exec(pid).unwrap().pml4_phys; 266 drop(ptable); 267 268 let virt = VirtAddr::new(0x0000_5000_0000_0000); 269 let result = unmap_user_page(pml4_phys, virt); 270 assert!( 271 result == Err(KernelError::InvalidAddress), 272 "unmapping a never-mapped page should fail" 273 ); 274 275 let mut ptable = PROCESSES.lock(); 276 ptable.destroy(pid, &mut BitmapFrameAllocator); 277 } 278); 279 280crate::kernel_test!( 281 fn pci_bar_map_double_map_fails() { 282 let idx = find_virtio_net_idx().expect("virtio-net must exist"); 283 let (phys_base, _) = find_first_memory_bar(idx) 284 .map(|(_, base, sz)| (base, sz)) 285 .expect("need a Memory BAR"); 286 let (pid, _address) = setup_process_with_pci_cap(idx as u8); 287 288 let pml4_phys = { 289 let ptable = PROCESSES.lock(); 290 ptable.exec(pid).unwrap().pml4_phys 291 }; 292 let mut allocator = BitmapFrameAllocator; 293 let vaddr = VirtAddr::new(0x0000_4000_0000_0000); 294 let frame = PhysFrame::<Size4KiB>::containing_address(PhysAddr::new(phys_base)); 295 296 map_fb_page_inner(pml4_phys, vaddr, frame, &mut allocator) 297 .expect("first map should succeed"); 298 299 let second = map_fb_page_inner(pml4_phys, vaddr, frame, &mut allocator); 300 assert!(second.is_err(), "mapping the same vaddr twice should fail"); 301 302 let _ = unmap_user_page(pml4_phys, vaddr); 303 304 let mut ptable = PROCESSES.lock(); 305 ptable.destroy(pid, &mut BitmapFrameAllocator); 306 } 307); 308 309fn make_test_pci_device( 310 ranges: [Option<crate::pci::device::BlockedRange>; 4], 311) -> crate::pci::PciDeviceInfo { 312 crate::pci::PciDeviceInfo { 313 bus: 0, 314 device: 0, 315 function: 0, 316 vendor_id: 0, 317 device_id: 0, 318 class_code: 0, 319 subclass: 0, 320 prog_if: 0, 321 header_type: 0, 322 interrupt_line: 0, 323 interrupt_pin: 0, 324 bars: [BarInfo::None; 6], 325 blocked_config_ranges: ranges, 326 msix_cap: None, 327 } 328} 329 330crate::kernel_test!( 331 fn pci_config_write_rejects_command_register() { 332 let dev = make_test_pci_device([None; 4]); 333 assert!( 334 !crate::syscall::pci::is_config_write_safe(0x04, &dev), 335 "command register (0x04) must be blocked" 336 ); 337 } 338); 339 340crate::kernel_test!( 341 fn pci_config_write_rejects_bar_offsets() { 342 let dev = make_test_pci_device([None; 4]); 343 [0x10u16, 0x14, 0x18, 0x1C, 0x20, 0x24] 344 .iter() 345 .for_each(|&offset| { 346 assert!( 347 !crate::syscall::pci::is_config_write_safe(offset, &dev), 348 "BAR offset {:#x} must be blocked", 349 offset 350 ); 351 }); 352 } 353); 354 355crate::kernel_test!( 356 fn pci_config_write_rejects_expansion_rom() { 357 let dev = make_test_pci_device([None; 4]); 358 assert!( 359 !crate::syscall::pci::is_config_write_safe(0x30, &dev), 360 "expansion ROM (0x30) must be blocked" 361 ); 362 } 363); 364 365crate::kernel_test!( 366 fn pci_config_write_rejects_msi_range() { 367 use crate::pci::device::BlockedRange; 368 let dev = make_test_pci_device([ 369 Some(BlockedRange { 370 start: 0x40, 371 end: 0x4E, 372 }), 373 None, 374 None, 375 None, 376 ]); 377 assert!( 378 !crate::syscall::pci::is_config_write_safe(0x40, &dev), 379 "MSI start (0x40) must be blocked" 380 ); 381 assert!( 382 !crate::syscall::pci::is_config_write_safe(0x48, &dev), 383 "MSI mid (0x48) must be blocked" 384 ); 385 assert!( 386 crate::syscall::pci::is_config_write_safe(0x38, &dev), 387 "offset 0x38 before MSI range should be allowed" 388 ); 389 assert!( 390 crate::syscall::pci::is_config_write_safe(0x50, &dev), 391 "offset 0x50 after MSI range should be allowed" 392 ); 393 } 394); 395 396crate::kernel_test!( 397 fn pci_config_write_allows_safe_offsets() { 398 let dev = make_test_pci_device([None; 4]); 399 assert!( 400 crate::syscall::pci::is_config_write_safe(0x3C, &dev), 401 "interrupt line (0x3C) should be allowed" 402 ); 403 } 404); 405 406crate::kernel_test!( 407 fn pci_bar_mapping_tracks_duplicates() { 408 use crate::pci::{BAR_MAPPINGS, BarMappingEntry}; 409 410 let pid = Pid::new(1); 411 let entry = BarMappingEntry { 412 pid, 413 device_idx: 5, 414 bar_idx: 2, 415 base_vaddr: 0x2000_0000, 416 }; 417 418 let mut mappings = BAR_MAPPINGS.lock(); 419 let _ = mappings.push(entry); 420 421 assert!( 422 mappings.iter().any(|e| e.matches(pid, 5, 2)), 423 "should find matching entry" 424 ); 425 assert!( 426 !mappings.iter().any(|e| e.matches(pid, 5, 3)), 427 "different bar_idx should not match" 428 ); 429 assert!( 430 !mappings.iter().any(|e| e.matches(pid, 6, 2)), 431 "different device_idx should not match" 432 ); 433 assert!( 434 !mappings.iter().any(|e| e.matches(Pid::new(2), 5, 2)), 435 "different pid should not match" 436 ); 437 438 let pos = mappings 439 .iter() 440 .enumerate() 441 .find(|(_, e)| e.matches(pid, 5, 2)) 442 .map(|(i, _)| i); 443 if let Some(i) = pos { 444 mappings.swap_remove(i); 445 } 446 } 447); 448 449crate::kernel_test!( 450 fn pci_device_info_wire_roundtrip() { 451 use lancer_core::pci::*; 452 use zerocopy::IntoBytes; 453 454 let table = DEVICE_TABLE.lock(); 455 table.iter().for_each(|dev| { 456 let wire = dev.to_wire(); 457 assert!(wire.bus == dev.bus, "bus mismatch"); 458 assert!(wire.device == dev.device, "device mismatch"); 459 assert!(wire.function == dev.function, "function mismatch"); 460 assert!(wire.vendor_id == dev.vendor_id, "vendor_id mismatch"); 461 assert!(wire.device_id == dev.device_id, "device_id mismatch"); 462 assert!(wire.class_code == dev.class_code, "class_code mismatch"); 463 assert!(wire.subclass == dev.subclass, "subclass mismatch"); 464 assert!(wire.prog_if == dev.prog_if, "prog_if mismatch"); 465 assert!(wire.header_type == dev.header_type, "header_type mismatch"); 466 assert!( 467 wire.interrupt_line == dev.interrupt_line, 468 "interrupt_line mismatch" 469 ); 470 assert!( 471 wire.interrupt_pin == dev.interrupt_pin, 472 "interrupt_pin mismatch" 473 ); 474 475 dev.bars.iter().zip(wire.bars.iter()).enumerate().for_each( 476 |(i, (orig, w))| match orig { 477 BarInfo::None => { 478 assert!(w.tag == BAR_TAG_NONE, "BAR{} tag should be None", i); 479 } 480 BarInfo::Memory { 481 phys_base, 482 size, 483 is_64bit, 484 prefetchable, 485 } => { 486 assert!(w.tag == BAR_TAG_MEMORY, "BAR{} tag should be Memory", i); 487 assert!(w.mem_phys_base == *phys_base, "BAR{} phys_base mismatch", i); 488 assert!(w.mem_size == *size, "BAR{} size mismatch", i); 489 assert!( 490 (w.flags & BAR_FLAG_64BIT != 0) == *is_64bit, 491 "BAR{} is_64bit mismatch", 492 i 493 ); 494 assert!( 495 (w.flags & BAR_FLAG_PREFETCHABLE != 0) == *prefetchable, 496 "BAR{} prefetchable mismatch", 497 i 498 ); 499 } 500 BarInfo::Io { port_base, size } => { 501 assert!(w.tag == BAR_TAG_IO, "BAR{} tag should be Io", i); 502 assert!(w.io_port_base == *port_base, "BAR{} port_base mismatch", i); 503 assert!(w.io_size == *size, "BAR{} io_size mismatch", i); 504 } 505 }, 506 ); 507 508 dev.blocked_config_ranges 509 .iter() 510 .zip(wire.blocked_config_ranges.iter()) 511 .for_each(|(orig, w)| match orig { 512 Some(r) => { 513 assert!(w.start == r.start, "blocked range start mismatch"); 514 assert!(w.end == r.end, "blocked range end mismatch"); 515 } 516 None => { 517 assert!(w.start == 0 && w.end == 0, "None range should be zeroed"); 518 } 519 }); 520 521 let bytes = wire.as_bytes(); 522 assert!(bytes.len() == 176, "wire bytes should be 176"); 523 }); 524 } 525); 526 527crate::kernel_test!( 528 fn pci_all_bar_types_valid() { 529 let table = DEVICE_TABLE.lock(); 530 table.iter().for_each(|dev| { 531 dev.bars.iter().enumerate().for_each(|(i, bar)| match bar { 532 BarInfo::Memory { 533 phys_base, size, .. 534 } => { 535 assert!( 536 *size > 0, 537 "device {:04x}:{:04x} BAR{} Memory with size=0", 538 dev.vendor_id, 539 dev.device_id, 540 i 541 ); 542 assert!( 543 *phys_base & 0xF == 0, 544 "device {:04x}:{:04x} BAR{} Memory phys_base not aligned", 545 dev.vendor_id, 546 dev.device_id, 547 i 548 ); 549 } 550 BarInfo::Io { size, .. } => { 551 assert!( 552 *size > 0, 553 "device {:04x}:{:04x} BAR{} IO with size=0", 554 dev.vendor_id, 555 dev.device_id, 556 i 557 ); 558 } 559 BarInfo::None => {} 560 }); 561 }); 562 } 563); 564 565crate::kernel_test!( 566 fn pci_virtio_net_has_msix_cap() { 567 let table = DEVICE_TABLE.lock(); 568 let dev = table 569 .iter() 570 .find(|d| d.vendor_id == 0x1AF4 && d.class_code == 0x02) 571 .expect("virtio-net device must exist"); 572 573 let msix = dev 574 .msix_cap 575 .expect("QEMU virtio-net should advertise MSI-X capability"); 576 577 assert!( 578 msix.table_size > 0, 579 "MSI-X table_size should be > 0, got {}", 580 msix.table_size 581 ); 582 assert!( 583 msix.table_bir < 6, 584 "MSI-X table BIR should be 0-5, got {}", 585 msix.table_bir 586 ); 587 assert!( 588 msix.cap_offset >= 0x40, 589 "MSI-X cap_offset should be >= 0x40 (past standard header), got {:#x}", 590 msix.cap_offset 591 ); 592 } 593); 594 595crate::kernel_test!( 596 fn pci_msix_table_map_and_configure() { 597 let idx = find_virtio_net_idx().expect("virtio-net must exist") as u8; 598 599 let has_msix = { 600 let table = DEVICE_TABLE.lock(); 601 table.get(idx as usize).and_then(|d| d.msix_cap).is_some() 602 }; 603 assert!(has_msix, "virtio-net should have MSI-X"); 604 605 let hhdm_offset = crate::mem::addr::hhdm_offset(); 606 let mut mapper = unsafe { crate::arch::paging::init(hhdm_offset) }; 607 let mut alloc = BitmapFrameAllocator; 608 609 let map_result = 610 crate::pci::msix::ensure_table_mapped(idx, &mut mapper, &mut alloc, hhdm_offset); 611 assert!( 612 map_result.is_ok(), 613 "ensure_table_mapped should succeed, got {:?}", 614 map_result, 615 ); 616 617 let vector = crate::arch::idt::IrqVector::new(38); 618 let cfg_result = crate::pci::msix::configure_entry(idx, 0, vector, 0); 619 assert!( 620 cfg_result.is_ok(), 621 "configure_entry should succeed, got {:?}", 622 cfg_result, 623 ); 624 625 let unmask_result = crate::pci::msix::unmask_entry(idx, 0); 626 assert!( 627 unmask_result.is_ok(), 628 "unmask_entry should succeed, got {:?}", 629 unmask_result, 630 ); 631 } 632); 633 634crate::kernel_test!( 635 fn pci_msix_invalid_device_idx_fails() { 636 let result = crate::pci::msix::unmask_entry(255, 0); 637 assert!( 638 result.is_err(), 639 "unmask_entry on non-existent device index should fail" 640 ); 641 } 642);