Procházet zdrojové kódy

refactor(pcie): rewrite pcie bus driver in rust

removed some code used by the c++ pcie module
greatbridf před 8 měsíci
rodič
revize
4691d469c5

+ 0 - 6
CMakeLists.txt

@@ -40,16 +40,10 @@ set(BOOTLOADER_SOURCES src/boot.s
 set(KERNEL_MAIN_SOURCES src/kernel/async/lock.cc
                         src/kernel/allocator.cc
                         src/kernel/mem/slab.cc
-                        src/kernel/hw/acpi.cc
-                        src/kernel/hw/pci.cc
                         src/types/libstdcpp.cpp
-                        include/defs.hpp
                         include/kernel/async/lock.hpp
                         include/kernel/mem/paging.hpp
                         include/kernel/mem/slab.hpp
-                        include/kernel/hw/acpi.hpp
-                        include/kernel/hw/pci.hpp
-                        include/kernel/hw/port.hpp
                         include/types/list.hpp
                         include/types/types.h
                         include/types/allocator.hpp

+ 18 - 0
Cargo.lock

@@ -2,6 +2,17 @@
 # It is not intended for manual editing.
 version = 4
 
+[[package]]
+name = "acpi"
+version = "5.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94476c7ef97af4c4d998b3f422c1b01d5211aad57c80ed200baf148d1f1efab6"
+dependencies = [
+ "bit_field",
+ "bitflags",
+ "log",
+]
+
 [[package]]
 name = "aho-corasick"
 version = "1.1.3"
@@ -50,6 +61,12 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "bit_field"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
+
 [[package]]
 name = "bitflags"
 version = "2.9.1"
@@ -143,6 +160,7 @@ dependencies = [
 name = "gbos-rust-part"
 version = "0.1.0"
 dependencies = [
+ "acpi",
  "arch",
  "atomic_unique_refcell",
  "bindgen",

+ 1 - 0
Cargo.toml

@@ -24,6 +24,7 @@ posix_types = { path = "./crates/posix_types" }
 bitflags = "2.6.0"
 intrusive-collections = "0.9.7"
 itertools = { version = "0.13.0", default-features = false }
+acpi = "5.2.0"
 
 [features]
 default = ["smp"]

+ 1 - 1
build.rs

@@ -7,7 +7,7 @@ fn main() {
     println!("cargo:rustc-link-search=native=./build/gblibstdc++");
     println!("cargo:rustc-link-lib=static=gblibstdc++");
 
-    let headers = ["rust-headers.hpp", "include/kernel/hw/pci.hpp"];
+    let headers = ["rust-headers.hpp"];
 
     let bindings = bindgen::Builder::default()
         .use_core()

+ 0 - 20
include/defs.hpp

@@ -1,20 +0,0 @@
-#pragma once
-
-#include <types/types.h>
-
-using u8 = uint8_t;
-using u16 = uint16_t;
-using u32 = uint32_t;
-using u64 = uint64_t;
-using usize = size_t;
-
-using i8 = char;
-using i16 = short;
-using i32 = int;
-using i64 = long long;
-using isize = long;
-
-template <typename T>
-constexpr bool test(T x, T y) {
-    return (x & y) == y;
-}

+ 0 - 35
include/kernel/hw/acpi.hpp

@@ -1,35 +0,0 @@
-#pragma once
-
-#include <types/path.hpp>
-#include <types/types.h>
-
-namespace kernel::hw::acpi {
-
-struct PACKED ACPI_table_header {
-    char signature[4];
-    uint32_t length;
-    uint8_t revision;
-    uint8_t checksum;
-    uint8_t oemid[6];
-    uint8_t oem_table_id[8];
-    uint32_t oem_revision;
-    uint32_t creator_id;
-    uint32_t creator_revision;
-};
-
-struct PACKED MCFG {
-    ACPI_table_header header;
-    uint64_t : 64;
-    struct MCFG_entry {
-        uint64_t base_address;
-        uint16_t segment_group;
-        uint8_t start_bus;
-        uint8_t end_bus;
-        uint32_t : 32;
-    } entries[];
-};
-
-int parse_acpi_tables();
-void* get_table(types::string_view name);
-
-} // namespace kernel::hw::acpi

+ 0 - 82
include/kernel/hw/pci.hpp

@@ -1,82 +0,0 @@
-#pragma once
-
-#include <functional>
-#include <memory>
-
-#include <stdint.h>
-
-#include <types/types.h>
-
-#include <kernel/mem/phys.hpp>
-
-namespace kernel::hw::pci {
-
-struct PACKED device_header_base {
-    uint16_t vendor;
-    uint16_t device;
-    uint16_t volatile command;
-    uint16_t volatile status;
-    uint8_t revision_id;
-    uint8_t prog_if;
-    uint8_t subclass;
-    uint8_t class_code;
-    uint8_t cache_line_size;
-    uint8_t latency_timer;
-    uint8_t header_type;
-    uint8_t volatile bist;
-};
-
-struct PACKED device_header_type0 {
-    device_header_base base;
-
-    uint32_t bars[6];
-    uint32_t cardbus_cis_pointer;
-    uint16_t subsystem_vendor_id;
-    uint16_t subsystem_id;
-    uint32_t expansion_rom_base_address;
-    uint8_t capabilities_pointer;
-    uint8_t __reserved[7];
-    uint8_t volatile interrupt_line;
-    uint8_t volatile interrupt_pin;
-    uint8_t min_grant;
-    uint8_t max_latency;
-};
-
-struct SegmentGroup {
-    mem::physaddr<void, false> base;
-    int number;
-};
-
-class pci_device {
-   private:
-    std::shared_ptr<SegmentGroup> segment_group;
-    int bus;
-    int device;
-    int function;
-    void* config_space;
-
-    explicit pci_device(std::shared_ptr<SegmentGroup> segment_group, int bus,
-                        int device, int function, void* config_space);
-
-   public:
-    static pci_device* probe(std::shared_ptr<SegmentGroup> segment_group,
-                             int bus, int device, int function);
-
-    device_header_base& header() const;
-    device_header_type0& header_type0() const;
-
-    void enableBusMastering();
-
-    template <typename T>
-    inline T& at(size_t offset) const {
-        return *reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(config_space) +
-                                     offset);
-    }
-};
-
-using driver_t = std::function<int(pci_device&)>;
-
-int register_driver(uint16_t vendor, uint16_t device, driver_t drv);
-int register_driver_r(uint16_t vendor, uint16_t device, int (*drv)(pci_device*));
-
-} // namespace kernel::hw::pci

+ 0 - 76
include/kernel/hw/port.hpp

@@ -1,76 +0,0 @@
-#pragma once
-
-#include <stdint.h>
-
-namespace kernel::hw {
-
-inline uint32_t inl(uint16_t pn) {
-    uint32_t ret;
-    asm volatile("inl %1, %0" : "=a"(ret) : "d"(pn));
-    return ret;
-}
-
-inline uint32_t outl(uint16_t pn, uint32_t n) {
-    asm volatile("outl %1, %0" : : "d"(pn), "a"(n));
-    return n;
-}
-
-inline uint16_t inw(uint16_t pn) {
-    uint16_t ret;
-    asm volatile("inw %1, %0" : "=a"(ret) : "d"(pn));
-    return ret;
-}
-
-inline uint16_t outw(uint16_t pn, uint16_t n) {
-    asm volatile("outw %1, %0" : : "d"(pn), "a"(n));
-    return n;
-}
-
-inline uint8_t inb(uint16_t pn) {
-    uint8_t ret;
-    asm volatile("inb %1, %0" : "=a"(ret) : "d"(pn));
-    return ret;
-}
-
-inline uint8_t outb(uint16_t pn, uint8_t n) {
-    asm volatile("outb %1, %0" : : "d"(pn), "a"(n));
-    return n;
-}
-
-struct p32 {
-    uint16_t mp;
-
-    explicit constexpr p32(uint16_t p) : mp(p) {}
-    inline uint32_t operator*() const { return inl(mp); }
-    inline uint32_t operator=(uint32_t n) const { return outl(mp, n); }
-};
-
-struct p16 {
-    uint16_t mp;
-
-    explicit constexpr p16(uint16_t p) : mp(p) {}
-    inline uint16_t operator*() const { return inw(mp); }
-    inline uint16_t operator=(uint16_t n) const { return outw(mp, n); }
-};
-
-struct p8 {
-    uint16_t mp;
-
-    explicit constexpr p8(uint16_t p) : mp(p) {}
-    inline uint8_t operator*() const { return inb(mp); }
-    inline uint8_t operator=(uint8_t n) const { return outb(mp, n); }
-};
-
-} // namespace kernel::hw
-
-namespace hw {
-
-// for backward compatibility
-using p8 = kernel::hw::p8;
-using p8r = kernel::hw::p8;
-using p8w = kernel::hw::p8;
-using p16 = kernel::hw::p16;
-using p16r = kernel::hw::p16;
-using p16w = kernel::hw::p16;
-
-} // namespace hw

+ 1 - 0
include/kernel/mem/phys.hpp

@@ -2,6 +2,7 @@
 
 #include <bit>
 #include <cstddef>
+#include <type_traits>
 
 #include <stdint.h>
 

+ 0 - 66
include/types/path.hpp

@@ -1,66 +0,0 @@
-#pragma once
-
-#include <cstddef>
-#include <string>
-
-namespace types {
-
-class string_view {
-   private:
-    const char* m_str;
-    std::size_t m_len;
-
-   public:
-    constexpr string_view() : m_str(nullptr), m_len(0) {}
-    constexpr string_view(const char* str, std::size_t len)
-        : m_str(str), m_len(len) {}
-    constexpr string_view(const std::string& str)
-        : m_str(str.c_str()), m_len(str.size()) {}
-    inline string_view(const char* str)
-        : m_str(str), m_len(std::char_traits<char>::length(str)) {}
-
-    constexpr const char* data() const { return m_str; }
-    constexpr std::size_t size() const { return m_len; }
-    constexpr bool empty() const { return m_len == 0; }
-
-    constexpr const char* begin() const { return m_str; }
-    constexpr const char* end() const { return m_str + m_len; }
-
-    constexpr char operator[](std::size_t pos) const { return m_str[pos]; }
-
-    constexpr bool operator==(const string_view& val) const {
-        if (m_len != val.m_len)
-            return false;
-        for (std::size_t i = 0; i < m_len; ++i) {
-            if (m_str[i] != val.m_str[i])
-                return false;
-        }
-        return true;
-    }
-
-    constexpr bool operator==(const char* str) const {
-        for (std::size_t i = 0; i < m_len; ++i) {
-            if (m_str[i] != str[i])
-                return false;
-        }
-        return str[m_len] == '\0';
-    }
-
-    constexpr bool operator==(const std::string& str) const {
-        if (m_len != str.size())
-            return false;
-        return operator==(str.c_str());
-    }
-
-    constexpr bool operator<(const string_view& val) const {
-        for (std::size_t i = 0; i < m_len && i < val.m_len; ++i) {
-            if (m_str[i] < val.m_str[i])
-                return true;
-            if (m_str[i] > val.m_str[i])
-                return false;
-        }
-        return m_len < val.m_len;
-    }
-};
-
-} // namespace types

+ 55 - 44
src/driver/ahci/mod.rs

@@ -3,17 +3,15 @@ use crate::{
     io::Buffer as _,
     kernel::{
         block::{make_device, BlockDevice},
+        constants::EINVAL,
         interrupt::register_irq_handler,
+        pcie::{self, Header, PCIDevice, PCIDriver, PciError},
     },
     prelude::*,
 };
 use alloc::{format, sync::Arc};
-use bindings::{
-    kernel::hw::pci::{self, pci_device},
-    EIO,
-};
+use bindings::EIO;
 use control::AdapterControl;
-use core::ptr::NonNull;
 use defs::*;
 use eonix_mm::address::{AddrOps as _, PAddr};
 use eonix_sync::SpinIrq as _;
@@ -30,6 +28,10 @@ mod register;
 pub(self) mod slot;
 mod stats;
 
+pub struct AHCIDriver {
+    devices: Spin<Vec<Arc<Device<'static>>>>,
+}
+
 pub struct BitsIterator {
     data: u32,
     n: u32,
@@ -64,8 +66,7 @@ impl Iterator for BitsIterator {
 struct Device<'a> {
     control_base: PAddr,
     control: AdapterControl,
-    // TODO: impl Drop to free pci device
-    pcidev: NonNull<pci_device>,
+    _pcidev: Arc<PCIDevice<'static>>,
     /// # Lock
     /// Might be accessed from irq handler, use with `lock_irq()`
     ports: Spin<[Option<Arc<AdapterPort<'a>>>; 32]>,
@@ -107,33 +108,6 @@ impl Device<'_> {
 }
 
 impl Device<'static> {
-    pub fn new(pcidev: NonNull<pci_device>) -> KResult<Arc<Self>> {
-        let base =
-            PAddr::from(unsafe { *pcidev.as_ref().header_type0() }.bars[PCI_REG_ABAR] as usize);
-        let irqno = unsafe { *pcidev.as_ref().header_type0() }.interrupt_line;
-
-        // use MMIO
-        if !base.is_aligned_to(16) {
-            return Err(EIO);
-        }
-
-        let device = Arc::new(Device {
-            control_base: base,
-            control: AdapterControl::new(base),
-            pcidev,
-            ports: Spin::new([const { None }; 32]),
-        });
-
-        device.control.enable_interrupts();
-
-        let device_irq = device.clone();
-        register_irq_handler(irqno as i32, move || device_irq.handle_interrupt())?;
-
-        device.probe_ports()?;
-
-        Ok(device)
-    }
-
     fn probe_ports(&self) -> KResult<()> {
         for nport in self.control.implemented_ports() {
             let port = Arc::new(AdapterPort::new(self.control_base, nport));
@@ -172,19 +146,56 @@ impl Device<'static> {
     }
 }
 
-unsafe extern "C" fn probe_device(pcidev: *mut pci_device) -> i32 {
-    match Device::new(NonNull::new(pcidev).expect("NULL `pci_device` pointer")) {
-        Ok(device) => {
-            // TODO!!!: save device to pci_device
-            Box::leak(Box::new(device));
-            0
+impl AHCIDriver {
+    pub fn new() -> Self {
+        Self {
+            devices: Spin::new(Vec::new()),
         }
-        Err(e) => -(e as i32),
     }
 }
 
-pub fn register_ahci_driver() {
-    let ret = unsafe { pci::register_driver_r(VENDOR_INTEL, DEVICE_AHCI, Some(probe_device)) };
+impl PCIDriver for AHCIDriver {
+    fn vendor_id(&self) -> u16 {
+        VENDOR_INTEL
+    }
+
+    fn device_id(&self) -> u16 {
+        DEVICE_AHCI
+    }
+
+    fn handle_device(&self, pcidev: Arc<PCIDevice<'static>>) -> Result<(), PciError> {
+        let Header::Endpoint(header) = pcidev.header else {
+            Err(EINVAL)?
+        };
+
+        let base = PAddr::from(header.bars[PCI_REG_ABAR] as usize);
+        let irqno = header.interrupt_line;
+
+        // use MMIO
+        if !base.is_aligned_to(16) {
+            Err(EIO)?;
+        }
+
+        let device = Arc::new(Device {
+            control_base: base,
+            control: AdapterControl::new(base),
+            _pcidev: pcidev,
+            ports: Spin::new([const { None }; 32]),
+        });
 
-    assert_eq!(ret, 0);
+        device.control.enable_interrupts();
+
+        let device_irq = device.clone();
+        register_irq_handler(irqno as i32, move || device_irq.handle_interrupt())?;
+
+        device.probe_ports()?;
+
+        self.devices.lock().push(device);
+
+        Ok(())
+    }
+}
+
+pub fn register_ahci_driver() {
+    pcie::register_driver(AHCIDriver::new()).expect("Register ahci driver failed");
 }

+ 1 - 0
src/kernel.rs

@@ -4,6 +4,7 @@ pub mod constants;
 pub mod cpu;
 pub mod interrupt;
 pub mod mem;
+pub mod pcie;
 pub mod syscall;
 pub mod task;
 pub mod timer;

+ 0 - 99
src/kernel/hw/acpi.cc

@@ -1,99 +0,0 @@
-#include <map>
-
-#include <assert.h>
-#include <errno.h>
-
-#include <types/path.hpp>
-#include <types/types.h>
-
-#include <kernel/hw/acpi.hpp>
-#include <kernel/mem/phys.hpp>
-
-using namespace kernel::mem;
-
-namespace kernel::hw::acpi {
-
-static std::map<types::string_view, ACPI_table_header*> acpi_tables;
-
-struct PACKED RSDP {
-    uint64_t signature;
-    uint8_t checksum;
-    uint8_t oemid[6];
-    uint8_t revision;
-    uint32_t rsdt_addr;
-};
-
-struct PACKED RSDT {
-    ACPI_table_header header;
-    uint32_t entries[];
-};
-
-static bool checksum(const void* data, size_t len) {
-    uint8_t sum = 0;
-    for (size_t i = 0; i < len; ++i)
-        sum += reinterpret_cast<const uint8_t*>(data)[i];
-    return sum == 0;
-}
-
-static const RSDP* _find_rsdp() {
-    for (uintptr_t addr = 0xe0000; addr < 0x100000; addr += 0x10) {
-        physaddr<RSDP> rsdp{addr};
-
-        // "RSD PTR "
-        if (rsdp->signature == 0x2052545020445352) {
-            if (checksum(rsdp, sizeof(RSDP)))
-                return rsdp;
-        }
-    }
-
-    return nullptr;
-}
-
-static const RSDT* _rsdt() {
-    static const RSDT* rsdt;
-    if (rsdt)
-        return rsdt;
-
-    auto* rsdp = _find_rsdp();
-    if (!rsdp || rsdp->revision != 0)
-        return nullptr;
-
-    const RSDT* cur_rsdt = physaddr<const RSDT>{rsdp->rsdt_addr};
-
-    constexpr auto rsdt_signature = types::string_view{"RSDT", 4};
-    auto signature = types::string_view{cur_rsdt->header.signature, 4};
-    if (signature != rsdt_signature)
-        return nullptr;
-    if (!checksum(cur_rsdt, cur_rsdt->header.length))
-        return nullptr;
-
-    return rsdt = cur_rsdt;
-}
-
-int parse_acpi_tables() {
-    auto rsdt = _rsdt();
-    if (!rsdt)
-        return -ENOENT;
-
-    auto entries =
-        (rsdt->header.length - sizeof(ACPI_table_header)) / sizeof(uint32_t);
-    for (uint32_t i = 0; i < entries; ++i) {
-        physaddr<ACPI_table_header> header{rsdt->entries[i]};
-        if (!checksum(header, header->length))
-            continue;
-
-        types::string_view table_name{header->signature, 4};
-        acpi_tables[table_name] = header;
-    }
-
-    return 0;
-}
-
-void* get_table(types::string_view name) {
-    auto iter = acpi_tables.find(name);
-    if (iter == acpi_tables.end())
-        return nullptr;
-    return iter->second;
-}
-
-} // namespace kernel::hw::acpi

+ 0 - 125
src/kernel/hw/pci.cc

@@ -1,125 +0,0 @@
-#include <map>
-
-#include <assert.h>
-#include <errno.h>
-#include <stdint.h>
-
-#include <types/types.h>
-
-#include <kernel/hw/acpi.hpp>
-#include <kernel/hw/pci.hpp>
-#include <kernel/mem/phys.hpp>
-
-using device_no = uint32_t;
-constexpr device_no make_device(uint32_t vendor, uint32_t device) {
-    return (vendor << 16) | device;
-}
-
-namespace kernel::hw::pci {
-
-// lower 16 bits are vendor id, higher 16 bits are device id
-std::map<device_no, pci_device*> s_pci_devices;
-std::map<device_no, driver_t> s_pci_drivers;
-
-pci_device::pci_device(std::shared_ptr<SegmentGroup> segment_group, int bus,
-                       int dev, int func, void* config_space)
-    : segment_group(segment_group)
-    , bus(bus)
-    , device(dev)
-    , function(func)
-    , config_space(config_space) {}
-
-pci_device* pci_device::probe(std::shared_ptr<SegmentGroup> segmentGroup,
-                              int bus, int device, int function) {
-    auto configSpaceAddress = segmentGroup->base.phys() + (bus << 20) +
-                              (device << 15) + (function << 12);
-
-    auto pConfigSpace =
-        mem::physaddr<device_header_base, false>{configSpaceAddress};
-
-    if (pConfigSpace->vendor == 0xffff)
-        return nullptr;
-
-    return new pci_device(segmentGroup, bus, device, function, pConfigSpace);
-}
-
-void pci_device::enableBusMastering() {
-    auto& header = this->header();
-    header.command |= 0x4;
-}
-
-device_header_base& pci_device::header() const {
-    return at<device_header_base>(0);
-}
-
-device_header_type0& pci_device::header_type0() const {
-    return at<device_header_type0>(0);
-}
-
-int register_driver(uint16_t vendor, uint16_t device, driver_t drv) {
-    device_no dev = make_device(vendor, device);
-
-    auto iter = s_pci_drivers.find(dev);
-    if (iter != s_pci_drivers.end())
-        return -EEXIST;
-
-    auto [_, inserted] = s_pci_drivers.emplace(dev, drv);
-    assert(inserted);
-
-    auto deviter = s_pci_devices.find(dev);
-
-    // TODO: check status or print log
-    if (deviter != s_pci_devices.end()) {
-        int ret = drv(*deviter->second);
-        assert(ret == 0);
-    }
-
-    return 0;
-}
-
-int register_driver_r(uint16_t vendor, uint16_t device,
-                      int (*drv)(pci_device*)) {
-    return register_driver(vendor, device,
-                           [=](pci_device& dev) -> int { return drv(&dev); });
-}
-
-} // namespace kernel::hw::pci
-
-namespace kernel::kinit {
-
-extern "C" void init_pci() {
-    using namespace hw::acpi;
-    using namespace hw::pci;
-
-    assert(parse_acpi_tables() == 0);
-    auto* mcfg = (MCFG*)get_table("MCFG");
-    assert(mcfg);
-
-    int n_entries =
-        (mcfg->header.length - sizeof(MCFG)) / sizeof(MCFG::MCFG_entry);
-    for (int i = 0; i < n_entries; ++i) {
-        auto& entry = *&mcfg->entries[i];
-        auto segment_group = std::make_shared<SegmentGroup>(
-            mem::physaddr<void, false>{entry.base_address},
-            entry.segment_group);
-
-        for (int bus = entry.start_bus; bus <= entry.end_bus; ++bus) {
-            for (int dev = 0; dev < 32; ++dev) {
-                for (int func = 0; func < 8; ++func) {
-                    auto* pdev =
-                        pci_device::probe(segment_group, bus, dev, func);
-                    if (!pdev)
-                        break;
-
-                    auto& header = pdev->header();
-
-                    auto [iter, inserted] = s_pci_devices.emplace(
-                        make_device(header.vendor, header.device), pdev);
-                    assert(inserted);
-                }
-            }
-        }
-    }
-}
-
-} // namespace kernel::kinit

+ 11 - 0
src/kernel/pcie.rs

@@ -0,0 +1,11 @@
+mod device;
+mod driver;
+mod error;
+mod header;
+mod init;
+
+pub use device::PCIDevice;
+pub use driver::{register_driver, PCIDriver};
+pub use error::PciError;
+pub use header::{CommonHeader, Header};
+pub use init::init_pcie;

+ 145 - 0
src/kernel/pcie/device.rs

@@ -0,0 +1,145 @@
+use super::{CommonHeader, Header};
+use crate::{kernel::mem::PhysAccess as _, sync::fence::memory_barrier};
+use acpi::mcfg::PciConfigEntry;
+use alloc::sync::Arc;
+use core::{ops::RangeInclusive, sync::atomic::Ordering};
+use eonix_mm::address::PAddr;
+use eonix_sync::{LazyLock, Spin};
+use intrusive_collections::{intrusive_adapter, KeyAdapter, RBTree, RBTreeAtomicLink};
+
+pub(super) static PCIE_DEVICES: LazyLock<Spin<RBTree<PCIDeviceAdapter>>> =
+    LazyLock::new(|| Spin::new(RBTree::new(PCIDeviceAdapter::new())));
+
+intrusive_adapter!(
+    pub PCIDeviceAdapter = Arc<PCIDevice<'static>> : PCIDevice { link: RBTreeAtomicLink }
+);
+
+#[allow(dead_code)]
+pub struct PCIDevice<'a> {
+    link: RBTreeAtomicLink,
+    segment_group: SegmentGroup,
+    config_space: ConfigSpace,
+    pub header: Header<'a>,
+    pub vendor_id: u16,
+    pub device_id: u16,
+}
+
+#[allow(dead_code)]
+#[derive(Clone)]
+pub struct SegmentGroup {
+    id: u16,
+    bus_range: RangeInclusive<u8>,
+    base_address: PAddr,
+}
+
+#[allow(dead_code)]
+#[derive(Clone)]
+pub struct ConfigSpace {
+    bus: u8,
+    device: u8,
+    function: u8,
+
+    base: PAddr,
+}
+
+impl SegmentGroup {
+    pub fn from_entry(entry: &PciConfigEntry) -> Self {
+        Self {
+            id: entry.segment_group,
+            bus_range: entry.bus_range.clone(),
+            base_address: PAddr::from(entry.physical_address),
+        }
+    }
+
+    pub fn iter(&self) -> impl Iterator<Item = ConfigSpace> + use<'_> {
+        self.bus_range
+            .clone()
+            .map(move |bus| {
+                (0..32)
+                    .map(move |device| {
+                        (0..8).map(move |function| ConfigSpace {
+                            bus,
+                            device,
+                            function,
+                            base: self.base_address
+                                + ((bus as usize) << 20)
+                                + ((device as usize) << 15)
+                                + ((function as usize) << 12),
+                        })
+                    })
+                    .flatten()
+            })
+            .flatten()
+    }
+}
+
+impl ConfigSpace {
+    pub fn header<'a>(&self) -> Option<Header<'a>> {
+        let common_header = unsafe {
+            // SAFETY: `self.base` is guaranteed to be pointing to a valid
+            //         configuration space area.
+            self.base.as_ptr::<CommonHeader>().as_ref()
+        };
+
+        if common_header.vendor_id == 0xffff {
+            return None;
+        }
+
+        // Bit 7 represents whether the device has multiple functions.
+        let header_type = common_header.header_type & !(1 << 7);
+
+        match header_type {
+            0 => Some(Header::Endpoint(unsafe {
+                // SAFETY: `self.base` is guaranteed to be pointing to a valid
+                //         configuration space area.
+                self.base.as_ptr().as_ref()
+            })),
+            1 | 2 => unimplemented!("Header type 1 and 2"),
+            _ => Some(Header::Unknown(unsafe {
+                // SAFETY: `self.base` is guaranteed to be pointing to a valid
+                //         configuration space area.
+                self.base.as_ptr().as_ref()
+            })),
+        }
+    }
+}
+
+impl PCIDevice<'static> {
+    pub fn new(
+        segment_group: SegmentGroup,
+        config_space: ConfigSpace,
+        header: Header<'static>,
+    ) -> Arc<Self> {
+        let common_header = header.common_header();
+
+        Arc::new(PCIDevice {
+            link: RBTreeAtomicLink::new(),
+            segment_group,
+            config_space,
+            vendor_id: common_header.vendor_id,
+            device_id: common_header.device_id,
+            header,
+        })
+    }
+}
+
+#[allow(dead_code)]
+impl PCIDevice<'_> {
+    pub fn enable_bus_mastering(&self) {
+        let header = self.header.common_header();
+        header.command().fetch_or(0x04, Ordering::Relaxed);
+
+        memory_barrier();
+    }
+}
+
+impl<'a> KeyAdapter<'a> for PCIDeviceAdapter {
+    type Key = u32;
+
+    fn get_key(
+        &self,
+        value: &'a <Self::PointerOps as intrusive_collections::PointerOps>::Value,
+    ) -> Self::Key {
+        ((value.vendor_id as u32) << 16) | value.device_id as u32
+    }
+}

+ 41 - 0
src/kernel/pcie/driver.rs

@@ -0,0 +1,41 @@
+use super::{
+    device::{PCIDevice, PCIE_DEVICES},
+    error::PciError,
+};
+use crate::{
+    kernel::constants::{EEXIST, ENOENT},
+    KResult,
+};
+use alloc::{
+    collections::btree_map::{self, BTreeMap},
+    sync::Arc,
+};
+use eonix_sync::Spin;
+
+static PCIE_DRIVERS: Spin<BTreeMap<u32, Arc<dyn PCIDriver>>> = Spin::new(BTreeMap::new());
+
+pub trait PCIDriver: Send + Sync {
+    fn vendor_id(&self) -> u16;
+    fn device_id(&self) -> u16;
+
+    fn handle_device(&self, device: Arc<PCIDevice<'static>>) -> Result<(), PciError>;
+}
+
+pub fn register_driver(driver: impl PCIDriver + 'static) -> KResult<()> {
+    let index = (driver.vendor_id() as u32) << 16 | driver.device_id() as u32;
+
+    let driver = Arc::new(driver);
+
+    match PCIE_DRIVERS.lock().entry(index) {
+        btree_map::Entry::Vacant(vacant_entry) => vacant_entry.insert(driver.clone()),
+        btree_map::Entry::Occupied(_) => Err(EEXIST)?,
+    };
+
+    let Some(device) = PCIE_DEVICES.lock().find(&index).clone_pointer() else {
+        Err(ENOENT)?
+    };
+
+    driver.handle_device(device)?;
+
+    Ok(())
+}

+ 38 - 0
src/kernel/pcie/error.rs

@@ -0,0 +1,38 @@
+use crate::kernel::constants::{EFAULT, EINVAL, ENOENT};
+use acpi::AcpiError;
+
+#[derive(Debug)]
+pub struct PciError(u32);
+
+impl From<AcpiError> for PciError {
+    fn from(error: AcpiError) -> Self {
+        match error {
+            AcpiError::NoValidRsdp => PciError(ENOENT),
+            AcpiError::RsdpIncorrectSignature => PciError(EINVAL),
+            AcpiError::RsdpInvalidOemId => PciError(EINVAL),
+            AcpiError::RsdpInvalidChecksum => PciError(EINVAL),
+            AcpiError::SdtInvalidSignature(_) => PciError(EINVAL),
+            AcpiError::SdtInvalidOemId(_) => PciError(EINVAL),
+            AcpiError::SdtInvalidTableId(_) => PciError(EINVAL),
+            AcpiError::SdtInvalidChecksum(_) => PciError(EINVAL),
+            AcpiError::TableMissing(_) => PciError(ENOENT),
+            AcpiError::InvalidFacsAddress => PciError(EFAULT),
+            AcpiError::InvalidDsdtAddress => PciError(EFAULT),
+            AcpiError::InvalidMadt(_) => PciError(EINVAL),
+            AcpiError::InvalidGenericAddress => PciError(EFAULT),
+            AcpiError::AllocError => PciError(EFAULT),
+        }
+    }
+}
+
+impl From<PciError> for u32 {
+    fn from(value: PciError) -> Self {
+        value.0
+    }
+}
+
+impl From<u32> for PciError {
+    fn from(value: u32) -> Self {
+        Self(value)
+    }
+}

+ 84 - 0
src/kernel/pcie/header.rs

@@ -0,0 +1,84 @@
+use core::{mem::offset_of, ops::Deref, sync::atomic::AtomicU32};
+
+#[repr(C)]
+pub struct CommonHeader {
+    pub vendor_id: u16,
+    pub device_id: u16,
+    /// Command Register.
+    ///
+    /// Use `command()` instead of accessing this field.
+    _command: u16,
+    /// Status Register.
+    ///
+    /// Use `command()` instead of accessing this field.
+    _status: u16,
+    pub revision_id: u8,
+    pub prog_if: u8,
+    pub subclass: u8,
+    pub class: u8,
+    pub cache_line_size: u8,
+    pub latency_timer: u8,
+    pub header_type: u8,
+    pub bist: u8,
+}
+
+#[repr(C)]
+pub struct Endpoint {
+    _header: CommonHeader,
+    pub bars: [u32; 6],
+    pub cardbus_cis_pointer: u32,
+    pub subsystem_vendor_id: u16,
+    pub subsystem_id: u16,
+    pub expansion_rom_address: u32,
+    pub capabilities_pointer: u8,
+    _reserved: [u8; 7],
+    pub interrupt_line: u8,
+    pub interrupt_pin: u8,
+    pub min_grant: u8,
+    pub max_latency: u8,
+}
+
+pub enum Header<'a> {
+    Unknown(&'a CommonHeader),
+    Endpoint(&'a Endpoint),
+}
+
+#[allow(dead_code)]
+impl CommonHeader {
+    pub fn command(&self) -> &AtomicU32 {
+        unsafe {
+            AtomicU32::from_ptr(
+                (&raw const *self)
+                    .byte_offset(offset_of!(CommonHeader, _command) as isize)
+                    .cast::<u32>() as *mut u32,
+            )
+        }
+    }
+
+    pub fn status(&self) -> &AtomicU32 {
+        unsafe {
+            AtomicU32::from_ptr(
+                (&raw const *self)
+                    .byte_offset(offset_of!(CommonHeader, _status) as isize)
+                    .cast::<u32>() as *mut u32,
+            )
+        }
+    }
+}
+
+impl Header<'_> {
+    pub fn common_header(&self) -> &CommonHeader {
+        match self {
+            Header::Unknown(header) => header,
+            Header::Endpoint(header) => &header,
+        }
+    }
+}
+
+impl Deref for Endpoint {
+    type Target = CommonHeader;
+
+    fn deref(&self) -> &Self::Target {
+        &self._header
+    }
+}

+ 49 - 0
src/kernel/pcie/init.rs

@@ -0,0 +1,49 @@
+use super::{
+    device::{PCIDevice, SegmentGroup, PCIE_DEVICES},
+    error::PciError,
+};
+use crate::kernel::mem::PhysAccess as _;
+use acpi::{AcpiHandler, AcpiTables, PciConfigRegions, PhysicalMapping};
+use eonix_mm::address::PAddr;
+
+#[derive(Clone)]
+struct AcpiHandlerImpl;
+
+impl AcpiHandler for AcpiHandlerImpl {
+    unsafe fn map_physical_region<T>(
+        &self,
+        physical_address: usize,
+        size: usize,
+    ) -> PhysicalMapping<Self, T> {
+        let virtual_address = unsafe {
+            // SAFETY: `physical_address` is guaranteed to be valid by the caller.
+            PAddr::from_val(physical_address).as_ptr()
+        };
+
+        PhysicalMapping::new(physical_address, virtual_address, size, size, self.clone())
+    }
+
+    fn unmap_physical_region<T>(_: &PhysicalMapping<Self, T>) {}
+}
+
+pub fn init_pcie() -> Result<(), PciError> {
+    let acpi_tables = unsafe {
+        // SAFETY: Our impl should be correct.
+        AcpiTables::search_for_rsdp_bios(AcpiHandlerImpl)?
+    };
+
+    let conf_regions = PciConfigRegions::new(&acpi_tables)?;
+    for region in conf_regions.iter() {
+        let segment_group = SegmentGroup::from_entry(&region);
+
+        for config_space in segment_group.iter() {
+            if let Some(header) = config_space.header() {
+                let pci_device = PCIDevice::new(segment_group.clone(), config_space, header);
+
+                PCIE_DEVICES.lock().insert(pci_device);
+            }
+        }
+    }
+
+    Ok(())
+}

+ 2 - 6
src/lib.rs

@@ -32,6 +32,7 @@ use eonix_mm::paging::PFN;
 use eonix_runtime::{run::FutureRun, scheduler::Scheduler, task::Task};
 use kernel::{
     mem::Page,
+    pcie::init_pcie,
     task::{KernelStack, ProcessBuilder, ProcessList, ThreadBuilder, ThreadRunnable},
     vfs::{
         dentry::Dentry,
@@ -91,12 +92,7 @@ static ALLOCATOR: Allocator = Allocator;
 
 #[no_mangle]
 pub extern "C" fn kernel_init(early_kstack_pfn: PFN) -> ! {
-    extern "C" {
-        fn init_pci();
-    }
-
-    // TODO: Move this to rust.
-    unsafe { init_pci() };
+    init_pcie().expect("Unable to initialize PCIe bus");
 
     // To satisfy the `Scheduler` "preempt count == 0" assertion.
     eonix_preempt::disable();