diff --git a/pci/src/lib.rs b/pci/src/lib.rs index b10c7756a..b738f324d 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -25,7 +25,7 @@ pub use self::device::{ BarReprogrammingParams, DeviceRelocation, Error as PciDeviceError, InterruptDelivery, InterruptParameters, PciDevice, }; -pub use self::msi::MsiCap; +pub use self::msi::{MsiCap, MsiConfig}; pub use self::msix::{MsixCap, MsixConfig, MsixTableEntry, MSIX_TABLE_ENTRY_SIZE}; use kvm_bindings::{kvm_irq_routing, kvm_irq_routing_entry}; use kvm_ioctls::*; diff --git a/pci/src/msi.rs b/pci/src/msi.rs index 33cce4d99..b1c5ed12b 100644 --- a/pci/src/msi.rs +++ b/pci/src/msi.rs @@ -6,7 +6,13 @@ extern crate byteorder; extern crate vm_memory; +use crate::InterruptRoute; use byteorder::{ByteOrder, LittleEndian}; +use kvm_bindings::kvm_irq_routing_entry; +use kvm_ioctls::VmFd; +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; +use vm_allocator::SystemAllocator; // MSI control masks const MSI_CTL_ENABLE: u16 = 0x1; @@ -58,11 +64,11 @@ impl MsiCap { self.msg_ctl & MSI_CTL_PER_VECTOR == MSI_CTL_PER_VECTOR } - pub fn enabled(&self) -> bool { + fn enabled(&self) -> bool { self.msg_ctl & MSI_CTL_ENABLE == MSI_CTL_ENABLE } - pub fn num_enabled_vectors(&self) -> usize { + fn num_enabled_vectors(&self) -> usize { let field = (self.msg_ctl >> 4) & 0x7; if field > 5 { @@ -72,7 +78,7 @@ impl MsiCap { 1 << field } - pub fn vector_masked(&self, vector: usize) -> bool { + fn vector_masked(&self, vector: usize) -> bool { if !self.per_vector_mask() { return false; } @@ -80,7 +86,7 @@ impl MsiCap { (self.mask_bits >> vector) & 0x1 == 0x1 } - pub fn size(&self) -> u64 { + fn size(&self) -> u64 { let mut size: u64 = 0xa; if self.addr_64_bits() { @@ -93,7 +99,7 @@ impl MsiCap { size } - pub fn update(&mut self, offset: u64, data: &[u8]) { + fn update(&mut self, offset: u64, data: &[u8]) { // Calculate message data offset depending on the address being 32 or // 64 bits. // Calculate upper address offset if the address is 64 bits. @@ -151,3 +157,56 @@ impl MsiCap { } } } + +pub struct MsiConfig { + pub cap: MsiCap, + pub irq_routes: Vec, + _vm_fd: Arc, + _gsi_msi_routes: Arc>>, +} + +impl MsiConfig { + pub fn new( + msg_ctl: u16, + allocator: &mut SystemAllocator, + vm_fd: Arc, + gsi_msi_routes: Arc>>, + ) -> Self { + let cap = MsiCap { + msg_ctl, + ..Default::default() + }; + + let mut irq_routes: Vec = Vec::new(); + for _ in 0..cap.num_enabled_vectors() { + irq_routes.push(InterruptRoute::new(allocator).unwrap()); + } + + MsiConfig { + cap, + irq_routes, + _vm_fd: vm_fd, + _gsi_msi_routes: gsi_msi_routes, + } + } + + pub fn enabled(&self) -> bool { + self.cap.enabled() + } + + pub fn update(&mut self, offset: u64, data: &[u8]) { + self.cap.update(offset, data) + } + + pub fn size(&self) -> u64 { + self.cap.size() + } + + pub fn num_enabled_vectors(&self) -> usize { + self.cap.num_enabled_vectors() + } + + pub fn vector_masked(&self, vector: usize) -> bool { + self.cap.vector_masked(vector) + } +} diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index ed9d0dc2c..44c636e73 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -16,7 +16,7 @@ use kvm_bindings::{ }; use kvm_ioctls::*; use pci::{ - BarReprogrammingParams, MsiCap, MsixCap, MsixConfig, PciBarConfiguration, PciBarRegionType, + BarReprogrammingParams, MsiConfig, MsixCap, MsixConfig, PciBarConfiguration, PciBarRegionType, PciCapabilityID, PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, PciSubclass, MSIX_TABLE_ENTRY_SIZE, }; @@ -79,19 +79,18 @@ enum InterruptUpdateAction { DisableMsix, } -#[derive(Copy, Clone)] struct VfioMsi { - cap: MsiCap, + cfg: MsiConfig, cap_offset: u32, } impl VfioMsi { fn update(&mut self, offset: u64, data: &[u8]) -> Option { - let old_enabled = self.cap.enabled(); + let old_enabled = self.cfg.enabled(); - self.cap.update(offset, data); + self.cfg.update(offset, data); - let new_enabled = self.cap.enabled(); + let new_enabled = self.cfg.enabled(); if !old_enabled && new_enabled { return Some(InterruptUpdateAction::EnableMsi); @@ -169,7 +168,7 @@ impl Interrupt { fn accessed(&self, offset: u64) -> Option<(PciCapabilityID, u64)> { if let Some(msi) = &self.msi { if offset >= u64::from(msi.cap_offset) - && offset < u64::from(msi.cap_offset) + msi.cap.size() + && offset < u64::from(msi.cap_offset) + msi.cfg.size() { return Some(( PciCapabilityID::MessageSignalledInterrupts, @@ -426,16 +425,18 @@ impl VfioPciDevice { }); } - fn parse_msi_capabilities(&mut self, cap: u8) { + fn parse_msi_capabilities(&mut self, cap: u8, allocator: &mut SystemAllocator) { let msg_ctl = self .vfio_pci_configuration .read_config_word((cap + 2).into()); self.interrupt.msi = Some(VfioMsi { - cap: MsiCap { + cfg: MsiConfig::new( msg_ctl, - ..Default::default() - }, + allocator, + self.vm_fd.clone(), + self.gsi_msi_routes.clone(), + ), cap_offset: cap.into(), }); } @@ -452,7 +453,7 @@ impl VfioPciDevice { match PciCapabilityID::from(cap_id) { PciCapabilityID::MessageSignalledInterrupts => { - self.parse_msi_capabilities(cap_next); + self.parse_msi_capabilities(cap_next, allocator); } PciCapabilityID::MSIX => { self.parse_msix_capabilities(cap_next, allocator); @@ -467,7 +468,7 @@ impl VfioPciDevice { } fn update_msi_interrupt_routes(&self, msi: &VfioMsi) -> Result<()> { - if msi.cap.enabled() { + if msi.cfg.enabled() { let mut gsi_msi_routes = self.gsi_msi_routes.lock().unwrap(); for (idx, route) in self.interrupt_routes.iter().enumerate() { @@ -475,12 +476,12 @@ impl VfioPciDevice { // guest OS does not match the expected amount. This is related // to "Multiple Message Capable" and "Multiple Message Enable" // fields from the "Message Control" register. - if idx >= msi.cap.num_enabled_vectors() { + if idx >= msi.cfg.num_enabled_vectors() { continue; } // Ignore MSI vector if masked. - if msi.cap.vector_masked(idx) { + if msi.cfg.vector_masked(idx) { continue; } @@ -490,9 +491,9 @@ impl VfioPciDevice { ..Default::default() }; - entry.u.msi.address_lo = msi.cap.msg_addr_lo; - entry.u.msi.address_hi = msi.cap.msg_addr_hi; - entry.u.msi.data = u32::from(msi.cap.msg_data) | (idx as u32); + entry.u.msi.address_lo = msi.cfg.cap.msg_addr_lo; + entry.u.msi.address_hi = msi.cfg.cap.msg_addr_hi; + entry.u.msi.data = u32::from(msi.cfg.cap.msg_data) | (idx as u32); gsi_msi_routes.insert(route.gsi, entry); } @@ -693,7 +694,7 @@ impl Drop for VfioPciDevice { } if let Some(msi) = &self.interrupt.msi { - if msi.cap.enabled() && self.device.disable_msi().is_err() { + if msi.cfg.enabled() && self.device.disable_msi().is_err() { error!("Could not disable MSI"); } }