summary refs log tree commit diff
path: root/devices
diff options
context:
space:
mode:
authorXiong Zhang <xiong.y.zhang@intel.corp-partner.google.com>2019-11-08 17:43:53 +0800
committerCommit Bot <commit-bot@chromium.org>2020-02-12 14:16:39 +0000
commitc13648b444a77ea850adc7da25859696a4b20578 (patch)
treebc4181d7718915b593175f683bd16566957fe535 /devices
parent17cdcf5deacf82c3e91213121907abd3690d1720 (diff)
downloadcrosvm-c13648b444a77ea850adc7da25859696a4b20578.tar
crosvm-c13648b444a77ea850adc7da25859696a4b20578.tar.gz
crosvm-c13648b444a77ea850adc7da25859696a4b20578.tar.bz2
crosvm-c13648b444a77ea850adc7da25859696a4b20578.tar.lz
crosvm-c13648b444a77ea850adc7da25859696a4b20578.tar.xz
crosvm-c13648b444a77ea850adc7da25859696a4b20578.tar.zst
crosvm-c13648b444a77ea850adc7da25859696a4b20578.zip
Vfio: Create Msix capability for vfio device
Loop vfio device config register, then find out the msi and msix
pci capability.

both msi and msix need IrqRequestSocket for adding its routing info
into kvm routing table, but vfio device has one IrqRequestSocket only,
and only msi or msix is enabled at runtime, so Arc is used to let msi
and msix share one device IrqRequestSocket.

BUG=chromium:992270
TEST=pass a device with msix capability to guest, and check device msix
function in guest

Change-Id: I008ccd0e98507dc4d587418fbe00aa23029bdbad
Signed-off-by: Xiong Zhang <xiong.y.zhang@intel.corp-partner.google.com>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1987812
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Diffstat (limited to 'devices')
-rw-r--r--devices/src/pci/msix.rs5
-rw-r--r--devices/src/pci/vfio_pci.rs134
-rw-r--r--devices/src/virtio/virtio_pci_device.rs5
3 files changed, 107 insertions, 37 deletions
diff --git a/devices/src/pci/msix.rs b/devices/src/pci/msix.rs
index 282771f..0b9f391 100644
--- a/devices/src/pci/msix.rs
+++ b/devices/src/pci/msix.rs
@@ -6,6 +6,7 @@ use crate::pci::{PciCapability, PciCapabilityID};
 use msg_socket::{MsgReceiver, MsgSender};
 use std::convert::TryInto;
 use std::os::unix::io::{AsRawFd, RawFd};
+use std::sync::Arc;
 use sys_util::{error, EventFd};
 use vm_control::{MaybeOwnedFd, VmIrqRequest, VmIrqRequestSocket, VmIrqResponse};
 
