use core::fmt::Write;
use hyperlight_common::arch::exn::{DataFault, DataFaultKind, Exception, decode_syndrome};
use hyperlight_common::vmem::{
BasicMapping, CowMapping, MappingKind, PAGE_SIZE, PhysAddr, VirtAddr,
};
use hyperlight_guest::error::ErrorCode;
use hyperlight_guest::exit::write_abort;
use hyperlight_guest::layout::{MAIN_STACK_LIMIT_GVA, MAIN_STACK_TOP_GVA};
use super::super::mrs;
use super::types::*;
use crate::HyperlightAbortWriter;
fn handle_stack_fault(far: u64) {
unsafe {
let new_page = hyperlight_guest::prim_alloc::alloc_phys_pages(1);
crate::paging::map_region(
new_page,
(far & !((PAGE_SIZE - 1) as u64)) as *mut u8,
PAGE_SIZE as u64,
MappingKind::Basic(BasicMapping {
readable: true,
writable: true,
executable: false,
}),
);
core::arch::asm!("dsb sy");
}
}
fn handle_cow_fault(_orig_phys: PhysAddr, virt: VirtAddr, perms: CowMapping) {
unsafe {
let new_page = hyperlight_guest::prim_alloc::alloc_phys_pages(1);
let target_virt = virt as *mut u8;
let Some(scratch_mapping_access) = crate::paging::phys_to_virt(new_page) else {
write_abort(&[ErrorCode::GuestError as u8, 0xfeu8]);
write_abort("impossible: phys_to_virt failed on alloc_phys_pages return".as_bytes());
write_abort(&[0xFF]);
unreachable!();
};
core::ptr::copy(target_virt, scratch_mapping_access, PAGE_SIZE);
crate::paging::map_region(
new_page,
target_virt,
PAGE_SIZE as u64,
MappingKind::Basic(BasicMapping {
readable: perms.readable,
writable: true,
executable: perms.executable,
}),
);
core::arch::asm!("
dsb ish
tlbi vae1is, {}
dsb ish
isb
",
in(reg) (virt >> 12),
options(readonly, nostack, preserves_flags)
);
}
}
#[unsafe(no_mangle)]
pub extern "Rust" fn _debug_print(x: &str) {
hyperlight_guest::exit::debug_print(x);
}
fn handle_internal_fault(exn: Exception, far: u64) -> bool {
match exn {
Exception::DataFault(DataFault {
from_lower_el: false,
kind: DataFaultKind::TranslationFault(_),
..
}) => {
if (MAIN_STACK_LIMIT_GVA..MAIN_STACK_TOP_GVA).contains(&far) {
handle_stack_fault(far);
true
} else {
false
}
}
Exception::DataFault(DataFault {
from_lower_el: false,
is_write: true,
kind: DataFaultKind::PermissionFault(_),
..
}) => {
let mut orig_mappings = crate::paging::virt_to_phys(far);
if let Some(mapping) = orig_mappings.next()
&& let None = orig_mappings.next()
&& let MappingKind::Cow(cm) = mapping.kind
{
handle_cow_fault(mapping.phys_base, mapping.virt_base, cm);
true
} else {
false
}
}
_ => false,
}
}
pub(super) extern "C" fn handle_exception(
typ: ExceptionType,
from: ExceptionFrom,
_regs: *mut ExceptionContext,
) {
let esr = unsafe { mrs!(ESR_EL1) };
let far = unsafe { mrs!(FAR_EL1) };
if typ == ExceptionType::Synchronous && from == ExceptionFrom::CurrentSP0 {
let exn = decode_syndrome(esr);
if handle_internal_fault(exn, far) {
return;
}
}
let elr = unsafe { mrs!(ELR_EL1) };
let insn_bytes = unsafe { (elr as *const [u8; 8]).read_volatile() };
let mut w = HyperlightAbortWriter;
write_abort(&[ErrorCode::GuestError as u8, 0xfeu8]);
let write_res = write!(
w,
"Exception vector: {:?} {:?}\n\
Faulting Instruction: {:#x}\n\
Bytes At Faulting Instruction: {:?}\n\
Faulting Address: {:#x}\n\
Exception Syndrome: {:#x}",
from, typ, elr, insn_bytes, far, esr
);
if write_res.is_err() {
write_abort("exception message format failed".as_bytes());
}
write_abort(&[0xFF]);
unreachable!();
}