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)?)
+}