@@ -55,12 +56,12 @@ pub struct MsixConfig {
     irq_vec: Vec<IrqfdGsi>,
     masked: bool,
     enabled: bool,
-    msi_device_socket: VmIrqRequestSocket,
+    msi_device_socket: Arc<VmIrqRequestSocket>,
     msix_num: u16,
 }
 
 impl MsixConfig {
-    pub fn new(msix_vectors: u16, vm_socket: VmIrqRequestSocket) -> Self {
+    pub fn new(msix_vectors: u16, vm_socket: Arc<VmIrqRequestSocket>) -> Self {
         assert!(msix_vectors <= MAX_MSIX_VECTORS_PER_DEVICE);
 
         let mut table_entries: Vec<MsixTableEntry> = Vec::new();
diff --git a/devices/src/pci/vfio_pci.rs b/devices/src/pci/vfio_pci.rs
index 216f5eb..216c6b2 100644
--- a/devices/src/pci/vfio_pci.rs
+++ b/devices/src/pci/vfio_pci.rs
@@ -17,6 +17,8 @@ use vm_control::{
     VmMemoryRequest, VmMemoryResponse,
 };
 
+use crate::pci::msix::MsixConfig;
+
 use crate::pci::pci_device::{Error as PciDeviceError, PciDevice};
 use crate::pci::{PciClassCode, PciInterruptPin};
 
@@ -94,6 +96,7 @@ impl VfioPciConfig {
 
 const PCI_CAPABILITY_LIST: u32 = 0x34;
 const PCI_CAP_ID_MSI: u8 = 0x05;
+const PCI_CAP_ID_MSIX: u8 = 0x11;
 
 // MSI registers
 const PCI_MSI_NEXT_POINTER: u32 = 0x1; // Next cap pointer
@@ -122,43 +125,37 @@ struct VfioMsiCap {
     ctl: u16,
     address: u64,
     data: u16,
-    vm_socket_irq: VmIrqRequestSocket,
+    vm_socket_irq: Arc<VmIrqRequestSocket>,
     irqfd: Option<EventFd>,
     gsi: Option<u32>,
 }
 
 impl VfioMsiCap {
-    fn new(config: &VfioPciConfig, vm_socket_irq: VmIrqRequestSocket) -> Option<Self> {
+    fn new(
+        config: &VfioPciConfig,
+        msi_cap_start: u32,
+        vm_socket_irq: Arc<VmIrqRequestSocket>,
+    ) -> Self {
         // msi minimum size is 0xa
         let mut msi_len: u32 = MSI_LENGTH_32BIT;
-        let mut cap_next: u32 = config.read_config_byte(PCI_CAPABILITY_LIST).into();
-        while cap_next != 0 {
-            let cap_id = config.read_config_byte(cap_next);
-            // find msi cap
-            if cap_id == PCI_CAP_ID_MSI {
-                let msi_ctl = config.read_config_word(cap_next + PCI_MSI_FLAGS);
-                if msi_ctl & PCI_MSI_FLAGS_64BIT != 0 {
-                    msi_len = MSI_LENGTH_64BIT_WITHOUT_MASK;
-                }
-                if msi_ctl & PCI_MSI_FLAGS_MASKBIT != 0 {
-                    msi_len = MSI_LENGTH_64BIT_WITH_MASK;
-                }
-                return Some(VfioMsiCap {
-                    offset: cap_next,
-                    size: msi_len,
-                    ctl: 0,
-                    address: 0,
-                    data: 0,
-                    vm_socket_irq,
-                    irqfd: None,
-                    gsi: None,
-                });
-            }
-            let offset = cap_next + PCI_MSI_NEXT_POINTER;
-            cap_next = config.read_config_byte(offset).into();
+        let msi_ctl = config.read_config_word(msi_cap_start + PCI_MSI_FLAGS);
+        if msi_ctl & PCI_MSI_FLAGS_64BIT != 0 {
+            msi_len = MSI_LENGTH_64BIT_WITHOUT_MASK;
+        }
+        if msi_ctl & PCI_MSI_FLAGS_MASKBIT != 0 {
+            msi_len = MSI_LENGTH_64BIT_WITH_MASK;
         }
 
-        None
+        VfioMsiCap {
+            offset: msi_cap_start,
+            size: msi_len,
+            ctl: 0,
+            address: 0,
+            data: 0,
+            vm_socket_irq,
+            irqfd: None,
+            gsi: None,
+        }
     }
 
     fn is_msi_reg(&self, index: u64, len: usize) -> bool {
@@ -290,9 +287,53 @@ impl VfioMsiCap {
     fn get_msi_irqfd(&self) -> Option<&EventFd> {
         self.irqfd.as_ref()
     }
+}
 
-    fn get_vm_socket(&self) -> RawFd {
-        self.vm_socket_irq.as_ref().as_raw_fd()
+// MSI-X registers in MSI-X capability
+const PCI_MSIX_FLAGS: u32 = 0x02; // Message Control
+const PCI_MSIX_FLAGS_QSIZE: u16 = 0x07FF; // Table size
+const PCI_MSIX_TABLE: u32 = 0x04; // Table offset
+const PCI_MSIX_TABLE_BIR: u32 = 0x07; // BAR index
+const PCI_MSIX_TABLE_OFFSET: u32 = 0xFFFFFFF8; // Offset into specified BAR
+const PCI_MSIX_PBA: u32 = 0x08; // Pending bit Array offset
+const PCI_MSIX_PBA_BIR: u32 = 0x07; // BAR index
+const PCI_MSIX_PBA_OFFSET: u32 = 0xFFFFFFF8; // Offset into specified BAR
+
+#[allow(dead_code)]
+struct VfioMsixCap {
+    config: MsixConfig,
+    offset: u32,
+    table_size: u16,
+    table_pci_bar: u32,
+    table_offset: u64,
+    pba_pci_bar: u32,
+    pba_offset: u64,
+}
+
+impl VfioMsixCap {
+    fn new(
+        config: &VfioPciConfig,
+        msix_cap_start: u32,
+        vm_socket_irq: Arc<VmIrqRequestSocket>,
+    ) -> Self {
+        let msix_ctl = config.read_config_word(msix_cap_start + PCI_MSIX_FLAGS);
+        let table_size = (msix_ctl & PCI_MSIX_FLAGS_QSIZE) + 1;
+        let table = config.read_config_dword(msix_cap_start + PCI_MSIX_TABLE);
+        let table_pci_bar = table & PCI_MSIX_TABLE_BIR;
+        let table_offset = (table & PCI_MSIX_TABLE_OFFSET) as u64;
+        let pba = config.read_config_dword(msix_cap_start + PCI_MSIX_PBA);
+        let pba_pci_bar = pba & PCI_MSIX_PBA_BIR;
+        let pba_offset = (pba & PCI_MSIX_PBA_OFFSET) as u64;
+
+        VfioMsixCap {
+            config: MsixConfig::new(table_size, vm_socket_irq),
+            offset: msix_cap_start,
+            table_size,
+            table_pci_bar,
+            table_offset,
+            pba_pci_bar,
+            pba_offset,
+        }
     }
 }
 
@@ -311,6 +352,7 @@ enum DeviceData {
 }
 
 /// Implements the Vfio Pci device, then a pci device is added into vm
+#[allow(dead_code)]
 pub struct VfioPciDevice {
     device: Arc<VfioDevice>,
     config: VfioPciConfig,
@@ -320,8 +362,10 @@ pub struct VfioPciDevice {
     mmio_regions: Vec<MmioInfo>,
     io_regions: Vec<IoInfo>,
     msi_cap: Option<VfioMsiCap>,
+    msix_cap: Option<VfioMsixCap>,
     irq_type: Option<VfioIrqType>,
     vm_socket_mem: VmMemoryControlRequestSocket,
+    vm_socket_irq: Arc<VmIrqRequestSocket>,
     device_data: Option<DeviceData>,
 
     // scratch MemoryMapping to avoid unmap beform vm exit
@@ -337,7 +381,29 @@ impl VfioPciDevice {
     ) -> Self {
         let dev = Arc::new(device);
         let config = VfioPciConfig::new(Arc::clone(&dev));
-        let msi_cap = VfioMsiCap::new(&config, vfio_device_socket_irq);
+        let vm_socket_irq = Arc::new(vfio_device_socket_irq);
+        let mut msi_cap: Option<VfioMsiCap> = None;
+        let mut msix_cap: Option<VfioMsixCap> = None;
+
+        let mut cap_next: u32 = config.read_config_byte(PCI_CAPABILITY_LIST).into();
+        while cap_next != 0 {
+            let cap_id = config.read_config_byte(cap_next);
+            if cap_id == PCI_CAP_ID_MSI {
+                msi_cap = Some(VfioMsiCap::new(
+                    &config,
+                    cap_next,
+                    Arc::clone(&vm_socket_irq),
+                ));
+            } else if cap_id == PCI_CAP_ID_MSIX {
+                msix_cap = Some(VfioMsixCap::new(
+                    &config,
+                    cap_next,
+                    Arc::clone(&vm_socket_irq),
+                ));
+            }
+            let offset = cap_next + PCI_MSI_NEXT_POINTER;
+            cap_next = config.read_config_byte(offset).into();
+        }
 
         let vendor_id = config.read_config_word(PCI_VENDOR_ID);
         let class_code = config.read_config_byte(PCI_BASE_CLASS_CODE);
@@ -361,8 +427,10 @@ impl VfioPciDevice {
             mmio_regions: Vec::new(),
             io_regions: Vec::new(),
             msi_cap,
+            msix_cap,
             irq_type: None,
             vm_socket_mem: vfio_device_socket_mem,
+            vm_socket_irq,
             device_data,
             mem: Vec::new(),
         }
@@ -566,10 +634,8 @@ impl PciDevice for VfioPciDevice {
         if let Some(ref interrupt_resample_evt) = self.interrupt_resample_evt {
             fds.push(interrupt_resample_evt.as_raw_fd());
         }
-        if let Some(msi_cap) = &self.msi_cap {
-            fds.push(msi_cap.get_vm_socket());
-        }
         fds.push(self.vm_socket_mem.as_raw_fd());
+        fds.push(self.vm_socket_irq.as_ref().as_raw_fd());
         fds
     }
 
diff --git a/devices/src/virtio/virtio_pci_device.rs b/devices/src/virtio/virtio_pci_device.rs
index 4b161d4..c6d6786 100644
--- a/devices/src/virtio/virtio_pci_device.rs
+++ b/devices/src/virtio/virtio_pci_device.rs
@@ -252,7 +252,10 @@ impl VirtioPciDevice {
 
         // One MSI-X vector per queue plus one for configuration changes.
         let msix_num = u16::try_from(num_queues + 1).map_err(|_| sys_util::Error::new(ERANGE))?;
-        let msix_config = Arc::new(Mutex::new(MsixConfig::new(msix_num, msi_device_socket)));
+        let msix_config = Arc::new(Mutex::new(MsixConfig::new(
+            msix_num,
+            Arc::new(msi_device_socket),
+        )));
 
         let config_regs = PciConfiguration::new(
             VIRTIO_PCI_VENDOR_ID,