pvmfw: Support managing page tables dynamically

Integrate the aarch64_paging crate to allow mapping/unmapping regions of
memory as necessary.

Bug: 249054080
Bug: 249723852
Bug: 256827715
Test: atest MicrodroidTestApp
Change-Id: I97947c3852d4748bc29ce00709b4cec2d236e5a7
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index 77de696..1150b83 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -12,6 +12,7 @@
         "legacy",
     ],
     rustlibs: [
+        "libaarch64_paging",
         "libbuddy_system_allocator",
         "liblog_rust_nostd",
         "libpvmfw_embedded_key",
diff --git a/pvmfw/src/entry.rs b/pvmfw/src/entry.rs
index c0ad878..07dd0c5 100644
--- a/pvmfw/src/entry.rs
+++ b/pvmfw/src/entry.rs
@@ -17,6 +17,7 @@
 use crate::heap;
 use crate::helpers;
 use crate::mmio_guard;
+use crate::mmu;
 use core::arch::asm;
 use core::slice;
 use log::debug;
@@ -86,11 +87,40 @@
 
     // SAFETY - We only get the appended payload from here, once. It is mapped and the linker
     // script prevents it from overlapping with other objects.
-    let bcc = as_bcc(unsafe { get_appended_data_slice() }).ok_or_else(|| {
+    let appended_data = unsafe { get_appended_data_slice() };
+
+    // Up to this point, we were using the built-in static (from .rodata) page tables.
+
+    let mut page_table = mmu::PageTable::from_static_layout().map_err(|e| {
+        error!("Failed to set up the dynamic page tables: {e}");
+        RebootReason::InternalError
+    })?;
+
+    const CONSOLE_LEN: usize = 1; // vmbase::uart::Uart only uses one u8 register.
+    let uart_range = console::BASE_ADDRESS..(console::BASE_ADDRESS + CONSOLE_LEN);
+    page_table.map_device(&uart_range).map_err(|e| {
+        error!("Failed to remap the UART as a dynamic page table entry: {e}");
+        RebootReason::InternalError
+    })?;
+
+    let appended_range = appended_data.as_ptr_range();
+    let appended_range = (appended_range.start as usize)..(appended_range.end as usize);
+    page_table.map_rodata(&appended_range).map_err(|e| {
+        error!("Failed to remap the appended payload as a dynamic page table entry: {e}");
+        RebootReason::InternalError
+    })?;
+
+    let bcc = as_bcc(appended_data).ok_or_else(|| {
         error!("Invalid BCC");
         RebootReason::InvalidBcc
     })?;
 
+    debug!("Activating dynamic page table...");
+    // SAFETY - page_table duplicates the static mappings for everything that the Rust code is
+    // aware of so activating it shouldn't have any visible effect.
+    unsafe { page_table.activate() };
+    debug!("... Success!");
+
     // This wrapper allows main() to be blissfully ignorant of platform details.
     crate::main(fdt, payload, bcc);
 
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index 8178d0b..8caf020 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -24,6 +24,7 @@
 mod heap;
 mod helpers;
 mod mmio_guard;
+mod mmu;
 mod smccc;
 
 use avb::PUBLIC_KEY;
diff --git a/pvmfw/src/mmu.rs b/pvmfw/src/mmu.rs
new file mode 100644
index 0000000..d68e389
--- /dev/null
+++ b/pvmfw/src/mmu.rs
@@ -0,0 +1,76 @@
+// Copyright 2022, 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.
+
+//! Memory management.
+
+use aarch64_paging::idmap::IdMap;
+use aarch64_paging::paging::Attributes;
+use aarch64_paging::paging::MemoryRegion;
+use aarch64_paging::MapError;
+use core::ops::Range;
+use vmbase::layout;
+
+// We assume that:
+// - MAIR_EL1.Attr0 = "Device-nGnRE memory" (0b0000_0100)
+// - MAIR_EL1.Attr1 = "Normal memory, Outer & Inner WB Non-transient, R/W-Allocate" (0b1111_1111)
+const MEMORY: Attributes = Attributes::NORMAL.union(Attributes::NON_GLOBAL);
+const DEVICE: Attributes = Attributes::DEVICE_NGNRE.union(Attributes::EXECUTE_NEVER);
+const CODE: Attributes = MEMORY.union(Attributes::READ_ONLY);
+const DATA: Attributes = MEMORY.union(Attributes::EXECUTE_NEVER);
+const RODATA: Attributes = DATA.union(Attributes::READ_ONLY);
+
+/// High-level API for managing MMU mappings.
+pub struct PageTable {
+    idmap: IdMap,
+}
+
+impl PageTable {
+    const ASID: usize = 1;
+    const ROOT_LEVEL: usize = 1;
+
+    /// Creates an instance pre-populated with pvmfw's binary layout.
+    pub fn from_static_layout() -> Result<Self, MapError> {
+        let mut page_table = Self { idmap: IdMap::new(Self::ASID, Self::ROOT_LEVEL) };
+
+        page_table.map_code(&layout::text_range())?;
+        page_table.map_data(&layout::writable_region())?;
+        page_table.map_rodata(&layout::rodata_range())?;
+
+        Ok(page_table)
+    }
+
+    pub unsafe fn activate(&mut self) {
+        self.idmap.activate()
+    }
+
+    pub fn map_device(&mut self, range: &Range<usize>) -> Result<(), MapError> {
+        self.map_range(range, DEVICE)
+    }
+
+    pub fn map_data(&mut self, range: &Range<usize>) -> Result<(), MapError> {
+        self.map_range(range, DATA)
+    }
+
+    pub fn map_code(&mut self, range: &Range<usize>) -> Result<(), MapError> {
+        self.map_range(range, CODE)
+    }
+
+    pub fn map_rodata(&mut self, range: &Range<usize>) -> Result<(), MapError> {
+        self.map_range(range, RODATA)
+    }
+
+    fn map_range(&mut self, range: &Range<usize>, attr: Attributes) -> Result<(), MapError> {
+        self.idmap.map_range(&MemoryRegion::new(range.start, range.end), attr)
+    }
+}