Pārlūkot izejas kodu

Allocate PCI memory BARs.

# Conflicts:
#	examples/aarch64/src/main.rs
Andrew Walbran 2 gadi atpakaļ
vecāks
revīzija
0acfa4c2a0
2 mainītis faili ar 113 papildinājumiem un 20 dzēšanām
  1. 101 20
      examples/aarch64/src/main.rs
  2. 12 0
      src/transport/pci/bus.rs

+ 101 - 20
examples/aarch64/src/main.rs

@@ -13,7 +13,7 @@ use log::{debug, error, info, trace, warn, LevelFilter};
 use psci::system_off;
 use virtio_drivers::{
     pci::{
-        bus::{Cam, PciRoot},
+        bus::{BarInfo, Cam, Command, DeviceFunction, MemoryBarType, PciRoot},
         virtio_device_type, PciTransport,
     },
     DeviceType, MmioTransport, Transport, VirtIOBlk, VirtIOGpu, VirtIOHeader, VirtIONet,
@@ -161,25 +161,7 @@ impl From<u8> for PciRangeType {
 
 fn enumerate_pci(pci_node: FdtNode, cam: Cam) {
     let reg = pci_node.reg().expect("PCI node missing reg property.");
-    let ranges = pci_node
-        .property("ranges")
-        .expect("PCI node missing ranges property.");
-    for i in 0..ranges.value.len() / 28 {
-        let range = &ranges.value[i * 28..(i + 1) * 28];
-        let prefetchable = range[0] & 0x80 != 0;
-        let range_type = PciRangeType::from(range[0] & 0x3);
-        let bus_address = u64::from_be_bytes(range[4..12].try_into().unwrap());
-        let cpu_physical = u64::from_be_bytes(range[12..20].try_into().unwrap());
-        let size = u64::from_be_bytes(range[20..28].try_into().unwrap());
-        info!(
-            "range: {:?} {}prefetchable bus address: {:#018x} host physical address: {:#018x} size: {:#018x}",
-            range_type,
-            if prefetchable { "" } else { "non-" },
-            bus_address,
-            cpu_physical,
-            size
-        );
-    }
+    let mut allocator = PciMemory32Allocator::for_pci_ranges(&pci_node);
 
     for region in reg {
         info!(
@@ -197,6 +179,7 @@ fn enumerate_pci(pci_node: FdtNode, cam: Cam) {
             );
             if let Some(virtio_type) = virtio_device_type(&info) {
                 info!("  VirtIO {:?}", virtio_type);
+                allocate_bars(&mut pci_root, device_function, &mut allocator);
                 let mut transport =
                     PciTransport::new::<HalImpl>(pci_root.clone(), device_function).unwrap();
                 info!("  Features: {:#018x}", transport.read_device_features());
@@ -205,6 +188,104 @@ fn enumerate_pci(pci_node: FdtNode, cam: Cam) {
     }
 }
 
+/// Allocates 32-bit memory addresses for PCI BARs.
+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 ranges = pci_node
+            .property("ranges")
+            .expect("PCI node missing ranges property.");
+        let mut memory_32_address = 0;
+        let mut memory_32_size = 0;
+        for i in 0..ranges.value.len() / 28 {
+            let range = &ranges.value[i * 28..(i + 1) * 28];
+            let prefetchable = range[0] & 0x80 != 0;
+            let range_type = PciRangeType::from(range[0] & 0x3);
+            let bus_address = u64::from_be_bytes(range[4..12].try_into().unwrap());
+            let cpu_physical = u64::from_be_bytes(range[12..20].try_into().unwrap());
+            let size = u64::from_be_bytes(range[20..28].try_into().unwrap());
+            info!(
+                "range: {:?} {}prefetchable bus address: {:#018x} host physical address: {:#018x} size: {:#018x}",
+                range_type,
+                if prefetchable { "" } else { "non-" },
+                bus_address,
+                cpu_physical,
+                size,
+            );
+            if range_type == PciRangeType::Memory32 && size > memory_32_size.into() {
+                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 32-bit PCI memory region found.");
+        }
+        Self {
+            start: memory_32_address,
+            end: memory_32_address + memory_32_size,
+        }
+    }
+
+    /// Allocates a 32-bit memory address region for a PCI BAR of the given power-of-2 size.
+    ///
+    /// It will have alignment matching the size. The size must be a power of 2.
+    pub fn allocate_memory_32(&mut self, size: u32) -> u32 {
+        assert!(size.is_power_of_two());
+        let allocated_address = align_up(self.start, size);
+        assert!(allocated_address + size <= self.end);
+        self.start = allocated_address + size;
+        allocated_address
+    }
+}
+
+const fn align_up(value: u32, alignment: u32) -> u32 {
+    ((value - 1) | (alignment - 1)) + 1
+}
+
+/// Allocates appropriately-sized memory regions and assigns them to the device's BARs.
+fn allocate_bars(
+    root: &mut PciRoot,
+    device_function: DeviceFunction,
+    allocator: &mut PciMemory32Allocator,
+) {
+    let mut bar_index = 0;
+    while bar_index < 6 {
+        let info = root.bar_info(device_function, bar_index).unwrap();
+        debug!("BAR {}: {}", bar_index, info);
+        // Ignore I/O bars, as they aren't required for the VirtIO driver.
+        if let BarInfo::Memory {
+            address_type, size, ..
+        } = info
+        {
+            if address_type != MemoryBarType::Width32 {
+                panic!("Memory BAR address type {:?} not supported.", address_type);
+            }
+            if size > 0 {
+                let address = allocator.allocate_memory_32(size);
+                debug!("Allocated address {:#010x}", address);
+                root.set_bar_32(device_function, bar_index, address);
+            }
+        }
+
+        bar_index += 1;
+        if info.takes_two_entries() {
+            bar_index += 1;
+        }
+    }
+
+    // Enable the device to use its BARs.
+    root.set_command(
+        device_function,
+        Command::IO_SPACE | Command::MEMORY_SPACE | Command::BUS_MASTER,
+    );
+}
+
 #[panic_handler]
 fn panic(info: &PanicInfo) -> ! {
     error!("{}", info);

+ 12 - 0
src/transport/pci/bus.rs

@@ -322,6 +322,18 @@ pub enum BarInfo {
 }
 
 impl BarInfo {
+    /// Returns whether this BAR is a 64-bit memory region, and so takes two entries in the table in
+    /// configuration space.
+    pub fn takes_two_entries(&self) -> bool {
+        matches!(
+            self,
+            BarInfo::Memory {
+                address_type: MemoryBarType::Width64,
+                ..
+            }
+        )
+    }
+
     /// Returns the address and size of this BAR if it is a memory bar, or `None` if it is an IO
     /// BAR.
     pub fn memory_address_size(&self) -> Option<(u64, u32)> {