From af8e5aca643f4174c2ee5f4d36ba7bb3738e5679 Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 21 Sep 2022 17:57:02 +0200 Subject: [PATCH] Add basic PCI driver --- kernel/include/io/PCI.h | 28 ++++++++++ kernel/src/io/PCI.cpp | 111 ++++++++++++++++++++++++++++++++++++++++ kernel/src/main.cpp | 6 +++ 3 files changed, 145 insertions(+) create mode 100644 kernel/include/io/PCI.h create mode 100644 kernel/src/io/PCI.cpp diff --git a/kernel/include/io/PCI.h b/kernel/include/io/PCI.h new file mode 100644 index 00000000..f4981f5e --- /dev/null +++ b/kernel/include/io/PCI.h @@ -0,0 +1,28 @@ +#pragma once +#include + +namespace PCI +{ + struct DeviceID + { + uint16_t vendor; + uint16_t device; + }; + struct DeviceType + { + uint8_t dev_class; + uint8_t dev_subclass; + uint8_t prog_if; + uint8_t revision; + }; + uint32_t raw_address(uint32_t bus, uint32_t slot, uint32_t function, int32_t offset); + void raw_write8(uint32_t bus, uint32_t slot, uint32_t function, int32_t offset, uint8_t value); + void raw_write16(uint32_t bus, uint32_t slot, uint32_t function, int32_t offset, uint16_t value); + void raw_write32(uint32_t bus, uint32_t slot, uint32_t function, int32_t offset, uint32_t value); + uint8_t raw_read8(uint32_t bus, uint32_t slot, uint32_t function, int32_t offset); + uint16_t raw_read16(uint32_t bus, uint32_t slot, uint32_t function, int32_t offset); + uint32_t raw_read32(uint32_t bus, uint32_t slot, uint32_t function, int32_t offset); + DeviceID get_device_id(uint32_t bus, uint32_t slot, uint32_t function); + DeviceType get_device_type(uint32_t bus, uint32_t slot, uint32_t function); + void scan(void (*callback)(PCI::DeviceID&, PCI::DeviceType&)); +} \ No newline at end of file diff --git a/kernel/src/io/PCI.cpp b/kernel/src/io/PCI.cpp new file mode 100644 index 00000000..cb72d84a --- /dev/null +++ b/kernel/src/io/PCI.cpp @@ -0,0 +1,111 @@ +#define MODULE "pci" + +#include "io/PCI.h" +#include "io/IO.h" +#include "log/Log.h" +#include "thread/Spinlock.h" + +#define PCI_ADDRESS 0xCF8 +#define PCI_VALUE 0xCFC + +#define PCI_VENDOR_FIELD 0x0 +#define PCI_DEVICE_FIELD 0x2 +#define PCI_SUBCLASS_FIELD 0xa +#define PCI_CLASS_FIELD 0xb +#define PCI_REVISION_ID_FIELD 0x8 +#define PCI_PROG_IF_FIELD 0x9 +#define PCI_HEADER_TYPE_FIELD 0xe +#define PCI_SECONDARY_BUS_NUMBER_FIELD 0x19 + +Spinlock pci_lock; + +uint32_t PCI::raw_address(uint32_t bus, uint32_t slot, uint32_t function, int32_t offset) +{ + return 0x80000000 | (bus << 16) | (slot << 11) | (function << 8) | ((offset)&0xFC); +} + +void PCI::raw_write8(uint32_t bus, uint32_t slot, uint32_t function, int32_t offset, uint8_t value) +{ + IO::outl(PCI_ADDRESS, raw_address(bus, slot, function, offset)); + IO::outl(PCI_VALUE, (uint32_t)value); +} + +void PCI::raw_write16(uint32_t bus, uint32_t slot, uint32_t function, int32_t offset, uint16_t value) +{ + IO::outl(PCI_ADDRESS, raw_address(bus, slot, function, offset)); + IO::outl(PCI_VALUE, (uint32_t)value); +} + +void PCI::raw_write32(uint32_t bus, uint32_t slot, uint32_t function, int32_t offset, uint32_t value) +{ + IO::outl(PCI_ADDRESS, raw_address(bus, slot, function, offset)); + IO::outl(PCI_VALUE, value); +} + +uint8_t PCI::raw_read8(uint32_t bus, uint32_t slot, uint32_t function, int32_t offset) +{ + IO::outl(PCI_ADDRESS, raw_address(bus, slot, function, offset)); + return IO::inl(PCI_VALUE + (offset & 3)); +} + +uint16_t PCI::raw_read16(uint32_t bus, uint32_t slot, uint32_t function, int32_t offset) +{ + IO::outl(PCI_ADDRESS, raw_address(bus, slot, function, offset)); + return IO::inl(PCI_VALUE + (offset & 2)); +} + +uint32_t PCI::raw_read32(uint32_t bus, uint32_t slot, uint32_t function, int32_t offset) +{ + IO::outl(PCI_ADDRESS, raw_address(bus, slot, function, offset)); + return IO::inl(PCI_VALUE); +} + +PCI::DeviceID PCI::get_device_id(uint32_t bus, uint32_t slot, uint32_t function) +{ + uint16_t vendor = PCI::raw_read16(bus, slot, function, PCI_VENDOR_FIELD); + uint16_t device = PCI::raw_read16(bus, slot, function, PCI_DEVICE_FIELD); + return {vendor, device}; +} + +PCI::DeviceType PCI::get_device_type(uint32_t bus, uint32_t slot, uint32_t function) +{ + uint8_t dev_subclass = PCI::raw_read8(bus, slot, function, PCI_SUBCLASS_FIELD); + uint8_t dev_class = PCI::raw_read8(bus, slot, function, PCI_CLASS_FIELD); + uint8_t prog_if = PCI::raw_read8(bus, slot, function, PCI_PROG_IF_FIELD); + uint8_t revision = PCI::raw_read8(bus, slot, function, PCI_REVISION_ID_FIELD); + return {dev_class, dev_subclass, prog_if, revision}; +} + +static void pci_scan_bus(uint8_t bus, void (*callback)(PCI::DeviceID&, PCI::DeviceType&)) +{ + for (uint8_t slot = 0; slot < 32; slot++) + { + uint8_t num_functions = 1; + for (uint8_t function = 0; function < num_functions; function++) + { + PCI::DeviceID device_id = PCI::get_device_id(bus, slot, function); + if (device_id.vendor == 0xFFFF || device_id.device == 0xFFFF) continue; + PCI::DeviceType device_type = PCI::get_device_type(bus, slot, function); + uint8_t header = PCI::raw_read8(bus, slot, function, PCI_HEADER_TYPE_FIELD); + if (header & 0x80) // multi function device + { + num_functions = 8; + } + if ((header & 0x7F) == 1) + { + uint8_t sub_bus = PCI::raw_read8(bus, slot, function, PCI_SECONDARY_BUS_NUMBER_FIELD); + pci_scan_bus(sub_bus, callback); + } + pci_lock.release(); + callback(device_id, device_type); + pci_lock.acquire(); + } + } +} + +void PCI::scan(void (*callback)(PCI::DeviceID&, PCI::DeviceType&)) +{ + pci_lock.acquire(); + pci_scan_bus(0, callback); + pci_lock.release(); +} \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index cbe860df..d90869da 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -10,6 +10,7 @@ #include "interrupts/IDT.h" #include "interrupts/Install.h" #include "interrupts/Interrupts.h" +#include "io/PCI.h" #include "io/PIC.h" #include "io/Serial.h" #include "log/Log.h" @@ -128,5 +129,10 @@ extern "C" void _start() kinfoln("Interrupts enabled"); + PCI::scan([](PCI::DeviceID& dev_id, PCI::DeviceType& dev_type) { + kinfoln("Found PCI device %x:%x, class %x, subclass %x, prog if %x, revision %d", dev_id.vendor, dev_id.device, + dev_type.dev_class, dev_type.dev_subclass, dev_type.prog_if, dev_type.revision); + }); + while (1) Scheduler::sleep(200); } \ No newline at end of file