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,