use std::sync::{Arc, Mutex};
use ax_errno::AxResult;
use axaddrspace::{GuestPhysAddr, GuestPhysAddrRange, device::AccessWidth};
use axdevice::{AxVmDeviceConfig, AxVmDevices};
use axdevice_base::BaseDeviceOps;
use axvmconfig::EmulatedDeviceType;
struct MockMmioDevice {
name: String,
range: GuestPhysAddrRange,
last_write: Mutex<Option<(usize, usize)>>,
}
impl MockMmioDevice {
fn new(name: &str, base: usize, len: usize) -> Self {
let start = GuestPhysAddr::from(base);
let end = GuestPhysAddr::from(base + len);
Self {
name: String::from(name),
range: GuestPhysAddrRange::new(start, end),
last_write: Mutex::new(None),
}
}
fn get_last_write(&self) -> Option<(usize, usize)> {
*self.last_write.lock().unwrap()
}
}
impl BaseDeviceOps<GuestPhysAddrRange> for MockMmioDevice {
fn address_range(&self) -> GuestPhysAddrRange {
self.range
}
fn emu_type(&self) -> EmulatedDeviceType {
EmulatedDeviceType::IVCChannel
}
fn handle_read(&self, _addr: GuestPhysAddr, _width: AccessWidth) -> AxResult<usize> {
Ok(0xDEAD_BEEF)
}
fn handle_write(&self, addr: GuestPhysAddr, _width: AccessWidth, val: usize) -> AxResult {
println!(
"[Test] Device {} write: addr={:?}, val={:#x}",
self.name, addr, val
);
let offset = addr.as_usize() - self.range.start.as_usize();
*self.last_write.lock().unwrap() = Some((offset, val));
Ok(())
}
}
#[test]
fn test_mmio_dispatch_functionality() {
let config = AxVmDeviceConfig::new(vec![]);
let mut devices = AxVmDevices::new(config);
let base_addr = 0x1000_0000;
let dev_size = 0x1000;
let mock_dev = Arc::new(MockMmioDevice::new("TestDev", base_addr, dev_size));
devices.add_mmio_dev(mock_dev.clone());
let write_offset = 0x40;
let target_addr = GuestPhysAddr::from(base_addr + write_offset);
let write_val = 0x1234_5678;
let width = AccessWidth::try_from(4).unwrap();
devices
.handle_mmio_write(target_addr, width, write_val)
.expect("MMIO write failed");
let last = mock_dev.get_last_write();
assert!(last.is_some(), "Device did not receive write command");
let (off, val) = last.unwrap();
assert_eq!(off, write_offset, "Write offset mismatch");
assert_eq!(val, write_val, "Write value mismatch");
let read_result = devices
.handle_mmio_read(target_addr, width)
.expect("MMIO read failed");
assert_eq!(read_result, 0xDEAD_BEEF, "Read value mismatch");
}
#[test]
#[should_panic(expected = "emu_device not found")]
fn test_mmio_panic_on_missing_device() {
let config = AxVmDeviceConfig::new(vec![]);
let devices = AxVmDevices::new(config);
let invalid_addr = GuestPhysAddr::from(0x9999_9999);
let width = AccessWidth::try_from(4).unwrap();
let _ = devices.handle_mmio_read(invalid_addr, width);
}