Map PCI CAM MMIO region and register pages with the MMIO guard.

Crosvm uses a memory-mapped Configuration Access Mechanism for PCI,
which we can find in the device tree. We need to map this as device
memory and register it with the hypervisor MMIO guard before trying to
access any PCI devices.

Bug: 237249346
Test: Ran pVM firmware manually.
Change-Id: Ide892c307e0c77a968560ecc6cb64f336002edf2
diff --git a/pvmfw/src/memory.rs b/pvmfw/src/memory.rs
index e88fa5b..ca1024d 100644
--- a/pvmfw/src/memory.rs
+++ b/pvmfw/src/memory.rs
@@ -14,7 +14,8 @@
 
 //! Low-level allocation and tracking of main memory.
 
-use crate::helpers;
+use crate::helpers::{self, page_4kb_of, SIZE_4KB};
+use crate::mmio_guard;
 use crate::mmu;
 use core::cmp::max;
 use core::cmp::min;
@@ -43,8 +44,7 @@
 impl MemoryRegion {
     /// True if the instance overlaps with the passed range.
     pub fn overlaps(&self, range: &MemoryRange) -> bool {
-        let our: &MemoryRange = self.as_ref();
-        max(our.start, range.start) < min(our.end, range.end)
+        overlaps(&self.range, range)
     }
 
     /// True if the instance is fully contained within the passed range.
@@ -60,11 +60,17 @@
     }
 }
 
+/// Returns true if one range overlaps with the other at all.
+fn overlaps<T: Copy + Ord>(a: &Range<T>, b: &Range<T>) -> bool {
+    max(a.start, b.start) < min(a.end, b.end)
+}
+
 /// Tracks non-overlapping slices of main memory.
 pub struct MemoryTracker {
-    regions: ArrayVec<[MemoryRegion; MemoryTracker::CAPACITY]>,
     total: MemoryRange,
     page_table: mmu::PageTable,
+    regions: ArrayVec<[MemoryRegion; MemoryTracker::CAPACITY]>,
+    mmio_regions: ArrayVec<[MemoryRange; MemoryTracker::MMIO_CAPACITY]>,
 }
 
 /// Errors for MemoryTracker operations.
@@ -84,6 +90,8 @@
     Overlaps,
     /// Region couldn't be mapped.
     FailedToMap,
+    /// Error from an MMIO guard call.
+    MmioGuard(mmio_guard::Error),
 }
 
 impl fmt::Display for MemoryTrackerError {
@@ -96,14 +104,22 @@
             Self::OutOfRange => write!(f, "Region is out of the tracked memory address space"),
             Self::Overlaps => write!(f, "New region overlaps with tracked regions"),
             Self::FailedToMap => write!(f, "Failed to map the new region"),
+            Self::MmioGuard(e) => e.fmt(f),
         }
     }
 }
 
+impl From<mmio_guard::Error> for MemoryTrackerError {
+    fn from(e: mmio_guard::Error) -> Self {
+        Self::MmioGuard(e)
+    }
+}
+
 type Result<T> = result::Result<T, MemoryTrackerError>;
 
 impl MemoryTracker {
     const CAPACITY: usize = 5;
+    const MMIO_CAPACITY: usize = 5;
     /// Base of the system's contiguous "main" memory.
     const BASE: usize = 0x8000_0000;
     /// First address that can't be translated by a level 1 TTBR0_EL1.
@@ -111,7 +127,12 @@
 
     /// Create a new instance from an active page table, covering the maximum RAM size.
     pub fn new(page_table: mmu::PageTable) -> Self {
-        Self { total: Self::BASE..Self::MAX_ADDR, page_table, regions: ArrayVec::new() }
+        Self {
+            total: Self::BASE..Self::MAX_ADDR,
+            page_table,
+            regions: ArrayVec::new(),
+            mmio_regions: ArrayVec::new(),
+        }
     }
 
     /// Resize the total RAM size.
@@ -164,6 +185,36 @@
         self.alloc_range_mut(&(base..(base + size.get())))
     }
 
+    /// Checks that the given range of addresses is within the MMIO region, and then maps it
+    /// 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 {
+            return Err(MemoryTrackerError::OutOfRange);
+        }
+        if self.mmio_regions.iter().any(|r| overlaps(r, &range)) {
+            return Err(MemoryTrackerError::Overlaps);
+        }
+        if self.mmio_regions.len() == self.mmio_regions.capacity() {
+            return Err(MemoryTrackerError::Full);
+        }
+
+        self.page_table.map_device(&range).map_err(|e| {
+            error!("Error during MMIO device mapping: {e}");
+            MemoryTrackerError::FailedToMap
+        })?;
+
+        for page_base in page_iterator(&range) {
+            mmio_guard::map(page_base)?;
+        }
+
+        if self.mmio_regions.try_push(range).is_some() {
+            return Err(MemoryTrackerError::Full);
+        }
+
+        Ok(())
+    }
+
     /// Checks that the given region is within the range of the `MemoryTracker` and doesn't overlap
     /// with any other previously allocated regions, and that the regions ArrayVec has capacity to
     /// add it.
@@ -187,11 +238,24 @@
 
         Ok(self.regions.last().unwrap().as_ref().clone())
     }
+
+    /// Unmaps all tracked MMIO regions from the MMIO guard.
+    ///
+    /// Note that they are not unmapped from the page table.
+    pub fn mmio_unmap_all(&self) -> Result<()> {
+        for region in &self.mmio_regions {
+            for page_base in page_iterator(region) {
+                mmio_guard::unmap(page_base)?;
+            }
+        }
+
+        Ok(())
+    }
 }
 
 impl Drop for MemoryTracker {
     fn drop(&mut self) {
-        for region in self.regions.iter() {
+        for region in &self.regions {
             match region.mem_type {
                 MemoryType::ReadWrite => {
                     // TODO: Use page table's dirty bit to only flush pages that were touched.
@@ -202,3 +266,8 @@
         }
     }
 }
+
+/// Returns an iterator which yields the base address of each 4 KiB page within the given range.
+fn page_iterator(range: &MemoryRange) -> impl Iterator<Item = usize> {
+    (page_4kb_of(range.start)..range.end).step_by(SIZE_4KB)
+}