// Copyright 2023, The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Helper functions and structs for exception handlers. use crate::{ console, eprintln, memory::{page_4kb_of, MemoryTrackerError}, read_sysreg, }; use aarch64_paging::paging::VirtualAddress; use core::fmt; const UART_PAGE: usize = page_4kb_of(console::BASE_ADDRESS); /// Represents an error that can occur while handling an exception. #[derive(Debug)] pub enum HandleExceptionError { /// The page table is unavailable. PageTableUnavailable, /// The page table has not been initialized. PageTableNotInitialized, /// An internal error occurred in the memory tracker. InternalError(MemoryTrackerError), /// An unknown exception occurred. UnknownException, } impl From for HandleExceptionError { fn from(other: MemoryTrackerError) -> Self { Self::InternalError(other) } } impl fmt::Display for HandleExceptionError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::PageTableUnavailable => write!(f, "Page table is not available."), Self::PageTableNotInitialized => write!(f, "Page table is not initialized."), Self::InternalError(e) => write!(f, "Error while updating page table: {e}"), Self::UnknownException => write!(f, "An unknown exception occurred, not handled."), } } } /// Represents the possible types of exception syndrome register (ESR) values. #[derive(Debug, PartialEq, Copy, Clone)] pub enum Esr { /// Data abort due to translation fault. DataAbortTranslationFault, /// Data abort due to permission fault. DataAbortPermissionFault, /// Data abort due to a synchronous external abort. DataAbortSyncExternalAbort, /// An unknown ESR value. Unknown(usize), } impl Esr { const EXT_DABT_32BIT: usize = 0x96000010; const TRANSL_FAULT_BASE_32BIT: usize = 0x96000004; const TRANSL_FAULT_ISS_MASK_32BIT: usize = !0x143; const PERM_FAULT_BASE_32BIT: usize = 0x9600004C; const PERM_FAULT_ISS_MASK_32BIT: usize = !0x103; } impl From for Esr { fn from(esr: usize) -> Self { if esr == Self::EXT_DABT_32BIT { Self::DataAbortSyncExternalAbort } else if esr & Self::TRANSL_FAULT_ISS_MASK_32BIT == Self::TRANSL_FAULT_BASE_32BIT { Self::DataAbortTranslationFault } else if esr & Self::PERM_FAULT_ISS_MASK_32BIT == Self::PERM_FAULT_BASE_32BIT { Self::DataAbortPermissionFault } else { Self::Unknown(esr) } } } impl fmt::Display for Esr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::DataAbortSyncExternalAbort => write!(f, "Synchronous external abort"), Self::DataAbortTranslationFault => write!(f, "Translation fault"), Self::DataAbortPermissionFault => write!(f, "Permission fault"), Self::Unknown(v) => write!(f, "Unknown exception esr={v:#08x}"), } } } /// A struct representing an Armv8 exception. pub struct ArmException { /// The value of the exception syndrome register. pub esr: Esr, /// The faulting virtual address read from the fault address register. pub far: VirtualAddress, } impl fmt::Display for ArmException { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "ArmException: esr={}, far={}", self.esr, self.far) } } impl ArmException { /// Reads the values of the EL1 exception syndrome register (`esr_el1`) /// and fault address register (`far_el1`) and returns a new instance of /// `ArmException` with these values. pub fn from_el1_regs() -> Self { let esr: Esr = read_sysreg!("esr_el1").into(); let far = read_sysreg!("far_el1"); Self { esr, far: VirtualAddress(far) } } /// Prints the details of an obj and the exception, excluding UART exceptions. pub fn print(&self, exception_name: &str, obj: T, elr: u64) { // Don't print to the UART if we are handling an exception it could raise. if !self.is_uart_exception() { eprintln!("{exception_name}"); eprintln!("{obj}"); eprintln!("{}, elr={:#08x}", self, elr); } } fn is_uart_exception(&self) -> bool { self.esr == Esr::DataAbortSyncExternalAbort && page_4kb_of(self.far.0) == UART_PAGE } }