/// Trait for all guest memory access. Every load/store goes through this, /// enabling MMIO checking and debugger observation on every access. /// This is the key abstraction that eliminates the need for MMIO exception handlers. pub trait MemoryAccess { fn read_u8(&self, addr: u32) -> u8; fn read_u16(&self, addr: u32) -> u16; fn read_u32(&self, addr: u32) -> u32; fn read_u64(&self, addr: u32) -> u64; fn read_f32(&self, addr: u32) -> f32 { f32::from_bits(self.read_u32(addr)) } fn read_f64(&self, addr: u32) -> f64 { f64::from_bits(self.read_u64(addr)) } fn write_u8(&mut self, addr: u32, val: u8); fn write_u16(&mut self, addr: u32, val: u16); fn write_u32(&mut self, addr: u32, val: u32); fn write_u64(&mut self, addr: u32, val: u64); fn write_f32(&mut self, addr: u32, val: f32) { self.write_u32(addr, val.to_bits()); } fn write_f64(&mut self, addr: u32, val: f64) { self.write_u64(addr, val.to_bits()); } /// Read a block of bytes from guest memory. fn read_bytes(&self, addr: u32, buf: &mut [u8]) { for (i, byte) in buf.iter_mut().enumerate() { *byte = self.read_u8(addr.wrapping_add(i as u32)); } } /// Write a block of bytes to guest memory. fn write_bytes(&mut self, addr: u32, buf: &[u8]) { for (i, &byte) in buf.iter().enumerate() { self.write_u8(addr.wrapping_add(i as u32), byte); } } /// Get a direct host pointer for the given guest address. /// Returns None if the address is invalid or in an MMIO region. fn translate(&self, addr: u32) -> Option<*const u8>; /// Get a mutable direct host pointer for the given guest address. fn translate_mut(&mut self, addr: u32) -> Option<*mut u8>; }