diff options
Diffstat (limited to 'devices/src/pci/vfio_pci.rs')
-rw-r--r-- | devices/src/pci/vfio_pci.rs | 134 |
1 files changed, 100 insertions, 34 deletions
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 } |