summary refs log tree commit diff
path: root/devices/src/vfio.rs
diff options
context:
space:
mode:
authorXiong Zhang <xiong.y.zhang@intel.corp-partner.google.com>2019-04-23 17:15:00 +0800
committerCommit Bot <commit-bot@chromium.org>2019-10-17 00:17:07 +0000
commitbed8b0017d2cb283c20dc50241adb4f5b2668489 (patch)
tree7a76e936a9e1aede56c08c112ca7d436ab8d9e98 /devices/src/vfio.rs
parent04a82c7be173b2068c4254ed4a129e24e9e3a2e4 (diff)
downloadcrosvm-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.rs52
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 {