Merge "Require unsafe blocks in unsafe functions"
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index dff7d13..7ea1189 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -34,6 +34,7 @@
         "libvmbase",
         "libzerocopy_nostd",
         "libzeroize_nostd",
+        "libspin_nostd",
     ],
 }
 
diff --git a/pvmfw/README.md b/pvmfw/README.md
index 4e93648..1eb7286 100644
--- a/pvmfw/README.md
+++ b/pvmfw/README.md
@@ -197,16 +197,20 @@
 that it differs from the `BccHandover` defined by the specification in that its
 `Bcc` field is mandatory (while optional in the original).
 
-Devices that fully implement DICE should provide a certificate rooted at the
-Unique Device Secret (UDS) in a boot stage preceding the pvmfw loader (typically
-ABL), in such a way that it would receive a valid `BccHandover`, that can be
-passed to [`BccHandoverMainFlow`][BccHandoverMainFlow] along with the inputs
-described below.
+Ideally devices that fully implement DICE should provide a certificate rooted at
+the Unique Device Secret (UDS) in a boot stage preceding the pvmfw loader
+(typically ABL), in such a way that it would receive a valid `BccHandover`, that
+can be passed to [`BccHandoverMainFlow`][BccHandoverMainFlow] along with the
+inputs described below.
 
-Otherwise, as an intermediate step towards supporting DICE throughout the
-software stack of the device, incomplete implementations may root the BCC at the
-pvmfw loader, using an arbitrary constant as initial CDI. The pvmfw loader can
-easily do so by:
+However, there is a limitation in Android 14 that means that a UDS-rooted DICE
+chain must not be used for pvmfw. A non-UDS rooted DICE chain is recommended for
+Android 14.
+
+As an intermediate step towards supporting DICE throughout the software stack of
+the device, incomplete implementations may root the BCC at the pvmfw loader,
+using an arbitrary constant as initial CDI. The pvmfw loader can easily do so
+by:
 
 1. Building a BCC-less `BccHandover` using CBOR operations
    ([example][Trusty-BCC]) and containing the constant CDIs
diff --git a/pvmfw/src/entry.rs b/pvmfw/src/entry.rs
index f625f1a..398c8df 100644
--- a/pvmfw/src/entry.rs
+++ b/pvmfw/src/entry.rs
@@ -19,7 +19,7 @@
 use crate::fdt;
 use crate::heap;
 use crate::helpers;
-use crate::memory::MemoryTracker;
+use crate::memory::{MemoryTracker, MEMORY};
 use crate::mmu;
 use crate::rand;
 use core::arch::asm;
@@ -217,8 +217,8 @@
     unsafe { page_table.activate() };
     debug!("... Success!");
 
-    let mut memory = MemoryTracker::new(page_table);
-    let slices = MemorySlices::new(fdt, payload, payload_size, &mut memory)?;
+    MEMORY.lock().replace(MemoryTracker::new(page_table));
+    let slices = MemorySlices::new(fdt, payload, payload_size, MEMORY.lock().as_mut().unwrap())?;
 
     rand::init().map_err(|e| {
         error!("Failed to initialize rand: {e}");
@@ -226,13 +226,20 @@
     })?;
 
     // This wrapper allows main() to be blissfully ignorant of platform details.
-    crate::main(slices.fdt, slices.kernel, slices.ramdisk, bcc_slice, debug_policy, &mut memory)?;
+    crate::main(
+        slices.fdt,
+        slices.kernel,
+        slices.ramdisk,
+        bcc_slice,
+        debug_policy,
+        MEMORY.lock().as_mut().unwrap(),
+    )?;
 
     helpers::flushed_zeroize(bcc_slice);
     helpers::flush(slices.fdt.as_slice());
 
     info!("Expecting a bug making MMIO_GUARD_UNMAP return NOT_SUPPORTED on success");
-    memory.mmio_unmap_all().map_err(|e| {
+    MEMORY.lock().as_mut().unwrap().mmio_unmap_all().map_err(|e| {
         error!("Failed to unshare MMIO ranges: {e}");
         RebootReason::InternalError
     })?;
@@ -240,6 +247,7 @@
         error!("Failed to unshare the UART: {e}");
         RebootReason::InternalError
     })?;
+    MEMORY.lock().take().unwrap();
 
     Ok(slices.kernel.as_ptr() as usize)
 }
diff --git a/pvmfw/src/exceptions.rs b/pvmfw/src/exceptions.rs
index 3ca4e0f..462a9cc 100644
--- a/pvmfw/src/exceptions.rs
+++ b/pvmfw/src/exceptions.rs
@@ -15,22 +15,92 @@
 //! Exception handlers.
 
 use crate::{helpers::page_4kb_of, read_sysreg};
