From d18c8d4c8cf2cf00e5b2158c5ee595c38807f38d Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Tue, 30 Jul 2019 07:42:07 -0700 Subject: [PATCH] vfio: pci: Add support for expansion ROM BAR Relying on the newly added code in the pci crate, the vfio crate can now properly expose an expansion ROM BAR if the device has one. Signed-off-by: Sebastien Boeuf --- vfio/src/vfio_pci.rs | 79 ++++++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 4a2e4bbe9..c9e50d551 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -728,14 +728,18 @@ impl PciDevice for VfioPciDevice { // We're not saving the BAR address to restore it, because we // are going to allocate a guest address for each BAR and write // that new address back. - while bar_id < VFIO_PCI_ROM_REGION_INDEX { + while bar_id < VFIO_PCI_CONFIG_REGION_INDEX { let mut lsb_size: u32 = 0xffff_ffff; let mut msb_size = 0; let mut region_size: u64; let bar_addr: GuestAddress; // Read the BAR size (Starts by all 1s to the BAR) - let bar_offset = PCI_CONFIG_BAR_OFFSET + bar_id * 4; + let bar_offset = if bar_id == VFIO_PCI_ROM_REGION_INDEX { + (PCI_ROM_EXP_BAR_INDEX * 4) as u32 + } else { + PCI_CONFIG_BAR_OFFSET + bar_id * 4 + }; self.vfio_pci_configuration .write_config_dword(lsb_size, bar_offset); @@ -750,15 +754,23 @@ impl PciDevice for VfioPciDevice { } // Is this an IO BAR? - let io_bar = match lsb_flag & PCI_CONFIG_IO_BAR { - PCI_CONFIG_IO_BAR => true, - _ => false, + let io_bar = if bar_id != VFIO_PCI_ROM_REGION_INDEX { + match lsb_flag & PCI_CONFIG_IO_BAR { + PCI_CONFIG_IO_BAR => true, + _ => false, + } + } else { + false }; // Is this a 64-bit BAR? - let is_64bit_bar = match lsb_flag & PCI_CONFIG_MEMORY_BAR_64BIT { - PCI_CONFIG_MEMORY_BAR_64BIT => true, - _ => false, + let is_64bit_bar = if bar_id != VFIO_PCI_ROM_REGION_INDEX { + match lsb_flag & PCI_CONFIG_MEMORY_BAR_64BIT { + PCI_CONFIG_MEMORY_BAR_64BIT => true, + _ => false, + } + } else { + false }; // By default, the region type is 32 bits memory BAR. @@ -810,14 +822,15 @@ impl PciDevice for VfioPciDevice { // In case the BAR is mappable directly, this means it might be // set as KVM user memory region, which expects to deal with 4K // pages. Therefore, the aligment has to be set accordingly. - let bar_alignment = - if self.device.get_region_flags(bar_id) & VFIO_REGION_INFO_FLAG_MMAP != 0 { - // 4K alignment - 0x1000 - } else { - // Default 16 bytes alignment - 0x10 - }; + let bar_alignment = if (bar_id == VFIO_PCI_ROM_REGION_INDEX) + || (self.device.get_region_flags(bar_id) & VFIO_REGION_INFO_FLAG_MMAP != 0) + { + // 4K alignment + 0x1000 + } else { + // Default 16 bytes alignment + 0x10 + }; if is_64bit_bar { bar_addr = allocator .allocate_mmio_addresses(None, region_size, Some(bar_alignment)) @@ -829,16 +842,28 @@ impl PciDevice for VfioPciDevice { } } + let reg_idx = if bar_id == VFIO_PCI_ROM_REGION_INDEX { + PCI_ROM_EXP_BAR_INDEX + } else { + bar_id as usize + }; + // We can now build our BAR configuration block. let config = PciBarConfiguration::default() - .set_register_index(bar_id as usize) + .set_register_index(reg_idx) .set_address(bar_addr.raw_value()) .set_size(region_size) .set_region_type(region_type); - self.configuration - .add_pci_bar(&config) - .map_err(|e| PciDeviceError::IoRegistrationFailed(bar_addr.raw_value(), e))?; + if bar_id == VFIO_PCI_ROM_REGION_INDEX { + self.configuration + .add_pci_rom_bar(&config, lsb_flag & 0x1) + .map_err(|e| PciDeviceError::IoRegistrationFailed(bar_addr.raw_value(), e))?; + } else { + self.configuration + .add_pci_bar(&config) + .map_err(|e| PciDeviceError::IoRegistrationFailed(bar_addr.raw_value(), e))?; + } ranges.push((bar_addr, region_size, region_type)); self.mmio_regions.push(MmioRegion { @@ -864,7 +889,9 @@ impl PciDevice for VfioPciDevice { // When the guest wants to write to a BAR, we trap it into // our local configuration space. We're not reprogramming // VFIO device. - if reg_idx >= PCI_CONFIG_BAR0_INDEX && reg_idx < PCI_CONFIG_BAR0_INDEX + BAR_NUMS { + if (reg_idx >= PCI_CONFIG_BAR0_INDEX && reg_idx < PCI_CONFIG_BAR0_INDEX + BAR_NUMS) + || reg_idx == PCI_ROM_EXP_BAR_INDEX + { // We keep our local cache updated with the BARs. // We'll read it back from there when the guest is asking // for BARs (see read_config_register()). @@ -902,16 +929,12 @@ impl PciDevice for VfioPciDevice { // from our local configuration space. We want the guest to // use that and not the VFIO device BARs as it does not map // with the guest address space. - if reg_idx >= PCI_CONFIG_BAR0_INDEX && reg_idx < PCI_CONFIG_BAR0_INDEX + BAR_NUMS { + if (reg_idx >= PCI_CONFIG_BAR0_INDEX && reg_idx < PCI_CONFIG_BAR0_INDEX + BAR_NUMS) + || reg_idx == PCI_ROM_EXP_BAR_INDEX + { return self.configuration.read_reg(reg_idx); } - // Since the ROM expansion BAR is not yet handled by the code, it is - // more proper to expose it to the guest as being disabled. - if reg_idx == PCI_ROM_EXP_BAR_INDEX { - return 0; - } - // Since we don't support INTx (only MSI and MSI-X), we should not // expose an invalid Interrupt Pin to the guest. By using a specific // mask in case the register being read correspond to the interrupt