Lazily acquire MSI-X resources
This commit is contained in:
parent
de0f8d1e54
commit
97a77c2884
|
|
@ -279,6 +279,7 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"component",
|
"component",
|
||||||
"fdt",
|
"fdt",
|
||||||
|
"int-to-c-enum",
|
||||||
"log",
|
"log",
|
||||||
"ostd",
|
"ostd",
|
||||||
"spin",
|
"spin",
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ align_ext.workspace = true
|
||||||
bitflags.workspace = true
|
bitflags.workspace = true
|
||||||
cfg-if.workspace = true
|
cfg-if.workspace = true
|
||||||
component.workspace = true
|
component.workspace = true
|
||||||
|
int-to-c-enum.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
ostd.workspace = true
|
ostd.workspace = true
|
||||||
spin.workspace = true
|
spin.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -5,80 +5,83 @@
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
use align_ext::AlignExt;
|
use align_ext::AlignExt;
|
||||||
|
use int_to_c_enum::TryFromInt;
|
||||||
|
use ostd::Result;
|
||||||
|
|
||||||
use self::{msix::CapabilityMsixData, vendor::CapabilityVndrData};
|
use self::{
|
||||||
use super::{cfg_space::Status, common_device::PciCommonDevice};
|
msix::{CapabilityMsixData, RawCapabilityMsix},
|
||||||
use crate::cfg_space::PciGeneralDeviceCfgOffset;
|
vendor::{CapabilityVndrData, RawCapabilityVndr},
|
||||||
|
};
|
||||||
|
use crate::{
|
||||||
|
PciDeviceLocation,
|
||||||
|
cfg_space::{PciGeneralDeviceCfgOffset, Status},
|
||||||
|
common_device::{BarManager, PciCommonDevice},
|
||||||
|
};
|
||||||
|
|
||||||
pub mod msix;
|
pub mod msix;
|
||||||
pub mod vendor;
|
pub mod vendor;
|
||||||
|
|
||||||
/// PCI Capability
|
/// Raw PCI Capabilities.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Capability {
|
pub(super) struct RawCapabilities {
|
||||||
cap_data: CapabilityData,
|
msix: Option<RawCapabilityMsix>,
|
||||||
|
vndr: Vec<RawCapabilityVndr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PCI Capability data.
|
/// PCI capability types.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Copy, TryFromInt)]
|
||||||
pub enum CapabilityData {
|
#[repr(u8)]
|
||||||
/// Id:0x01, Power Management
|
enum CapabilityType {
|
||||||
Pm,
|
/// Power Management
|
||||||
/// Id:0x02, Accelerated Graphics Part
|
Pm = 0x01,
|
||||||
Agp,
|
/// Accelerated Graphics Part
|
||||||
/// Id:0x03, Vital Product Data
|
Agp = 0x02,
|
||||||
Vpd,
|
/// Vital Product Data
|
||||||
/// Id:0x04, Slot Identification
|
Vpd = 0x03,
|
||||||
SlotId,
|
/// Slot Identification
|
||||||
/// Id:0x05, Message Signalled Interrupts
|
SlotId = 0x04,
|
||||||
Msi,
|
/// Message Signalled Interrupts
|
||||||
/// Id:0x06, CompactPCI HotSwap
|
Msi = 0x05,
|
||||||
Chswp,
|
/// CompactPCI HotSwap
|
||||||
/// Id:0x07, PCI-X
|
Chswp = 0x06,
|
||||||
PciX,
|
/// PCI-X
|
||||||
/// Id:0x08, HyperTransport
|
PciX = 0x07,
|
||||||
Hp,
|
/// HyperTransport
|
||||||
/// Id:0x09, Vendor-Specific
|
Hp = 0x08,
|
||||||
Vndr(CapabilityVndrData),
|
/// Vendor-Specific
|
||||||
/// Id:0x0A, Debug port
|
Vndr = 0x09,
|
||||||
Dbg,
|
/// Debug port
|
||||||
/// Id:0x0B, CompactPCI Central Resource Control
|
Dbg = 0x0A,
|
||||||
Ccrc,
|
/// CompactPCI Central Resource Control
|
||||||
/// Id:0x0C, PCI Standard Hot-Plug Controller
|
Ccrc = 0x0B,
|
||||||
Shpc,
|
/// PCI Standard Hot-Plug Controller
|
||||||
/// Id:0x0D, Bridge subsystem vendor/device ID
|
Shpc = 0x0C,
|
||||||
Ssvid,
|
/// Bridge subsystem vendor/device ID
|
||||||
/// Id:0x0R, AGP Target PCI-PCI bridge
|
Ssvid = 0x0D,
|
||||||
Agp3,
|
/// AGP Target PCI-PCI bridge
|
||||||
/// Id:0x0F, Secure Device
|
Agp3 = 0x0E,
|
||||||
Secdev,
|
/// Secure Device
|
||||||
/// Id:0x10, PCI Express
|
Secdev = 0x0F,
|
||||||
Exp,
|
/// PCI Express
|
||||||
/// Id:0x11, MSI-X
|
Exp = 0x10,
|
||||||
Msix(CapabilityMsixData),
|
/// MSI-X
|
||||||
/// Id:0x12, SATA Data/Index Conf
|
Msix = 0x11,
|
||||||
Sata,
|
/// SATA Data/Index Conf
|
||||||
/// Id:0x13, PCI Advanced Features
|
Sata = 0x12,
|
||||||
Af,
|
/// PCI Advanced Features
|
||||||
/// Id:0x14, Enhanced Allocation
|
Af = 0x13,
|
||||||
Ea,
|
/// Enhanced Allocation
|
||||||
/// Id:?, Unknown
|
Ea = 0x14,
|
||||||
Unknown(u8),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Capability {
|
impl RawCapabilities {
|
||||||
/// The top of the capability position.
|
/// The top of the capability position.
|
||||||
const CAPABILITY_TOP: u16 = 0xFC;
|
const CAPABILITY_TOP: u16 = 0xFC;
|
||||||
|
|
||||||
/// Gets the capability data
|
/// Parses the capabilities of the PCI device.
|
||||||
pub fn capability_data(&self) -> &CapabilityData {
|
pub(super) fn parse(dev: &PciCommonDevice) -> Self {
|
||||||
&self.cap_data
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the capabilities of one device
|
|
||||||
pub(super) fn device_capabilities(dev: &mut PciCommonDevice) -> Vec<Self> {
|
|
||||||
if !dev.read_status().contains(Status::CAPABILITIES_LIST) {
|
if !dev.read_status().contains(Status::CAPABILITIES_LIST) {
|
||||||
return Vec::new();
|
return Self::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The offset of the first capability pointer is the same for PCI general devices and PCI
|
// The offset of the first capability pointer is the same for PCI general devices and PCI
|
||||||
|
|
@ -87,7 +90,6 @@ impl Capability {
|
||||||
let mut cap_ptr =
|
let mut cap_ptr =
|
||||||
(dev.location().read8(CAP_OFFSET) as u16).align_down(align_of::<u32>() as _);
|
(dev.location().read8(CAP_OFFSET) as u16).align_down(align_of::<u32>() as _);
|
||||||
let mut cap_ptr_vec = Vec::new();
|
let mut cap_ptr_vec = Vec::new();
|
||||||
let mut capabilities = Vec::new();
|
|
||||||
|
|
||||||
// Read all capability pointers so that it is easy for us to get the length of each
|
// Read all capability pointers so that it is easy for us to get the length of each
|
||||||
// capability.
|
// capability.
|
||||||
|
|
@ -100,39 +102,60 @@ impl Capability {
|
||||||
// Push the top position so that we can calculate the length of the last capability.
|
// Push the top position so that we can calculate the length of the last capability.
|
||||||
cap_ptr_vec.push(Self::CAPABILITY_TOP);
|
cap_ptr_vec.push(Self::CAPABILITY_TOP);
|
||||||
|
|
||||||
|
let mut caps = Self::default();
|
||||||
|
|
||||||
let length = cap_ptr_vec.len();
|
let length = cap_ptr_vec.len();
|
||||||
for i in 0..length - 1 {
|
for i in 0..length - 1 {
|
||||||
let cap_ptr = cap_ptr_vec[i];
|
let cap_ptr = cap_ptr_vec[i];
|
||||||
let next_ptr = cap_ptr_vec[i + 1];
|
let next_ptr = cap_ptr_vec[i + 1];
|
||||||
let cap_type = dev.location().read8(cap_ptr);
|
let raw_cap_type = dev.location().read8(cap_ptr);
|
||||||
let data = match cap_type {
|
|
||||||
0x01 => CapabilityData::Pm,
|
let Ok(cap_type) = CapabilityType::try_from(raw_cap_type) else {
|
||||||
0x02 => CapabilityData::Agp,
|
continue;
|
||||||
0x03 => CapabilityData::Vpd,
|
|
||||||
0x04 => CapabilityData::SlotId,
|
|
||||||
0x05 => CapabilityData::Msi,
|
|
||||||
0x06 => CapabilityData::Chswp,
|
|
||||||
0x07 => CapabilityData::PciX,
|
|
||||||
0x08 => CapabilityData::Hp,
|
|
||||||
0x09 => {
|
|
||||||
CapabilityData::Vndr(CapabilityVndrData::new(dev, cap_ptr, next_ptr - cap_ptr))
|
|
||||||
}
|
|
||||||
0x0A => CapabilityData::Dbg,
|
|
||||||
0x0B => CapabilityData::Ccrc,
|
|
||||||
0x0C => CapabilityData::Shpc,
|
|
||||||
0x0D => CapabilityData::Ssvid,
|
|
||||||
0x0E => CapabilityData::Agp3,
|
|
||||||
0x0F => CapabilityData::Secdev,
|
|
||||||
0x10 => CapabilityData::Exp,
|
|
||||||
0x11 => CapabilityData::Msix(CapabilityMsixData::new(dev, cap_ptr)),
|
|
||||||
0x12 => CapabilityData::Sata,
|
|
||||||
0x13 => CapabilityData::Af,
|
|
||||||
0x14 => CapabilityData::Ea,
|
|
||||||
_ => CapabilityData::Unknown(cap_type),
|
|
||||||
};
|
};
|
||||||
capabilities.push(Self { cap_data: data });
|
match cap_type {
|
||||||
|
CapabilityType::Msix => {
|
||||||
|
// "More than one MSI-X Capability structure per Function is prohibited."
|
||||||
|
if caps.msix.is_some() {
|
||||||
|
log::warn!(
|
||||||
|
"superfluous MSI-X Capability structures at {:?} are ignored",
|
||||||
|
dev.location()
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
caps.msix = Some(RawCapabilityMsix::parse(dev, cap_ptr));
|
||||||
|
}
|
||||||
|
CapabilityType::Vndr => {
|
||||||
|
caps.vndr
|
||||||
|
.push(RawCapabilityVndr::new(cap_ptr, next_ptr - cap_ptr));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
capabilities
|
caps
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Acquires a new [`CapabilityMsixData`] instance.
|
||||||
|
pub(super) fn acquire_msix_data(
|
||||||
|
&self,
|
||||||
|
loc: &PciDeviceLocation,
|
||||||
|
bar_manager: &mut BarManager,
|
||||||
|
) -> Result<Option<CapabilityMsixData>> {
|
||||||
|
let Some(raw_msix) = self.msix.as_ref() else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Some(CapabilityMsixData::new(loc, bar_manager, raw_msix)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterates over [`CapabilityVndrData`] instances.
|
||||||
|
pub(super) fn iter_vndr_data(
|
||||||
|
&self,
|
||||||
|
loc: &PciDeviceLocation,
|
||||||
|
) -> impl Iterator<Item = CapabilityVndrData> {
|
||||||
|
self.vndr
|
||||||
|
.iter()
|
||||||
|
.map(|raw_vndr| CapabilityVndrData::new(loc, raw_vndr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,42 @@
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
use ostd::{io::IoMem, irq::IrqLine, mm::VmIoOnce};
|
use ostd::{Error, Result, io::IoMem, irq::IrqLine, mm::VmIoOnce};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
PciDeviceLocation,
|
PciDeviceLocation,
|
||||||
arch::{MSIX_DEFAULT_MSG_ADDR, construct_remappable_msix_address},
|
arch::{MSIX_DEFAULT_MSG_ADDR, construct_remappable_msix_address},
|
||||||
cfg_space::{BarAccess, Command},
|
cfg_space::{BarAccess, Command, PciCommonCfgOffset},
|
||||||
common_device::PciCommonDevice,
|
common_device::{BarManager, PciCommonDevice},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// MSI-X capability. It will set the BAR space it uses to be hidden.
|
/// Raw information about MSI-X capability.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(super) struct RawCapabilityMsix {
|
||||||
|
cap_ptr: u16,
|
||||||
|
msg_ctrl: u16,
|
||||||
|
table_info: u32,
|
||||||
|
pba_info: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RawCapabilityMsix {
|
||||||
|
pub(super) fn parse(dev: &PciCommonDevice, cap_ptr: u16) -> Self {
|
||||||
|
let msg_ctrl = dev.location().read16(cap_ptr + 2);
|
||||||
|
let table_info = dev.location().read32(cap_ptr + 4);
|
||||||
|
let pba_info = dev.location().read32(cap_ptr + 8);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
cap_ptr,
|
||||||
|
msg_ctrl,
|
||||||
|
table_info,
|
||||||
|
pba_info,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// MSI-X capability.
|
||||||
|
///
|
||||||
|
/// It will acquire the access to the BAR space it uses.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CapabilityMsixData {
|
pub struct CapabilityMsixData {
|
||||||
loc: PciDeviceLocation,
|
loc: PciDeviceLocation,
|
||||||
|
|
@ -23,69 +49,41 @@ pub struct CapabilityMsixData {
|
||||||
/// | Vector Control: u32 | Msg Data: u32 | Msg Upper Addr: u32 | Msg Addr: u32 |
|
/// | Vector Control: u32 | Msg Data: u32 | Msg Upper Addr: u32 | Msg Addr: u32 |
|
||||||
table_bar: IoMem,
|
table_bar: IoMem,
|
||||||
/// Pending bits table.
|
/// Pending bits table.
|
||||||
|
#[expect(dead_code)]
|
||||||
pending_table_bar: IoMem,
|
pending_table_bar: IoMem,
|
||||||
table_offset: usize,
|
table_offset: usize,
|
||||||
|
#[expect(dead_code)]
|
||||||
pending_table_offset: usize,
|
pending_table_offset: usize,
|
||||||
irqs: Vec<Option<IrqLine>>,
|
irqs: Vec<Option<IrqLine>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for CapabilityMsixData {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
let new_vec = self.irqs.clone().to_vec();
|
|
||||||
Self {
|
|
||||||
loc: self.loc,
|
|
||||||
ptr: self.ptr,
|
|
||||||
table_size: self.table_size,
|
|
||||||
table_bar: self.table_bar.clone(),
|
|
||||||
pending_table_bar: self.pending_table_bar.clone(),
|
|
||||||
irqs: new_vec,
|
|
||||||
table_offset: self.table_offset,
|
|
||||||
pending_table_offset: self.pending_table_offset,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CapabilityMsixData {
|
impl CapabilityMsixData {
|
||||||
pub(super) fn new(dev: &mut PciCommonDevice, cap_ptr: u16) -> Self {
|
pub(super) fn new(
|
||||||
// Get Table and PBA offset, provide functions to modify them
|
loc: &PciDeviceLocation,
|
||||||
let table_info = dev.location().read32(cap_ptr + 4);
|
bar_manager: &mut BarManager,
|
||||||
let pba_info = dev.location().read32(cap_ptr + 8);
|
raw_cap: &RawCapabilityMsix,
|
||||||
|
) -> Result<Self> {
|
||||||
let table_bar;
|
let pba_bar = match bar_manager
|
||||||
let pba_bar;
|
.bar_mut((raw_cap.pba_info & 0b111) as u8)
|
||||||
|
.ok_or(Error::InvalidArgs)?
|
||||||
let bar_manager = dev.bar_manager_mut();
|
.acquire()?
|
||||||
match bar_manager
|
|
||||||
.bar_mut((pba_info & 0b111) as u8)
|
|
||||||
.expect("MSIX cfg:pba BAR is none")
|
|
||||||
.acquire()
|
|
||||||
.expect("MSIX cfg:pba BAR is unavailable")
|
|
||||||
{
|
{
|
||||||
BarAccess::Memory(io_mem) => {
|
BarAccess::Memory(io_mem) => io_mem,
|
||||||
pba_bar = io_mem.clone();
|
BarAccess::Io => return Err(Error::InvalidArgs),
|
||||||
}
|
|
||||||
BarAccess::Io => {
|
|
||||||
panic!("MSIX cfg:pba BAR is IO type")
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
match bar_manager
|
let pba_offset = (raw_cap.pba_info & !(0b111u32)) as usize;
|
||||||
.bar_mut((table_info & 0b111) as u8)
|
|
||||||
.expect("MSIX cfg:table BAR is none")
|
let table_bar = match bar_manager
|
||||||
.acquire()
|
.bar_mut((raw_cap.table_info & 0b111) as u8)
|
||||||
.expect("MSIX cfg:table BAR is unavailable")
|
.ok_or(Error::InvalidArgs)?
|
||||||
|
.acquire()?
|
||||||
{
|
{
|
||||||
BarAccess::Memory(io_mem) => {
|
BarAccess::Memory(io_mem) => io_mem,
|
||||||
table_bar = io_mem.clone();
|
BarAccess::Io => return Err(Error::InvalidArgs),
|
||||||
}
|
};
|
||||||
BarAccess::Io => {
|
let table_offset = (raw_cap.table_info & !(0b111u32)) as usize;
|
||||||
panic!("MSIX cfg:table BAR is IO type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let pba_offset = (pba_info & !(0b111u32)) as usize;
|
let table_size = (raw_cap.msg_ctrl & 0b11_1111_1111) + 1;
|
||||||
let table_offset = (table_info & !(0b111u32)) as usize;
|
|
||||||
|
|
||||||
let table_size = (dev.location().read16(cap_ptr + 2) & 0b11_1111_1111) + 1;
|
|
||||||
|
|
||||||
// Set the message address and disable all MSI-X vectors.
|
// Set the message address and disable all MSI-X vectors.
|
||||||
let message_address = MSIX_DEFAULT_MSG_ADDR;
|
let message_address = MSIX_DEFAULT_MSG_ADDR;
|
||||||
|
|
@ -103,32 +101,37 @@ impl CapabilityMsixData {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable MSI-X (bit 15: MSI-X Enable).
|
// Enable MSI-X (bit 15: MSI-X Enable).
|
||||||
dev.location()
|
loc.write16(
|
||||||
.write16(cap_ptr + 2, dev.location().read16(cap_ptr + 2) | 0x8000);
|
raw_cap.cap_ptr + 2,
|
||||||
|
loc.read16(raw_cap.cap_ptr + 2) | 0x8000,
|
||||||
|
);
|
||||||
// Disable INTx. Enable bus master.
|
// Disable INTx. Enable bus master.
|
||||||
dev.write_command(dev.read_command() | Command::INTERRUPT_DISABLE | Command::BUS_MASTER);
|
loc.write16(
|
||||||
|
PciCommonCfgOffset::Command as u16,
|
||||||
|
loc.read16(PciCommonCfgOffset::Command as u16)
|
||||||
|
| (Command::INTERRUPT_DISABLE | Command::BUS_MASTER).bits(),
|
||||||
|
);
|
||||||
|
|
||||||
let mut irqs = Vec::with_capacity(table_size as usize);
|
let mut irqs = Vec::with_capacity(table_size as usize);
|
||||||
for _ in 0..table_size {
|
for _ in 0..table_size {
|
||||||
irqs.push(None);
|
irqs.push(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
Ok(Self {
|
||||||
loc: *dev.location(),
|
loc: *loc,
|
||||||
ptr: cap_ptr,
|
ptr: raw_cap.cap_ptr,
|
||||||
table_size: (dev.location().read16(cap_ptr + 2) & 0b11_1111_1111) + 1,
|
table_size,
|
||||||
table_bar,
|
table_bar,
|
||||||
pending_table_bar: pba_bar,
|
pending_table_bar: pba_bar,
|
||||||
irqs,
|
irqs,
|
||||||
table_offset,
|
table_offset,
|
||||||
pending_table_offset: pba_offset,
|
pending_table_offset: pba_offset,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the size of the MSI-X Table.
|
/// Returns the size of the MSI-X Table.
|
||||||
pub fn table_size(&self) -> u16 {
|
pub fn table_size(&self) -> u16 {
|
||||||
// bit 10:0 table size
|
self.table_size
|
||||||
(self.loc.read16(self.ptr + 2) & 0b11_1111_1111) + 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enables an interrupt line.
|
/// Enables an interrupt line.
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,24 @@
|
||||||
|
|
||||||
use ostd::{Error, Result};
|
use ostd::{Error, Result};
|
||||||
|
|
||||||
use crate::{PciDeviceLocation, common_device::PciCommonDevice};
|
use crate::PciDeviceLocation;
|
||||||
|
|
||||||
/// Vendor specific capability. Users can access this capability area at will,
|
/// Raw information about vendor-specific capability.
|
||||||
/// except for the PCI configuration space which cannot be accessed at will through this structure.
|
#[derive(Debug)]
|
||||||
|
pub(super) struct RawCapabilityVndr {
|
||||||
|
cap_ptr: u16,
|
||||||
|
length: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RawCapabilityVndr {
|
||||||
|
pub(super) fn new(cap_ptr: u16, length: u16) -> Self {
|
||||||
|
Self { cap_ptr, length }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Vendor-specific capability.
|
||||||
|
///
|
||||||
|
/// Users can access this capability area at will.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct CapabilityVndrData {
|
pub struct CapabilityVndrData {
|
||||||
location: PciDeviceLocation,
|
location: PciDeviceLocation,
|
||||||
|
|
@ -16,11 +30,11 @@ pub struct CapabilityVndrData {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CapabilityVndrData {
|
impl CapabilityVndrData {
|
||||||
pub(super) fn new(dev: &PciCommonDevice, cap_ptr: u16, length: u16) -> Self {
|
pub(super) fn new(loc: &PciDeviceLocation, raw_cap: &RawCapabilityVndr) -> Self {
|
||||||
Self {
|
Self {
|
||||||
location: *dev.location(),
|
location: *loc,
|
||||||
cap_ptr,
|
cap_ptr: raw_cap.cap_ptr,
|
||||||
length,
|
length: raw_cap.length,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,12 @@
|
||||||
|
|
||||||
//! PCI device common definitions or functions.
|
//! PCI device common definitions or functions.
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use ostd::Result;
|
||||||
|
|
||||||
use super::{
|
|
||||||
capability::Capability,
|
|
||||||
cfg_space::{AddrLen, Bar, Command, Status},
|
|
||||||
device_info::PciDeviceId,
|
|
||||||
};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
cfg_space::{PciBridgeCfgOffset, PciCommonCfgOffset},
|
capability::{RawCapabilities, msix::CapabilityMsixData, vendor::CapabilityVndrData},
|
||||||
device_info::PciDeviceLocation,
|
cfg_space::{AddrLen, Bar, Command, PciBridgeCfgOffset, PciCommonCfgOffset, Status},
|
||||||
|
device_info::{PciDeviceId, PciDeviceLocation},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// PCI common device.
|
/// PCI common device.
|
||||||
|
|
@ -23,7 +19,7 @@ pub struct PciCommonDevice {
|
||||||
location: PciDeviceLocation,
|
location: PciDeviceLocation,
|
||||||
header_type: PciHeaderType,
|
header_type: PciHeaderType,
|
||||||
bar_manager: BarManager,
|
bar_manager: BarManager,
|
||||||
capabilities: Vec<Capability>,
|
capabilities: RawCapabilities,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PciCommonDevice {
|
impl PciCommonDevice {
|
||||||
|
|
@ -47,16 +43,6 @@ impl PciCommonDevice {
|
||||||
&mut self.bar_manager
|
&mut self.bar_manager
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the PCI capabilities.
|
|
||||||
pub fn capabilities(&self) -> &Vec<Capability> {
|
|
||||||
&self.capabilities
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the PCI capabilities and a mutable reference to the BAR manager.
|
|
||||||
pub fn capabilities_and_bar_manager_mut(&mut self) -> (&Vec<Capability>, &mut BarManager) {
|
|
||||||
(&self.capabilities, &mut self.bar_manager)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the PCI device type.
|
/// Returns the PCI device type.
|
||||||
pub fn device_type(&self) -> PciDeviceType {
|
pub fn device_type(&self) -> PciDeviceType {
|
||||||
self.header_type.device_type()
|
self.header_type.device_type()
|
||||||
|
|
@ -83,20 +69,41 @@ impl PciCommonDevice {
|
||||||
Status::from_bits_truncate(self.location.read16(PciCommonCfgOffset::Status as u16))
|
Status::from_bits_truncate(self.location.read16(PciCommonCfgOffset::Status as u16))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Acquires necessary resources to build the MSI-X capability data, if the capability exists.
|
||||||
|
///
|
||||||
|
/// Note that the MSI-X capability data occupies some memory BARs. Therefore, it will fail if
|
||||||
|
/// the necessary resources are not available.
|
||||||
|
pub fn acquire_msix_capability(&mut self) -> Result<Option<CapabilityMsixData>> {
|
||||||
|
self.capabilities
|
||||||
|
.acquire_msix_data(&self.location, &mut self.bar_manager)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets access to the vendor-specific capability data.
|
||||||
|
pub fn iter_vndr_capability(&self) -> impl Iterator<Item = CapabilityVndrData> {
|
||||||
|
self.capabilities.iter_vndr_data(&self.location)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets access to the vendor-specific capability data with a mutable reference to the BAR
|
||||||
|
/// manager.
|
||||||
|
pub fn iter_vndr_capability_with_bar_manager(
|
||||||
|
&mut self,
|
||||||
|
) -> (impl Iterator<Item = CapabilityVndrData>, &mut BarManager) {
|
||||||
|
(
|
||||||
|
self.capabilities.iter_vndr_data(&self.location),
|
||||||
|
&mut self.bar_manager,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn new(location: PciDeviceLocation) -> Option<Self> {
|
pub(super) fn new(location: PciDeviceLocation) -> Option<Self> {
|
||||||
if location.read16(0) == 0xFFFF {
|
if location.read16(0) == 0xFFFF {
|
||||||
// No device.
|
// No device.
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let capabilities = Vec::new();
|
|
||||||
let device_id = PciDeviceId::new(location);
|
let device_id = PciDeviceId::new(location);
|
||||||
let bar_manager = BarManager {
|
|
||||||
bars: [const { None }; 6],
|
|
||||||
};
|
|
||||||
let mut header_type =
|
let mut header_type =
|
||||||
PciHeaderType::try_from_raw(location.read8(PciCommonCfgOffset::HeaderType as u16))?;
|
PciHeaderType::try_from_raw(location.read8(PciCommonCfgOffset::HeaderType as u16))?;
|
||||||
|
|
||||||
if let PciDeviceType::PciToPciBridge(primary_bus, secondary_bus, subordinate_bus) =
|
if let PciDeviceType::PciToPciBridge(primary_bus, secondary_bus, subordinate_bus) =
|
||||||
&mut header_type.device_type
|
&mut header_type.device_type
|
||||||
{
|
{
|
||||||
|
|
@ -105,6 +112,11 @@ impl PciCommonDevice {
|
||||||
*subordinate_bus = location.read8(PciBridgeCfgOffset::SubordinateBusNumber as u16);
|
*subordinate_bus = location.read8(PciBridgeCfgOffset::SubordinateBusNumber as u16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let bar_manager = BarManager {
|
||||||
|
bars: [const { None }; 6],
|
||||||
|
};
|
||||||
|
let capabilities = RawCapabilities::default();
|
||||||
|
|
||||||
let mut device = Self {
|
let mut device = Self {
|
||||||
device_id,
|
device_id,
|
||||||
location,
|
location,
|
||||||
|
|
@ -121,7 +133,7 @@ impl PciCommonDevice {
|
||||||
device.bar_manager = BarManager::new(device.header_type.device_type(), location);
|
device.bar_manager = BarManager::new(device.header_type.device_type(), location);
|
||||||
device.write_command(command_val | (Command::MEMORY_SPACE | Command::IO_SPACE));
|
device.write_command(command_val | (Command::MEMORY_SPACE | Command::IO_SPACE));
|
||||||
|
|
||||||
device.capabilities = Capability::device_capabilities(&mut device);
|
device.capabilities = RawCapabilities::parse(&device);
|
||||||
|
|
||||||
Some(device)
|
Some(device)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,7 @@ use alloc::{boxed::Box, sync::Arc};
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
|
|
||||||
use aster_pci::{
|
use aster_pci::{
|
||||||
PciDeviceId, bus::PciDevice, capability::CapabilityData, cfg_space::BarAccess,
|
PciDeviceId, bus::PciDevice, cfg_space::BarAccess, common_device::PciCommonDevice,
|
||||||
common_device::PciCommonDevice,
|
|
||||||
};
|
};
|
||||||
use aster_util::{field_ptr, safe_ptr::SafePtr};
|
use aster_util::{field_ptr, safe_ptr::SafePtr};
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
|
@ -284,15 +283,12 @@ impl VirtioPciModernTransport {
|
||||||
|
|
||||||
info!("[Virtio]: Found device:{:?}", device_type);
|
info!("[Virtio]: Found device:{:?}", device_type);
|
||||||
|
|
||||||
let mut msix = None;
|
|
||||||
let mut notify = None;
|
let mut notify = None;
|
||||||
let mut common_cfg = None;
|
let mut common_cfg = None;
|
||||||
let mut device_cfg = None;
|
let mut device_cfg = None;
|
||||||
let (caps, bar_manager) = common_device.capabilities_and_bar_manager_mut();
|
let (vndr_caps, bar_manager) = common_device.iter_vndr_capability_with_bar_manager();
|
||||||
for cap in caps {
|
for vndr_cap in vndr_caps {
|
||||||
match cap.capability_data() {
|
let data = VirtioPciCapabilityData::new(bar_manager, vndr_cap);
|
||||||
CapabilityData::Vndr(vendor) => {
|
|
||||||
let data = VirtioPciCapabilityData::new(bar_manager, *vendor);
|
|
||||||
match data.typ() {
|
match data.typ() {
|
||||||
VirtioPciCpabilityType::CommonCfg => {
|
VirtioPciCpabilityType::CommonCfg => {
|
||||||
common_cfg = Some(VirtioPciCommonCfg::new(&data));
|
common_cfg = Some(VirtioPciCommonCfg::new(&data));
|
||||||
|
|
@ -311,23 +307,14 @@ impl VirtioPciModernTransport {
|
||||||
VirtioPciCpabilityType::PciCfg => {}
|
VirtioPciCpabilityType::PciCfg => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CapabilityData::Msix(data) => {
|
|
||||||
msix = Some(data.clone());
|
|
||||||
}
|
|
||||||
CapabilityData::Unknown(id) => {
|
|
||||||
panic!("unknown capability: {}", id)
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
panic!("PCI Virtio device should not have other type of capability")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: Support interrupt without MSI-X
|
|
||||||
let msix = msix.unwrap();
|
|
||||||
let notify = notify.unwrap();
|
let notify = notify.unwrap();
|
||||||
let common_cfg = common_cfg.unwrap();
|
let common_cfg = common_cfg.unwrap();
|
||||||
let device_cfg = device_cfg.unwrap();
|
let device_cfg = device_cfg.unwrap();
|
||||||
|
|
||||||
|
// TODO: Support interrupt without MSI-X.
|
||||||
|
let msix = common_device.acquire_msix_capability().unwrap().unwrap();
|
||||||
let msix_manager = VirtioMsixManager::new(msix);
|
let msix_manager = VirtioMsixManager::new(msix);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
common_device,
|
common_device,
|
||||||
common_cfg,
|
common_cfg,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ use alloc::{boxed::Box, collections::vec_deque::VecDeque, sync::Arc};
|
||||||
|
|
||||||
use aster_pci::{
|
use aster_pci::{
|
||||||
bus::{PciDevice, PciDriver},
|
bus::{PciDevice, PciDriver},
|
||||||
capability::CapabilityData,
|
|
||||||
common_device::PciCommonDevice,
|
common_device::PciCommonDevice,
|
||||||
};
|
};
|
||||||
use ostd::{bus::BusProbeError, sync::SpinLock};
|
use ostd::{bus::BusProbeError, sync::SpinLock};
|
||||||
|
|
@ -42,10 +41,7 @@ impl PciDriver for VirtioPciDriver {
|
||||||
return Err((BusProbeError::DeviceNotMatch, device));
|
return Err((BusProbeError::DeviceNotMatch, device));
|
||||||
}
|
}
|
||||||
|
|
||||||
let has_vendor_cap = device
|
let has_vendor_cap = device.iter_vndr_capability().next().is_some();
|
||||||
.capabilities()
|
|
||||||
.iter()
|
|
||||||
.any(|cap| matches!(cap.capability_data(), CapabilityData::Vndr(_)));
|
|
||||||
let device_id = *device.device_id();
|
let device_id = *device.device_id();
|
||||||
let transport: Box<dyn VirtioTransport> = match device_id.device_id {
|
let transport: Box<dyn VirtioTransport> = match device_id.device_id {
|
||||||
0x1000..0x1040 if (device.device_id().revision_id == 0) => {
|
0x1000..0x1040 if (device.device_id().revision_id == 0) => {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
use alloc::{boxed::Box, sync::Arc};
|
use alloc::{boxed::Box, sync::Arc};
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
|
|
||||||
use aster_pci::{capability::CapabilityData, cfg_space::BarAccess, common_device::PciCommonDevice};
|
use aster_pci::{cfg_space::BarAccess, common_device::PciCommonDevice};
|
||||||
use aster_util::safe_ptr::SafePtr;
|
use aster_util::safe_ptr::SafePtr;
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use ostd::{
|
use ostd::{
|
||||||
|
|
@ -112,17 +112,8 @@ impl VirtioPciLegacyTransport {
|
||||||
num_queues += 1;
|
num_queues += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Support interrupt without MSI-X
|
// TODO: Support interrupt without MSI-X.
|
||||||
let mut msix = None;
|
let Ok(Some(msix)) = common_device.acquire_msix_capability() else {
|
||||||
for cap in common_device.capabilities().iter() {
|
|
||||||
match cap.capability_data() {
|
|
||||||
CapabilityData::Msix(data) => {
|
|
||||||
msix = Some(data.clone());
|
|
||||||
}
|
|
||||||
_ => continue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let Some(msix) = msix else {
|
|
||||||
return Err((BusProbeError::ConfigurationSpaceError, common_device));
|
return Err((BusProbeError::ConfigurationSpaceError, common_device));
|
||||||
};
|
};
|
||||||
let msix_manager = VirtioMsixManager::new(msix);
|
let msix_manager = VirtioMsixManager::new(msix);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue