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/libs/fdtpci/Android.bp b/libs/fdtpci/Android.bp
new file mode 100644
index 0000000..f650385
--- /dev/null
+++ b/libs/fdtpci/Android.bp
@@ -0,0 +1,14 @@
+rust_library_rlib {
+ name: "libfdtpci",
+ edition: "2021",
+ no_stdlibs: true,
+ host_supported: false,
+ crate_name: "fdtpci",
+ srcs: ["src/lib.rs"],
+ rustlibs: [
+ "liblibfdt",
+ "liblog_rust_nostd",
+ "libvirtio_drivers",
+ ],
+ apex_available: ["com.android.virt"],
+}
diff --git a/libs/fdtpci/TEST_MAPPING b/libs/fdtpci/TEST_MAPPING
new file mode 100644
index 0000000..c315b4a
--- /dev/null
+++ b/libs/fdtpci/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "avf-presubmit": [
+ {
+ "name": "vmbase_example.integration_test"
+ }
+ ]
+}
diff --git a/libs/fdtpci/src/lib.rs b/libs/fdtpci/src/lib.rs
new file mode 100644
index 0000000..a63e05b
--- /dev/null
+++ b/libs/fdtpci/src/lib.rs
@@ -0,0 +1,231 @@
+// 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.
+
+//! Library for working with (VirtIO) PCI devices discovered from a device tree.
+
+#![no_std]
+
+use core::{
+ ffi::CStr,
+ fmt::{self, Display, Formatter},
+ ops::Range,
+};
+use libfdt::{AddressRange, Fdt, FdtError, FdtNode};
+use log::debug;
+use virtio_drivers::pci::bus::{Cam, PciRoot};
+
+/// PCI MMIO configuration region size.
+const PCI_CFG_SIZE: usize = 0x100_0000;
+
+/// An error parsing a PCI node from an FDT.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum PciError {
+ /// Error getting PCI node from FDT.
+ FdtErrorPci(FdtError),
+ /// Failed to find PCI bus in FDT.
+ FdtNoPci,
+ /// Error getting `reg` property from PCI node.
+ FdtErrorReg(FdtError),
+ /// PCI node missing `reg` property.
+ FdtMissingReg,
+ /// Empty `reg property on PCI node.
+ FdtRegEmpty,
+ /// PCI `reg` property missing size.
+ FdtRegMissingSize,
+ /// PCI CAM size reported by FDT is not what we expected.
+ CamWrongSize(usize),
+ /// Error getting `ranges` property from PCI node.
+ FdtErrorRanges(FdtError),
+ /// PCI node missing `ranges` property.
+ FdtMissingRanges,
+ /// Bus address is not equal to CPU physical address in `ranges` property.
+ RangeAddressMismatch {
+ /// A bus address from the `ranges` property.
+ bus_address: u64,
+ /// The corresponding CPU physical address from the `ranges` property.
+ cpu_physical: u64,
+ },
+ /// No suitable PCI memory range found.
+ 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.
+ pub cam_range: Range<usize>,
+ /// The MMIO range from which 32-bit PCI BARs should be allocated.
+ pub 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 })
+ }
+
+ /// 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<Range<usize>, 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),
+ }
+ }
+}
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.
diff --git a/vmbase/example/Android.bp b/vmbase/example/Android.bp
index fbad8f4..94eb21a 100644
--- a/vmbase/example/Android.bp
+++ b/vmbase/example/Android.bp
@@ -12,6 +12,7 @@
"libaarch64_paging",
"libbuddy_system_allocator",
"libdice_nostd",
+ "libfdtpci",
"liblibfdt",
"liblog_rust_nostd",
"libvirtio_drivers",
diff --git a/vmbase/example/src/main.rs b/vmbase/example/src/main.rs
index 888f273..ec28a11 100644
--- a/vmbase/example/src/main.rs
+++ b/vmbase/example/src/main.rs
@@ -28,18 +28,16 @@
bionic_tls, dtb_range, print_addresses, rodata_range, stack_chk_guard, text_range,
writable_region, DEVICE_REGION,
};
-use crate::pci::{check_pci, pci_node, PciMemory32Allocator};
+use crate::pci::{check_pci, get_bar_region};
use aarch64_paging::{idmap::IdMap, paging::Attributes};
use alloc::{vec, vec::Vec};
use buddy_system_allocator::LockedHeap;
use core::ffi::CStr;
+use fdtpci::PciInfo;
use libfdt::Fdt;
use log::{debug, info, trace, LevelFilter};
use vmbase::{logger, main, println};
-/// PCI MMIO configuration region size.
-const AARCH64_PCI_CFG_SIZE: u64 = 0x1000000;
-
static INITIALISED_DATA: [u32; 4] = [1, 2, 3, 4];
static mut ZEROED_DATA: [u32; 10] = [0; 10];
static mut MUTABLE_DATA: [u32; 4] = [1, 2, 3, 4];
@@ -73,16 +71,8 @@
info!("FDT passed verification.");
check_fdt(fdt);
- let pci_node = pci_node(fdt);
- // Parse reg property to find CAM.
- let pci_reg = pci_node.reg().unwrap().unwrap().next().unwrap();
- debug!("Found PCI CAM at {:#x}-{:#x}", pci_reg.addr, pci_reg.addr + pci_reg.size.unwrap());
- // 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.
- assert_eq!(pci_reg.size.unwrap(), AARCH64_PCI_CFG_SIZE);
- // Parse ranges property to find memory ranges from which to allocate PCI BARs.
- let pci_allocator = PciMemory32Allocator::for_pci_ranges(&pci_node);
+ let pci_info = PciInfo::from_fdt(fdt).unwrap();
+ debug!("Found PCI CAM at {:#x}-{:#x}", pci_info.cam_range.start, pci_info.cam_range.end);
modify_fdt(fdt);
@@ -125,10 +115,7 @@
)
.unwrap();
idmap
- .map_range(
- &pci_allocator.get_region(),
- Attributes::DEVICE_NGNRE | Attributes::EXECUTE_NEVER,
- )
+ .map_range(&get_bar_region(&pci_info), Attributes::DEVICE_NGNRE | Attributes::EXECUTE_NEVER)
.unwrap();
info!("Activating IdMap...");
@@ -139,7 +126,8 @@
check_data();
check_dice();
- check_pci(pci_reg);
+ let mut pci_root = unsafe { pci_info.make_pci_root() };
+ check_pci(&mut pci_root);
}
fn check_stack_guard() {
diff --git a/vmbase/example/src/pci.rs b/vmbase/example/src/pci.rs
index bd5b5ba..a204b90 100644
--- a/vmbase/example/src/pci.rs
+++ b/vmbase/example/src/pci.rs
@@ -16,14 +16,11 @@
use aarch64_paging::paging::MemoryRegion;
use alloc::alloc::{alloc, dealloc, Layout};
-use core::{ffi::CStr, mem::size_of};
-use libfdt::{AddressRange, Fdt, FdtNode, Reg};
+use core::mem::size_of;
+use fdtpci::PciInfo;
use log::{debug, info};
use virtio_drivers::{
- pci::{
- bus::{Cam, PciRoot},
- virtio_device_type, PciTransport,
- },
+ pci::{bus::PciRoot, virtio_device_type, PciTransport},
DeviceType, Hal, PhysAddr, Transport, VirtAddr, VirtIOBlk, PAGE_SIZE,
};
@@ -33,24 +30,14 @@
/// The size in sectors of the test block device we expect.
const EXPECTED_SECTOR_COUNT: usize = 4;
-/// Finds an FDT node with compatible=pci-host-cam-generic.
-pub fn pci_node(fdt: &Fdt) -> FdtNode {
- fdt.compatible_nodes(CStr::from_bytes_with_nul(b"pci-host-cam-generic\0").unwrap())
- .unwrap()
- .next()
- .unwrap()
-}
-
-pub fn check_pci(reg: Reg<u64>) {
- let mut pci_root = unsafe { PciRoot::new(reg.addr as *mut u8, Cam::MmioCam) };
+pub fn check_pci(pci_root: &mut PciRoot) {
let mut checked_virtio_device_count = 0;
for (device_function, info) in pci_root.enumerate_bus(0) {
let (status, command) = pci_root.get_status_command(device_function);
info!("Found {} at {}, status {:?} command {:?}", info, device_function, status, command);
if let Some(virtio_type) = virtio_device_type(&info) {
info!(" VirtIO {:?}", virtio_type);
- let mut transport =
- PciTransport::new::<HalImpl>(&mut pci_root, device_function).unwrap();
+ let mut transport = PciTransport::new::<HalImpl>(pci_root, device_function).unwrap();
info!(
"Detected virtio PCI device with device type {:?}, features {:#018x}",
transport.device_type(),
@@ -88,89 +75,9 @@
}
}
-#[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)
- }
-}
-
-/// Allocates 32-bit memory addresses for PCI BARs.
-pub struct PciMemory32Allocator {
- start: u32,
- end: u32,
-}
-
-impl PciMemory32Allocator {
- /// Creates a new allocator based on the ranges property of the given PCI node.
- pub fn for_pci_ranges(pci_node: &FdtNode) -> Self {
- let mut memory_32_address = 0;
- let mut memory_32_size = 0;
- 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(flags);
- let prefetchable = flags.prefetchable();
- let range_type = flags.range_type();
- info!(
- "range: {:?} {}prefetchable bus address: {:#018x} host physical address: {:#018x} size: {:#018x}",
- range_type,
- if prefetchable { "" } else { "non-" },
- bus_address,
- cpu_physical,
- size,
- );
- if !prefetchable
- && ((range_type == PciRangeType::Memory32 && size > memory_32_size.into())
- || (range_type == PciRangeType::Memory64
- && size > memory_32_size.into()
- && bus_address + size < u32::MAX.into()))
- {
- // Use the 64-bit range for 32-bit memory, if it is low enough.
- assert_eq!(bus_address, cpu_physical);
- memory_32_address = u32::try_from(cpu_physical).unwrap();
- memory_32_size = u32::try_from(size).unwrap();
- }
- }
- if memory_32_size == 0 {
- panic!("No PCI memory regions found.");
- }
-
- Self { start: memory_32_address, end: memory_32_address + memory_32_size }
- }
-
- /// Gets a memory region covering the address space from which this allocator will allocate.
- pub fn get_region(&self) -> MemoryRegion {
- MemoryRegion::new(self.start as usize, self.end as usize)
- }
-}
-
-#[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),
- }
- }
+/// Gets the memory region in which BARs are allocated.
+pub fn get_bar_region(pci_info: &PciInfo) -> MemoryRegion {
+ MemoryRegion::new(pci_info.bar_range.start as usize, pci_info.bar_range.end as usize)
}
struct HalImpl;