blob: 11fcd9398790ece381378b4552ef36fad020290a [file] [log] [blame]
// 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::{
eprintln,
layout::UART_PAGE_ADDR,
memory::{page_4kb_of, MemoryTrackerError},
read_sysreg,
};
use aarch64_paging::paging::VirtualAddress;
use core::fmt;
/// 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<MemoryTrackerError> 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<usize> 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<T: fmt::Display>(&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_ADDR
}
}