Add new library to share FDT/PCI/VirtIO code.

This is used both by the vmbase example and by the pVM firmware.

Bug: 237249743
Test: Ran vmbase and pVM firmware manually
Test: atest vmbase_example.integration_test
Change-Id: Ia0e8e2434480bf6425c7396aa64cf2a1e9c520d7
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index 4218fae..1dae64e 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -16,6 +16,7 @@
         "libavb_nostd",
         "libbuddy_system_allocator",
         "libdice_nostd",
+        "libfdtpci",
         "liblibfdt",
         "liblog_rust_nostd",
         "libpvmfw_embedded_key",
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index 9c5fb60..da2e9d5 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -37,10 +37,11 @@
     avb::PUBLIC_KEY,
     entry::RebootReason,
     memory::MemoryTracker,
-    pci::{find_virtio_devices, PciError, PciInfo},
+    pci::{find_virtio_devices, map_mmio},
 };
 use ::avb::verify_image;
 use dice::bcc;
+use fdtpci::{PciError, PciInfo};
 use libfdt::Fdt;
 use log::{debug, error, info, trace};
 
@@ -64,7 +65,7 @@
     // Set up PCI bus for VirtIO devices.
     let pci_info = PciInfo::from_fdt(fdt).map_err(handle_pci_error)?;
     debug!("PCI: {:#x?}", pci_info);
-    pci_info.map(memory)?;
+    map_mmio(&pci_info, memory)?;
     // Safety: This is the only place where we call make_pci_root, and this main function is only
     // called once.
     let mut pci_root = unsafe { pci_info.make_pci_root() };
diff --git a/pvmfw/src/pci.rs b/pvmfw/src/pci.rs
index 301ecfc..e9ac45b 100644
--- a/pvmfw/src/pci.rs
+++ b/pvmfw/src/pci.rs
@@ -14,225 +14,26 @@
 
 //! Functions to scan the PCI bus for VirtIO devices.
 
-use crate::{
-    entry::RebootReason,
-    memory::{MemoryRange, MemoryTracker},
-};
-use core::{
-    ffi::CStr,
-    fmt::{self, Display, Formatter},
-    ops::Range,
-};
-use libfdt::{AddressRange, Fdt, FdtError, FdtNode};
+use crate::{entry::RebootReason, memory::MemoryTracker};
+use fdtpci::{PciError, PciInfo};
 use log::{debug, error};
-use virtio_drivers::pci::{
-    bus::{Cam, PciRoot},
-    virtio_device_type,
-};
+use virtio_drivers::pci::{bus::PciRoot, virtio_device_type};
 
