Skip to content

Commit

Permalink
dragonball: add pci root bus and root device
Browse files Browse the repository at this point in the history
In order to follow up the PCI implementation in Dragonball, we need to
add PCI root device and root bus support.

root device is a pseudo PCI root device to manage accessing to PCI
configuration space.

root bus is mainly for emulating PCI root bridge and also create the PCI
root bus with the given bus ID with the PCI root bridge.

fixes: #8563

Signed-off-by: Gerry Liu <gerry@linux.alibaba.com>
Signed-off-by: Zizheng Bian <zizheng.bian@linux.alibaba.com>
Signed-off-by: Shifang Feng <fengshifang@linux.alibaba.com>
Signed-off-by: Yang Su <yang.su@linux.alibaba.com>
Signed-off-by: Zha Bin <zhabin@linux.alibaba.com>
Signed-off-by: Xin Lin <jingshan@linux.alibaba.com>
Signed-off-by: Chao Wu <chaowu@linux.alibaba.com>
  • Loading branch information
studychao committed Dec 12, 2023
1 parent ee74fca commit b079e1a
Show file tree
Hide file tree
Showing 5 changed files with 608 additions and 8 deletions.
6 changes: 5 additions & 1 deletion src/dragonball/src/dbs_pci/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ There are several components in `dbs-pci` crate building together to emulate PCI

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.
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.

4. root bus mod: mainly for emulating PCI root bridge and also create the PCI root bus with the given bus ID with the PCI root bridge.

5. root device mod: A pseudo PCI root device to manage accessing to PCI configuration space.
2 changes: 0 additions & 2 deletions src/dragonball/src/dbs_pci/src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
//
// SPDX-License-Identifier: Apache-2.0

use std::any::Any;

#[cfg(target_arch = "aarch64")]
use dbs_device::resources::DeviceResources;
use dbs_device::DeviceIo;
Expand Down
23 changes: 18 additions & 5 deletions src/dragonball/src/dbs_pci/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,38 @@ use dbs_device::device_manager::IoManagerContext;
use dbs_interrupt::KvmIrqManager;

mod bus;
mod configuration;
mod device;
pub use bus::PciBus;