+use core::fmt;
 use vmbase::console;
+use vmbase::logger;
 use vmbase::{eprintln, power::reboot};
 
-const ESR_32BIT_EXT_DABT: usize = 0x96000010;
 const UART_PAGE: usize = page_4kb_of(console::BASE_ADDRESS);
 
+#[derive(Debug)]
+enum HandleExceptionError {
+    UnknownException,
+}
+
+impl fmt::Display for HandleExceptionError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            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}"),
+        }
+    }
+}
+
+fn handle_exception(_esr: Esr, _far: usize) -> Result<(), HandleExceptionError> {
+    Err(HandleExceptionError::UnknownException)
+}
+
+#[inline]
+fn handling_uart_exception(esr: Esr, far: usize) -> bool {
+    esr == Esr::DataAbortSyncExternalAbort && page_4kb_of(far) == UART_PAGE
+}
+
 #[no_mangle]
 extern "C" fn sync_exception_current(_elr: u64, _spsr: u64) {
-    let esr = read_sysreg!("esr_el1");
+    // Disable logging in exception handler to prevent unsafe writes to UART.
+    let _guard = logger::suppress();
+    let esr: Esr = read_sysreg!("esr_el1").into();
     let far = read_sysreg!("far_el1");
-    // Don't print to the UART if we're handling the exception it could raise.
-    if esr != ESR_32BIT_EXT_DABT || page_4kb_of(far) != UART_PAGE {
-        eprintln!("sync_exception_current");
-        eprintln!("esr={esr:#08x}");
+
+    if let Err(e) = handle_exception(esr, far) {
+        // Don't print to the UART if we are handling an exception it could raise.
+        if !handling_uart_exception(esr, far) {
+            eprintln!("sync_exception_current");
+            eprintln!("{e}");
+            eprintln!("{esr}, far={far:#08x}");
+        }
+        reboot()
     }
-    reboot();
 }
 
 #[no_mangle]
diff --git a/pvmfw/src/helpers.rs b/pvmfw/src/helpers.rs
index 9c739d1..8c05217 100644
--- a/pvmfw/src/helpers.rs
+++ b/pvmfw/src/helpers.rs
@@ -19,6 +19,7 @@
 
 pub const SIZE_4KB: usize = 4 << 10;
 pub const SIZE_2MB: usize = 2 << 20;
+pub const SIZE_4MB: usize = 4 << 20;
 
 pub const GUEST_PAGE_SIZE: usize = SIZE_4KB;
 
diff --git a/pvmfw/src/memory.rs b/pvmfw/src/memory.rs
index 2d5eb5c..7df25f2 100644
--- a/pvmfw/src/memory.rs
+++ b/pvmfw/src/memory.rs
@@ -16,7 +16,7 @@
 
 #![deny(unsafe_op_in_unsafe_fn)]
 
-use crate::helpers::{self, align_down, align_up, page_4kb_of, SIZE_4KB};
+use crate::helpers::{self, align_down, align_up, page_4kb_of, SIZE_4KB, SIZE_4MB};
 use crate::mmu;
 use alloc::alloc::alloc_zeroed;
 use alloc::alloc::dealloc;
@@ -31,6 +31,7 @@
 use core::result;
 use hyp::get_hypervisor;
 use log::error;
+use spin::mutex::SpinMutex;
 use tinyvec::ArrayVec;
 
 /// Base of the system's contiguous "main" memory.
@@ -40,6 +41,9 @@
 
 pub type MemoryRange = Range<usize>;
 
+pub static MEMORY: SpinMutex<Option<MemoryTracker>> = SpinMutex::new(None);
+unsafe impl Send for MemoryTracker {}
+
 #[derive(Clone, Copy, Debug, Default)]
 enum MemoryType {
     #[default]
@@ -132,6 +136,7 @@
 impl MemoryTracker {
     const CAPACITY: usize = 5;
     const MMIO_CAPACITY: usize = 5;
+    const PVMFW_RANGE: MemoryRange = (BASE_ADDR - SIZE_4MB)..BASE_ADDR;
 
     /// Create a new instance from an active page table, covering the maximum RAM size.
     pub fn new(page_table: mmu::PageTable) -> Self {
@@ -197,7 +202,7 @@
     /// appropriately.
     pub fn map_mmio_range(&mut self, range: MemoryRange) -> Result<()> {
         // MMIO space is below the main memory region.
-        if range.end > self.total.start {
+        if range.end > self.total.start || overlaps(&Self::PVMFW_RANGE, &range) {
             return Err(MemoryTrackerError::OutOfRange);
         }
         if self.mmio_regions.iter().any(|r| overlaps(r, &range)) {