Merge branch 'pcid_cleanup8' into 'master'
Get rid of PciHeader See merge request redox-os/drivers!177
This commit is contained in:
+88
-31
@@ -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:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user