diff --git a/arch/src/lib.rs b/arch/src/lib.rs index 096cef78c1..4e3ca29819 100644 --- a/arch/src/lib.rs +++ b/arch/src/lib.rs @@ -343,34 +343,23 @@ pub fn configure_pci_device( .allocate_device_bars(resources) .map_err(DeviceRegistrationError::AllocateDeviceAddrs)?; + // Do not suggest INTx for hot-plug devices. + let intx_event = Event::new().map_err(DeviceRegistrationError::EventCreate)?; + let intx_resample = Event::new().map_err(DeviceRegistrationError::EventCreate)?; + + if let Some((gsi, _pin)) = device.assign_irq(&intx_event, &intx_resample, None) { + resources.reserve_irq(gsi); + + linux + .irq_chip + .as_irq_chip_mut() + .register_irq_event(gsi, &intx_event, Some(&intx_resample)) + .map_err(DeviceRegistrationError::RegisterIrqfd)?; + } + let mut keep_rds = device.keep_rds(); syslog::push_descriptors(&mut keep_rds); - let irqfd = Event::new().map_err(DeviceRegistrationError::EventCreate)?; - let irq_resample_fd = Event::new().map_err(DeviceRegistrationError::EventCreate)?; - - let irq_num = resources - .allocate_irq() - .ok_or(DeviceRegistrationError::AllocateIrq)?; - - // Rotate interrupt pins across PCI logical functions. - let pci_irq_pin = match pci_address.func % 4 { - 0 => PciInterruptPin::IntA, - 1 => PciInterruptPin::IntB, - 2 => PciInterruptPin::IntC, - 3 => PciInterruptPin::IntD, - _ => unreachable!(), // Obviously not possible, but the compiler is not smart enough. - }; - - linux - .irq_chip - .as_irq_chip_mut() - .register_irq_event(irq_num, &irqfd, Some(&irq_resample_fd)) - .map_err(DeviceRegistrationError::RegisterIrqfd)?; - - keep_rds.push(irqfd.as_raw_descriptor()); - keep_rds.push(irq_resample_fd.as_raw_descriptor()); - device.assign_irq(irqfd, irq_resample_fd, irq_num, pci_irq_pin); device .register_device_capabilities() .map_err(DeviceRegistrationError::RegisterDeviceCapabilities)?; @@ -434,11 +423,8 @@ pub fn generate_pci_root( DeviceRegistrationError, > { let mut root = PciRoot::new(mmio_bus.clone(), io_bus.clone()); - let mut pci_irqs = Vec::new(); let mut pid_labels = BTreeMap::new(); - let mut irqs: Vec> = vec![None; max_irqs]; - // Allocate PCI device address before allocating BARs. let mut device_addrs = Vec::::new(); for (device, _jail) in devices.iter_mut() { @@ -466,14 +452,12 @@ pub fn generate_pci_root( device_ranges.insert(dev_idx, ranges); } - for (dev_idx, (mut device, jail)) in devices.into_iter().enumerate() { - let address = device_addrs[dev_idx]; - let mut keep_rds = device.keep_rds(); - syslog::push_descriptors(&mut keep_rds); - keep_rds.append(&mut vm.get_memory().as_raw_descriptors()); + // Allocate legacy INTx + let mut pci_irqs = Vec::new(); + let mut irqs: Vec> = vec![None; max_irqs]; - let irqfd = Event::new().map_err(DeviceRegistrationError::EventCreate)?; - let irq_resample_fd = Event::new().map_err(DeviceRegistrationError::EventCreate)?; + for (dev_idx, (device, _jail)) in devices.iter_mut().enumerate() { + // For default interrupt routing use next preallocated interrupt from the pool. let irq_num = if let Some(irq) = irqs[dev_idx % max_irqs] { irq } else { @@ -483,23 +467,30 @@ pub fn generate_pci_root( irqs[dev_idx % max_irqs] = Some(irq); irq }; - // Rotate interrupt pins across PCI logical functions. - let pci_irq_pin = match address.func % 4 { - 0 => PciInterruptPin::IntA, - 1 => PciInterruptPin::IntB, - 2 => PciInterruptPin::IntC, - 3 => PciInterruptPin::IntD, - _ => unreachable!(), // Obviously not possible, but the compiler is not smart enough. - }; - irq_chip - .register_irq_event(irq_num, &irqfd, Some(&irq_resample_fd)) - .map_err(DeviceRegistrationError::RegisterIrqfd)?; + let intx_event = Event::new().map_err(DeviceRegistrationError::EventCreate)?; + let intx_resample = Event::new().map_err(DeviceRegistrationError::EventCreate)?; + + if let Some((gsi, pin)) = device.assign_irq(&intx_event, &intx_resample, Some(irq_num)) { + // reserve INTx if needed and non-default. + if gsi != irq_num { + resources.reserve_irq(gsi); + }; + irq_chip + .register_irq_event(gsi, &intx_event, Some(&intx_resample)) + .map_err(DeviceRegistrationError::RegisterIrqfd)?; + + pci_irqs.push((device_addrs[dev_idx], gsi, pin)); + } + } + + for (dev_idx, (mut device, jail)) in devices.into_iter().enumerate() { + let address = device_addrs[dev_idx]; + + let mut keep_rds = device.keep_rds(); + syslog::push_descriptors(&mut keep_rds); + keep_rds.append(&mut vm.get_memory().as_raw_descriptors()); - keep_rds.push(irqfd.as_raw_descriptor()); - keep_rds.push(irq_resample_fd.as_raw_descriptor()); - device.assign_irq(irqfd, irq_resample_fd, irq_num, pci_irq_pin); - pci_irqs.push((address, irq_num, pci_irq_pin)); let ranges = io_ranges.remove(&dev_idx).unwrap_or_default(); let device_ranges = device_ranges.remove(&dev_idx).unwrap_or_default(); device @@ -511,6 +502,7 @@ pub fn generate_pci_root( .map_err(DeviceRegistrationError::RegisterIoevent)?; keep_rds.push(event.as_raw_descriptor()); } + let arced_dev: Arc> = if let Some(jail) = jail { let proxy = ProxyDevice::new(device, &jail, keep_rds) .map_err(DeviceRegistrationError::ProxyDeviceCreation)?; diff --git a/devices/src/pci/ac97.rs b/devices/src/pci/ac97.rs index e0c4fae23c..d053f858f9 100644 --- a/devices/src/pci/ac97.rs +++ b/devices/src/pci/ac97.rs @@ -298,14 +298,15 @@ impl PciDevice for Ac97Dev { fn assign_irq( &mut self, - irq_evt: Event, - irq_resample_evt: Event, - irq_num: u32, - irq_pin: PciInterruptPin, - ) { - self.config_regs.set_irq(irq_num as u8, irq_pin); - self.irq_evt = Some(irq_evt); - self.irq_resample_evt = Some(irq_resample_evt); + irq_evt: &Event, + irq_resample_evt: &Event, + irq_num: Option, + ) -> Option<(u32, PciInterruptPin)> { + self.irq_evt = Some(irq_evt.try_clone().ok()?); + self.irq_resample_evt = Some(irq_resample_evt.try_clone().ok()?); + let gsi = irq_num?; + self.config_regs.set_irq(gsi as u8, PciInterruptPin::IntA); + Some((gsi, PciInterruptPin::IntA)) } fn allocate_io_bars(&mut self, resources: &mut SystemAllocator) -> Result> { diff --git a/devices/src/pci/pci_device.rs b/devices/src/pci/pci_device.rs index 1c736ed2ac..9acf165538 100644 --- a/devices/src/pci/pci_device.rs +++ b/devices/src/pci/pci_device.rs @@ -89,13 +89,15 @@ pub trait PciDevice: Send { /// Assign a legacy PCI IRQ to this device. /// The device may write to `irq_evt` to trigger an interrupt. /// When `irq_resample_evt` is signaled, the device should re-assert `irq_evt` if necessary. + /// Optional irq_num can be used for default INTx allocation, device can overwrite it. + /// If legacy INTx is used, function shall return requested IRQ number and PCI INTx pin. fn assign_irq( &mut self, - _irq_evt: Event, - _irq_resample_evt: Event, - _irq_num: u32, - _irq_pin: PciInterruptPin, - ) { + _irq_evt: &Event, + _irq_resample_evt: &Event, + _irq_num: Option, + ) -> Option<(u32, PciInterruptPin)> { + None } /// Allocates the needed IO BAR space using the `allocate` function which takes a size and /// returns an address. Returns a Vec of (address, length) tuples. @@ -222,12 +224,11 @@ impl PciDevice for Box { } fn assign_irq( &mut self, - irq_evt: Event, - irq_resample_evt: Event, - irq_num: u32, - irq_pin: PciInterruptPin, - ) { - (**self).assign_irq(irq_evt, irq_resample_evt, irq_num, irq_pin) + irq_evt: &Event, + irq_resample_evt: &Event, + irq_num: Option, + ) -> Option<(u32, PciInterruptPin)> { + (**self).assign_irq(irq_evt, irq_resample_evt, irq_num) } fn allocate_io_bars(&mut self, resources: &mut SystemAllocator) -> Result> { (**self).allocate_io_bars(resources) diff --git a/devices/src/pci/pcie.rs b/devices/src/pci/pcie.rs index 2a797e8128..a2af30ad0b 100644 --- a/devices/src/pci/pcie.rs +++ b/devices/src/pci/pcie.rs @@ -109,17 +109,19 @@ impl PciDevice for PciBridge { fn assign_irq( &mut self, - irq_evt: Event, - irq_resample_evt: Event, - irq_num: u32, - irq_pin: PciInterruptPin, - ) { - self.config.set_irq(irq_num as u8, irq_pin); - self.interrupt_evt = Some(irq_evt); - self.interrupt_resample_evt = Some(irq_resample_evt); - + irq_evt: &Event, + irq_resample_evt: &Event, + irq_num: Option, + ) -> Option<(u32, PciInterruptPin)> { + self.interrupt_evt = Some(irq_evt.try_clone().ok()?); + self.interrupt_resample_evt = Some(irq_resample_evt.try_clone().ok()?); let msix_config_clone = self.msix_config.clone(); self.device.clone_interrupt(msix_config_clone); + + let gsi = irq_num?; + self.config.set_irq(gsi as u8, PciInterruptPin::IntA); + + Some((gsi, PciInterruptPin::IntA)) } fn allocate_io_bars( diff --git a/devices/src/pci/vfio_pci.rs b/devices/src/pci/vfio_pci.rs index 0c059c8f7c..e8b8bb5e4e 100644 --- a/devices/src/pci/vfio_pci.rs +++ b/devices/src/pci/vfio_pci.rs @@ -4,6 +4,8 @@ use std::cmp::{max, min}; use std::collections::BTreeSet; +use std::fs; +use std::path::PathBuf; use std::sync::Arc; use std::u32; @@ -35,7 +37,7 @@ const INTEL_VENDOR_ID: u16 = 0x8086; const PCI_COMMAND: u32 = 0x4; const PCI_COMMAND_MEMORY: u8 = 0x2; const PCI_BASE_CLASS_CODE: u32 = 0x0B; - +const PCI_INTERRUPT_NUM: u32 = 0x3C; const PCI_INTERRUPT_PIN: u32 = 0x3D; struct VfioPciConfig { @@ -932,19 +934,38 @@ impl PciDevice for VfioPciDevice { fn assign_irq( &mut self, - irq_evt: Event, - irq_resample_evt: Event, - irq_num: u32, - _irq_pin: PciInterruptPin, - ) { - self.config.write_config_byte(irq_num as u8, 0x3C); - self.interrupt_evt = Some(irq_evt); - self.interrupt_resample_evt = Some(irq_resample_evt); + irq_evt: &Event, + irq_resample_evt: &Event, + _irq_num: Option, + ) -> Option<(u32, PciInterruptPin)> { + // Keep event/resample event references. + self.interrupt_evt = Some(irq_evt.try_clone().ok()?); + self.interrupt_resample_evt = Some(irq_resample_evt.try_clone().ok()?); + + // Is INTx configured? + let pin = match self.config.read_config_byte(PCI_INTERRUPT_PIN) { + 1 => Some(PciInterruptPin::IntA), + 2 => Some(PciInterruptPin::IntB), + 3 => Some(PciInterruptPin::IntC), + 4 => Some(PciInterruptPin::IntD), + _ => None, + }?; // enable INTX - if self.config.read_config_byte(PCI_INTERRUPT_PIN) > 0 { - self.enable_intx(); - } + self.enable_intx(); + + // TODO: replace sysfs/irq value parsing with vfio interface + // reporting host allocated interrupt number and type. + let mut path = PathBuf::from("/sys/bus/pci/devices"); + path.push(self.device.device_name()); + path.push("irq"); + let gsi = fs::read_to_string(path) + .map(|v| v.trim().parse::().unwrap_or(0)) + .unwrap_or(0); + + self.config.write_config_byte(gsi as u8, PCI_INTERRUPT_NUM); + + Some((gsi, pin)) } fn allocate_io_bars( diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index b0257fc339..76f00f303b 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -13,7 +13,7 @@ use crate::usb::xhci::xhci::Xhci; use crate::usb::xhci::xhci_backend_device_provider::XhciBackendDeviceProvider; use crate::usb::xhci::xhci_regs::{init_xhci_mmio_space_and_regs, XhciRegs}; use crate::utils::FailHandle; -use base::{error, Event, RawDescriptor}; +use base::{error, AsRawDescriptor, Event, RawDescriptor}; use resources::{Alloc, MmioType, SystemAllocator}; use std::mem; use std::sync::atomic::{AtomicBool, Ordering}; @@ -189,6 +189,16 @@ impl PciDevice for XhciController { fn keep_rds(&self) -> Vec { match &self.state { XhciControllerState::Created { device_provider } => device_provider.keep_rds(), + XhciControllerState::IrqAssigned { + device_provider, + irq_evt, + irq_resample_evt, + } => { + let mut keep_rds = device_provider.keep_rds(); + keep_rds.push(irq_evt.as_raw_descriptor()); + keep_rds.push(irq_resample_evt.as_raw_descriptor()); + keep_rds + } _ => { error!("xhci controller is in a wrong state"); vec![] @@ -198,24 +208,25 @@ impl PciDevice for XhciController { fn assign_irq( &mut self, - irq_evt: Event, - irq_resample_evt: Event, - irq_num: u32, - irq_pin: PciInterruptPin, - ) { + irq_evt: &Event, + irq_resample_evt: &Event, + irq_num: Option, + ) -> Option<(u32, PciInterruptPin)> { + let gsi = irq_num?; match mem::replace(&mut self.state, XhciControllerState::Unknown) { XhciControllerState::Created { device_provider } => { - self.config_regs.set_irq(irq_num as u8, irq_pin); + self.config_regs.set_irq(gsi as u8, PciInterruptPin::IntA); self.state = XhciControllerState::IrqAssigned { device_provider, - irq_evt, - irq_resample_evt, + irq_evt: irq_evt.try_clone().ok()?, + irq_resample_evt: irq_resample_evt.try_clone().ok()?, } } _ => { error!("xhci controller is in a wrong state"); } } + Some((gsi, PciInterruptPin::IntA)) } fn allocate_io_bars( diff --git a/devices/src/virtio/virtio_pci_device.rs b/devices/src/virtio/virtio_pci_device.rs index 8652820240..9c726bd81f 100644 --- a/devices/src/virtio/virtio_pci_device.rs +++ b/devices/src/virtio/virtio_pci_device.rs @@ -440,14 +440,15 @@ impl PciDevice for VirtioPciDevice { fn assign_irq( &mut self, - irq_evt: Event, - irq_resample_evt: Event, - irq_num: u32, - irq_pin: PciInterruptPin, - ) { - self.config_regs.set_irq(irq_num as u8, irq_pin); - self.interrupt_evt = Some(irq_evt); - self.interrupt_resample_evt = Some(irq_resample_evt); + irq_evt: &Event, + irq_resample_evt: &Event, + irq_num: Option, + ) -> Option<(u32, PciInterruptPin)> { + self.interrupt_evt = Some(irq_evt.try_clone().ok()?); + self.interrupt_resample_evt = Some(irq_resample_evt.try_clone().ok()?); + let gsi = irq_num?; + self.config_regs.set_irq(gsi as u8, PciInterruptPin::IntA); + Some((gsi, PciInterruptPin::IntA)) } fn allocate_io_bars(