mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-07-19 01:39:48 +00:00
Merge pull request #8480 from openanolis/chao/add_dbs_pci
dragonball: init dbs-pci lib with pci bus & pci conf
This commit is contained in:
commit
1550ee6767
1
src/dragonball/.gitignore
vendored
1
src/dragonball/.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
target
|
||||
.idea
|
||||
src/dbs_*/Cargo.lock
|
||||
|
@ -32,6 +32,7 @@ and configuration process.
|
||||
- `dbs_upcall`: [`dbs_upcall` Document](src/dbs_upcall/README.md)
|
||||
- `dbs_utils`: [`dbs_utils` Document](src/dbs_utils/README.md)
|
||||
- `dbs_virtio_devices`: [`dbs_virtio_devices` Document](src/dbs_virtio_devices/README.md)
|
||||
- `dbs_pci`: [`dbc_pci` Document](src/dbs_pci/README.md)
|
||||
|
||||
Currently, the documents are still actively adding.
|
||||
You could see the [official documentation](docs/) page for more details.
|
||||
|
20
src/dragonball/src/dbs_pci/Cargo.toml
Normal file
20
src/dragonball/src/dbs_pci/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "dbs-pci"
|
||||
version = "0.1.0"
|
||||
authors = ["Alibaba Dragonball Team"]
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
description = "dbs-pci is a crate for emulating pci device"
|
||||
homepage = "https://github.com/openanolis/dragonball-sandbox"
|
||||
repository = "https://github.com/openanolis/dragonball-sandbox/tree/main/crates/dbs-pci"
|
||||
keywords = ["dragonball", "secure-sandbox", "devices", "pci"]
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
dbs-device = { path = "../dbs_device" }
|
||||
dbs-interrupt = { path = "../dbs_interrupt", features = ["kvm-irq"] }
|
||||
dbs-allocator = { path = "../dbs_allocator" }
|
||||
log = "0.4.14"
|
||||
downcast-rs = "1.2.0"
|
||||
byteorder = "1.4.3"
|
||||
thiserror = "1"
|
13
src/dragonball/src/dbs_pci/README.md
Normal file
13
src/dragonball/src/dbs_pci/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
# `dbs-pci`
|
||||
|
||||
## Introduction
|
||||
|
||||
`dbs-pci` is a crate for emulating PCI device.
|
||||
|
||||
There are several components in `dbs-pci` crate building together to emulate PCI device behaviour :
|
||||
|
||||
1. device mod: mainly provide the trait for `PciDevice`, providing the ability to get id, write PCI configuration space, read PCI configuration space and `as_any` to downcast the trait object to the actual device type.
|
||||
|
||||
2. configuration mod: simulate PCI device configuration header and manage PCI Bar configuration. The PCI Specification defines the organization of the 256-byte Configuration Space registers and imposes a specific template for the space. The first 64 bytes of configuration space are standardised as configuration space header.
|
||||
|
||||
3. bus mod: simulate PCI buses, to simplify the implementation, PCI hierarchy is not supported. So all PCI devices are directly connected to the PCI root bus. PCI Bus has bus id, PCI devices attached and PCI bus I/O port, I/O mem resource use condition.
|
414
src/dragonball/src/dbs_pci/src/bus.rs
Normal file
414
src/dragonball/src/dbs_pci/src/bus.rs
Normal file
@ -0,0 +1,414 @@
|
||||
// Copyright (C) 2023 Alibaba Cloud. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! PciBus to manage child PCI devices and bus resources.
|
||||
//!
|
||||
//! According to PCI Local Bus and PCIe specifications, the hardware may build up an hierarchy
|
||||
//! topology with PCI root bridges, P2P bridges, PCIe switches and PCI endpoint devices.
|
||||
//! To simplify the implementation, P2P bridges and PCIe switches aren't supported by most VMMs.
|
||||
//! The PCI/PCIe topology for virtual machines are abstracted as:
|
||||
//! - one PCI root device: handle accesses to the PCI configuration spaces and owns PCI root buses.
|
||||
//! - one or more PCI root buses: manages resources (IO port, MMIO address range) and all children
|
||||
//! connecting to it. All PCI buses are PCI root buses, no P2P bus on virtual machines.
|
||||
//! - PCI devices: implement device functionality by allocating resources from the parent bus and
|
||||
//! registering to the global address space manager.
|
||||
//!
|
||||
//! The VMM allocates resources from the global resource manager and assigns those allocated
|
||||
//! resources to PCI root buses. All PCI devices allocates resources from PCI root buses instead
|
||||
//! of from the global resource allocator. By this way, it will be easier to handle PCI Bar
|
||||
//! reprogramming events and better follows the hardware logic.
|
||||
|
||||
use std::sync::{Arc, RwLock, RwLockWriteGuard};
|
||||
|
||||
use dbs_allocator::{Constraint, IntervalTree, NodeState, Range};
|
||||
use dbs_device::resources::{DeviceResources, Resource, ResourceConstraint};
|
||||
use log::debug;
|
||||
|
||||
use crate::{fill_config_data, Error, PciDevice, Result};
|
||||
|
||||
#[derive(PartialEq)]
|
||||
struct PciBusContent {
|
||||
resources: Option<DeviceResources>,
|
||||
ioport_resources: IntervalTree<()>,
|
||||
iomem_resources: IntervalTree<()>,
|
||||
}
|
||||
|
||||
impl PciBusContent {
|
||||
fn new() -> Self {
|
||||
PciBusContent {
|
||||
resources: None,
|
||||
ioport_resources: IntervalTree::new(),
|
||||
iomem_resources: IntervalTree::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn assign_resources(&mut self, resources: DeviceResources, id: u8) -> Result<()> {
|
||||
for res in resources.iter() {
|
||||
match res {
|
||||
Resource::PioAddressRange { base, size } => {
|
||||
debug!(
|
||||
"assign pio address base:0x{:x}, size:0x{:x} to PCI bus {}",
|
||||
*base, *size, id
|
||||
);
|
||||
if *size == 0 {
|
||||
return Err(Error::InvalidResource(res.clone()));
|
||||
}
|
||||
let end = base
|
||||
.checked_add(*size - 1)
|
||||
.ok_or_else(|| Error::InvalidResource(res.clone()))?;
|
||||
self.ioport_resources.insert(Range::new(*base, end), None);
|
||||
}
|
||||
Resource::MmioAddressRange { base, size } => {
|
||||
debug!(
|
||||
"assign mmio address base:0x{:x}, size:0x{:x} to PCI bus {}",
|
||||
*base, *size, id
|
||||
);
|
||||
if *size == 0 {
|
||||
return Err(Error::InvalidResource(res.clone()));
|
||||
}
|
||||
let end = base
|
||||
.checked_add(*size - 1)
|
||||
.ok_or_else(|| Error::InvalidResource(res.clone()))?;
|
||||
self.iomem_resources.insert(Range::new(*base, end), None);
|
||||
}
|
||||
_ => debug!("unknown resource assigned to PCI bus {}", id),
|
||||
}
|
||||
}
|
||||
|
||||
self.resources = Some(resources);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Struct to emulate PCI buses.
|
||||
///
|
||||
/// To simplify the implementation, PCI hierarchy topology is not supported. That means all PCI
|
||||
/// devices are directly connected to the PCI root bus.
|
||||
pub struct PciBus {
|
||||
bus_id: u8,
|
||||
state: RwLock<PciBusContent>,
|
||||
devices: RwLock<IntervalTree<Arc<dyn PciDevice>>>,
|
||||
}
|
||||
|
||||
impl PciBus {
|
||||
/// Create a new PCI bus object with the assigned bus id.
|
||||
pub fn new(bus_id: u8) -> Self {
|
||||
let mut devices = IntervalTree::new();
|
||||
|
||||
Self::assign_default_device_id(&mut devices);
|
||||
|
||||
PciBus {
|
||||
bus_id,
|
||||
devices: RwLock::new(devices),
|
||||
state: RwLock::new(PciBusContent::new()),
|
||||
}
|
||||
}
|
||||
|
||||
fn assign_default_device_id(devices: &mut IntervalTree<Arc<dyn PciDevice>>) {
|
||||
// At present, device id means slot id of device.
|
||||
// TODO: Code logic needs to be optimized to support multifunction devices.
|
||||
devices.insert(Range::new(0x0u8, 0x1fu8), None);
|
||||
}
|
||||
|
||||
/// Get bus ID for this PCI bus instance.
|
||||
pub fn bus_id(&self) -> u8 {
|
||||
self.bus_id
|
||||
}
|
||||
|
||||
/// Allocate an unused PCI device ID.
|
||||
///
|
||||
/// # Arguments:
|
||||
/// * - `device_id`: allocate the specified device ID if it's valid.
|
||||
pub fn allocate_device_id(&self, device_id: Option<u8>) -> Option<u8> {
|
||||
// A PCI device supports 8 functions at most, aligning on 8 means only using function 0.
|
||||
let mut constraint = Constraint::new(1u64).align(1u64);
|
||||
if let Some(id) = device_id {
|
||||
constraint = constraint.min(id as u64).max(id as u64);
|
||||
}
|
||||
|
||||
debug!(
|
||||
"allocate device id constraint size: {}, min: {}, max: {}",
|
||||
constraint.size, constraint.min, constraint.max
|
||||
);
|
||||
// Do not expect poisoned lock here.
|
||||
self.devices
|
||||
.write()
|
||||
.expect("poisoned RwLock() for PCI bus")
|
||||
.allocate(&constraint)
|
||||
.map(|e| e.min as u8)
|
||||
}
|
||||
|
||||
/// Free the previously allocated device id and return data associated with the id.
|
||||
pub fn free_device_id(&self, device_id: u32) -> Option<Arc<dyn PciDevice>> {
|
||||
if device_id > 0x1f {
|
||||
return None;
|
||||
}
|
||||
// Safe to unwrap because no legal way to generate a poisoned RwLock.
|
||||
self.devices
|
||||
.write()
|
||||
.unwrap()
|
||||
.free(&Range::new(device_id as u64, device_id as u64))
|
||||
}
|
||||
|
||||
/// Add a child PCI device to the bus.
|
||||
pub fn register_device(&self, device: Arc<dyn PciDevice>) -> Result<()> {
|
||||
// Do not expect poisoned lock here.
|
||||
let device_id = device.id();
|
||||
let mut devices = self.devices.write().expect("poisoned lock for PCI bus");
|
||||
|
||||
debug!("add device id {} to bus", device_id);
|
||||
let old = devices.update(&Range::new(device_id, device_id), device.clone());
|
||||
assert!(old.is_none());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the device instance associated with the `device_id`.
|
||||
pub fn get_device(&self, device_id: u32) -> Option<Arc<dyn PciDevice>> {
|
||||
if device_id > 0x1f {
|
||||
return None;
|
||||
}
|
||||
let devices = self.devices.read().unwrap();
|
||||
match devices.get(&Range::new(device_id as u64, device_id as u64)) {
|
||||
Some(NodeState::Valued(d)) => Some(d.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Read from PCI device configuration space.
|
||||
pub fn read_config(&self, dev: u32, func: u32, offset: u32, data: &mut [u8]) {
|
||||
if check_pci_cfg_valid(dev, func, offset, data.len())
|
||||
{
|
||||
return fill_config_data(data);
|
||||
}
|
||||
|
||||
// Safe to unwrap because no legal way to generate a poisoned RwLock.
|
||||
let devices = self.devices.read().unwrap();
|
||||
match devices.get(&Range::new(dev as u64, dev as u64)) {
|
||||
Some(NodeState::Valued(d)) => d.read_config(offset, data),
|
||||
_ => fill_config_data(data),
|
||||
};
|
||||
}
|
||||
|
||||
/// Write to PCI device configuration space.
|
||||
pub fn write_config(&self, dev: u32, func: u32, offset: u32, data: &[u8]) {
|
||||
if check_pci_cfg_valid(dev, func, offset, data.len())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Safe to unwrap because no legal way to generate a poisoned RwLock.
|
||||
let devices = self.devices.read().unwrap();
|
||||
if let Some(NodeState::Valued(d)) = devices.get(&Range::new(dev as u64, dev as u64)) {
|
||||
d.write_config(offset, data);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get PCI bus device resource. This function is use for create PCI_BUS fdt node for arm,
|
||||
/// so there is only care about mmio resource. We need to copy mmio resource, because there is
|
||||
/// a read write lock, we can't return DeviceResources's address for caller using.
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub fn get_device_resources(&self) -> DeviceResources {
|
||||
let mut device_resources = DeviceResources::new();
|
||||
if let Some(resources) = &self
|
||||
.state
|
||||
.read()
|
||||
.expect("poisoned RwLock for PCI bus")
|
||||
.resources
|
||||
{
|
||||
let mmio_resources = resources.get_mmio_address_ranges();
|
||||
for (base, size) in mmio_resources {
|
||||
let entry = Resource::MmioAddressRange { base, size };
|
||||
device_resources.append(entry);
|
||||
}
|
||||
}
|
||||
device_resources
|
||||
}
|
||||
|
||||
/// Assign resources to be allocated by all child devices.
|
||||
pub fn assign_resources(&self, resources: DeviceResources) -> Result<()> {
|
||||
// Do not expect poisoned lock here.
|
||||
self.state
|
||||
.write()
|
||||
.expect("poisoned RwLock for PCI bus")
|
||||
.assign_resources(resources, self.bus_id)
|
||||
}
|
||||
|
||||
/// Allocate PCI IO resources from the bus resource pool.
|
||||
pub fn allocate_resources(
|
||||
&self,
|
||||
constraints: &[ResourceConstraint],
|
||||
) -> Result<DeviceResources> {
|
||||
let mut resources = DeviceResources::new();
|
||||
// Safe to unwrap because no legal way to generate a poisoned RwLock.
|
||||
let mut state = self.state.write().unwrap();
|
||||
for req in constraints {
|
||||
match req {
|
||||
ResourceConstraint::PioAddress { range, align, size } => {
|
||||
let mut constraint = Constraint::new(*size as u64).align(*align as u64);
|
||||
if let Some((min, max)) = range {
|
||||
constraint = constraint.min(*min as u64).max(*max as u64);
|
||||
}
|
||||
match state.ioport_resources.allocate(&constraint) {
|
||||
Some(range) => {
|
||||
resources.append(Resource::PioAddressRange {
|
||||
base: range.min as u16,
|
||||
size: range.len() as u16,
|
||||
});
|
||||
}
|
||||
None => {
|
||||
Self::free_all_resource(state, resources);
|
||||
return Err(Error::NoResources);
|
||||
}
|
||||
}
|
||||
}
|
||||
ResourceConstraint::MmioAddress { range, align, size } => {
|
||||
let mut constraint = Constraint::new(*size).align(*align);
|
||||
if let Some((min, max)) = range {
|
||||
constraint = constraint.min(*min).max(*max);
|
||||
}
|
||||
match state.iomem_resources.allocate(&constraint) {
|
||||
Some(range) => {
|
||||
resources.append(Resource::MmioAddressRange {
|
||||
base: range.min,
|
||||
size: range.len(),
|
||||
});
|
||||
}
|
||||
None => {
|
||||
Self::free_all_resource(state, resources);
|
||||
return Err(Error::NoResources);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(resources)
|
||||
}
|
||||
|
||||
/// Free allocated PCI IO resources.
|
||||
pub fn free_resources(&self, resources: DeviceResources) {
|
||||
// Don't expect poisoned lock here.
|
||||
let state = self.state.write().unwrap();
|
||||
Self::free_all_resource(state, resources);
|
||||
}
|
||||
|
||||
// free all device resource under pci bus
|
||||
fn free_all_resource(mut state: RwLockWriteGuard<PciBusContent>, resources: DeviceResources) {
|
||||
for res in resources.get_all_resources().iter() {
|
||||
match res {
|
||||
Resource::PioAddressRange { base, size } => {
|
||||
let range = Range::new(*base as u64, (*base + *size - 1) as u64);
|
||||
state.ioport_resources.free(&range);
|
||||
}
|
||||
Resource::MmioAddressRange { base, size } => {
|
||||
let range = Range::new(*base, *base + *size - 1);
|
||||
state.iomem_resources.free(&range);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for PciBus {
|
||||
fn eq(&self, other: &PciBus) -> bool {
|
||||
self.bus_id == other.bus_id
|
||||
&& *self.state.read().unwrap() == *other.state.read().unwrap()
|
||||
&& *self.devices.read().unwrap() == *other.devices.read().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_pci_cfg_valid(dev: u32, func: u32, offset: u32, data_len: usize) -> bool {
|
||||
dev > 0x1f || func !=0 || offset >= 0x1000 || offset & (data_len - 1 ) as u32 & 0x3 != 0
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_bus_allocate_device_id() {
|
||||
let bus = PciBus::new(0);
|
||||
let id = bus.allocate_device_id(None);
|
||||
assert_eq!(id, Some(0));
|
||||
let id = bus.allocate_device_id(None);
|
||||
assert_eq!(id, Some(1));
|
||||
|
||||
let id = bus.allocate_device_id(Some(15));
|
||||
assert_eq!(id, Some(15));
|
||||
|
||||
assert!(bus.get_device(0x1f).is_none());
|
||||
|
||||
let old = bus.free_device_id(15);
|
||||
if old.is_some() {
|
||||
panic!("invalid return value for free_device_id");
|
||||
}
|
||||
let id = bus.allocate_device_id(Some(15));
|
||||
assert_eq!(id, Some(15));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bus_allocate_resource() {
|
||||
let bus = PciBus::new(0);
|
||||
|
||||
let mut resources = DeviceResources::new();
|
||||
resources.append(Resource::PioAddressRange {
|
||||
base: 0,
|
||||
size: 0x1000,
|
||||
});
|
||||
resources.append(Resource::MmioAddressRange {
|
||||
base: 0x10_0000,
|
||||
size: 0x10_0000,
|
||||
});
|
||||
assert_eq!(resources.get_all_resources().len(), 2);
|
||||
assert!(bus.assign_resources(resources).is_ok());
|
||||
assert!(bus.state.read().unwrap().resources.is_some());
|
||||
|
||||
let constraints = [
|
||||
ResourceConstraint::PioAddress {
|
||||
range: Some((0x100, 0x10f)),
|
||||
size: 0xf,
|
||||
align: 1,
|
||||
},
|
||||
ResourceConstraint::MmioAddress {
|
||||
range: Some((0x10_0001, 0x10_2000)),
|
||||
size: 0x100,
|
||||
align: 0x1000,
|
||||
},
|
||||
];
|
||||
let resources = bus.allocate_resources(&constraints).unwrap();
|
||||
assert_eq!(resources.len(), 2);
|
||||
|
||||
let pio = resources.get_pio_address_ranges();
|
||||
assert_eq!(pio.len(), 1);
|
||||
assert_eq!(pio[0].0, 0x100);
|
||||
assert_eq!(pio[0].1, 0xf);
|
||||
|
||||
let mmio = resources.get_mmio_address_ranges();
|
||||
assert_eq!(mmio.len(), 1);
|
||||
assert_eq!(mmio[0].0, 0x10_1000);
|
||||
assert_eq!(mmio[0].0 & 0xfff, 0x0);
|
||||
assert_eq!(mmio[0].1, 0x100);
|
||||
|
||||
bus.free_resources(resources);
|
||||
|
||||
let resources = bus.allocate_resources(&constraints).unwrap();
|
||||
assert_eq!(resources.len(), 2);
|
||||
|
||||
let pio = resources.get_pio_address_ranges();
|
||||
assert_eq!(pio.len(), 1);
|
||||
assert_eq!(pio[0].0, 0x100);
|
||||
assert_eq!(pio[0].1, 0xf);
|
||||
|
||||
let mmio = resources.get_mmio_address_ranges();
|
||||
assert_eq!(mmio.len(), 1);
|
||||
assert_eq!(mmio[0].0, 0x10_1000);
|
||||
assert_eq!(mmio[0].0 & 0xfff, 0x0);
|
||||
assert_eq!(mmio[0].1, 0x100);
|
||||
}
|
||||
}
|
1979
src/dragonball/src/dbs_pci/src/configuration.rs
Normal file
1979
src/dragonball/src/dbs_pci/src/configuration.rs
Normal file
File diff suppressed because it is too large
Load Diff
41
src/dragonball/src/dbs_pci/src/device.rs
Normal file
41
src/dragonball/src/dbs_pci/src/device.rs
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright (C) 2023 Alibaba Cloud. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::any::Any;
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use dbs_device::resources::DeviceResources;
|
||||
use dbs_device::DeviceIo;
|
||||
|
||||
/// Define PCI ECAM space length
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub const ECAM_SPACE_LENGTH: u64 = 0x100000;
|
||||
|
||||
/// PCI bus resources are used to create pci bus fdt node
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub struct PciBusResources {
|
||||
/// Save the ecam space, it only contain one mmio address space
|
||||
pub ecam_space: DeviceResources,
|
||||
/// Save the bar space, it contains 2 mmio address space
|
||||
pub bar_space: DeviceResources,
|
||||
}
|
||||
|
||||
pub trait PciDevice: DeviceIo + Send + Sync {
|
||||
/// Get PCI device/function id on the PCI bus, which is in [0x0, 0xff].
|
||||
///
|
||||
/// The higher 5 bits are device id and the lower 3 bits are function id.
|
||||
fn id(&self) -> u8;
|
||||
|
||||
/// Write to the PCI device's configuration space.
|
||||
fn write_config(&self, offset: u32, data: &[u8]);
|
||||
|
||||
/// Read from the PCI device's configuration space.
|
||||
fn read_config(&self, offset: u32, data: &mut [u8]);
|
||||
}
|
||||
|
||||
impl PartialEq for dyn PciDevice {
|
||||
fn eq(&self, other: &dyn PciDevice) -> bool {
|
||||
self.id() == other.id()
|
||||
}
|
||||
}
|
108
src/dragonball/src/dbs_pci/src/lib.rs
Normal file
108
src/dragonball/src/dbs_pci/src/lib.rs
Normal file
@ -0,0 +1,108 @@
|
||||
// Copyright (C) 2023 Alibaba Cloud. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// 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-BSD-3-Clause file.
|
||||
//
|
||||
//![deny(missing_docs)]
|
||||
//!
|
||||
//! Implements PCI devices and buses.
|
||||
//!
|
||||
//! The role and relationship about PCI related traits/structs:
|
||||
//! - PCI root: a pseudo device to handle PCI configuration accesses.
|
||||
//! - PCI bus: a container object to hold PCI devices and resources, corresponding to the PCI bus
|
||||
//! defined in PCI/PCIe specs.
|
||||
//! - PCI root bus: a special PCI bus which has no parent PCI bus. The device 0 under PCI root bus
|
||||
//! represent the root bus itself.
|
||||
//! - PCI device: the real object to emulate a PCI device. For most PCI devices, it needs to
|
||||
//! handle accesses to PCI configuration space and PCI BARs.
|
||||
//! - PCI configuration: a common framework to emulator PCI configuration space header.
|
||||
//! - PCI MSI/MSIx: structs to emulate PCI MSI/MSIx capabilities.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use dbs_device::device_manager::IoManagerContext;
|
||||
use dbs_interrupt::KvmIrqManager;
|
||||
|
||||
mod bus;
|
||||
mod configuration;
|
||||
mod device;
|
||||
|
||||
pub use self::configuration::{
|
||||
BarProgrammingParams, PciBarConfiguration, PciBarPrefetchable, PciBarRegionType,
|
||||
PciBridgeSubclass, PciCapability, PciCapabilityID, PciClassCode, PciConfiguration,
|
||||
PciHeaderType, PciInterruptPin, PciMassStorageSubclass, PciMultimediaSubclass,
|
||||
PciNetworkControllerSubclass, PciProgrammingInterface, PciSerialBusSubClass, PciSubclass,
|
||||
NUM_BAR_REGS, NUM_CONFIGURATION_REGISTERS,
|
||||
};
|
||||
pub use self::bus::PciBus;
|
||||
pub use self::device::PciDevice;
|
||||
|
||||
/// Error codes related to PCI root/bus/device operations.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
/// Invalid resource assigned/allocated.
|
||||
#[error("invalid resource {0:?}")]
|
||||
InvalidResource(dbs_device::resources::Resource),
|
||||
/// Errors from IoManager
|
||||
/// No resources available.
|
||||
#[error("No resources available")]
|
||||
NoResources,
|
||||
/// Zero sized PCI capability
|
||||
#[error("empty capabilities are invalid")]
|
||||
CapabilityEmpty,
|
||||
/// No space available for new PCI capability.
|
||||
#[error("capability of size {0} doesn't fit")]
|
||||
CapabilitySpaceFull(usize),
|
||||
/// PCI BAR is already in use.
|
||||
#[error("bar {0} already used")]
|
||||
BarInUse(usize),
|
||||
/// PCI BAR is invalid.
|
||||
#[error("bar {0} invalid, max {}", NUM_BAR_REGS - 1)]
|
||||
BarInvalid(usize),
|
||||
/// PCI BAR size is invalid.
|
||||
#[error("bar address {0} not a power of two")]
|
||||
BarSizeInvalid(u64),
|
||||
/// PCI BAR address is invalid.
|
||||
#[error("address {0} size {1} too big")]
|
||||
BarAddressInvalid(u64, u64),
|
||||
/// 64 bits MMIO PCI BAR is invalid.
|
||||
#[error("64 bit bar {0} invalid, requires two regs, max {}", NUM_BAR_REGS - 1)]
|
||||
BarInvalid64(usize),
|
||||
/// 64 bits MMIO PCI BAR is in use.
|
||||
#[error("64bit bar {0} already used(requires two regs)")]
|
||||
BarInUse64(usize),
|
||||
/// PCI ROM BAR is invalid.
|
||||
#[error("ROM bar {0} invalid, max {}", NUM_BAR_REGS - 1)]
|
||||
RomBarInvalid(usize),
|
||||
/// PCI ROM BAR is already in use.
|
||||
#[error("rom bar {0} already used")]
|
||||
RomBarInUse(usize),
|
||||
/// PCI ROM BAR size is invalid.
|
||||
#[error("rom bar address {0} not a power of two")]
|
||||
RomBarSizeInvalid(u64),
|
||||
/// PCI ROM BAR address is invalid.
|
||||
#[error("address {0} size {1} too big")]
|
||||
RomBarAddressInvalid(u64, u64),
|
||||
}
|
||||
|
||||
/// Specialized `Result` for PCI related operations.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
pub trait PciSystemContext: Sync + Send + Clone {
|
||||
type D: IoManagerContext + Send + Sync + Clone;
|
||||
|
||||
fn get_device_manager_context(&self) -> Self::D;
|
||||
|
||||
fn get_interrupt_manager(&self) -> Arc<KvmIrqManager>;
|
||||
}
|
||||
|
||||
/// Fill the buffer with all bits set for invalid PCI configuration space access.
|
||||
pub fn fill_config_data(data: &mut [u8]) {
|
||||
// Return data with all bits set.
|
||||
for pos in data.iter_mut() {
|
||||
*pos = 0xff;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user