blob: b04cb16a9cc00f45dd4c74e2192225ef6b6f6220 [file] [log] [blame]
Alice Wanga9fe1fb2023-07-04 09:10:35 +00001// 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
17use crate::{
Pierre-Clément Tosi38a36212024-06-06 11:30:39 +010018 eprintln,
19 layout::UART_PAGE_ADDR,
Pierre-Clément Tosi1d012772024-10-30 19:20:14 +000020 memory::{page_4kb_of, MemoryTrackerError, MEMORY},
Alice Wanga9fe1fb2023-07-04 09:10:35 +000021 read_sysreg,
22};
23use aarch64_paging::paging::VirtualAddress;
24use core::fmt;
Pierre-Clément Tosi1d012772024-10-30 19:20:14 +000025use core::result;
Alice Wanga9fe1fb2023-07-04 09:10:35 +000026
Alice Wanga9fe1fb2023-07-04 09:10:35 +000027/// Represents an error that can occur while handling an exception.
28#[derive(Debug)]
29pub 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
40impl From<MemoryTrackerError> for HandleExceptionError {
41 fn from(other: MemoryTrackerError) -> Self {
42 Self::InternalError(other)
43 }
44}
45
46impl 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)]
59pub 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
70impl 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
78impl 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
92impl 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.
103pub 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
110impl 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
116impl 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 Tosi38a36212024-06-06 11:30:39 +0100137 self.esr == Esr::DataAbortSyncExternalAbort && page_4kb_of(self.far.0) == UART_PAGE_ADDR
Alice Wanga9fe1fb2023-07-04 09:10:35 +0000138 }
139}
Pierre-Clément Tosi1d012772024-10-30 19:20:14 +0000140
141/// Handles a translation fault with the given fault address register (FAR).
142#[inline]
143pub 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]
151pub 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}