summary refs log tree commit diff
path: root/devices/src/pci/vfio_pci.rs
diff options
context:
space:
mode:
authorXiong Zhang <xiong.y.zhang@intel.corp-partner.google.com>2019-06-21 13:56:52 +0800
committerCommit Bot <commit-bot@chromium.org>2020-01-07 05:58:46 +0000
commit5c6bf3e32ded73afae857b10d5ddd6ca1cfaceb9 (patch)
tree5d2c9d33ecdc894337359e15b18a244d4d7056d6 /devices/src/pci/vfio_pci.rs
parentf2eecc4152eca8d395566cffa2c102ec090a152d (diff)
downloadcrosvm-5c6bf3e32ded73afae857b10d5ddd6ca1cfaceb9.tar
crosvm-5c6bf3e32ded73afae857b10d5ddd6ca1cfaceb9.tar.gz
crosvm-5c6bf3e32ded73afae857b10d5ddd6ca1cfaceb9.tar.bz2
crosvm-5c6bf3e32ded73afae857b10d5ddd6ca1cfaceb9.tar.lz
crosvm-5c6bf3e32ded73afae857b10d5ddd6ca1cfaceb9.tar.xz
crosvm-5c6bf3e32ded73afae857b10d5ddd6ca1cfaceb9.tar.zst
crosvm-5c6bf3e32ded73afae857b10d5ddd6ca1cfaceb9.zip
Vfio: Add igd Opregion support
igd opregion is used by igd driver to get vbt info and exhange info
between bios and driver, but it isn't a standard pci resource, host
bios allocate, reserve its memory, and report the memory base address
through cfg_register 0xFC on native.

As crosvm doesn't have bios, it is hard to allocate and reserve opregion
for guest. Here opregion is faked as mmio memory, and let crosvm
allocate guest memory from mmio space, report its base to cfg_register
0xFC also.

guest driver read cfg_register 0xFC to get opregion base address, then rw
it throgh this address. Read is forwarded to vfio kernel and write is
ignored.

BUG=chromium:992270
TEST=crosvm --vfio /sys/devices/pci0000:00/0000:00:02.0, pass through
host igd into linux guest, the physical local display lightup and show linux desktop.

Change-Id: I1cc3618e99313fc1f88b96dcbc635f090b19340c
Signed-off-by: Xiong Zhang <xiong.y.zhang@intel.corp-partner.google.com>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1688689
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Diffstat (limited to 'devices/src/pci/vfio_pci.rs')
-rw-r--r--devices/src/pci/vfio_pci.rs63
1 files changed, 58 insertions, 5 deletions
diff --git a/devices/src/pci/vfio_pci.rs b/devices/src/pci/vfio_pci.rs
index f0cebde..38615d9 100644
--- a/devices/src/pci/vfio_pci.rs
+++ b/devices/src/pci/vfio_pci.rs
@@ -305,7 +305,7 @@ struct IoInfo {
 }
 
 enum DeviceData {
-    IntelGfxData,
+    IntelGfxData { opregion_index: u32 },
 }
 
 /// Implements the Vfio Pci device, then a pci device is added into vm
@@ -343,7 +343,9 @@ impl VfioPciDevice {
         let is_intel_gfx = vendor_id == INTEL_VENDOR_ID
             && class_code == PciClassCode::DisplayController.get_register_value();
         let device_data = if is_intel_gfx {
-            Some(DeviceData::IntelGfxData)
+            Some(DeviceData::IntelGfxData {
+                opregion_index: u32::max_value(),
+            })
         } else {
             None
         };
@@ -369,7 +371,7 @@ impl VfioPciDevice {
 
         if let Some(device_data) = &self.device_data {
             match *device_data {
-                DeviceData::IntelGfxData => ret = true,
+                DeviceData::IntelGfxData { .. } => ret = true,
             }
         }
 
@@ -686,9 +688,49 @@ impl PciDevice for VfioPciDevice {
 
     fn allocate_device_bars(
         &mut self,
-        _resources: &mut SystemAllocator,
+        resources: &mut SystemAllocator,
     ) -> Result<Vec<(u64, u64)>, PciDeviceError> {
-        Ok(Vec::new())
+        let mut ranges = Vec::new();
+
+        if !self.is_intel_gfx() {
+            return Ok(ranges);
+        }
+
+        // Make intel gfx's opregion as mmio bar, and allocate a gpa for it
+        // then write this gpa into pci cfg register
+        if let Some((index, size)) = self.device.get_cap_type_info(
+            VFIO_REGION_TYPE_PCI_VENDOR_TYPE | (INTEL_VENDOR_ID as u32),
+            VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION,
+        ) {
+            let (bus, dev) = self
+                .pci_bus_dev
+                .expect("assign_bus_dev must be called prior to allocate_device_bars");
+            let bar_addr = resources
+                .mmio_allocator(MmioType::Low)
+                .allocate(
+                    size,
+                    Alloc::PciBar {
+                        bus,
+                        dev,
+                        bar: (index * 4) as u8,
+                    },
+                    "vfio_bar".to_string(),
+                )
+                .map_err(|e| PciDeviceError::IoAllocationFailed(size, e))?;
+            ranges.push((bar_addr, size));
+            self.device_data = Some(DeviceData::IntelGfxData {
+                opregion_index: index,
+            });
+
+            self.mmio_regions.push(MmioInfo {
+                bar_index: index,
+                start: bar_addr,
+                length: size,
+            });
+            self.config.write_config_dword(bar_addr as u32, 0xFC);
+        }
+
+        Ok(ranges)
     }
 
     fn register_device_capabilities(&mut self) -> Result<(), PciDeviceError> {
@@ -759,6 +801,17 @@ impl PciDevice for VfioPciDevice {
 
     fn write_bar(&mut self, addr: u64, data: &[u8]) {
         if let Some(mmio_info) = self.find_region(addr) {
+            // Ignore igd opregion's write
+            if let Some(device_data) = &self.device_data {
+                match *device_data {
+                    DeviceData::IntelGfxData { opregion_index } => {
+                        if opregion_index == mmio_info.bar_index {
+                            return;
+                        }
+                    }
+                }
+            }
+
             let offset = addr - mmio_info.start;
             self.device.region_write(mmio_info.bar_index, data, offset);
         }