-/// PCI MMIO configuration region size.
-const PCI_CFG_SIZE: usize = 0x100_0000;
+/// Maps the CAM and BAR range in the page table and MMIO guard.
+pub fn map_mmio(pci_info: &PciInfo, memory: &mut MemoryTracker) -> Result<(), RebootReason> {
+    memory.map_mmio_range(pci_info.cam_range.clone()).map_err(|e| {
+        error!("Failed to map PCI CAM: {}", e);
+        RebootReason::InternalError
+    })?;
 
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub enum PciError {
-    FdtErrorPci(FdtError),
-    FdtNoPci,
-    FdtErrorReg(FdtError),
-    FdtMissingReg,
-    FdtRegEmpty,
-    FdtRegMissingSize,
-    CamWrongSize(usize),
-    FdtErrorRanges(FdtError),
-    FdtMissingRanges,
-    RangeAddressMismatch { bus_address: u64, cpu_physical: u64 },
-    NoSuitableRange,
-}
-
-impl Display for PciError {
-    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
-        match self {
-            Self::FdtErrorPci(e) => write!(f, "Error getting PCI node from FDT: {}", e),
-            Self::FdtNoPci => write!(f, "Failed to find PCI bus in FDT."),
-            Self::FdtErrorReg(e) => write!(f, "Error getting reg property from PCI node: {}", e),
-            Self::FdtMissingReg => write!(f, "PCI node missing reg property."),
-            Self::FdtRegEmpty => write!(f, "Empty reg property on PCI node."),
-            Self::FdtRegMissingSize => write!(f, "PCI reg property missing size."),
-            Self::CamWrongSize(cam_size) => write!(
-                f,
-                "FDT says PCI CAM is {} bytes but we expected {}.",
-                cam_size, PCI_CFG_SIZE
-            ),
-            Self::FdtErrorRanges(e) => {
-                write!(f, "Error getting ranges property from PCI node: {}", e)
-            }
-            Self::FdtMissingRanges => write!(f, "PCI node missing ranges property."),
-            Self::RangeAddressMismatch { bus_address, cpu_physical } => {
-                write!(
-                    f,
-                    "bus address {:#018x} != CPU physical address {:#018x}",
-                    bus_address, cpu_physical
-                )
-            }
-            Self::NoSuitableRange => write!(f, "No suitable PCI memory range found."),
-        }
-    }
-}
-
-/// 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, PciError> {
-        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);
+    memory
+        .map_mmio_range(pci_info.bar_range.start as usize..pci_info.bar_range.end as usize)
+        .map_err(|e| {
+            error!("Failed to map PCI MMIO range: {}", 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(())
-    }
-
-    /// Returns the `PciRoot` for the memory-mapped CAM found in the FDT. The CAM should be mapped
-    /// before this is called, by calling [`PciInfo::map`].
-    ///
-    /// # Safety
-    ///
-    /// To prevent concurrent access, only one `PciRoot` should exist in the program. Thus this
-    /// method must only be called once, and there must be no other `PciRoot` constructed using the
-    /// same CAM.
-    pub unsafe fn make_pci_root(&self) -> PciRoot {
-        PciRoot::new(self.cam_range.start as *mut u8, Cam::MmioCam)
-    }
-}
-
-/// Finds an FDT node with compatible=pci-host-cam-generic.
-fn pci_node(fdt: &Fdt) -> Result<FdtNode, PciError> {
-    fdt.compatible_nodes(CStr::from_bytes_with_nul(b"pci-host-cam-generic\0").unwrap())
-        .map_err(PciError::FdtErrorPci)?
-        .next()
-        .ok_or(PciError::FdtNoPci)
-}
-
-/// 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, PciError> {
-    let pci_reg = pci_node
-        .reg()
-        .map_err(PciError::FdtErrorReg)?
-        .ok_or(PciError::FdtMissingReg)?
-        .next()
-        .ok_or(PciError::FdtRegEmpty)?;
-    let cam_addr = pci_reg.addr as usize;
-    let cam_size = pci_reg.size.ok_or(PciError::FdtRegMissingSize)? as usize;
-    debug!("Found PCI CAM at {:#x}-{:#x}", cam_addr, cam_addr + cam_size);
-    // Check that the CAM is the size we expect, so we don't later try accessing it beyond its
-    // bounds. If it is a different size then something is very wrong and we shouldn't continue to
-    // access it; maybe there is some new version of PCI we don't know about.
-    if cam_size != PCI_CFG_SIZE {
-        return Err(PciError::CamWrongSize(cam_size));
-    }
-
-    Ok(cam_addr..cam_addr + cam_size)
-}
-
-/// 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>, PciError> {
-    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(PciError::FdtErrorRanges)?
-        .ok_or(PciError::FdtMissingRanges)?
-    {
-        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 {
-                return Err(PciError::RangeAddressMismatch { bus_address, cpu_physical });
-            }
-            memory_address = u32::try_from(cpu_physical).unwrap();
-            memory_size = u32::try_from(size).unwrap();
-        }
-    }
-
-    if memory_size == 0 {
-        return Err(PciError::NoSuitableRange);
-    }
-
-    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),
-        }
-    }
+    Ok(())
 }
 
 /// Finds VirtIO PCI devices.