ableos/ableos/src/devices/pci/mod.rs

222 lines
6.5 KiB
Rust

// The MIT License (MIT)
// Copyright (c) 2021 trashbyte
// See LICENSE.txt for full license
extern crate alloc;
use alloc::{format, string::String, vec::Vec};
use core::fmt::{Display, Error, Formatter};
#[cfg(feature = "serde")]
#[macro_use]
extern crate serde;
pub mod devices;
mod enums;
pub mod support;
pub mod vendors;
pub use enums::*;
pub mod port;
use port::*;
use self::devices::DeviceID;
// PciDeviceInfo ///////////////////////////////////////////////////////////////
#[allow(dead_code)]
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
/// A struct containing info about a PCI device.
pub struct PciDeviceInfo {
pub device: u8,
pub bus: u8,
pub device_id: DeviceID,
pub full_class: PciFullClass,
pub prog_if: u8,
pub rev_id: u8,
pub header_type: u8,
pub bars: [u32; 6],
pub supported_fns: [bool; 8],
pub interrupt_line: u8,
pub interrupt_pin: u8,
}
impl PciDeviceInfo {
/// Get the class of the PCI device as a PciClass
pub fn class(&self) -> PciClass {
PciClass::from_u8(((self.full_class.as_u16() >> 8) & 0xFF) as u8)
}
/// Get the full class of the PCI device as a PciFullClass
pub fn subclass(&self) -> PciClass {
PciClass::from_u8((self.full_class.as_u16() & 0xFF) as u8)
}
}
impl Display for PciDeviceInfo {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
let vendor_name = &self.device_id.vendor;
let device_id = &self.device_id.id;
writeln!(
f,
"Device: {} | Bus: 0x{:X} | Vendor: {} | Device ID: 0x{:X}",
self.device, self.bus, vendor_name, device_id,
)?;
writeln!(f, "{}", self.full_class)?;
writeln!(f, " Revision ID: {}", self.rev_id)?;
writeln!(f, " Prog IF: 0b{:b}", self.prog_if)?;
writeln!(f, " Header type: 0x{:X}", self.header_type)?;
write!(f, " Supported functions: 0")?;
for (i, b) in self.supported_fns.iter().enumerate().skip(1) {
if *b {
write!(f, ", {}", i)?;
}
}
writeln!(f)?;
write!(f, " BARs: [ ")?;
for i in self.bars.iter() {
if *i == 0 {
write!(f, "0x0 ")?;
} else {
write!(f, "{:#010X} ", i)?;
}
}
writeln!(f, "]")?;
writeln!(
f,
" Interrupt line / pin: {} / {}",
self.interrupt_line, self.interrupt_pin
)?;
Ok(())
}
}
// Public functions ////////////////////////////////////////////////////////////
/// Brute force scans for devices 0-31 on buses 0-255.
pub fn brute_force_scan() -> Vec<PciDeviceInfo> {
let mut infos = Vec::new();
for bus in 0u8..=255 {
for device in 0u8..32 {
if let Some(info) = check_device(bus, device) {
infos.push(info);
}
}
}
infos
}
// Internal functions //////////////////////////////////////////////////////////
fn check_device(bus: u8, device: u8) -> Option<PciDeviceInfo> {
assert!(device < 32);
let function = 0u8;
let (device_id, vendor_id) = get_ids(bus, device, function);
if vendor_id == 0xFFFF {
// Device doesn't exist
return None;
}
let reg2 = unsafe { pci_config_read(bus, device, 0, 0x8) };
let rev_id = (reg2 & 0x000000FF) as u8;
let prog_if = ((reg2 >> 8) & 0x000000FF) as u8;
let class = ((reg2 >> 16) & 0x0000FFFF) as u16;
let pci_class = PciFullClass::from_u16(class);
let header_type = get_header_type(bus, device, function);
let mut supported_fns = [true, false, false, false, false, false, false, false];
if (header_type & 0x80) != 0 {
// It is a multi-function device, so check remaining functions
for function in 0u8..8 {
if get_ids(bus, device, function).1 != 0xFFFF {
if check_function(bus, device, function) {
supported_fns[function as usize] = true;
}
}
}
}
let mut bars = [0, 0, 0, 0, 0, 0];
unsafe {
bars[0] = pci_config_read(bus, device, 0, 0x10);
bars[1] = pci_config_read(bus, device, 0, 0x14);
bars[2] = pci_config_read(bus, device, 0, 0x18);
bars[3] = pci_config_read(bus, device, 0, 0x1C);
bars[4] = pci_config_read(bus, device, 0, 0x20);
bars[5] = pci_config_read(bus, device, 0, 0x24);
}
let last_row = unsafe { pci_config_read(bus, device, 0, 0x3C) };
Some(PciDeviceInfo {
device,
bus,
device_id: DeviceID {
vendor: vendor_id.into(),
id: device_id,
},
// vendor_id,
full_class: pci_class,
prog_if,
rev_id,
header_type,
bars,
supported_fns,
interrupt_line: (last_row & 0xFF) as u8,
interrupt_pin: ((last_row >> 8) & 0xFF) as u8,
})
}
unsafe fn pci_config_read(bus: u8, device: u8, func: u8, offset: u8) -> u32 {
let bus = bus as u32;
let device = device as u32;
let func = func as u32;
let offset = offset as u32;
// construct address param
let address =
((bus << 16) | (device << 11) | (func << 8) | (offset & 0xfc) | 0x80000000) as u32;
// write address
Port::<u32>::new(0xCF8).write(address);
// read data
Port::<u32>::new(0xCFC).read()
}
#[allow(dead_code)]
unsafe fn pci_config_write(bus: u8, device: u8, func: u8, offset: u8, value: u32) {
let bus = bus as u32;
let device = device as u32;
let func = func as u32;
let offset = offset as u32;
// construct address param
let address =
((bus << 16) | (device << 11) | (func << 8) | (offset & 0xfc) | 0x80000000) as u32;
// write address
Port::<u32>::new(0xCF8).write(address);
// write data
Port::<u32>::new(0xCFC).write(value);
}
fn get_header_type(bus: u8, device: u8, function: u8) -> u8 {
assert!(device < 32);
assert!(function < 8);
let res = unsafe { pci_config_read(bus, device, function, 0x0C) };
((res >> 16) & 0xFF) as u8
}
fn check_function(bus: u8, device: u8, function: u8) -> bool {
assert!(device < 32);
assert!(function < 8);
get_ids(bus, device, function).1 != 0xFFFF
}
fn get_ids(bus: u8, device: u8, function: u8) -> (u16, u16) {
assert!(device < 32);
assert!(function < 8);
let res = unsafe { pci_config_read(bus, device, function, 0) };
let dev_id = ((res >> 16) & 0xFFFF) as u16;
let vnd_id = (res & 0xFFFF) as u16;
(dev_id, vnd_id)
}