[vmbase] Move current exception handling helper functions to vmbase
The MemoryTracker in vmbase maps the MMIO memory range as lazy
device memory, which requires users of vmbase to handle
translation fault during the MMIO memory mapping. To avoid
duplicating the same fault handling code in each user, this cl
moves the fault handling helper functions to vmbase to streamline
the process.
This cl only moved the code from pvmfw to vmbase and added doc to
them. No functionality change in this cl.
Bug: 284462758
Test: m pvmfw_img
Change-Id: Ie3c8f2d871b4ebf13763e36f952b5b71f4660e77
diff --git a/pvmfw/src/exceptions.rs b/pvmfw/src/exceptions.rs
index 802ebbb..d9f0891 100644
--- a/pvmfw/src/exceptions.rs
+++ b/pvmfw/src/exceptions.rs
@@ -14,95 +14,14 @@
//! Exception handlers.
-use aarch64_paging::paging::VirtualAddress;
-use core::fmt;
-use vmbase::console;
-use vmbase::logger;
-use vmbase::memory::{page_4kb_of, MemoryTrackerError, MEMORY};
-use vmbase::read_sysreg;
-use vmbase::{eprintln, power::reboot};
-
-const UART_PAGE: usize = page_4kb_of(console::BASE_ADDRESS);
-
-#[derive(Debug)]
-enum HandleExceptionError {
- PageTableUnavailable,
- PageTableNotInitialized,
- InternalError(MemoryTrackerError),
- 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."),
- }
- }
-}
-
-#[derive(Debug, PartialEq, Copy, Clone)]
-enum Esr {
- DataAbortTranslationFault,
- DataAbortPermissionFault,
- DataAbortSyncExternalAbort,
- 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}"),
- }
- }
-}
-
-#[inline]
-fn handle_translation_fault(far: VirtualAddress) -> Result<(), HandleExceptionError> {
- let mut guard = MEMORY.try_lock().ok_or(HandleExceptionError::PageTableUnavailable)?;
- let memory = guard.as_mut().ok_or(HandleExceptionError::PageTableNotInitialized)?;
- Ok(memory.handle_mmio_fault(far)?)
-}
-
-#[inline]
-fn handle_permission_fault(far: VirtualAddress) -> Result<(), HandleExceptionError> {
- let mut guard = MEMORY.try_lock().ok_or(HandleExceptionError::PageTableUnavailable)?;
- let memory = guard.as_mut().ok_or(HandleExceptionError::PageTableNotInitialized)?;
- Ok(memory.handle_permission_fault(far)?)
-}
+use vmbase::{
+ eprintln,
+ exceptions::{ArmException, Esr, HandleExceptionError},
+ logger,
+ memory::{handle_permission_fault, handle_translation_fault},
+ power::reboot,
+ read_sysreg,
+};
fn handle_exception(exception: &ArmException) -> Result<(), HandleExceptionError> {
// Handle all translation faults on both read and write, and MMIO guard map
@@ -115,45 +34,6 @@
}
}
-/// A struct representing an Armv8 exception.
-struct ArmException {
- /// The value of the exception syndrome register.
- esr: Esr,
- /// The faulting virtual address read from the fault address register.
- 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.
- 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.
- 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
- }
-}
-
#[no_mangle]
extern "C" fn sync_exception_current(elr: u64, _spsr: u64) {
// Disable logging in exception handler to prevent unsafe writes to UART.
diff --git a/vmbase/src/exceptions.rs b/vmbase/src/exceptions.rs
new file mode 100644
index 0000000..7833334
--- /dev/null
+++ b/vmbase/src/exceptions.rs
@@ -0,0 +1,139 @@
+// 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<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
+ }
+}
diff --git a/vmbase/src/lib.rs b/vmbase/src/lib.rs
index e490faa..f637a40 100644
--- a/vmbase/src/lib.rs
+++ b/vmbase/src/lib.rs
@@ -24,6 +24,7 @@
mod bionic;
pub mod console;
mod entry;
+pub mod exceptions;
pub mod fdt;
pub mod heap;
mod hvc;
diff --git a/vmbase/src/memory/mod.rs b/vmbase/src/memory/mod.rs
index 5e78565..898aa10 100644
--- a/vmbase/src/memory/mod.rs
+++ b/vmbase/src/memory/mod.rs
@@ -22,7 +22,10 @@
pub use error::MemoryTrackerError;
pub use page_table::PageTable;
-pub use shared::{alloc_shared, dealloc_shared, MemoryRange, MemoryTracker, MEMORY};
+pub use shared::{
+ alloc_shared, dealloc_shared, handle_permission_fault, handle_translation_fault, MemoryRange,
+ MemoryTracker, MEMORY,
+};
pub use util::{
flush, flushed_zeroize, min_dcache_line_size, page_4kb_of, phys_to_virt, virt_to_phys,
PAGE_SIZE, SIZE_128KB, SIZE_2MB, SIZE_4KB, SIZE_4MB, SIZE_64KB,
diff --git a/vmbase/src/memory/shared.rs b/vmbase/src/memory/shared.rs
index 3faee5b..ebb55df 100644
--- a/vmbase/src/memory/shared.rs
+++ b/vmbase/src/memory/shared.rs
@@ -19,6 +19,7 @@
use super::page_table::{is_leaf_pte, PageTable, MMIO_LAZY_MAP_FLAG};
use super::util::{page_4kb_of, virt_to_phys};
use crate::dsb;
+use crate::exceptions::HandleExceptionError;
use crate::util::RangeExt as _;
use aarch64_paging::paging::{Attributes, Descriptor, MemoryRegion as VaRange, VirtualAddress};
use alloc::alloc::{alloc_zeroed, dealloc, handle_alloc_error};
@@ -270,7 +271,7 @@
/// Handles translation fault for blocks flagged for lazy MMIO mapping by enabling the page
/// table entry and MMIO guard mapping the block. Breaks apart a block entry if required.
- pub fn handle_mmio_fault(&mut self, addr: VirtualAddress) -> Result<()> {
+ fn handle_mmio_fault(&mut self, addr: VirtualAddress) -> Result<()> {
let page_start = VirtualAddress(page_4kb_of(addr.0));
let page_range: VaRange = (page_start..page_start + MMIO_GUARD_GRANULE_SIZE).into();
self.page_table
@@ -301,7 +302,7 @@
/// Handles permission fault for read-only blocks by setting writable-dirty state.
/// In general, this should be called from the exception handler when hardware dirty
/// state management is disabled or unavailable.
- pub fn handle_permission_fault(&mut self, addr: VirtualAddress) -> Result<()> {
+ fn handle_permission_fault(&mut self, addr: VirtualAddress) -> Result<()> {
self.page_table
.modify_range(&(addr..addr + 1).into(), &mark_dirty_block)
.map_err(|_| MemoryTrackerError::SetPteDirtyFailed)
@@ -462,3 +463,19 @@
}
Ok(())
}
+
+/// Handles a translation fault with the given fault address register (FAR).
+#[inline]
+pub fn handle_translation_fault(far: VirtualAddress) -> result::Result<(), HandleExceptionError> {
+ let mut guard = MEMORY.try_lock().ok_or(HandleExceptionError::PageTableUnavailable)?;
+ let memory = guard.as_mut().ok_or(HandleExceptionError::PageTableNotInitialized)?;
+ Ok(memory.handle_mmio_fault(far)?)
+}
+
+/// Handles a permission fault with the given fault address register (FAR).
+#[inline]
+pub fn handle_permission_fault(far: VirtualAddress) -> result::Result<(), HandleExceptionError> {
+ let mut guard = MEMORY.try_lock().ok_or(HandleExceptionError::PageTableUnavailable)?;
+ let memory = guard.as_mut().ok_or(HandleExceptionError::PageTableNotInitialized)?;
+ Ok(memory.handle_permission_fault(far)?)
+}