/* * Copyright (c) 2022, able * Copyright (c) 2022, Umut İnan Erdoğan * * SPDX-License-Identifier: MPL-2.0 */ use core::fmt; use serde::de::value; use x86_64::instructions::port::Port; use super::{ vendors::Vendor::{self, *}, PciClass, PciFullClass, }; // 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(Copy, 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.read(0, 0x10 + bar * 4) } } /// Get the interrupt pin pub fn interrupt_pin(&self) -> u8 { let last_row = unsafe { self.read(0, 0x3C) }; ((last_row >> 8) & 0xFF) as u8 } /// Enable bus mastering. This allows the PCI device to do DMA pub fn enable_bus_mastering(&self) { let command = unsafe { self.read(0, 4) } | 1 << 2; unsafe { self.write(0, 4, command) } } /// Read from configuration space pub unsafe fn read(&self, func: u8, offset: u8) -> u32 { pci_config_read(self.bus, self.device, func, offset) } /// Write to IO space pub unsafe fn write(&self, func: u8, offset: u8, value: u32) { pci_config_write(self.bus, self.device, func, offset, value) } } 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(Copy, Clone, Debug, PartialEq, Eq)] 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_device(bus: u8, device: u8) -> Option { 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_config_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_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::new(0xCF8).write(address); // read data Port::new(0xCFC).read() } 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::new(0xCF8).write(address); // write data Port::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 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) }