diff options
author | Xiong Zhang <xiong.y.zhang@intel.corp-partner.google.com> | 2019-04-23 17:15:00 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2019-10-17 00:17:07 +0000 |
commit | bed8b0017d2cb283c20dc50241adb4f5b2668489 (patch) | |
tree | 7a76e936a9e1aede56c08c112ca7d436ab8d9e98 /devices/src/vfio.rs | |
parent | 04a82c7be173b2068c4254ed4a129e24e9e3a2e4 (diff) | |
download | crosvm-bed8b0017d2cb283c20dc50241adb4f5b2668489.tar crosvm-bed8b0017d2cb283c20dc50241adb4f5b2668489.tar.gz crosvm-bed8b0017d2cb283c20dc50241adb4f5b2668489.tar.bz2 crosvm-bed8b0017d2cb283c20dc50241adb4f5b2668489.tar.lz crosvm-bed8b0017d2cb283c20dc50241adb4f5b2668489.tar.xz crosvm-bed8b0017d2cb283c20dc50241adb4f5b2668489.tar.zst crosvm-bed8b0017d2cb283c20dc50241adb4f5b2668489.zip |
vfio: Add msi support
crosvm doesn't support MSI/MSI-x, but kvmgt vgpu support MSI only through cfg msi capability. This is a simple msi implementation, it detects msi capability and track msi control, data and address info, then call vfio kernel to enable / disable msi interrupt. Currently it supports one vetor per MSI. It could extend to multi vetors and MSI-x. BUG=chromium:992270 TEST=none Change-Id: I04fc95f23a07f9698237c014d9f909d011f447ef Signed-off-by: Xiong Zhang <xiong.y.zhang@intel.corp-partner.google.com> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1581142 Reviewed-by: Daniel Verkamp <dverkamp@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
Diffstat (limited to 'devices/src/vfio.rs')
-rw-r--r-- | devices/src/vfio.rs | 52 |
1 files changed, 50 insertions, 2 deletions
diff --git a/devices/src/vfio.rs b/devices/src/vfio.rs index 966085f..cdaadca 100644 --- a/devices/src/vfio.rs +++ b/devices/src/vfio.rs @@ -14,8 +14,8 @@ use std::u32; use kvm::Vm; use sys_util::{ - ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref, ioctl_with_val, warn, Error, - GuestMemory, + ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref, ioctl_with_val, + vec_with_array_field, warn, Error, EventFd, GuestMemory, }; use vfio_sys::*; @@ -38,6 +38,8 @@ pub enum VfioError { InvalidPath, IommuDmaMap(Error), IommuDmaUnmap(Error), + VfioMsiEnable(Error), + VfioMsiDisable(Error), } impl fmt::Display for VfioError { @@ -59,6 +61,8 @@ impl fmt::Display for VfioError { VfioError::InvalidPath => write!(f,"invalid file path"), VfioError::IommuDmaMap(e) => write!(f, "failed to add guest memory map into iommu table: {}", e), VfioError::IommuDmaUnmap(e) => write!(f, "failed to remove guest memory map from iommu table: {}", e), + VfioError::VfioMsiEnable(e) => write!(f, "failed to enable vfio deviece's MSI: {}", e), + VfioError::VfioMsiDisable(e) => write!(f, "failed to disable vfio deviece's MSI: {}", e), } } } @@ -306,6 +310,50 @@ impl VfioDevice { }) } + /// enable vfio device's MSI and associate EventFd with this MSI + pub fn msi_enable(&self, fd: &EventFd) -> Result<(), VfioError> { + let mut irq_set = vec_with_array_field::<vfio_irq_set, u32>(1); + irq_set[0].argsz = (mem::size_of::<vfio_irq_set>() + mem::size_of::<u32>()) as u32; + irq_set[0].flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER; + irq_set[0].index = VFIO_PCI_MSI_IRQ_INDEX; + irq_set[0].start = 0; + irq_set[0].count = 1; + + { + // irq_set.data could be none, bool or fd according to flags, so irq_set.data + // is u8 default, here irq_set.data is fd as u32, so 4 default u8 are combined + // together as u32. It is safe as enough space is reserved through + // vec_with_array_field(u32)<1>. + let fds = unsafe { irq_set[0].data.as_mut_slice(4) }; + fds.copy_from_slice(&fd.as_raw_fd().to_le_bytes()[..]); + } + + // Safe as we are the owner of self and irq_set which are valid value + let ret = unsafe { ioctl_with_ref(self, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) }; + if ret < 0 { + Err(VfioError::VfioMsiEnable(get_error())) + } else { + Ok(()) + } + } + + pub fn msi_disable(&self) -> Result<(), VfioError> { + let mut irq_set = vec_with_array_field::<vfio_irq_set, u32>(0); + irq_set[0].argsz = mem::size_of::<vfio_irq_set>() as u32; + irq_set[0].flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER; + irq_set[0].index = VFIO_PCI_MSI_IRQ_INDEX; + irq_set[0].start = 0; + irq_set[0].count = 0; + + // Safe as we are the owner of self and irq_set which are valid value + let ret = unsafe { ioctl_with_ref(self, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) }; + if ret < 0 { + Err(VfioError::VfioMsiDisable(get_error())) + } else { + Ok(()) + } + } + fn get_regions(dev: &File) -> Result<Vec<VfioRegion>, VfioError> { let mut regions: Vec<VfioRegion> = Vec::new(); let mut dev_info = vfio_device_info { |