//! Register field facts checked against Arm's AArch64 exception model and the //! primary Linux arm64 ESR definitions. No external implementation is imported. //! https://raw.githubusercontent.com/torvalds/linux/master/arch/arm64/include/asm/esr.h //! https://developer.arm.com/documentation/102376/latest #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Evidence { pub vector: u8, pub esr: u64, pub far: Option, pub elr: u64, pub spsr: u64, pub sp_el0: u64, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Access { Read, Write, Execute, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Kind { Translation, AccessFlag, Permission, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Unsupported { Vector, Origin, InstructionLength, Syndrome, Address, Status, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct UserFault { evidence: Evidence, kind: Kind, access: Access, level: u8, address: u64, } impl UserFault { pub const fn evidence(self) -> Evidence { self.evidence } pub const fn kind(self) -> Kind { self.kind } pub const fn access(self) -> Access { self.access } pub const fn level(self) -> u8 { self.level } pub const fn address(self) -> u64 { self.address } pub const fn stack_pointer(self) -> u64 { self.evidence.sp_el0 } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Classification { Kernel, User(UserFault), Unsupported(Unsupported), } /// Every current-EL vector or abort class remains a kernel fault, even if /// malformed context also suggests a user syndrome. Only ordinary lower-A64 /// synchronous aborts can produce the sealed UserFault accepted by Pager. pub fn classify(e: Evidence) -> Classification { let ec = (e.esr >> 26) & 63; if e.vector < 8 || false { return Classification::Kernel; } if e.vector != 8 { return Classification::Unsupported(Unsupported::Vector); } if e.spsr & 31 != 0 { return Classification::Kernel; } if !matches!(ec, 0x20 | 0x24) { return Classification::Unsupported(Unsupported::Origin); } if e.esr & (1 << 25) == 0 { return Classification::Unsupported(Unsupported::InstructionLength); } if e.esr >> 32 != 0 { return Classification::Unsupported(Unsupported::Syndrome); } let iss = e.esr & 0x1ff_ffff; let allowed = if ec == 0x24 { // ISV's recognized register/size fields may describe an ordinary load // or store. Without ISV those bits must be zero in this baseline. 63 | 64 | if iss & (1 << 24) != 0 { 0x1ff_c000 } else { 0 } } else { 63 }; if iss & !allowed != 0 { return Classification::Unsupported(Unsupported::Syndrome); } let Some(address) = e.far else { return Classification::Unsupported(Unsupported::Address); }; if address >= 1 << 48 || e.elr >= 1 << 48 || e.elr & 3 != 0 || e.sp_el0 > 1 << 48 { return Classification::Unsupported(Unsupported::Address); } let fsc = iss & 63; let kind = match fsc { 4..=7 => Kind::Translation, 8..=11 => Kind::AccessFlag, 12..=15 => Kind::Permission, _ => return Classification::Unsupported(Unsupported::Status), }; let access = if ec == 0x20 { Access::Execute } else if iss & 64 != 0 { Access::Write } else { Access::Read }; Classification::User(UserFault { evidence: e, kind, access, level: (fsc & 3) as u8, address, }) }