use aarch64_cpu::registers::*;
use ax_errno::{AxResult, ax_err};
use axaddrspace::GuestPhysAddr;
#[inline(always)]
pub fn exception_esr() -> usize {
ESR_EL2.get() as usize
}
#[inline(always)]
pub fn exception_class() -> Option<ESR_EL2::EC::Value> {
ESR_EL2.read_as_enum(ESR_EL2::EC)
}
#[inline(always)]
pub fn exception_class_value() -> usize {
ESR_EL2.read(ESR_EL2::EC) as usize
}
#[inline(always)]
fn exception_hpfar() -> usize {
let hpfar: u64;
unsafe {
core::arch::asm!("mrs {}, HPFAR_EL2", out(reg) hpfar);
}
hpfar as usize
}
#[allow(non_upper_case_globals)]
const ESR_ELx_S1PTW_SHIFT: usize = 7;
#[allow(non_upper_case_globals)]
const ESR_ELx_S1PTW: usize = 1 << ESR_ELx_S1PTW_SHIFT;
macro_rules! arm_at {
($at_op:expr, $addr:expr) => {
unsafe {
core::arch::asm!(concat!("AT ", $at_op, ", {0}"), in(reg) $addr, options(nomem, nostack));
core::arch::asm!("isb");
}
};
}
fn translate_far_to_hpfar(far: usize) -> AxResult<usize> {
fn par_to_far(par: u64) -> u64 {
let mask = ((1 << (52 - 12)) - 1) << 12;
(par & mask) >> 8
}
let par = PAR_EL1.get();
arm_at!("s1e1r", far);
let tmp = PAR_EL1.get();
PAR_EL1.set(par);
if (tmp & PAR_EL1::F::TranslationAborted.value) != 0 {
ax_err!(BadState, "PAR_EL1::F::TranslationAborted value")
} else {
Ok(par_to_far(tmp) as usize)
}
}
#[inline(always)]
pub fn exception_fault_addr() -> AxResult<GuestPhysAddr> {
let far = FAR_EL2.get() as usize;
let hpfar =
if (exception_esr() & ESR_ELx_S1PTW) == 0 && exception_data_abort_is_permission_fault() {
translate_far_to_hpfar(far)?
} else {
exception_hpfar()
};
Ok(GuestPhysAddr::from((far & 0xfff) | (hpfar << 8)))
}
#[inline(always)]
fn exception_instruction_length() -> usize {
(exception_esr() >> 25) & 1
}
#[inline(always)]
pub fn exception_next_instruction_step() -> usize {
2 + 2 * exception_instruction_length()
}
#[inline(always)]
pub fn exception_iss() -> usize {
ESR_EL2.read(ESR_EL2::ISS) as usize
}
#[inline(always)]
pub fn exception_sysreg_direction_write(iss: u64) -> bool {
const ESR_ISS_SYSREG_DIRECTION: u64 = 0b1;
(iss & ESR_ISS_SYSREG_DIRECTION) == 0
}
#[inline(always)]
pub fn exception_sysreg_gpr(iss: u64) -> u64 {
const ESR_ISS_SYSREG_REG_OFF: u64 = 5;
const ESR_ISS_SYSREG_REG_LEN: u64 = 5;
const ESR_ISS_SYSREG_REG_MASK: u64 = (1 << ESR_ISS_SYSREG_REG_LEN) - 1;
(iss >> ESR_ISS_SYSREG_REG_OFF) & ESR_ISS_SYSREG_REG_MASK
}
#[inline(always)]
pub const fn exception_sysreg_addr(iss: usize) -> usize {
const ESR_ISS_SYSREG_ADDR: usize = (0xfff << 10) | (0xf << 1);
iss & ESR_ISS_SYSREG_ADDR
}
#[inline(always)]
pub fn exception_data_abort_is_permission_fault() -> bool {
(exception_iss() & 0b111111 & (0xf << 2)) == 12
}
#[inline(always)]
pub fn exception_data_abort_access_width() -> usize {
1 << ((exception_iss() >> 22) & 0b11)
}
#[inline(always)]
pub fn exception_data_abort_handleable() -> bool {
(!(exception_iss() & (1 << 10)) | (exception_iss() & (1 << 24))) != 0
}
#[inline(always)]
pub fn exception_data_abort_is_translate_fault() -> bool {
(exception_iss() & 0b111111 & (0xf << 2)) == 4
}
#[inline(always)]
pub fn exception_data_abort_access_is_write() -> bool {
(exception_iss() & (1 << 6)) != 0
}
#[inline(always)]
pub fn exception_data_abort_access_reg() -> usize {
(exception_iss() >> 16) & 0b11111
}
#[allow(unused)]
#[inline(always)]
pub fn exception_data_abort_access_reg_width() -> usize {
4 + 4 * ((exception_iss() >> 15) & 1)
}
#[allow(unused)]
#[inline(always)]
pub fn exception_data_abort_access_is_sign_ext() -> bool {
((exception_iss() >> 21) & 1) != 0
}
macro_rules! save_regs_to_stack {
() => {
"
sub sp, sp, 12 * 8
stp x29, x30, [sp, 10 * 8]
stp x27, x28, [sp, 8 * 8]
stp x25, x26, [sp, 6 * 8]
stp x23, x24, [sp, 4 * 8]
stp x21, x22, [sp, 2 * 8]
stp x19, x20, [sp]"
};
}
macro_rules! restore_regs_from_stack {
() => {
"
ldp x19, x20, [sp]
ldp x21, x22, [sp, 2 * 8]
ldp x23, x24, [sp, 4 * 8]
ldp x25, x26, [sp, 6 * 8]
ldp x27, x28, [sp, 8 * 8]
ldp x29, x30, [sp, 10 * 8]
add sp, sp, 12 * 8"
};
}