pci: Add minimal PCI host emulation crate

This crate is based on the crosvm devices/src/pci implementation from 107edb3e
We introduced a few changes:

- This one is a standalone crate. The device crate does not carry any
  PCI specific bits.
- Simplified PCI root configuration. We only carry a pointer to a
  PciConfiguration, not a wrapper around it.
- Simplified BAR allocation API. All BARs from the PciDevice instance
  must be generated at once through the PciDevice.allocate_bars()
  method.
- The PCI BARs are added to the MMIO bus from the PciRoot add_device()
  method.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
Samuel Ortiz
2019-04-18 11:59:12 +02:00
parent fa3951df22
commit e8308dd13b
5 changed files with 1072 additions and 0 deletions

80
pci/src/device.rs Executable file
View File

@@ -0,0 +1,80 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::configuration::{self, PciConfiguration};
use crate::PciInterruptPin;
use devices::BusDevice;
use kvm_ioctls::*;
use std;
use std::fmt::{self, Display};
use vm_allocator::SystemAllocator;
use vm_memory::{GuestAddress, GuestUsize};
use vmm_sys_util::EventFd;
#[derive(Debug)]
pub enum Error {
/// Setup of the device capabilities failed.
CapabilitiesSetup(configuration::Error),
/// Allocating space for an IO BAR failed.
IoAllocationFailed(u64),
/// Registering an IO BAR failed.
IoRegistrationFailed(u64, configuration::Error),
}
pub type Result<T> = std::result::Result<T, Error>;
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
match self {
CapabilitiesSetup(e) => write!(f, "failed to add capability {}", e),
IoAllocationFailed(size) => {
write!(f, "failed to allocate space for an IO BAR, size={}", size)
}
IoRegistrationFailed(addr, e) => {
write!(f, "failed to register an IO BAR, addr={} err={}", addr, e)
}
}
}
}
pub trait PciDevice: BusDevice {
/// Assign a legacy PCI IRQ to this device.
/// The device may write to `irq_evt` to trigger an interrupt.
fn assign_irq(&mut self, _irq_evt: EventFd, _irq_num: u32, _irq_pin: PciInterruptPin) {}
/// Allocates the needed PCI BARs space using the `allocate` function which takes a size and
/// returns an address. Returns a Vec of (GuestAddress, GuestUsize) tuples.
fn allocate_bars(
&mut self,
_allocator: &mut SystemAllocator,
) -> Result<Vec<(GuestAddress, GuestUsize)>> {
Ok(Vec::new())
}
/// Register any capabilties specified by the device.
fn register_device_capabilities(&mut self) -> Result<()> {
Ok(())
}
/// Gets a list of ioeventfds that should be registered with the running VM. The list is
/// returned as a Vec of (eventfd, addr, datamatch) tuples.
fn ioeventfds(&self) -> Vec<(&EventFd, u64, NoDatamatch)> {
Vec::new()
}
/// Gets the configuration registers of the Pci Device.
fn config_registers(&self) -> &PciConfiguration; // TODO - remove these
/// Gets the configuration registers of the Pci Device for modification.
fn config_registers_mut(&mut self) -> &mut PciConfiguration;
/// Reads from a BAR region mapped in to the device.
/// * `addr` - The guest address inside the BAR.
/// * `data` - Filled with the data from `addr`.
fn read_bar(&mut self, addr: u64, data: &mut [u8]);
/// Writes to a BAR region mapped in to the device.
/// * `addr` - The guest address inside the BAR.
/// * `data` - The data to write.
fn write_bar(&mut self, addr: u64, data: &[u8]);
/// Invoked when the device is sandboxed.
fn on_device_sandboxed(&mut self) {}
}