use core::{arch::asm, marker::PhantomData};
use super::{GuestVirtualHsCsrs, GuestVsCsrs, Vcpu, VirtualizationError};
macro_rules! read_csr {
($csr:literal) => {{ let value;
unsafe { asm!(concat!("csrr {}, ", $csr), out(reg) value, options(nostack)); }
value
}};
}
macro_rules! write_csr {
($csr:literal, $value:expr) => {{
unsafe { asm!(concat!("csrw ", $csr, ", {}"), in(reg) $value, options(nostack)); }
}};
}
#[must_use = "the hardware binding must remain alive until guest execution finishes"]
pub struct GuestBinding {
host_vs: GuestVsCsrs,
host_hs: GuestVirtualHsCsrs,
#[cfg(feature = "riscv-sstc")]
host_henvcfg: usize,
_not_send_sync: PhantomData<*mut ()>,
}
impl GuestBinding {
pub unsafe fn load(registers: &Vcpu) -> Result<Self, VirtualizationError> {
let _irqs = LocalIrqs::disable();
#[cfg(feature = "riscv-sstc")]
let host_henvcfg = {
let saved: usize = read_csr!("henvcfg");
write_csr!("henvcfg", saved | (1usize << 63));
let actual: usize = read_csr!("henvcfg");
if actual & (1usize << 63) == 0 {
write_csr!("henvcfg", saved);
return Err(VirtualizationError::UnsupportedTimer);
}
saved
};
let (host_vs, host_hs) = unsafe { capture_banks() };
let binding = Self {
host_vs,
host_hs,
#[cfg(feature = "riscv-sstc")]
host_henvcfg,
_not_send_sync: PhantomData,
};
unsafe {
install_banks(®isters.vs_csrs, ®isters.virtual_hs_csrs);
}
Ok(binding)
}
pub unsafe fn unload(self, registers: &mut Vcpu) {
let _irqs = LocalIrqs::disable();
let (vs, hs) = unsafe { capture_banks() };
registers.vs_csrs = vs;
registers.virtual_hs_csrs = hs;
drop(self);
}
}
impl Drop for GuestBinding {
fn drop(&mut self) {
let enabled = crate::interrupt::irqs_enabled();
crate::interrupt::disable_irqs();
unsafe {
install_banks(&self.host_vs, &self.host_hs);
}
#[cfg(feature = "riscv-sstc")]
write_csr!("henvcfg", self.host_henvcfg);
if enabled {
crate::interrupt::enable_irqs();
}
}
}
unsafe fn capture_banks() -> (GuestVsCsrs, GuestVirtualHsCsrs) {
(
GuestVsCsrs {
htimedelta: read_csr!("htimedelta"),
vsstatus: read_csr!("vsstatus"),
vsie: read_csr!("vsie"),
vstvec: read_csr!("vstvec"),
vsscratch: read_csr!("vsscratch"),
vsepc: read_csr!("vsepc"),
vscause: read_csr!("vscause"),
vstval: read_csr!("vstval"),
vsatp: read_csr!("vsatp"),
vstimecmp: {
#[cfg(feature = "riscv-sstc")]
{
read_csr!("vstimecmp")
}
#[cfg(not(feature = "riscv-sstc"))]
{
0
}
},
},
GuestVirtualHsCsrs {
hie: read_csr!("hie"),
hvip: read_csr!("hvip"),
hgeie: read_csr!("hgeie"),
hgatp: read_csr!("hgatp"),
},
)
}
unsafe fn install_banks(vs: &GuestVsCsrs, hs: &GuestVirtualHsCsrs) {
write_csr!("htimedelta", vs.htimedelta);
write_csr!("vsstatus", vs.vsstatus);
write_csr!("vsie", vs.vsie);
write_csr!("vstvec", vs.vstvec);
write_csr!("vsscratch", vs.vsscratch);
write_csr!("vsepc", vs.vsepc);
write_csr!("vscause", vs.vscause);
write_csr!("vstval", vs.vstval);
write_csr!("vsatp", vs.vsatp);
#[cfg(feature = "riscv-sstc")]
write_csr!("vstimecmp", vs.vstimecmp);
write_csr!("hie", hs.hie);
write_csr!("hvip", hs.hvip);
write_csr!("hgeie", hs.hgeie);
write_csr!("hgatp", hs.hgatp);
unsafe {
asm!(
".option push",
".option arch, +h",
"hfence.gvma",
"hfence.vvma",
".option pop",
options(nostack)
);
}
}
pub(super) struct LocalIrqs(bool);
impl LocalIrqs {
pub(super) fn disable() -> Self {
let enabled = crate::interrupt::irqs_enabled();
crate::interrupt::disable_irqs();
Self(enabled)
}
}
impl Drop for LocalIrqs {
fn drop(&mut self) {
if self.0 {
crate::interrupt::enable_irqs();
}
}
}