pci: add lspci-like utility
This commit is contained in:
parent
abdf53368b
commit
9e48530e62
@ -19,6 +19,16 @@ use crate::{
|
||||
pub struct PciDeviceInfo {
|
||||
/// Address of the device
|
||||
pub address: PciAddress,
|
||||
/// Class field of the configuration space
|
||||
pub class: u8,
|
||||
/// Subclass field of the configuration space
|
||||
pub subclass: u8,
|
||||
/// Prog IF field of the configuration space
|
||||
pub prog_if: u8,
|
||||
/// Vendor ID field of the configuration space
|
||||
pub vendor_id: u16,
|
||||
/// Device ID field of the configuration space
|
||||
pub device_id: u16,
|
||||
/// Configuration space access method
|
||||
pub config_space: PciConfigSpace,
|
||||
/// Describes the PCI segment this device is a part of
|
||||
@ -83,7 +93,8 @@ pub struct PciDriver {
|
||||
/// Used to store PCI bus devices which were enumerated by the kernel
|
||||
pub struct PciBusDevice {
|
||||
pub(crate) info: PciDeviceInfo,
|
||||
pub(crate) driver: Option<Arc<dyn Device>>,
|
||||
pub(crate) device: Option<Arc<dyn Device>>,
|
||||
pub(crate) driver_name: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl PciDeviceInfo {
|
||||
|
@ -8,10 +8,11 @@ use core::fmt;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use acpi::mcfg::McfgEntry;
|
||||
use alloc::{collections::BTreeMap, sync::Arc, vec::Vec};
|
||||
use alloc::{collections::BTreeMap, format, sync::Arc, vec::Vec};
|
||||
use bitflags::bitflags;
|
||||
use device::{PciBusDevice, PciDeviceInfo, PciDriver, PciInterrupt, PciInterruptRoute, PciMatch};
|
||||
use device_api::device::Device;
|
||||
use libk::fs::sysfs::{self, object::KObject};
|
||||
use libk_mm::address::PhysicalAddress;
|
||||
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
|
||||
use space::legacy;
|
||||
@ -19,6 +20,7 @@ use yggdrasil_abi::error::Error;
|
||||
|
||||
pub mod capability;
|
||||
pub mod device;
|
||||
mod nodes;
|
||||
mod space;
|
||||
|
||||
pub use space::{
|
||||
@ -208,7 +210,7 @@ pub struct PciSegmentInfo {
|
||||
pub struct PciBusSegment {
|
||||
allocator: Option<BusAddressAllocator>,
|
||||
info: Arc<PciSegmentInfo>,
|
||||
devices: Vec<PciBusDevice>,
|
||||
devices: Vec<Arc<KObject<IrqSafeSpinlock<PciBusDevice>>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -240,6 +242,14 @@ impl PciBaseAddress {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_zero(&self) -> bool {
|
||||
match *self {
|
||||
Self::Memory32(base) => base == 0,
|
||||
Self::Memory64(base) => base == 0,
|
||||
Self::Io(base) => base == 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PciBusSegment {
|
||||
@ -348,13 +358,39 @@ impl PciBusSegment {
|
||||
}
|
||||
}
|
||||
|
||||
let vendor_id = config.vendor_id();
|
||||
let device_id = config.device_id();
|
||||
let class = config.class_code();
|
||||
let subclass = config.subclass();
|
||||
let prog_if = config.prog_if();
|
||||
|
||||
let info = PciDeviceInfo {
|
||||
address,
|
||||
vendor_id,
|
||||
device_id,
|
||||
class,
|
||||
subclass,
|
||||
prog_if,
|
||||
segment: self.info.clone(),
|
||||
config_space: config,
|
||||
interrupt_config: Arc::new(OneTimeInit::new()),
|
||||
};
|
||||
self.devices.push(PciBusDevice { info, driver: None });
|
||||
|
||||
let object = nodes::make_sysfs_object(PciBusDevice {
|
||||
info,
|
||||
driver_name: None,
|
||||
device: None,
|
||||
});
|
||||
let pci_object = PCI_SYSFS_NODE.or_init_with(|| {
|
||||
let bus_object = sysfs::bus().unwrap();
|
||||
let pci_object = KObject::new(());
|
||||
bus_object.add_object("pci", pci_object.clone()).ok();
|
||||
pci_object
|
||||
});
|
||||
|
||||
let name = format!("{address}");
|
||||
pci_object.add_object(name, object.clone()).ok();
|
||||
self.devices.push(object);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -407,7 +443,8 @@ impl PciBusManager {
|
||||
|
||||
for segment in this.segments.iter_mut() {
|
||||
for device in segment.devices.iter_mut() {
|
||||
if !f(device)? {
|
||||
let mut device = device.lock();
|
||||
if !f(&mut *device)? {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
@ -555,39 +592,29 @@ impl PciConfigurationSpace for PciConfigSpace {
|
||||
}
|
||||
|
||||
fn setup_bus_device(device: &mut PciBusDevice) -> Result<(), Error> {
|
||||
if device.driver.is_some() {
|
||||
if device.device.is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let config = &device.info.config_space;
|
||||
|
||||
log::debug!(
|
||||
"{}: {:04x}:{:04x}",
|
||||
device.info.address,
|
||||
config.vendor_id(),
|
||||
config.device_id()
|
||||
device.info.vendor_id,
|
||||
device.info.device_id
|
||||
);
|
||||
|
||||
let class = config.class_code();
|
||||
let subclass = config.subclass();
|
||||
let prog_if = config.prog_if();
|
||||
|
||||
let drivers = PCI_DRIVERS.lock();
|
||||
for driver in drivers.iter() {
|
||||
if driver
|
||||
.check
|
||||
.check_device(&device.info, class, subclass, prog_if)
|
||||
{
|
||||
if driver.check.check_device(&device.info) {
|
||||
// TODO add the device to the bus
|
||||
log::debug!(" -> {:?}", driver.name);
|
||||
let instance = (driver.probe)(&device.info)?;
|
||||
|
||||
unsafe { instance.clone().init() }?;
|
||||
|
||||
device.driver.replace(instance);
|
||||
device.device.replace(instance);
|
||||
device.driver_name.replace(driver.name);
|
||||
break;
|
||||
} else {
|
||||
log::debug!(" -> No driver");
|
||||
}
|
||||
}
|
||||
|
||||
@ -595,17 +622,19 @@ fn setup_bus_device(device: &mut PciBusDevice) -> Result<(), Error> {
|
||||
}
|
||||
|
||||
impl PciMatch {
|
||||
pub fn check_device(&self, info: &PciDeviceInfo, class: u8, subclass: u8, prog_if: u8) -> bool {
|
||||
pub fn check_device(&self, info: &PciDeviceInfo) -> bool {
|
||||
match self {
|
||||
Self::Generic(f) => f(info),
|
||||
&Self::Vendor(vendor_, device_) => {
|
||||
info.config_space.vendor_id() == vendor_ && info.config_space.device_id() == device_
|
||||
info.vendor_id == vendor_ && info.device_id == device_
|
||||
}
|
||||
&Self::Class(class_, Some(subclass_), Some(prog_if_)) => {
|
||||
class_ == class && subclass_ == subclass && prog_if_ == prog_if
|
||||
class_ == info.class && subclass_ == info.subclass && prog_if_ == info.prog_if
|
||||
}
|
||||
&Self::Class(class_, Some(subclass_), _) => class_ == class && subclass_ == subclass,
|
||||
&Self::Class(class_, _, _) => class_ == class,
|
||||
&Self::Class(class_, Some(subclass_), _) => {
|
||||
class_ == info.class && subclass_ == info.subclass
|
||||
}
|
||||
&Self::Class(class_, _, _) => class_ == info.class,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -651,3 +680,4 @@ pub fn register_generic_driver(
|
||||
|
||||
static PCI_DRIVERS: IrqSafeSpinlock<Vec<PciDriver>> = IrqSafeSpinlock::new(Vec::new());
|
||||
static PCI_MANAGER: IrqSafeSpinlock<PciBusManager> = IrqSafeSpinlock::new(PciBusManager::new());
|
||||
static PCI_SYSFS_NODE: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
|
||||
|
135
kernel/driver/bus/pci/src/nodes.rs
Normal file
135
kernel/driver/bus/pci/src/nodes.rs
Normal file
@ -0,0 +1,135 @@
|
||||
use alloc::{format, string::String, sync::Arc};
|
||||
use libk::{
|
||||
error::Error,
|
||||
fs::sysfs::{
|
||||
attribute::{StringAttribute, StringAttributeOps},
|
||||
object::KObject,
|
||||
},
|
||||
};
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
|
||||
use crate::{device::PciBusDevice, PciBaseAddress, PciCapabilityId, PciConfigurationSpace};
|
||||
|
||||
pub(crate) fn make_sysfs_object(
|
||||
device: PciBusDevice,
|
||||
) -> Arc<KObject<IrqSafeSpinlock<PciBusDevice>>> {
|
||||
struct Resources;
|
||||
struct Capabilities;
|
||||
struct Driver;
|
||||
struct Class;
|
||||
struct Id;
|
||||
|
||||
impl StringAttributeOps for Driver {
|
||||
type Data = IrqSafeSpinlock<PciBusDevice>;
|
||||
const NAME: &'static str = "driver";
|
||||
|
||||
fn read(state: &Self::Data) -> Result<String, Error> {
|
||||
let state = state.lock();
|
||||
if let Some(driver) = state.driver_name {
|
||||
Ok(driver.into())
|
||||
} else {
|
||||
Ok("".into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StringAttributeOps for Id {
|
||||
type Data = IrqSafeSpinlock<PciBusDevice>;
|
||||
const NAME: &'static str = "id";
|
||||
|
||||
fn read(state: &Self::Data) -> Result<String, Error> {
|
||||
let state = state.lock();
|
||||
Ok(format!(
|
||||
"{:04x}:{:04x}",
|
||||
state.info.vendor_id, state.info.device_id
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl StringAttributeOps for Class {
|
||||
type Data = IrqSafeSpinlock<PciBusDevice>;
|
||||
const NAME: &'static str = "class";
|
||||
|
||||
fn read(state: &Self::Data) -> Result<String, Error> {
|
||||
let state = state.lock();
|
||||
Ok(format!(
|
||||
"{:02x}:{:02x}:{:02x}",
|
||||
state.info.class, state.info.subclass, state.info.prog_if
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl StringAttributeOps for Resources {
|
||||
type Data = IrqSafeSpinlock<PciBusDevice>;
|
||||
const NAME: &'static str = "resources";
|
||||
const NEWLINE: bool = false;
|
||||
|
||||
fn read(state: &Self::Data) -> Result<String, Error> {
|
||||
use core::fmt::Write;
|
||||
|
||||
let state = state.lock();
|
||||
let mut output = String::new();
|
||||
for i in 0..6 {
|
||||
if let Some(bar) = state.info.config_space.bar(i) {
|
||||
if bar.is_zero() {
|
||||
continue;
|
||||
}
|
||||
|
||||
match bar {
|
||||
PciBaseAddress::Io(base) => {
|
||||
writeln!(output, "{i}:pio:{base:#06x}").ok();
|
||||
}
|
||||
PciBaseAddress::Memory32(base) => {
|
||||
writeln!(output, "{i}:m32:{base:#010x}").ok();
|
||||
}
|
||||
PciBaseAddress::Memory64(base) => {
|
||||
writeln!(output, "{i}:m64:{base:#018x}").ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if output.is_empty() {
|
||||
output.push('\n');
|
||||
}
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
|
||||
impl StringAttributeOps for Capabilities {
|
||||
type Data = IrqSafeSpinlock<PciBusDevice>;
|
||||
const NAME: &'static str = "capabilities";
|
||||
const NEWLINE: bool = false;
|
||||
|
||||
fn read(state: &Self::Data) -> Result<String, Error> {
|
||||
use core::fmt::Write;
|
||||
let state = state.lock();
|
||||
let mut output = String::new();
|
||||
for (capability, offset, _) in state.info.config_space.capability_iter() {
|
||||
write!(output, "{offset:04x}:").ok();
|
||||
match capability {
|
||||
PciCapabilityId::Msi => write!(output, "msi").ok(),
|
||||
PciCapabilityId::MsiX => write!(output, "msix").ok(),
|
||||
PciCapabilityId::VendorSpecific => write!(output, "vendor-specific").ok(),
|
||||
PciCapabilityId::Unknown => write!(output, "unknown").ok(),
|
||||
};
|
||||
writeln!(output).ok();
|
||||
}
|
||||
if output.is_empty() {
|
||||
output.push('\n');
|
||||
}
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
|
||||
let object = KObject::new(IrqSafeSpinlock::new(device));
|
||||
|
||||
object
|
||||
.add_attribute(StringAttribute::from(Capabilities))
|
||||
.ok();
|
||||
object.add_attribute(StringAttribute::from(Resources)).ok();
|
||||
object.add_attribute(StringAttribute::from(Driver)).ok();
|
||||
object.add_attribute(StringAttribute::from(Class)).ok();
|
||||
object.add_attribute(StringAttribute::from(Id)).ok();
|
||||
|
||||
object
|
||||
}
|
@ -25,6 +25,10 @@ pub fn device() -> Option<&'static Arc<KObject<()>>> {
|
||||
object::DEVICE_OBJECT.try_get()
|
||||
}
|
||||
|
||||
pub fn bus() -> Option<&'static Arc<KObject<()>>> {
|
||||
object::BUS_OBJECT.try_get()
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
ROOT.init(object::setup_fixed_objects());
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use libk_util::OneTimeInit;
|
||||
use yggdrasil_abi::{error::Error, io::FileMode};
|
||||
@ -42,6 +44,20 @@ impl<D> KObject<D> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Deref for KObject<D> {
|
||||
type Target = D;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> DerefMut for KObject<D> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<D: Send> Send for KObject<D> {}
|
||||
unsafe impl<D: Send> Sync for KObject<D> {}
|
||||
|
||||
@ -54,6 +70,8 @@ pub static KERNEL_OBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
|
||||
pub static DEVICE_OBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
|
||||
// `/debug`
|
||||
pub static DEBUG_OBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
|
||||
// `/bus`
|
||||
pub static BUS_OBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
|
||||
|
||||
fn setup_fixed_object(root: &Arc<KObject<()>>, obj: &OneTimeInit<Arc<KObject<()>>>, name: &str) {
|
||||
let obj = obj.init(KObject::new(()));
|
||||
@ -66,6 +84,7 @@ pub fn setup_fixed_objects() -> Arc<Node> {
|
||||
setup_fixed_object(root, &KERNEL_OBJECT, "kernel");
|
||||
setup_fixed_object(root, &DEVICE_OBJECT, "device");
|
||||
setup_fixed_object(root, &DEBUG_OBJECT, "debug");
|
||||
setup_fixed_object(root, &BUS_OBJECT, "bus");
|
||||
|
||||
root.node.clone()
|
||||
}
|
||||
|
73
userspace/Cargo.lock
generated
73
userspace/Cargo.lock
generated
@ -331,7 +331,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"ed25519-dalek",
|
||||
"rand 0.8.5",
|
||||
"rand 0.8.5 (git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil-rng_core-0.6.4)",
|
||||
"rsa",
|
||||
"thiserror",
|
||||
]
|
||||
@ -862,7 +862,7 @@ dependencies = [
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-traits",
|
||||
"rand 0.8.5",
|
||||
"rand 0.8.5 (git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil-rng_core-0.6.4)",
|
||||
"smallvec",
|
||||
"zeroize",
|
||||
]
|
||||
@ -941,6 +941,19 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pci-ids"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d88ae3281b415d856e9c2ddbcdd5961e71c1a3e90138512c04d720241853a6af"
|
||||
dependencies = [
|
||||
"nom",
|
||||
"phf",
|
||||
"phf_codegen",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pem-rfc7468"
|
||||
version = "0.7.0"
|
||||
@ -956,6 +969,44 @@ version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
"rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.15"
|
||||
@ -1045,6 +1096,15 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
@ -1225,7 +1285,7 @@ dependencies = [
|
||||
"env_logger",
|
||||
"libterm",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"rand 0.8.5 (git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil-rng_core-0.6.4)",
|
||||
"sha2",
|
||||
"thiserror",
|
||||
"x25519-dalek",
|
||||
@ -1393,6 +1453,12 @@ dependencies = [
|
||||
"rand_core 0.6.4 (git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil-rng_core-0.6.4)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
@ -1485,6 +1551,7 @@ dependencies = [
|
||||
"humansize",
|
||||
"init",
|
||||
"libterm",
|
||||
"pci-ids",
|
||||
"rand 0.9.0-alpha.1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -21,6 +21,7 @@ chrono.workspace = true
|
||||
|
||||
# TODO own impl
|
||||
humansize = { version = "2.1.3", features = ["impl_style"] }
|
||||
pci-ids = { version = "0.2.5" }
|
||||
|
||||
init = { path = "../init" }
|
||||
|
||||
@ -117,9 +118,14 @@ path = "src/sync.rs"
|
||||
name = "sleep"
|
||||
path = "src/sleep.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "lspci"
|
||||
path = "src/lspci.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "tst"
|
||||
path = "src/tst.rs"
|
||||
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
248
userspace/sysutils/src/lspci.rs
Normal file
248
userspace/sysutils/src/lspci.rs
Normal file
@ -0,0 +1,248 @@
|
||||
use std::{fmt, fs, io, path::PathBuf, process::ExitCode, str::FromStr};
|
||||
|
||||
use clap::Parser;
|
||||
use pci_ids::FromId;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
struct Args {
|
||||
#[clap(short, help = "Display numeric IDs only")]
|
||||
numeric: bool,
|
||||
#[clap(short, help = "Print all the information about the devices")]
|
||||
verbose: bool,
|
||||
}
|
||||
|
||||
struct PciAddress {
|
||||
bus: u8,
|
||||
device: u8,
|
||||
function: u8,
|
||||
}
|
||||
|
||||
struct DeviceId {
|
||||
vendor: u16,
|
||||
device: u16,
|
||||
}
|
||||
|
||||
struct Device {
|
||||
path: PathBuf,
|
||||
address: PciAddress,
|
||||
}
|
||||
|
||||
enum Resource {
|
||||
Io(u16),
|
||||
Memory32(u32),
|
||||
Memory64(u64),
|
||||
}
|
||||
|
||||
struct PciRegion {
|
||||
index: usize,
|
||||
resource: Resource
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
enum Error {
|
||||
#[error("{0}")]
|
||||
Io(#[from] io::Error),
|
||||
#[error("Invalid PCI ID")]
|
||||
InvalidId,
|
||||
}
|
||||
|
||||
impl FromStr for PciAddress {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut elements = s.split(':');
|
||||
let bus = elements
|
||||
.next()
|
||||
.and_then(|p| u8::from_str_radix(p, 16).ok())
|
||||
.ok_or(Error::InvalidId)?;
|
||||
let device = elements
|
||||
.next()
|
||||
.and_then(|p| u8::from_str_radix(p, 16).ok())
|
||||
.ok_or(Error::InvalidId)?;
|
||||
let function = elements
|
||||
.next()
|
||||
.and_then(|p| u8::from_str_radix(p, 16).ok())
|
||||
.ok_or(Error::InvalidId)?;
|
||||
if elements.next().is_some() {
|
||||
return Err(Error::InvalidId);
|
||||
}
|
||||
Ok(Self {
|
||||
bus,
|
||||
device,
|
||||
function,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for DeviceId {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let (vendor, device) = s.split_once(':').ok_or(Error::InvalidId)?;
|
||||
let vendor = u16::from_str_radix(vendor, 16).map_err(|_| Error::InvalidId)?;
|
||||
let device = u16::from_str_radix(device, 16).map_err(|_| Error::InvalidId)?;
|
||||
Ok(Self { vendor, device })
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Resource {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let (ty, addr) = s.split_once(':').ok_or(Error::InvalidId)?;
|
||||
let addr = addr.strip_prefix("0x").ok_or(Error::InvalidId)?;
|
||||
match ty {
|
||||
"pio" => {
|
||||
let addr = u16::from_str_radix(addr, 16).map_err(|_| Error::InvalidId)?;
|
||||
Ok(Self::Io(addr))
|
||||
},
|
||||
"m32" => {
|
||||
let addr = u32::from_str_radix(addr, 16).map_err(|_| Error::InvalidId)?;
|
||||
Ok(Self::Memory32(addr))
|
||||
},
|
||||
"m64" => {
|
||||
let addr = u64::from_str_radix(addr, 16).map_err(|_| Error::InvalidId)?;
|
||||
Ok(Self::Memory64(addr))
|
||||
},
|
||||
_ => Err(Error::InvalidId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for PciRegion {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let (bar, resource) = s.split_once(':').ok_or(Error::InvalidId)?;
|
||||
let index = usize::from_str(bar).map_err(|_| Error::InvalidId)?;
|
||||
let resource = Resource::from_str(resource)?;
|
||||
Ok(Self {
|
||||
index, resource
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PciAddress {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{:02x}:{:02x}.{:x}",
|
||||
self.bus, self.device, self.function
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DeviceId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:04x}:{:04x}", self.vendor, self.device)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Resource {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
Self::Io(addr) => write!(f, "I/O at {addr:#x}"),
|
||||
Self::Memory32(addr) => write!(f, "Memory (32-bit) at {addr:#x}"),
|
||||
Self::Memory64(addr) => write!(f, "Memory (64-bit) at {addr:#x}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn list_devices() -> Result<Vec<Device>, Error> {
|
||||
let mut entries = Vec::new();
|
||||
for entry in fs::read_dir("/sys/bus/pci")? {
|
||||
let Ok(entry) = entry else { continue };
|
||||
let file_name = entry.file_name();
|
||||
let Some(name) = file_name.to_str() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Ok(address) = PciAddress::from_str(name) {
|
||||
entries.push(Device {
|
||||
path: entry.path(),
|
||||
address,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(entries)
|
||||
}
|
||||
|
||||
fn read_device_id(device: &Device) -> Result<DeviceId, Error> {
|
||||
let str = fs::read_to_string(device.path.join("id"))?;
|
||||
DeviceId::from_str(str.trim())
|
||||
}
|
||||
|
||||
fn read_device_regions(device: &Device) -> Result<Vec<PciRegion>, Error> {
|
||||
let str = fs::read_to_string(device.path.join("resources"))?;
|
||||
let mut regions = Vec::new();
|
||||
for line in str.split('\n') {
|
||||
let line = line.trim();
|
||||
let Ok(region) = PciRegion::from_str(line) else { continue };
|
||||
regions.push(region);
|
||||
}
|
||||
Ok(regions)
|
||||
}
|
||||
|
||||
fn vendor_id(args: &Args, id: u16) -> String {
|
||||
if args.numeric {
|
||||
format!("{id:04x}")
|
||||
} else {
|
||||
pci_ids::Vendor::from_id(id).map(|v| v.name().into()).unwrap_or_else(|| format!("{id:04x}"))
|
||||
}
|
||||
}
|
||||
|
||||
fn device_id(args: &Args, vid: u16, pid: u16) -> String {
|
||||
if args.numeric {
|
||||
format!("{pid:04x}")
|
||||
} else {
|
||||
pci_ids::Device::from_vid_pid(vid, pid).map(|v| v.name().into()).unwrap_or_else(|| format!("{pid:04x}"))
|
||||
}
|
||||
}
|
||||
|
||||
fn print_device(args: &Args, device: &Device) -> Result<(), Error> {
|
||||
let id = read_device_id(device)?;
|
||||
|
||||
print!("{}: ", device.address);
|
||||
|
||||
let vid = vendor_id(args, id.vendor);
|
||||
let pid = device_id(args, id.vendor, id.device);
|
||||
let device_name = if args.numeric {
|
||||
format!("{vid}:{pid}")
|
||||
}else {
|
||||
format!("[{vid}] {pid}")
|
||||
};
|
||||
|
||||
println!("{device_name}");
|
||||
|
||||
if args.verbose {
|
||||
for region in read_device_regions(device)? {
|
||||
println!(" BAR {}: {}", region.index, region.resource);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(args: &Args) -> Result<(), Error> {
|
||||
let devices = list_devices()?;
|
||||
for device in devices {
|
||||
if let Err(error) = print_device(args, &device) {
|
||||
eprintln!("{}: {error}", device.address);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let args = Args::parse();
|
||||
|
||||
match run(&args) {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(error) => {
|
||||
eprintln!("{error}");
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
}
|
@ -45,6 +45,7 @@ const PROGRAMS: &[(&str, &str)] = &[
|
||||
("date", "bin/date"),
|
||||
("sync", "bin/sync"),
|
||||
("sleep", "bin/sleep"),
|
||||
("lspci", "bin/lspci"),
|
||||
("tst", "bin/tst"),
|
||||
// netutils
|
||||
("netconf", "sbin/netconf"),
|
||||
|
Loading…
x
Reference in New Issue
Block a user