|
| 1 | +use axaddrspace::device::AccessWidth; |
| 2 | +use axaddrspace::{GuestPhysAddr, GuestPhysAddrRange}; |
| 3 | +use axdevice::{AxVmDeviceConfig, AxVmDevices}; |
| 4 | +use axdevice_base::BaseDeviceOps; |
| 5 | +use axerrno::AxResult; |
| 6 | +use axvmconfig::EmulatedDeviceType; |
| 7 | +use std::sync::{Arc, Mutex}; |
| 8 | + |
| 9 | +struct MockMmioDevice { |
| 10 | + name: String, |
| 11 | + range: GuestPhysAddrRange, |
| 12 | + last_write: Mutex<Option<(usize, usize)>>, |
| 13 | +} |
| 14 | + |
| 15 | +impl MockMmioDevice { |
| 16 | + fn new(name: &str, base: usize, len: usize) -> Self { |
| 17 | + let start = GuestPhysAddr::from(base); |
| 18 | + let end = GuestPhysAddr::from(base + len); |
| 19 | + |
| 20 | + Self { |
| 21 | + name: String::from(name), |
| 22 | + range: GuestPhysAddrRange::new(start, end), |
| 23 | + last_write: Mutex::new(None), |
| 24 | + } |
| 25 | + } |
| 26 | + |
| 27 | + fn get_last_write(&self) -> Option<(usize, usize)> { |
| 28 | + *self.last_write.lock().unwrap() |
| 29 | + } |
| 30 | +} |
| 31 | + |
| 32 | +impl BaseDeviceOps<GuestPhysAddrRange> for MockMmioDevice { |
| 33 | + fn address_range(&self) -> GuestPhysAddrRange { |
| 34 | + self.range |
| 35 | + } |
| 36 | + |
| 37 | + fn emu_type(&self) -> EmulatedDeviceType { |
| 38 | + EmulatedDeviceType::IVCChannel |
| 39 | + } |
| 40 | + |
| 41 | + fn handle_read(&self, _addr: GuestPhysAddr, _width: AccessWidth) -> AxResult<usize> { |
| 42 | + Ok(0xDEAD_BEEF) |
| 43 | + } |
| 44 | + |
| 45 | + fn handle_write(&self, addr: GuestPhysAddr, _width: AccessWidth, val: usize) -> AxResult { |
| 46 | + println!( |
| 47 | + "[Test] Device {} write: addr={:?}, val={:#x}", |
| 48 | + self.name, addr, val |
| 49 | + ); |
| 50 | + |
| 51 | + let offset = addr.as_usize() - self.range.start.as_usize(); |
| 52 | + *self.last_write.lock().unwrap() = Some((offset, val)); |
| 53 | + Ok(()) |
| 54 | + } |
| 55 | +} |
| 56 | + |
| 57 | +#[test] |
| 58 | +fn test_mmio_dispatch_functionality() { |
| 59 | + let config = AxVmDeviceConfig::new(vec![]); |
| 60 | + let mut devices = AxVmDevices::new(config); |
| 61 | + |
| 62 | + let base_addr = 0x1000_0000; |
| 63 | + let dev_size = 0x1000; |
| 64 | + let mock_dev = Arc::new(MockMmioDevice::new("TestDev", base_addr, dev_size)); |
| 65 | + |
| 66 | + devices.add_mmio_dev(mock_dev.clone()); |
| 67 | + |
| 68 | + let write_offset = 0x40; |
| 69 | + let target_addr = GuestPhysAddr::from(base_addr + write_offset); |
| 70 | + let write_val = 0x1234_5678; |
| 71 | + |
| 72 | + let width = AccessWidth::try_from(4).unwrap(); |
| 73 | + |
| 74 | + devices |
| 75 | + .handle_mmio_write(target_addr, width, write_val) |
| 76 | + .expect("MMIO write failed"); |
| 77 | + |
| 78 | + let last = mock_dev.get_last_write(); |
| 79 | + assert!(last.is_some(), "Device did not receive write command"); |
| 80 | + let (off, val) = last.unwrap(); |
| 81 | + assert_eq!(off, write_offset, "Write offset mismatch"); |
| 82 | + assert_eq!(val, write_val, "Write value mismatch"); |
| 83 | + |
| 84 | + let read_result = devices |
| 85 | + .handle_mmio_read(target_addr, width) |
| 86 | + .expect("MMIO read failed"); |
| 87 | + |
| 88 | + assert_eq!(read_result, 0xDEAD_BEEF, "Read value mismatch"); |
| 89 | +} |
| 90 | + |
| 91 | +#[test] |
| 92 | +#[should_panic(expected = "emu_device not found")] |
| 93 | +fn test_mmio_panic_on_missing_device() { |
| 94 | + let config = AxVmDeviceConfig::new(vec![]); |
| 95 | + let devices = AxVmDevices::new(config); |
| 96 | + |
| 97 | + let invalid_addr = GuestPhysAddr::from(0x9999_9999); |
| 98 | + let width = AccessWidth::try_from(4).unwrap(); |
| 99 | + |
| 100 | + let _ = devices.handle_mmio_read(invalid_addr, width); |
| 101 | +} |
0 commit comments