Merge branch 'pcid_cleanup8' into 'master'

Get rid of PciHeader

See merge request redox-os/drivers!177
This commit is contained in:
Jeremy Soller
2024-06-16 13:05:12 +00:00
3 changed files with 124 additions and 355 deletions
+88 -31
View File
@@ -4,21 +4,23 @@ use std::sync::{Arc, Mutex};
use std::thread;
use log::{debug, info, trace, warn};
use pci_types::{CommandRegister, PciAddress};
use pci_types::{
Bar as TyBar, CommandRegister, EndpointHeader, HeaderType, PciAddress,
PciHeader as TyPciHeader, PciPciBridgeHeader,
};
use redox_log::{OutputBuilder, RedoxLogger};
use structopt::StructOpt;
use crate::cfg_access::Pcie;
use crate::config::Config;
use crate::driver_interface::LegacyInterruptLine;
use crate::pci_header::{PciEndpointHeader, PciHeader, PciHeaderError};
use crate::pci::{FullDeviceId, PciBar};
mod cfg_access;
mod config;
mod driver_handler;
mod driver_interface;
mod pci;
mod pci_header;
#[derive(StructOpt)]
#[structopt(about)]
@@ -42,9 +44,14 @@ pub struct State {
pcie: Pcie,
}
fn handle_parsed_header(state: Arc<State>, config: &Config, header: PciEndpointHeader) {
fn handle_parsed_header(
state: Arc<State>,
config: &Config,
mut endpoint_header: EndpointHeader,
full_device_id: FullDeviceId,
) {
for driver in config.drivers.iter() {
if !driver.match_function(header.full_device_id()) {
if !driver.match_function(&full_device_id) {
continue;
}
@@ -52,8 +59,43 @@ fn handle_parsed_header(state: Arc<State>, config: &Config, header: PciEndpointH
continue;
};
let mut bars = [PciBar::None; 6];
let mut skip = false;
for i in 0..6 {
if skip {
skip = false;
continue;
}
match endpoint_header.bar(i, &state.pcie) {
Some(TyBar::Io { port }) => {
bars[i as usize] = PciBar::Port(port.try_into().unwrap())
}
Some(TyBar::Memory32 {
address,
size,
prefetchable: _,
}) => {
bars[i as usize] = PciBar::Memory32 {
addr: address,
size,
}
}
Some(TyBar::Memory64 {
address,
size,
prefetchable: _,
}) => {
bars[i as usize] = PciBar::Memory64 {
addr: address,
size,
};
skip = true; // Each 64bit memory BAR occupies two slots
}
None => bars[i as usize] = PciBar::None,
}
}
let mut string = String::new();
let bars = header.bars(&state.pcie);
for (i, bar) in bars.iter().enumerate() {
if !bar.is_none() {
string.push_str(&format!(" {i}={}", bar.display()));
@@ -64,8 +106,6 @@ fn handle_parsed_header(state: Arc<State>, config: &Config, header: PciEndpointH
info!(" BAR{}", string);
}
let mut endpoint_header = header.endpoint_header(&state.pcie);
// Enable bus mastering, memory space, and I/O space
endpoint_header.update_command(&state.pcie, |cmd| {
cmd | CommandRegister::BUS_MASTER_ENABLE
@@ -114,13 +154,13 @@ fn handle_parsed_header(state: Arc<State>, config: &Config, header: PciEndpointH
let func = driver_interface::PciFunction {
bars,
addr: header.address(),
addr: endpoint_header.header().address(),
legacy_interrupt_line: if legacy_interrupt_enabled {
Some(LegacyInterruptLine(irq))
} else {
None
},
full_device_id: header.full_device_id().clone(),
full_device_id: full_device_id.clone(),
};
driver_handler::DriverHandler::spawn(Arc::clone(&state), func, capabilities, args);
@@ -227,29 +267,46 @@ fn main(args: Args) {
'dev: for dev_num in 0..32 {
for func_num in 0..8 {
let func_addr = PciAddress::new(0, bus_num, dev_num, func_num);
match PciHeader::from_reader(&state.pcie, func_addr) {
Ok(header) => {
info!("{}", header.display());
match header {
PciHeader::General(endpoint_header) => {
handle_parsed_header(Arc::clone(&state), &config, endpoint_header);
}
PciHeader::PciToPci {
secondary_bus_num, ..
} => {
bus_nums.push(secondary_bus_num);
}
}
let header = TyPciHeader::new(PciAddress::new(0, bus_num, dev_num, func_num));
let (vendor_id, device_id) = header.id(&state.pcie);
if vendor_id == 0xffff && device_id == 0xffff {
if func_num == 0 {
trace!("PCI {:>02X}:{:>02X}: no dev", bus_num, dev_num);
continue 'dev;
}
Err(PciHeaderError::NoDevice) => {
if func_addr.function() == 0 {
trace!("PCI {:>02X}:{:>02X}: no dev", bus_num, dev_num);
continue 'dev;
}
continue;
}
let (revision, class, subclass, interface) = header.revision_and_class(&state.pcie);
let full_device_id = FullDeviceId {
vendor_id,
device_id,
class,
subclass,
interface,
revision,
};
info!("PCI {} {}", header.address(), full_device_id.display());
match header.header_type(&state.pcie) {
HeaderType::Endpoint => {
handle_parsed_header(
Arc::clone(&state),
&config,
EndpointHeader::from_header(header, &state.pcie).unwrap(),
full_device_id,
);
}
Err(PciHeaderError::UnknownHeaderType(id)) => {
warn!("pcid: unknown header type: {id:?}");
HeaderType::PciPciBridge => {
let bridge_header =
PciPciBridgeHeader::from_header(header, &state.pcie).unwrap();
bus_nums.push(bridge_header.secondary_bus_number(&state.pcie));
}
ty => {
warn!("pcid: unknown header type: {ty:?}");
}
}
}
+36
View File
@@ -1,3 +1,4 @@
use pci_types::device_type::DeviceType;
use serde::{Deserialize, Serialize};
/// All identifying information of a PCI function.
@@ -10,3 +11,38 @@ pub struct FullDeviceId {
pub interface: u8,
pub revision: u8,
}
impl FullDeviceId {
pub(crate) fn display(&self) -> String {
let mut string = format!(
"{:>04X}:{:>04X} {:>02X}.{:>02X}.{:>02X}.{:>02X} {:?}",
self.vendor_id,
self.device_id,
self.class,
self.subclass,
self.interface,
self.revision,
self.class,
);
let device_type = DeviceType::from((self.class, self.subclass));
match device_type {
DeviceType::LegacyVgaCompatible => string.push_str(" VGA CTL"),
DeviceType::IdeController => string.push_str(" IDE"),
DeviceType::SataController => match self.interface {
0 => string.push_str(" SATA VND"),
1 => string.push_str(" SATA AHCI"),
_ => (),
},
DeviceType::UsbController => match self.interface {
0x00 => string.push_str(" UHCI"),
0x10 => string.push_str(" OHCI"),
0x20 => string.push_str(" EHCI"),
0x30 => string.push_str(" XHCI"),
_ => (),
},
DeviceType::NvmeController => string.push_str(" NVME"),
_ => (),
}
string
}
}
-324
View File
@@ -1,324 +0,0 @@
use pci_types::device_type::DeviceType;
use pci_types::{
Bar as TyBar, ConfigRegionAccess, EndpointHeader, HeaderType, PciAddress,
PciHeader as TyPciHeader, PciPciBridgeHeader,
};
use crate::pci::{FullDeviceId, PciBar};
#[derive(Debug, PartialEq)]
pub enum PciHeaderError {
NoDevice,
UnknownHeaderType(HeaderType),
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct SharedPciHeader {
full_device_id: FullDeviceId,
addr: PciAddress,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct PciEndpointHeader {
shared: SharedPciHeader,
subsystem_vendor_id: u16,
subsystem_id: u16,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PciHeader {
General(PciEndpointHeader),
PciToPci {
shared: SharedPciHeader,
secondary_bus_num: u8,
},
}
impl PciHeader {
/// Parse the bytes found in the Configuration Space of the PCI device into
/// a more usable PciHeader.
pub fn from_reader(
access: &impl ConfigRegionAccess,
addr: PciAddress,
) -> Result<PciHeader, PciHeaderError> {
let header = TyPciHeader::new(addr);
let (vendor_id, device_id) = header.id(access);
if vendor_id == 0xffff && device_id == 0xffff {
return Err(PciHeaderError::NoDevice);
}
let (revision, class, subclass, interface) = header.revision_and_class(access);
let header_type = header.header_type(access);
let shared = SharedPciHeader {
full_device_id: FullDeviceId {
vendor_id,
device_id,
class,
subclass,
interface,
revision,
},
addr,
};
match header_type {
HeaderType::Endpoint => {
let endpoint_header = EndpointHeader::from_header(header, access).unwrap();
let (subsystem_id, subsystem_vendor_id) = endpoint_header.subsystem(access);
Ok(PciHeader::General(PciEndpointHeader {
shared,
subsystem_vendor_id,
subsystem_id,
}))
}
HeaderType::PciPciBridge => {
let bridge_header = PciPciBridgeHeader::from_header(header, access).unwrap();
let secondary_bus_num = bridge_header.secondary_bus_number(access);
Ok(PciHeader::PciToPci {
shared,
secondary_bus_num,
})
}
ty => Err(PciHeaderError::UnknownHeaderType(ty)),
}
}
/// Return all identifying information of the PCI function.
pub fn full_device_id(&self) -> &FullDeviceId {
match self {
PciHeader::General(PciEndpointHeader {
shared:
SharedPciHeader {
full_device_id: device_id,
..
},
..
})
| PciHeader::PciToPci {
shared:
SharedPciHeader {
full_device_id: device_id,
..
},
..
} => device_id,
}
}
/// Return the PCI address.
pub fn address(&self) -> PciAddress {
match self {
PciHeader::General(header) => header.address(),
PciHeader::PciToPci { shared, .. } => shared.addr,
}
}
/// Return the Vendor ID field.
pub fn vendor_id(&self) -> u16 {
self.full_device_id().vendor_id
}
/// Return the Device ID field.
pub fn device_id(&self) -> u16 {
self.full_device_id().device_id
}
/// Return the Revision field.
pub fn revision(&self) -> u8 {
self.full_device_id().revision
}
/// Return the Interface field.
pub fn interface(&self) -> u8 {
self.full_device_id().interface
}
/// Return the Subclass field.
pub fn subclass(&self) -> u8 {
self.full_device_id().subclass
}
/// Return the Class field.
pub fn class(&self) -> u8 {
self.full_device_id().class
}
/// Format a human readable string indicating the address and type of PCI device.
pub fn display(&self) -> String {
let mut string = format!(
"PCI {} {:>04X}:{:>04X} {:>02X}.{:>02X}.{:>02X}.{:>02X} {:?}",
self.address(),
self.vendor_id(),
self.device_id(),
self.class(),
self.subclass(),
self.interface(),
self.revision(),
self.class()
);
let device_type = DeviceType::from((self.class(), self.subclass()));
match device_type {
DeviceType::LegacyVgaCompatible => string.push_str(" VGA CTL"),
DeviceType::IdeController => string.push_str(" IDE"),
DeviceType::SataController => match self.interface() {
0 => string.push_str(" SATA VND"),
1 => string.push_str(" SATA AHCI"),
_ => (),
},
DeviceType::UsbController => match self.interface() {
0x00 => string.push_str(" UHCI"),
0x10 => string.push_str(" OHCI"),
0x20 => string.push_str(" EHCI"),
0x30 => string.push_str(" XHCI"),
_ => (),
},
_ => (),
}
string
}
}
impl PciEndpointHeader {
pub fn address(&self) -> PciAddress {
self.shared.addr
}
pub fn endpoint_header(&self, access: &impl ConfigRegionAccess) -> EndpointHeader {
EndpointHeader::from_header(TyPciHeader::new(self.shared.addr), access).unwrap()
}
pub fn full_device_id(&self) -> &FullDeviceId {
&self.shared.full_device_id
}
/// Return the Headers BARs.
pub fn bars(&self, access: &impl ConfigRegionAccess) -> [PciBar; 6] {
let endpoint_header = self.endpoint_header(access);
let mut bars = [PciBar::None; 6];
let mut skip = false;
for i in 0..6 {
if skip {
skip = false;
continue;
}
match endpoint_header.bar(i, access) {
Some(TyBar::Io { port }) => {
bars[i as usize] = PciBar::Port(port.try_into().unwrap())
}
Some(TyBar::Memory32 {
address,
size,
prefetchable: _,
}) => {
bars[i as usize] = PciBar::Memory32 {
addr: address,
size,
}
}
Some(TyBar::Memory64 {
address,
size,
prefetchable: _,
}) => {
bars[i as usize] = PciBar::Memory64 {
addr: address,
size,
};
skip = true; // Each 64bit memory BAR occupies two slots
}
None => bars[i as usize] = PciBar::None,
}
}
bars
}
}
#[cfg(test)]
mod test {
use std::convert::TryInto;
use pci_types::device_type::DeviceType;
use pci_types::{ConfigRegionAccess, PciAddress};
use super::{PciHeader, PciHeaderError};
struct TestCfgAccess<'a> {
addr: PciAddress,
bytes: &'a [u8],
}
impl ConfigRegionAccess for TestCfgAccess<'_> {
unsafe fn read(&self, addr: PciAddress, offset: u16) -> u32 {
assert_eq!(addr, self.addr);
let offset = offset as usize;
assert!(offset < self.bytes.len());
u32::from_le_bytes(self.bytes[offset..offset + 4].try_into().unwrap())
}
unsafe fn write(&self, _addr: PciAddress, _offset: u16, _value: u32) {
unreachable!("should not write during tests");
}
}
#[rustfmt::skip]
const IGB_DEV_BYTES: [u8; 256] = [
0x86, 0x80, 0x33, 0x15, 0x07, 0x04, 0x10, 0x00, 0x03, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x50, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x58, 0xf7,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x15, 0x33, 0x15,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x01, 0x00, 0x00,
0x01, 0x50, 0x23, 0xc8, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x05, 0x70, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x11, 0xa0, 0x04, 0x80, 0x03, 0x00, 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0x10, 0x00, 0x02, 0x00, 0xc2, 0x8c, 0x00, 0x10, 0x0f, 0x28, 0x19, 0x00, 0x11, 0x5c, 0x42, 0x00,
0x42, 0x00, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
];
#[test]
fn tset_parse_igb_dev() {
let header = PciHeader::from_reader(
&TestCfgAccess {
addr: PciAddress::new(0, 2, 4, 0),
bytes: &IGB_DEV_BYTES,
},
PciAddress::new(0, 2, 4, 0),
)
.unwrap();
match header {
PciHeader::General { .. } => {}
_ => panic!("wrong header type"),
}
assert_eq!(header.device_id(), 0x1533);
assert_eq!(header.vendor_id(), 0x8086);
assert_eq!(header.revision(), 3);
assert_eq!(header.interface(), 0);
assert_eq!(
DeviceType::from((header.class(), header.subclass())),
DeviceType::EthernetController
);
assert_eq!(header.subclass(), 0);
}
#[test]
fn test_parse_nonexistent() {
let bytes = &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
assert_eq!(
PciHeader::from_reader(
&TestCfgAccess {
addr: PciAddress::new(0, 2, 4, 0),
bytes,
},
PciAddress::new(0, 2, 4, 0),
),
Err(PciHeaderError::NoDevice)
);
}
}