Map PCI (BAR) MMIO range too.

Bug: 237249346
Test: Ran pVM firmware manually.
Change-Id: Id1be96e080a3ee90b3cf842fdfed1dc8043aa2df
diff --git a/libs/libfdt/src/iterators.rs b/libs/libfdt/src/iterators.rs
index 41fd492..a7ea0ee 100644
--- a/libs/libfdt/src/iterators.rs
+++ b/libs/libfdt/src/iterators.rs
@@ -178,16 +178,13 @@
     }
 }
 
-impl FromAddrCells for u128 {
+impl FromAddrCells for (u32, u64) {
     fn from_addr_cells(cells: &mut CellIterator, cell_count: AddrCells) -> Option<Self> {
         Some(match cell_count {
-            AddrCells::Single => cells.next()?.into(),
-            AddrCells::Double => (cells.next()? as Self) << 32 | cells.next()? as Self,
             AddrCells::Triple => {
-                (cells.next()? as Self) << 64
-                    | (cells.next()? as Self) << 32
-                    | cells.next()? as Self
+                (cells.next()?, (cells.next()? as u64) << 32 | cells.next()? as u64)
             }
+            _ => panic!("Invalid addr_cells {:?} for (u32, u64)", cell_count),
         })
     }
 }
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index e6a158d..970a66a 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -32,11 +32,7 @@
 mod pci;
 mod smccc;
 
-use crate::{
-    entry::RebootReason,
-    memory::MemoryTracker,
-    pci::{map_cam, pci_node},
-};
+use crate::{entry::RebootReason, memory::MemoryTracker, pci::PciInfo};
 use avb::PUBLIC_KEY;
 use avb_nostd::verify_image;
 use libfdt::Fdt;
@@ -60,8 +56,9 @@
     debug!("BCC: {:?} ({:#x} bytes)", bcc.as_ptr(), bcc.len());
 
     // Set up PCI bus for VirtIO devices.
-    let pci_node = pci_node(fdt)?;
-    map_cam(&pci_node, memory)?;
+    let pci_info = PciInfo::from_fdt(fdt)?;
+    info!("PCI: {:#x?}", pci_info);
+    pci_info.map(memory)?;
 
     verify_image(signed_kernel, PUBLIC_KEY).map_err(|e| {
         error!("Failed to verify the payload: {e}");
diff --git a/pvmfw/src/memory.rs b/pvmfw/src/memory.rs
index ca1024d..892089e 100644
--- a/pvmfw/src/memory.rs
+++ b/pvmfw/src/memory.rs
@@ -26,7 +26,7 @@
 use log::error;
 use tinyvec::ArrayVec;
 
-type MemoryRange = Range<usize>;
+pub type MemoryRange = Range<usize>;
 
 #[derive(Clone, Copy, Debug, Default)]
 enum MemoryType {
diff --git a/pvmfw/src/pci.rs b/pvmfw/src/pci.rs
index 7baabed..3e6915a 100644
--- a/pvmfw/src/pci.rs
+++ b/pvmfw/src/pci.rs
@@ -14,16 +14,57 @@
 
 //! Functions to scan the PCI bus for VirtIO device and allocate BARs.
 
-use crate::{entry::RebootReason, memory::MemoryTracker};
-use core::ffi::CStr;
-use libfdt::{Fdt, FdtNode};
+use crate::{
+    entry::RebootReason,
+    memory::{MemoryRange, MemoryTracker},
+};
+use core::{ffi::CStr, ops::Range};
+use libfdt::{AddressRange, Fdt, FdtNode};
 use log::{debug, error};
 
 /// PCI MMIO configuration region size.
 const PCI_CFG_SIZE: usize = 0x100_0000;
 
+/// Information about the PCI bus parsed from the device tree.
+#[derive(Debug)]
+pub struct PciInfo {
+    /// The MMIO range used by the memory-mapped PCI CAM.
+    cam_range: MemoryRange,
+    /// The MMIO range from which 32-bit PCI BARs should be allocated.
+    bar_range: Range<u32>,
+}
+
+impl PciInfo {
+    /// Finds the PCI node in the FDT, parses its properties and validates it.
+    pub fn from_fdt(fdt: &Fdt) -> Result<Self, RebootReason> {
+        let pci_node = pci_node(fdt)?;
+
+        let cam_range = parse_cam_range(&pci_node)?;
+        let bar_range = parse_ranges(&pci_node)?;
+
+        Ok(Self { cam_range, bar_range })
+    }
+
+    /// Maps the CAM and BAR range in the page table and MMIO guard.
+    pub fn map(&self, memory: &mut MemoryTracker) -> Result<(), RebootReason> {
+        memory.map_mmio_range(self.cam_range.clone()).map_err(|e| {
+            error!("Failed to map PCI CAM: {}", e);
+            RebootReason::InternalError
+        })?;
+
+        memory.map_mmio_range(self.bar_range.start as usize..self.bar_range.end as usize).map_err(
+            |e| {
+                error!("Failed to map PCI MMIO range: {}", e);
+                RebootReason::InternalError
+            },
+        )?;
+
+        Ok(())
+    }
+}
+
 /// Finds an FDT node with compatible=pci-host-cam-generic.
-pub fn pci_node(fdt: &Fdt) -> Result<FdtNode, RebootReason> {
+fn pci_node(fdt: &Fdt) -> Result<FdtNode, RebootReason> {
     fdt.compatible_nodes(CStr::from_bytes_with_nul(b"pci-host-cam-generic\0").unwrap())
         .map_err(|e| {
             error!("Failed to find PCI bus in FDT: {}", e);
@@ -33,8 +74,8 @@
         .ok_or(RebootReason::InvalidFdt)
 }
 
-pub fn map_cam(pci_node: &FdtNode, memory: &mut MemoryTracker) -> Result<(), RebootReason> {
-    // Parse reg property to find CAM.
+/// Parses the "reg" property of the given PCI FDT node to find the MMIO CAM range.
+fn parse_cam_range(pci_node: &FdtNode) -> Result<MemoryRange, RebootReason> {
     let pci_reg = pci_node
         .reg()
         .map_err(|e| {
@@ -64,11 +105,94 @@
         return Err(RebootReason::InvalidFdt);
     }
 
-    // Map the CAM as MMIO.
-    memory.map_mmio_range(cam_addr..cam_addr + cam_size).map_err(|e| {
-        error!("Failed to map PCI CAM: {}", e);
-        RebootReason::InternalError
-    })?;
+    Ok(cam_addr..cam_addr + cam_size)
+}
 
-    Ok(())
+/// Parses the "ranges" property of the given PCI FDT node, and returns the largest suitable range
+/// to use for non-prefetchable 32-bit memory BARs.
+fn parse_ranges(pci_node: &FdtNode) -> Result<Range<u32>, RebootReason> {
+    let mut memory_address = 0;
+    let mut memory_size = 0;
+
+    for AddressRange { addr: (flags, bus_address), parent_addr: cpu_physical, size } in pci_node
+        .ranges::<(u32, u64), u64, u64>()
+        .map_err(|e| {
+            error!("Error getting ranges property from PCI node: {}", e);
+            RebootReason::InvalidFdt
+        })?
+        .ok_or_else(|| {
+            error!("PCI node missing ranges property.");
+            RebootReason::InvalidFdt
+        })?
+    {
+        let flags = PciMemoryFlags(flags);
+        let prefetchable = flags.prefetchable();
+        let range_type = flags.range_type();
+        debug!(
+            "range: {:?} {}prefetchable bus address: {:#018x} CPU physical address: {:#018x} size: {:#018x}",
+            range_type,
+            if prefetchable { "" } else { "non-" },
+            bus_address,
+            cpu_physical,
+            size,
+        );
+
+        // Use a 64-bit range for 32-bit memory, if it is low enough, because crosvm doesn't
+        // currently provide any 32-bit ranges.
+        if !prefetchable
+            && matches!(range_type, PciRangeType::Memory32 | PciRangeType::Memory64)
+            && size > memory_size.into()
+            && bus_address + size < u32::MAX.into()
+        {
+            if bus_address != cpu_physical {
+                error!(
+                    "bus address {:#018x} != CPU physical address {:#018x}",
+                    bus_address, cpu_physical
+                );
+                return Err(RebootReason::InvalidFdt);
+            }
+            memory_address = u32::try_from(cpu_physical).unwrap();
+            memory_size = u32::try_from(size).unwrap();
+        }
+    }
+
+    if memory_size == 0 {
+        error!("No suitable PCI memory range found.");
+        return Err(RebootReason::InvalidFdt);
+    }
+
+    Ok(memory_address..memory_address + memory_size)
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+struct PciMemoryFlags(u32);
+
+impl PciMemoryFlags {
+    pub fn prefetchable(self) -> bool {
+        self.0 & 0x80000000 != 0
+    }
+
+    pub fn range_type(self) -> PciRangeType {
+        PciRangeType::from((self.0 & 0x3000000) >> 24)
+    }
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+enum PciRangeType {
+    ConfigurationSpace,
+    IoSpace,
+    Memory32,
+    Memory64,
+}
+
+impl From<u32> for PciRangeType {
+    fn from(value: u32) -> Self {
+        match value {
+            0 => Self::ConfigurationSpace,
+            1 => Self::IoSpace,
+            2 => Self::Memory32,
+            3 => Self::Memory64,
+            _ => panic!("Tried to convert invalid range type {}", value),
+        }
+    }
 }
diff --git a/vmbase/example/src/pci.rs b/vmbase/example/src/pci.rs
index 10a67b9..82ea7cc 100644
--- a/vmbase/example/src/pci.rs
+++ b/vmbase/example/src/pci.rs
@@ -17,7 +17,7 @@
 use aarch64_paging::paging::MemoryRegion;
 use alloc::alloc::{alloc, dealloc, Layout};
 use core::{ffi::CStr, mem::size_of};
-use libfdt::{Fdt, FdtNode, Reg};
+use libfdt::{AddressRange, Fdt, FdtNode, Reg};
 use log::{debug, info};
 use virtio_drivers::{
     pci::{
@@ -113,17 +113,14 @@
     pub fn for_pci_ranges(pci_node: &FdtNode) -> Self {
         let mut memory_32_address = 0;
         let mut memory_32_size = 0;
-        for range in pci_node
-            .ranges::<u128, u64, u64>()
+        for AddressRange { addr: (flags, bus_address), parent_addr: cpu_physical, size } in pci_node
+            .ranges::<(u32, u64), u64, u64>()
             .expect("Error getting ranges property from PCI node")
             .expect("PCI node missing ranges property.")
         {
-            let flags = PciMemoryFlags((range.addr >> 64) as u32);
+            let flags = PciMemoryFlags(flags);
             let prefetchable = flags.prefetchable();
             let range_type = flags.range_type();
-            let bus_address = range.addr as u64;
-            let cpu_physical = range.parent_addr;
-            let size = range.size;
             info!(
                 "range: {:?} {}prefetchable bus address: {:#018x} host physical address: {:#018x} size: {:#018x}",
                 range_type,