#[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, } use crate::alloc::string::ToString; /// Enumerate PCI devices and run initialisation routines on ones we support pub fn init() { let mut dt = DEVICE_TREE.lock(); dt.devices .insert("Unidentified PCI".to_string(), alloc::vec![]); let mut devices = alloc::vec![]; for bus in 0..=255 { for device in 0..32 { if let Some(device_info) = check_device(bus, device) { let vendor = device_info.device_id.vendor; let id = device_info.device_id.id; use Vendor::*; let (dev_type, dev_name) = match (vendor, id) { (Qemu, 4369) => ("GPUs", "QEMU VGA"), (VirtIO, 4176) => ("GPUs", "VirtIO PCI GPU"), (CirrusLogic, 184) => ("GPUs", "Cirrus SVGA"), //GD 5446? (_, _) => ("Unidentified PCI", "UNKNOWN DEVICE"), }; // let (dev_type, dev_name) = match device_info.full_class { // PciFullClass::Unclassified_NonVgaCompatible => todo!(), // PciFullClass::Unclassified_VgaCompatible => todo!(), // PciFullClass::Display_VGA => ("GPUs", "VGA Device"), // PciFullClass::Display_XGA => ("GPUs", "XGA Device"), // PciFullClass::Display_3D => ("GPUs", "3D Device"), // PciFullClass::Display_Other => ("GPUs", "Other"), // _ => ("Unidentified PCI", "UNKNOWN DEVICE"), // }; let mut dev = xml::XMLElement::new(dev_name); let mut pci_info = xml::XMLElement::new("PCI Info"); pci_info.set_attribute("id", id); pci_info.set_attribute("device", device_info.device); pci_info.set_attribute("vendor", vendor); pci_info.set_attribute("class", device_info.full_class.to_string()); dev.set_child(pci_info); devices.push((dev_type, dev)); } } } for (dev_type, dev) in devices { if let Some(abc) = dt.devices.get_mut(dev_type) { abc.push(dev); } } } 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, }) } #[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 } } } #[derive(PartialEq, Debug, Copy, Clone, Eq)] #[repr(u16)] pub enum Vendor { ThreeDfxInteractiveInc = 0x121A, ThreeDLabs = 0x3D3D, AllianceSemiconductorCorp = 0x1142, ARKLogicInc = 0xEDD8, ATITechnologiesInc = 0x1002, AvanceLogicIncALI = 0x1005, ChipsandTechnologies = 0x102C, CirrusLogic = 0x1013, Compaq = 0x0E11, CyrixCorp = 0x1078, DiamondMultimediaSystems = 0x1092, DigitalEquipmentCorp = 0x1011, Iit = 0x1061, IntegratedMicroSolutionsInc = 0x10E0, IntelCorp = 0x8086, IntergraphicsSystems = 0x10EA, MacronixInc = 0x10D9, MatroxGraphicsInc = 0x102B, MiroComputersProductsAG = 0x1031, NationalSemiconductorCorp = 0x100B, NeoMagicCorp = 0x10C8, Number9ComputerCompany = 0x105D, NVidiaCorporation = 0x10DE, NVidiaSgsthomson = 0x12D2, OakTechnologyInc = 0x104E, Qemu = 0x1234, QuantumDesignsHKLtd = 0x1098, Real3D = 0x003D, Rendition = 0x1163, S3Inc = 0x5333, SierraSemiconductor = 0x10A8, SiliconIntegratedSystemsSiS = 0x1039, SiliconMotionInc = 0x126F, STBSystemsInc = 0x10B4, TexasInstruments = 0x104C, ToshibaAmericaInfoSystems = 0x1179, TridentMicrosystems = 0x1023, TsengLabsInc = 0x100C, TundraSemiconductorCorp = 0x10E3, VIATechnologiesInc = 0x1106, VirtIO = 0x1AF4, VMWareInc = 0x15AD, Weitek = 0x100E, Unknown(u16), } impl From for Vendor { fn from(vendor_id: u16) -> Self { use Vendor::*; match vendor_id { 0x121A => ThreeDfxInteractiveInc, 0x3D3D => ThreeDLabs, 0x1142 => AllianceSemiconductorCorp, 0xEDD8 => ARKLogicInc, 0x1002 => ATITechnologiesInc, 0x1005 => AvanceLogicIncALI, 0x102C => ChipsandTechnologies, 0x1013 => CirrusLogic, 0x0E11 => Compaq, 0x1078 => CyrixCorp, 0x1092 => DiamondMultimediaSystems, 0x1011 => DigitalEquipmentCorp, 0x1061 => Iit, 0x10E0 => IntegratedMicroSolutionsInc, 0x8086 => IntelCorp, 0x10EA => IntergraphicsSystems, 0x10D9 => MacronixInc, 0x102B => MatroxGraphicsInc, 0x1031 => MiroComputersProductsAG, 0x100B => NationalSemiconductorCorp, 0x10C8 => NeoMagicCorp, 0x105D => Number9ComputerCompany, 0x10DE => NVidiaCorporation, 0x12D2 => NVidiaSgsthomson, 0x104E => OakTechnologyInc, 0x1234 => Qemu, 0x1098 => QuantumDesignsHKLtd, 0x003D => Real3D, 0x1163 => Rendition, 0x5333 => S3Inc, 0x10A8 => SierraSemiconductor, 0x1039 => SiliconIntegratedSystemsSiS, 0x126F => SiliconMotionInc, 0x10B4 => STBSystemsInc, 0x104C => TexasInstruments, 0x1179 => ToshibaAmericaInfoSystems, 0x1023 => TridentMicrosystems, 0x100C => TsengLabsInc, 0x10E3 => TundraSemiconductorCorp, 0x1106 => VIATechnologiesInc, 0x1AF4 => VirtIO, 0x15AD => VMWareInc, 0x100E => Weitek, id => Unknown(id), } } } impl Into for Vendor { fn into(self) -> u16 { use Vendor::*; match self { ThreeDfxInteractiveInc => 0x121A, ThreeDLabs => 0x3D3D, AllianceSemiconductorCorp => 0x1142, ARKLogicInc => 0xEDD8, ATITechnologiesInc => 0x1002, AvanceLogicIncALI => 0x1005, ChipsandTechnologies => 0x102C, CirrusLogic => 0x1013, Compaq => 0x0E11, CyrixCorp => 0x1078, DiamondMultimediaSystems => 0x1092, DigitalEquipmentCorp => 0x1011, Iit => 0x1061, IntegratedMicroSolutionsInc => 0x10E0, IntelCorp => 0x8086, IntergraphicsSystems => 0x10EA, MacronixInc => 0x10D9, MatroxGraphicsInc => 0x102B, MiroComputersProductsAG => 0x1031, NationalSemiconductorCorp => 0x100B, NeoMagicCorp => 0x10C8, Number9ComputerCompany => 0x105D, NVidiaCorporation => 0x10DE, NVidiaSgsthomson => 0x12D2, OakTechnologyInc => 0x104E, Qemu => 0x1234, QuantumDesignsHKLtd => 0x1098, Real3D => 0x003D, Rendition => 0x1163, S3Inc => 0x5333, SierraSemiconductor => 0x10A8, SiliconIntegratedSystemsSiS => 0x1039, SiliconMotionInc => 0x126F, STBSystemsInc => 0x10B4, TexasInstruments => 0x104C, ToshibaAmericaInfoSystems => 0x1179, TridentMicrosystems => 0x1023, TsengLabsInc => 0x100C, TundraSemiconductorCorp => 0x10E3, VIATechnologiesInc => 0x1106, VirtIO => 0x1AF4, VMWareInc => 0x15AD, Weitek => 0x100E, Unknown(id) => id, } } } impl core::fmt::Display for Vendor { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { use Vendor::*; match self { Qemu => write!(f, "QEMU (0x1234)"), VirtIO => write!(f, "VirtIO (0x1AF4)"), VMWareInc => write!(f, "VMWARE (0x15AD)"), S3Inc => write!(f, "S3 Incorporated (0x5333)"), IntelCorp => write!(f, "Intel Corp. (0x8086)"), ATITechnologiesInc => write!(f, "ATI (0x1002)"), Unknown(id) => write!(f, "Unknown ({:#6})", id), other => write!(f, "{other:?}"), }?; Ok(()) } } use core::fmt::Display; use crate::kmain::DEVICE_TREE; use {alloc::fmt::format, x86_64::instructions::port::Port}; #[allow(non_camel_case_types, dead_code)] #[derive(Debug, Clone, Copy, PartialEq)] #[repr(C)] /// Class specification for a PCI device pub enum PciClass { Unclassified = 0x00, MassStorage = 0x01, Network = 0x02, Display = 0x03, Multimedia = 0x04, Memory = 0x05, Bridge = 0x06, Unknown = 0xFF, } impl From for PciClass { /// Convert a u8 into the corresponding PciClass fn from(n: u8) -> Self { use PciClass::*; match n { 0x00 => Unclassified, 0x01 => MassStorage, 0x02 => Network, 0x03 => Display, 0x04 => Multimedia, 0x05 => Memory, 0x06 => Bridge, _ => Unknown, } } } #[allow(non_camel_case_types, dead_code)] #[derive(Debug, Clone, Copy, PartialEq)] #[repr(C)] /// Full class specification (type and subtype) for a PCI device. /// /// Uses non-camel-case types for readability. pub enum PciFullClass { Unclassified_NonVgaCompatible = 0x0000, Unclassified_VgaCompatible = 0x0001, MassStorage_ScsiBus = 0x0100, MassStorage_IDE = 0x0101, MassStorage_Floppy = 0x0102, MassStorage_IpiBus = 0x0103, MassStorage_RAID = 0x0104, MassStorage_ATA = 0x0105, MassStorage_SATA = 0x0106, MassStorage_SerialSCSI = 0x0107, MassStorage_NVM = 0x0108, MassStorage_Other = 0x0180, Network_Ethernet = 0x0200, Network_TokenRing = 0x0201, Network_FDDI = 0x0202, Network_ATM = 0x0203, Network_ISDN = 0x0204, Network_WorldFlip = 0x0205, Network_PICMG = 0x0206, Network_Infiniband = 0x0207, Network_Fabric = 0x0208, Network_Other = 0x0280, Display_VGA = 0x0300, Display_XGA = 0x0301, Display_3D = 0x0302, Display_Other = 0x0380, Multimedia_Video = 0x0400, Multimedia_AudioController = 0x0401, Multimedia_Telephony = 0x0402, Multimedia_AudioDevice = 0x0403, Multimedia_Other = 0x0480, Memory_RAM = 0x0500, Memory_Flash = 0x0501, Memory_Other = 0x0580, Bridge_Host = 0x0600, Bridge_ISA = 0x0601, Bridge_EISA = 0x0602, Bridge_MCA = 0x0603, Bridge_PciToPci = 0x0604, Bridge_PCMCIA = 0x0605, Bridge_NuBus = 0x0606, Bridge_CardBus = 0x0607, Bridge_RACEway = 0x0608, Bridge_PciToPciSemiTransparent = 0x0609, Bridge_InfinibandToPci = 0x060A, Bridge_Other = 0x0680, Unknown = 0xFFFF, } impl PciFullClass { // listen, i know this sucks, but i didn't want to include // `num`, `num-traits` and `num-derive` as dependencies for // this crate just for a convenience function /// Convert a u16 into the corresponding PciFullClass pub fn from_u16(n: u16) -> PciFullClass { match n { 0x0000 => PciFullClass::Unclassified_NonVgaCompatible, 0x0001 => PciFullClass::Unclassified_VgaCompatible, 0x0100 => PciFullClass::MassStorage_ScsiBus, 0x0101 => PciFullClass::MassStorage_IDE, 0x0102 => PciFullClass::MassStorage_Floppy, 0x0103 => PciFullClass::MassStorage_IpiBus, 0x0104 => PciFullClass::MassStorage_RAID, 0x0105 => PciFullClass::MassStorage_ATA, 0x0106 => PciFullClass::MassStorage_SATA, 0x0107 => PciFullClass::MassStorage_SerialSCSI, 0x0108 => PciFullClass::MassStorage_NVM, 0x0180 => PciFullClass::MassStorage_Other, 0x0200 => PciFullClass::Network_Ethernet, 0x0201 => PciFullClass::Network_TokenRing, 0x0202 => PciFullClass::Network_FDDI, 0x0203 => PciFullClass::Network_ATM, 0x0204 => PciFullClass::Network_ISDN, 0x0205 => PciFullClass::Network_WorldFlip, 0x0206 => PciFullClass::Network_PICMG, 0x0207 => PciFullClass::Network_Infiniband, 0x0208 => PciFullClass::Network_Fabric, 0x0280 => PciFullClass::Network_Other, 0x0300 => PciFullClass::Display_VGA, 0x0301 => PciFullClass::Display_XGA, 0x0302 => PciFullClass::Display_3D, 0x0380 => PciFullClass::Display_Other, 0x0400 => PciFullClass::Multimedia_Video, 0x0401 => PciFullClass::Multimedia_AudioController, 0x0402 => PciFullClass::Multimedia_Telephony, 0x0403 => PciFullClass::Multimedia_AudioDevice, 0x0480 => PciFullClass::Multimedia_Other, 0x0500 => PciFullClass::Memory_RAM, 0x0501 => PciFullClass::Memory_Flash, 0x0580 => PciFullClass::Memory_Other, 0x0600 => PciFullClass::Bridge_Host, 0x0601 => PciFullClass::Bridge_ISA, 0x0602 => PciFullClass::Bridge_EISA, 0x0603 => PciFullClass::Bridge_MCA, 0x0604 => PciFullClass::Bridge_PciToPci, 0x0605 => PciFullClass::Bridge_PCMCIA, 0x0606 => PciFullClass::Bridge_NuBus, 0x0607 => PciFullClass::Bridge_CardBus, 0x0608 => PciFullClass::Bridge_RACEway, 0x0609 => PciFullClass::Bridge_PciToPciSemiTransparent, 0x060A => PciFullClass::Bridge_InfinibandToPci, 0x0680 => PciFullClass::Bridge_Other, _ => PciFullClass::Unknown, } } /// Convert a PciFullClass to its u16 representation pub fn as_u16(&self) -> u16 { *self as u16 } } impl From for PciFullClass { /// Convert a u16 into the corresponding PciFullClass fn from(n: u16) -> Self { Self::from_u16(n) } } impl Display for PciFullClass { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{:?} ({:#06X})", self, self.as_u16())?; Ok(()) } } 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) }