use core::{arch::asm, mem::offset_of};
use crate::virtualization::{GuestPhysAddr, GuestVirtAddr};
#[derive(Debug, Default, Clone, Copy)]
#[repr(C)]
pub struct GuestAccessFault {
pub scause: usize,
pub stval: usize,
pub htval: usize,
}
#[derive(Debug, Clone, Copy)]
pub enum GuestPrivilege {
User,
Supervisor,
}
core::arch::global_asm!(include_str!("../entry/guest_memory.S"),
fault_scause = const offset_of!(GuestAccessFault, scause),
fault_stval = const offset_of!(GuestAccessFault, stval),
fault_htval = const offset_of!(GuestAccessFault, htval),
);
unsafe extern "C" {
fn __ax_cpu_copy_from_guest(dst: *mut u8, gva: usize, length: usize) -> usize;
fn __ax_cpu_copy_to_guest(gva: usize, src: *const u8, length: usize) -> usize;
fn __ax_cpu_fetch_guest_instruction(
gva: usize,
instruction: *mut u32,
fault: *mut GuestAccessFault,
) -> usize;
}
pub unsafe fn copy_from_guest_virtual(
dst: &mut [u8],
address: GuestVirtAddr,
privilege: GuestPrivilege,
) -> usize {
let _access = unsafe { AccessScope::new(privilege, false) };
unsafe { __ax_cpu_copy_from_guest(dst.as_mut_ptr(), address.as_usize(), dst.len()) }
}
pub unsafe fn copy_to_guest_virtual(
src: &[u8],
address: GuestVirtAddr,
privilege: GuestPrivilege,
) -> usize {
let _access = unsafe { AccessScope::new(privilege, false) };
unsafe { __ax_cpu_copy_to_guest(address.as_usize(), src.as_ptr(), src.len()) }
}
pub unsafe fn copy_from_guest_physical(dst: &mut [u8], address: GuestPhysAddr) -> usize {
let _access = unsafe { AccessScope::new(GuestPrivilege::Supervisor, true) };
unsafe { __ax_cpu_copy_from_guest(dst.as_mut_ptr(), address.as_usize(), dst.len()) }
}
pub unsafe fn copy_to_guest_physical(src: &[u8], address: GuestPhysAddr) -> usize {
let _access = unsafe { AccessScope::new(GuestPrivilege::Supervisor, true) };
unsafe { __ax_cpu_copy_to_guest(address.as_usize(), src.as_ptr(), src.len()) }
}
pub unsafe fn fetch_guest_instruction(
address: GuestVirtAddr,
privilege: GuestPrivilege,
) -> Result<u32, GuestAccessFault> {
let _access = unsafe { AccessScope::new(privilege, false) };
let mut instruction = 0;
let mut fault = GuestAccessFault::default();
let result = unsafe {
__ax_cpu_fetch_guest_instruction(address.as_usize(), &mut instruction, &mut fault)
};
if result == 0 {
Ok(instruction)
} else {
Err(fault)
}
}
struct AccessScope {
host_hstatus: usize,
host_vsatp: Option<usize>,
irq: bool,
}
impl AccessScope {
unsafe fn new(privilege: GuestPrivilege, physical: bool) -> Self {
let irq = crate::interrupt::irqs_enabled();
crate::interrupt::disable_irqs();
let host_hstatus: usize;
unsafe {
asm!("csrr {}, hstatus", out(reg) host_hstatus, options(nostack));
}
let guest_hstatus = match privilege {
GuestPrivilege::User => host_hstatus & !(1 << 8),
GuestPrivilege::Supervisor => host_hstatus | (1 << 8),
};
unsafe {
asm!("csrw hstatus, {}", in(reg) guest_hstatus, options(nostack));
}
let host_vsatp = if physical {
let saved;
unsafe {
asm!("csrrw {}, vsatp, zero", out(reg) saved, options(nostack));
fence_vs();
}
Some(saved)
} else {
None
};
Self {
host_hstatus,
host_vsatp,
irq,
}
}
}
impl Drop for AccessScope {
fn drop(&mut self) {
if let Some(vsatp) = self.host_vsatp {
unsafe {
asm!("csrw vsatp, {}", in(reg) vsatp, options(nostack));
fence_vs();
}
}
unsafe {
asm!("csrw hstatus, {}", in(reg) self.host_hstatus, options(nostack));
}
if self.irq {
crate::interrupt::enable_irqs();
}
}
}
unsafe fn fence_vs() {
unsafe {
asm!(
".option push",
".option arch, +h",
"hfence.vvma",
".option pop",
options(nostack)
);
}
}