Alice Wang | a9fe1fb | 2023-07-04 09:10:35 +0000 | [diff] [blame] | 1 | // Copyright 2023, The Android Open Source Project |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | //! Helper functions and structs for exception handlers. |
| 16 | |
| 17 | use crate::{ |
Pierre-Clément Tosi | 38a3621 | 2024-06-06 11:30:39 +0100 | [diff] [blame] | 18 | eprintln, |
| 19 | layout::UART_PAGE_ADDR, |
Pierre-Clément Tosi | 1d01277 | 2024-10-30 19:20:14 +0000 | [diff] [blame] | 20 | memory::{page_4kb_of, MemoryTrackerError, MEMORY}, |
Alice Wang | a9fe1fb | 2023-07-04 09:10:35 +0000 | [diff] [blame] | 21 | read_sysreg, |
| 22 | }; |
| 23 | use aarch64_paging::paging::VirtualAddress; |
| 24 | use core::fmt; |
Pierre-Clément Tosi | 1d01277 | 2024-10-30 19:20:14 +0000 | [diff] [blame] | 25 | use core::result; |
Alice Wang | a9fe1fb | 2023-07-04 09:10:35 +0000 | [diff] [blame] | 26 | |
Alice Wang | a9fe1fb | 2023-07-04 09:10:35 +0000 | [diff] [blame] | 27 | /// Represents an error that can occur while handling an exception. |
| 28 | #[derive(Debug)] |
| 29 | pub enum HandleExceptionError { |
| 30 | /// The page table is unavailable. |
| 31 | PageTableUnavailable, |
| 32 | /// The page table has not been initialized. |
| 33 | PageTableNotInitialized, |
| 34 | /// An internal error occurred in the memory tracker. |
| 35 | InternalError(MemoryTrackerError), |
| 36 | /// An unknown exception occurred. |
| 37 | UnknownException, |
| 38 | } |
| 39 | |
| 40 | impl From<MemoryTrackerError> for HandleExceptionError { |
| 41 | fn from(other: MemoryTrackerError) -> Self { |
| 42 | Self::InternalError(other) |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | impl fmt::Display for HandleExceptionError { |
| 47 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 48 | match self { |
| 49 | Self::PageTableUnavailable => write!(f, "Page table is not available."), |
| 50 | Self::PageTableNotInitialized => write!(f, "Page table is not initialized."), |
| 51 | Self::InternalError(e) => write!(f, "Error while updating page table: {e}"), |
| 52 | Self::UnknownException => write!(f, "An unknown exception occurred, not handled."), |
| 53 | } |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | /// Represents the possible types of exception syndrome register (ESR) values. |
| 58 | #[derive(Debug, PartialEq, Copy, Clone)] |
| 59 | pub enum Esr { |
| 60 | /// Data abort due to translation fault. |
| 61 | DataAbortTranslationFault, |
| 62 | /// Data abort due to permission fault. |
| 63 | DataAbortPermissionFault, |
| 64 | /// Data abort due to a synchronous external abort. |
| 65 | DataAbortSyncExternalAbort, |
| 66 | /// An unknown ESR value. |
| 67 | Unknown(usize), |
| 68 | } |
| 69 | |
| 70 | impl Esr { |
| 71 | const EXT_DABT_32BIT: usize = 0x96000010; |
| 72 | const TRANSL_FAULT_BASE_32BIT: usize = 0x96000004; |
| 73 | const TRANSL_FAULT_ISS_MASK_32BIT: usize = !0x143; |
| 74 | const PERM_FAULT_BASE_32BIT: usize = 0x9600004C; |
| 75 | const PERM_FAULT_ISS_MASK_32BIT: usize = !0x103; |
| 76 | } |
| 77 | |
| 78 | impl From<usize> for Esr { |
| 79 | fn from(esr: usize) -> Self { |
| 80 | if esr == Self::EXT_DABT_32BIT { |
| 81 | Self::DataAbortSyncExternalAbort |
| 82 | } else if esr & Self::TRANSL_FAULT_ISS_MASK_32BIT == Self::TRANSL_FAULT_BASE_32BIT { |
| 83 | Self::DataAbortTranslationFault |
| 84 | } else if esr & Self::PERM_FAULT_ISS_MASK_32BIT == Self::PERM_FAULT_BASE_32BIT { |
| 85 | Self::DataAbortPermissionFault |
| 86 | } else { |
| 87 | Self::Unknown(esr) |
| 88 | } |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | impl fmt::Display for Esr { |
| 93 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 94 | match self { |
| 95 | Self::DataAbortSyncExternalAbort => write!(f, "Synchronous external abort"), |
| 96 | Self::DataAbortTranslationFault => write!(f, "Translation fault"), |
| 97 | Self::DataAbortPermissionFault => write!(f, "Permission fault"), |
| 98 | Self::Unknown(v) => write!(f, "Unknown exception esr={v:#08x}"), |
| 99 | } |
| 100 | } |
| 101 | } |
| 102 | /// A struct representing an Armv8 exception. |
| 103 | pub struct ArmException { |
| 104 | /// The value of the exception syndrome register. |
| 105 | pub esr: Esr, |
| 106 | /// The faulting virtual address read from the fault address register. |
| 107 | pub far: VirtualAddress, |
| 108 | } |
| 109 | |
| 110 | impl fmt::Display for ArmException { |
| 111 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 112 | write!(f, "ArmException: esr={}, far={}", self.esr, self.far) |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | impl ArmException { |
| 117 | /// Reads the values of the EL1 exception syndrome register (`esr_el1`) |
| 118 | /// and fault address register (`far_el1`) and returns a new instance of |
| 119 | /// `ArmException` with these values. |
| 120 | pub fn from_el1_regs() -> Self { |
| 121 | let esr: Esr = read_sysreg!("esr_el1").into(); |
| 122 | let far = read_sysreg!("far_el1"); |
| 123 | Self { esr, far: VirtualAddress(far) } |
| 124 | } |
| 125 | |
| 126 | /// Prints the details of an obj and the exception, excluding UART exceptions. |
| 127 | pub fn print<T: fmt::Display>(&self, exception_name: &str, obj: T, elr: u64) { |
| 128 | // Don't print to the UART if we are handling an exception it could raise. |
| 129 | if !self.is_uart_exception() { |
| 130 | eprintln!("{exception_name}"); |
| 131 | eprintln!("{obj}"); |
| 132 | eprintln!("{}, elr={:#08x}", self, elr); |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | fn is_uart_exception(&self) -> bool { |
Pierre-Clément Tosi | 38a3621 | 2024-06-06 11:30:39 +0100 | [diff] [blame] | 137 | self.esr == Esr::DataAbortSyncExternalAbort && page_4kb_of(self.far.0) == UART_PAGE_ADDR |
Alice Wang | a9fe1fb | 2023-07-04 09:10:35 +0000 | [diff] [blame] | 138 | } |
| 139 | } |
Pierre-Clément Tosi | 1d01277 | 2024-10-30 19:20:14 +0000 | [diff] [blame] | 140 | |
| 141 | /// Handles a translation fault with the given fault address register (FAR). |
| 142 | #[inline] |
| 143 | pub fn handle_translation_fault(far: VirtualAddress) -> result::Result<(), HandleExceptionError> { |
| 144 | let mut guard = MEMORY.try_lock().ok_or(HandleExceptionError::PageTableUnavailable)?; |
| 145 | let memory = guard.as_mut().ok_or(HandleExceptionError::PageTableNotInitialized)?; |
| 146 | Ok(memory.handle_mmio_fault(far)?) |
| 147 | } |
| 148 | |
| 149 | /// Handles a permission fault with the given fault address register (FAR). |
| 150 | #[inline] |
| 151 | pub fn handle_permission_fault(far: VirtualAddress) -> result::Result<(), HandleExceptionError> { |
| 152 | let mut guard = MEMORY.try_lock().ok_or(HandleExceptionError::PageTableUnavailable)?; |
| 153 | let memory = guard.as_mut().ok_or(HandleExceptionError::PageTableNotInitialized)?; |
| 154 | Ok(memory.handle_permission_fault(far)?) |
| 155 | } |