Merge "[vmbase] Move current exception handling helper functions to vmbase" into main
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 3d2b1c2..ca8756d 100644
--- a/vmbase/src/lib.rs
+++ b/vmbase/src/lib.rs
@@ -24,6 +24,7 @@
pub 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 8935934..173c0ec 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};
@@ -293,7 +294,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();
let mmio_guard = get_mmio_guard().unwrap();
@@ -325,7 +326,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)
@@ -491,3 +492,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)?)
+}