diff options
author | Xiong Zhang <xiong.y.zhang@intel.corp-partner.google.com> | 2019-06-21 13:56:52 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-01-07 05:58:46 +0000 |
commit | 5c6bf3e32ded73afae857b10d5ddd6ca1cfaceb9 (patch) | |
tree | 5d2c9d33ecdc894337359e15b18a244d4d7056d6 /devices/src | |
parent | f2eecc4152eca8d395566cffa2c102ec090a152d (diff) | |
download | crosvm-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')
-rw-r--r-- | devices/src/pci/vfio_pci.rs | 63 | ||||
-rw-r--r-- | devices/src/vfio.rs | 36 |
2 files changed, 94 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); } diff --git a/devices/src/vfio.rs b/devices/src/vfio.rs index f8c15c8..791dc78 100644 --- a/devices/src/vfio.rs +++ b/devices/src/vfio.rs @@ -289,6 +289,8 @@ struct VfioRegion { offset: u64, // vectors for mmap offset and size mmaps: Vec<vfio_region_sparse_mmap_area>, + // type and subtype for cap type + cap_info: Option<(u32, u32)>, } /// Vfio device for exposing regions which could be read/write to kernel vfio device. @@ -497,6 +499,7 @@ impl VfioDevice { } let mut mmaps: Vec<vfio_region_sparse_mmap_area> = Vec::new(); + let mut cap_info: Option<(u32, u32)> = None; if reg_info.argsz > argsz { let cap_len: usize = (reg_info.argsz - argsz) as usize; let mut region_with_cap = @@ -527,6 +530,7 @@ impl VfioDevice { let cap_header_sz = mem::size_of::<vfio_info_cap_header>() as u32; let mmap_cap_sz = mem::size_of::<vfio_region_info_cap_sparse_mmap>() as u32; let mmap_area_sz = mem::size_of::<vfio_region_sparse_mmap_area>() as u32; + let type_cap_sz = mem::size_of::<vfio_region_info_cap_type>() as u32; let region_info_sz = reg_info.argsz; // region_with_cap[0].cap_info may contain many structures, like @@ -567,6 +571,17 @@ impl VfioDevice { for area in areas.iter() { mmaps.push(area.clone()); } + } else if cap_header.id as u32 == VFIO_REGION_INFO_CAP_TYPE { + if offset + type_cap_sz > region_info_sz { + break; + } + // cap_ptr is vfio_region_info_cap_type here + // Safe, this vfio_region_info_cap_type is in this function allocated + // region_with_cap vec + let cap_type_info = + unsafe { &*(cap_ptr as *mut u8 as *const vfio_region_info_cap_type) }; + + cap_info = Some((cap_type_info.type_, cap_type_info.subtype)); } offset = cap_header.next; @@ -583,6 +598,7 @@ impl VfioDevice { size: reg_info.size, offset: reg_info.offset, mmaps, + cap_info, }; regions.push(region); } @@ -629,6 +645,26 @@ impl VfioDevice { } } + /// find the specified cap type in device regions + /// Input: + /// type_: cap type + /// sub_type: cap sub_type + /// Output: + /// None: device doesn't have the specified cap type + /// Some((bar_index, region_size)): device has the specified cap type, return region's + /// index and size + pub fn get_cap_type_info(&self, type_: u32, sub_type: u32) -> Option<(u32, u64)> { + for (index, region) in self.regions.iter().enumerate() { + if let Some(cap_info) = ®ion.cap_info { + if cap_info.0 == type_ && cap_info.1 == sub_type { + return Some((index as u32, region.size)); + } + } + } + + None + } + /// Read region's data from VFIO device into buf /// index: region num /// buf: data destination and buf length is read size |