mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2024-11-24 20:48:55 +00:00
crosvm/pci: update to PCI/INTx allocation
For VFIO devices, allow host interrupt allocation numbering to match ACPI _CRS. BUG=b:179648314 TEST=./test_all, boot on sytem with vfio devices connected. Change-Id: Ied1153f3fc72876d8f3df8822ff6d4e869c40f62 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2893367 Tested-by: kokoro <noreply+kokoro@google.com> Commit-Queue: Tomasz Jeznach <tjeznach@chromium.org> Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
This commit is contained in:
parent
737ff125ca
commit
7ab02aa85b
7 changed files with 136 additions and 107 deletions
|
@ -343,34 +343,23 @@ pub fn configure_pci_device<V: VmArch, Vcpu: VcpuArch>(
|
|||
.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<Option<u32>> = vec![None; max_irqs];
|
||||
|
||||
// Allocate PCI device address before allocating BARs.
|
||||
let mut device_addrs = Vec::<PciAddress>::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<Option<u32>> = 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<Mutex<dyn BusDevice>> = if let Some(jail) = jail {
|
||||
let proxy = ProxyDevice::new(device, &jail, keep_rds)
|
||||
.map_err(DeviceRegistrationError::ProxyDeviceCreation)?;
|
||||
|
|
|
@ -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<u32>,
|
||||
) -> 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<Vec<(u64, u64)>> {
|
||||
|
|
|
@ -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<u32>,
|
||||
) -> 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<T: PciDevice + ?Sized> PciDevice for Box<T> {
|
|||
}
|
||||
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<u32>,
|
||||
) -> Option<(u32, PciInterruptPin)> {
|
||||
(**self).assign_irq(irq_evt, irq_resample_evt, irq_num)
|
||||
}
|
||||
fn allocate_io_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<(u64, u64)>> {
|
||||
(**self).allocate_io_bars(resources)
|
||||
|
|
|
@ -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<u32>,
|
||||
) -> 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(
|
||||
|
|
|
@ -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<u32>,
|
||||
) -> 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::<u32>().unwrap_or(0))
|
||||
.unwrap_or(0);
|
||||
|
||||
self.config.write_config_byte(gsi as u8, PCI_INTERRUPT_NUM);
|
||||
|
||||
Some((gsi, pin))
|
||||
}
|
||||
|
||||
fn allocate_io_bars(
|
||||
|
|
|
@ -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<RawDescriptor> {
|
||||
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<u32>,
|
||||
) -> 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(
|
||||
|
|
|
@ -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<u32>,
|
||||
) -> 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(
|
||||
|
|
Loading…
Reference in a new issue