#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[repr(u64)]
pub enum ExitKind {
#[default]
Synchronous = 0,
Irq = 1,
Fiq = 2,
SystemError = 3,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct Exit {
pub kind: ExitKind,
pub syndrome: u64,
pub fault_address: u64,
pub physical_fault_address: u64,
pub pc: u64,
pub irq_ack: u32,
pub guest_address: Result<crate::virtualization::GuestPhysAddr, GuestAddressError>,
}
impl Default for Exit {
fn default() -> Self {
Self {
kind: ExitKind::Synchronous,
syndrome: 0,
fault_address: 0,
physical_fault_address: 0,
pc: 0,
irq_ack: u32::MAX,
guest_address: Err(GuestAddressError::NotDataAbort),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
pub enum GuestAddressError {
#[error("exit does not report a guest data abort")]
NotDataAbort,
#[error("guest stage-1 translation failed: PAR_EL1={par:#x}")]
TranslationFault {
par: u64,
},
}
impl Exit {
pub unsafe fn resolve_guest_address(
&self,
) -> Result<crate::virtualization::GuestPhysAddr, GuestAddressError> {
use aarch64_cpu::registers::{PAR_EL1, Readable, Writeable};
if self.kind != ExitKind::Synchronous || (self.syndrome >> 26) & 63 != 0x24 {
return Err(GuestAddressError::NotDataAbort);
}
let mut hpfar = self.physical_fault_address;
if self.syndrome & (1 << 7) == 0 && self.syndrome & 0x3c == 0x0c {
let saved = PAR_EL1.get();
unsafe {
core::arch::asm!("at s1e1r, {}", "isb", in(reg) self.fault_address, options(nostack));
}
let result = PAR_EL1.get();
PAR_EL1.set(saved);
if result & 1 != 0 {
return Err(GuestAddressError::TranslationFault { par: result });
}
hpfar = (result & 0x000f_ffff_ffff_f000) >> 8;
}
Ok(crate::virtualization::GuestPhysAddr::from_usize(
((self.fault_address & 0xfff) | (hpfar << 8)) as usize,
))
}
}