use ax_memory_addr::PhysAddr;
use axvm_types::GuestPhysAddr;
use crate::{AddrSpaceError, AddrSpaceResult};
pub trait GuestMemoryAccessor {
fn translate_and_get_limit(&self, guest_addr: GuestPhysAddr) -> Option<(PhysAddr, usize)>;
fn read_obj<V: Copy>(&self, guest_addr: GuestPhysAddr) -> AddrSpaceResult<V> {
let (host_addr, limit) =
self.translate_and_get_limit(guest_addr)
.ok_or(AddrSpaceError::Unmapped {
address: guest_addr,
})?;
if limit < core::mem::size_of::<V>() {
return Err(AddrSpaceError::InsufficientAccess {
operation: "read guest object",
address: guest_addr,
requested: core::mem::size_of::<V>(),
available: limit,
});
}
unsafe {
let ptr = host_addr.as_usize() as *const V;
Ok(core::ptr::read_volatile(ptr))
}
}
fn write_obj<V: Copy>(&self, guest_addr: GuestPhysAddr, val: V) -> AddrSpaceResult {
let (host_addr, limit) =
self.translate_and_get_limit(guest_addr)
.ok_or(AddrSpaceError::Unmapped {
address: guest_addr,
})?;
if limit < core::mem::size_of::<V>() {
return Err(AddrSpaceError::InsufficientAccess {
operation: "write guest object",
address: guest_addr,
requested: core::mem::size_of::<V>(),
available: limit,
});
}
unsafe {
let ptr = host_addr.as_usize() as *mut V;
core::ptr::write_volatile(ptr, val);
}
Ok(())
}
fn read_buffer(&self, guest_addr: GuestPhysAddr, buffer: &mut [u8]) -> AddrSpaceResult {
if buffer.is_empty() {
return Ok(());
}
let (host_addr, accessible_size) =
self.translate_and_get_limit(guest_addr)
.ok_or(AddrSpaceError::Unmapped {
address: guest_addr,
})?;
if accessible_size >= buffer.len() {
unsafe {
let src_ptr = host_addr.as_usize() as *const u8;
core::ptr::copy_nonoverlapping(src_ptr, buffer.as_mut_ptr(), buffer.len());
}
return Ok(());
}
let mut current_guest_addr = guest_addr;
let mut remaining_buffer = buffer;
while !remaining_buffer.is_empty() {
let (current_host_addr, current_accessible_size) = self
.translate_and_get_limit(current_guest_addr)
.ok_or(AddrSpaceError::Unmapped {
address: current_guest_addr,
})?;
if current_accessible_size == 0 {
return Err(AddrSpaceError::InsufficientAccess {
operation: "read guest buffer",
address: current_guest_addr,
requested: remaining_buffer.len(),
available: 0,
});
}
let bytes_to_read = remaining_buffer.len().min(current_accessible_size);
unsafe {
let src_ptr = current_host_addr.as_usize() as *const u8;
core::ptr::copy_nonoverlapping(
src_ptr,
remaining_buffer.as_mut_ptr(),
bytes_to_read,
);
}
current_guest_addr = advance_guest_address(current_guest_addr, bytes_to_read)?;
remaining_buffer = &mut remaining_buffer[bytes_to_read..];
}
Ok(())
}
fn write_buffer(&self, guest_addr: GuestPhysAddr, buffer: &[u8]) -> AddrSpaceResult {
if buffer.is_empty() {
return Ok(());
}
let (host_addr, accessible_size) =
self.translate_and_get_limit(guest_addr)
.ok_or(AddrSpaceError::Unmapped {
address: guest_addr,
})?;
if accessible_size >= buffer.len() {
unsafe {
let dst_ptr = host_addr.as_usize() as *mut u8;
core::ptr::copy_nonoverlapping(buffer.as_ptr(), dst_ptr, buffer.len());
}
return Ok(());
}
let mut current_guest_addr = guest_addr;
let mut remaining_buffer = buffer;
while !remaining_buffer.is_empty() {
let (current_host_addr, current_accessible_size) = self
.translate_and_get_limit(current_guest_addr)
.ok_or(AddrSpaceError::Unmapped {
address: current_guest_addr,
})?;
if current_accessible_size == 0 {
return Err(AddrSpaceError::InsufficientAccess {
operation: "write guest buffer",
address: current_guest_addr,
requested: remaining_buffer.len(),
available: 0,
});
}
let bytes_to_write = remaining_buffer.len().min(current_accessible_size);
unsafe {
let dst_ptr = current_host_addr.as_usize() as *mut u8;
core::ptr::copy_nonoverlapping(remaining_buffer.as_ptr(), dst_ptr, bytes_to_write);
}
current_guest_addr = advance_guest_address(current_guest_addr, bytes_to_write)?;
remaining_buffer = &remaining_buffer[bytes_to_write..];
}
Ok(())
}
fn read_volatile<V: Copy>(&self, guest_addr: GuestPhysAddr) -> AddrSpaceResult<V> {
self.read_obj(guest_addr)
}
fn write_volatile<V: Copy>(&self, guest_addr: GuestPhysAddr, val: V) -> AddrSpaceResult {
self.write_obj(guest_addr, val)
}
}
fn advance_guest_address(address: GuestPhysAddr, size: usize) -> AddrSpaceResult<GuestPhysAddr> {
let next = address
.as_usize()
.checked_add(size)
.ok_or(AddrSpaceError::AddressOverflow {
start: address.as_usize(),
size,
})?;
Ok(GuestPhysAddr::from_usize(next))
}