pub use self::bus::PciBus;
pub use self::configuration::{
mod configuration;
pub use 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::device::PciDevice;

mod device;
pub use device::PciDevice;

mod root_bus;
pub use root_bus::create_pci_root_bus;

mod root_device;
pub use root_device::PciRootDevice;

/// Error codes related to PCI root/bus/device operations.
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// Failed to activate the PCI root/bus/device.
#[error("failed to activate PCI device, {0:?}")]
ActivateFailure(#[source] dbs_device::device_manager::Error),
/// Invalid resource assigned/allocated.
#[error("invalid resource {0:?}")]
InvalidResource(dbs_device::resources::Resource),
/// Invalid bus id
#[error("bus id {0} invalid")]
InvalidBusId(u8),
/// Errors from IoManager
/// No resources available.
#[error("No resources available")]
Expand Down
147 changes: 147 additions & 0 deletions src/dragonball/src/dbs_pci/src/root_bus.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// Copyright (C) 2023 Alibaba Cloud. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0

/// Emulate a PCI root bus.
///
/// A PCI root bus is a special PCI bus, who has no parent PCI bus. The device 0 on PCI root bus
/// represents the root bus itself.
///
use std::sync::{Arc, Mutex, Weak};

use dbs_device::DeviceIo;

use crate::{
Error, PciBridgeSubclass, PciBus, PciClassCode, PciConfiguration, PciDevice, PciHeaderType,
Result,
};

const VENDOR_ID_INTEL: u16 = 0x8086;
const DEVICE_ID_INTEL_VIRT_PCIE_HOST: u16 = 0x0d57;
pub const PCI_ROOT_DEVICE_ID: u8 = 0;

/// Emulates the PCI host bridge device.
pub(crate) struct PciHostBridge {
/// Device and Function Id.
id: u8,
/// Configuration space.
config: Mutex<PciConfiguration>,
}

impl PciHostBridge {
/// Create an empty PCI root bridge.
pub fn new(id: u8, bus: Weak<PciBus>) -> Result<Self> {
let host_bridge = PciHostBridge {
id,
config: Mutex::new(PciConfiguration::new(
bus,
VENDOR_ID_INTEL,
DEVICE_ID_INTEL_VIRT_PCIE_HOST,
PciClassCode::BridgeDevice,
&PciBridgeSubclass::HostBridge,
None,
PciHeaderType::Device,
0,
0,
None,
)?),
};

Ok(host_bridge)
}
}

impl PciDevice for PciHostBridge {
fn id(&self) -> u8 {
self.id
}

fn write_config(&self, offset: u32, data: &[u8]) {
// Don't expect poisoned lock here.
self.config
.lock()
.expect("poisoned lock for root bus configuration")
.write_config(offset as usize, data);
}

fn read_config(&self, offset: u32, data: &mut [u8]) {
// Don't expect poisoned lock here.
self.config
.lock()
.expect("poisoned lock for root bus configuration")
.read_config(offset as usize, data);
}
}

impl DeviceIo for PciHostBridge {
fn as_any(&self) -> &dyn std::any::Any {
self
}
}

/// Create the PCI root bus with the given bus ID.
pub fn create_pci_root_bus(bus_id: u8) -> Result<Arc<PciBus>> {
let bus = Arc::new(PciBus::new(bus_id));
let id = bus
.allocate_device_id(Some(PCI_ROOT_DEVICE_ID))
.ok_or(Error::NoResources)?;
let dev = Arc::new(PciHostBridge::new(id, Arc::downgrade(&bus))?);

bus.register_device(dev)?;

Ok(bus)
}

#[cfg(test)]
mod tests {
#[cfg(target_arch = "x86_64")]
use dbs_device::resources::{DeviceResources, Resource};
#[cfg(target_arch = "x86_64")]
use dbs_device::PioAddress;

use super::*;
#[cfg(target_arch = "x86_64")]
use crate::PciRootDevice;

#[test]
fn test_create_pci_root_bus() {
let root_bus = create_pci_root_bus(0).unwrap();
let host_bridge = PciHostBridge::new(0, Arc::downgrade(&root_bus));

assert_eq!(root_bus.bus_id(), 0);
assert_eq!(host_bridge.unwrap().id(), 0);
assert!(root_bus.get_device(0).is_some());
}

#[cfg(target_arch = "x86_64")]
#[test]
fn test_read_pci_root_root_bus_cfg() {
let mut resources = DeviceResources::new();
resources.append(Resource::PioAddressRange {
base: 0xCF8,
size: 8,
});
let root = PciRootDevice::create(255, resources).unwrap();

let root_bus = create_pci_root_bus(0).unwrap();
let host_bridge = PciHostBridge::new(0, Arc::downgrade(&root_bus));
assert_eq!(host_bridge.unwrap().id(), 0);

root.add_bus(root_bus, 0).unwrap();

let buf = [0x00u8, 0x00u8, 0x00u8, 0x80u8];
root.pio_write(PioAddress(0xcf8), PioAddress(0), &buf);

let mut buf = [0u8; 4];
root.pio_read(PioAddress(0xcf8), PioAddress(4), &mut buf);
assert_eq!(buf, [0x86u8, 0x80u8, 0x57u8, 0x0du8]);

let buf = [0x08u8, 0x00u8, 0x00u8, 0x80u8];
root.pio_write(PioAddress(0xcf8), PioAddress(0), &buf);

let mut buf = [0u8; 4];
root.pio_read(PioAddress(0xcf8), PioAddress(4), &mut buf);
assert_eq!(buf[3], PciClassCode::BridgeDevice.get_register_value());
root.pio_write(PioAddress(0xcf8), PioAddress(7), &buf);
}
}

0 comments on commit b079e1a

Please sign in to comment.