forked from AbleOS/ableos
182 lines
5.0 KiB
Rust
182 lines
5.0 KiB
Rust
|
/*
|
||
|
* Copyright (c) 2022, able <abl3theabove@gmail.com>
|
||
|
* Copyright (c) 2022, Umut İnan Erdoğan <umutinanerdogan@pm.me>
|
||
|
*
|
||
|
* SPDX-License-Identifier: MPL-2.0
|
||
|
*/
|
||
|
|
||
|
use core::fmt;
|
||
|
|
||
|
use x86_64::instructions::port::Port;
|
||
|
|
||
|
use super::{vendors::Vendor::{self, *}, PciFullClass, PciClass};
|
||
|
|
||
|
// FIXME: Unknown class
|
||
|
pub const S3INC_TRIO64V2: DeviceID = DeviceID::new(S3Inc, 0x8900);
|
||
|
|
||
|
// MassStorage_IDE (0x0101)
|
||
|
pub const INTEL_PIIX3_IDE: DeviceID = DeviceID::new(IntelCorp, 0x7010);
|
||
|
pub const INTEL_PIIX4_IDE: DeviceID = DeviceID::new(IntelCorp, 0x7111);
|
||
|
|
||
|
// Display_VGA (0x0300)
|
||
|
pub const VMWARE_SVGA2: DeviceID = DeviceID::new(VMWareInc, 0x0405);
|
||
|
|
||
|
#[derive(Clone, Debug)]
|
||
|
/// A struct containing info about a PCI device.
|
||
|
pub struct PciDeviceInfo {
|
||
|
pub header_type: u8,
|
||
|
pub device: u8,
|
||
|
pub bus: u8,
|
||
|
pub device_id: DeviceID,
|
||
|
pub full_class: PciFullClass,
|
||
|
pub rev_id: u8,
|
||
|
}
|
||
|
|
||
|
impl PciDeviceInfo {
|
||
|
/// Get the class of the PCI device as a PciClass
|
||
|
pub fn class(&self) -> PciClass {
|
||
|
(((self.full_class.as_u16() >> 8) & 0xFF) as u8).into()
|
||
|
}
|
||
|
|
||
|
/// Get the bar, 0-indexed
|
||
|
pub fn bar(&self, bar: u8) -> u32 {
|
||
|
assert!(bar < 6);
|
||
|
unsafe {
|
||
|
self.io_read(0, 0x10 + bar * 4)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Get the interrupt pin
|
||
|
pub fn interrupt_pin(&self) -> u8 {
|
||
|
let last_row = unsafe { self.io_read(0, 0x3C) };
|
||
|
((last_row >> 8) & 0xFF) as u8
|
||
|
}
|
||
|
|
||
|
/// Read from IO space
|
||
|
pub unsafe fn io_read(&self, func: u8, offset: u8) -> u32 {
|
||
|
pci_io_read(self.bus, self.device, func, offset)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl fmt::Display for PciDeviceInfo {
|
||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::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, " Header type: 0x{:X}", self.header_type)?;
|
||
|
writeln!(f, " Revision ID: {}", self.rev_id)?;
|
||
|
// 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(())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[derive(PartialEq, Clone, Eq, Debug)]
|
||
|
pub struct DeviceID {
|
||
|
pub vendor: Vendor,
|
||
|
pub id: u16,
|
||
|
}
|
||
|
|
||
|
impl DeviceID {
|
||
|
pub const fn new(vendor: Vendor, id: u16) -> Self {
|
||
|
Self { vendor, id }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn check_pci_support(device_id: DeviceID) -> bool {
|
||
|
match device_id {
|
||
|
// FIXME: Unknown class
|
||
|
S3INC_TRIO64V2 => true,
|
||
|
|
||
|
// MassStorage_IDE (0x0101)
|
||
|
INTEL_PIIX3_IDE => true,
|
||
|
INTEL_PIIX4_IDE => true,
|
||
|
|
||
|
// Display_VGA (0x0300)
|
||
|
VMWARE_SVGA2 => true,
|
||
|
_ => false,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn check_device(bus: u8, device: u8) -> Option<PciDeviceInfo> {
|
||
|
assert!(device < 32);
|
||
|
let (device_id, vendor_id) = get_ids(bus, device, 0);
|
||
|
if vendor_id == 0xFFFF {
|
||
|
// Device doesn't exist
|
||
|
return None;
|
||
|
}
|
||
|
|
||
|
let reg2 = unsafe { pci_io_read(bus, device, 0, 0x8) };
|
||
|
let class = ((reg2 >> 16) & 0x0000FFFF) as u16;
|
||
|
let pci_class = PciFullClass::from_u16(class);
|
||
|
let header_type = get_header_type(bus, device, 0);
|
||
|
|
||
|
Some(PciDeviceInfo {
|
||
|
header_type,
|
||
|
device,
|
||
|
bus,
|
||
|
device_id: DeviceID {
|
||
|
vendor: vendor_id.into(),
|
||
|
id: device_id,
|
||
|
},
|
||
|
full_class: pci_class,
|
||
|
rev_id: (reg2 & 0x000000FF) as u8,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
unsafe fn pci_io_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()
|
||
|
}
|
||
|
|
||
|
fn get_header_type(bus: u8, device: u8, function: u8) -> u8 {
|
||
|
assert!(device < 32);
|
||
|
assert!(function < 8);
|
||
|
let res = unsafe { pci_io_read(bus, device, function, 0x0C) };
|
||
|
((res >> 16) & 0xFF) as u8
|
||
|
}
|
||
|
|
||
|
fn get_ids(bus: u8, device: u8, function: u8) -> (u16, u16) {
|
||
|
assert!(device < 32);
|
||
|
assert!(function < 8);
|
||
|
let res = unsafe { pci_io_read(bus, device, function, 0) };
|
||
|
let dev_id = ((res >> 16) & 0xFFFF) as u16;
|
||
|
let vnd_id = (res & 0xFFFF) as u16;
|
||
|
(dev_id, vnd_id)
|
||
|
}
|