m³OS docs GitHub

Phase 15

Hardware Discovery

Replace the hardcoded, single-core interrupt model with proper hardware discovery via ACPI and PCI, and switch from the legacy 8259 PIC to the APIC. This unlocks multi-core support and arbitrary device discovery in later phases.

Complete

Learning goal

Understand how ACPI tables describe the hardware topology to the OS.

Category

infrastructure

Why this phase matters

Replace the hardcoded, single-core interrupt model with proper hardware discovery via ACPI and PCI, and switch from the legacy 8259 PIC to the APIC. This unlocks multi-core support and arbitrary device discovery in later phases.

How it works in m3OS

The roadmap entry defines the learning scope for this phase; a deeper implementation doc has not been matched yet.

How it builds on earlier phases

This phase builds on Shell And Tools and turns that foundation into Hardware Discovery.

Implementation outline

  • Parse the RSDP address from BootInfo (UEFI already locates it).
  • Walk the RSDT/XSDT and collect pointers to each SDT.
  • Parse the MADT and record the Local APIC base address and all I/O APIC descriptors.
  • Identity-map the Local APIC MMIO page and write the initialization sequence.
  • Program the I/O APIC redirection table for the IRQs the kernel currently handles.
  • Disable the 8259 PIC.
  • Switch the scheduler’s timer source from the PIT to the LAPIC timer.
  • Implement PCI config space reads (port I/O: address port 0xCF8, data port 0xCFC).
  • Enumerate all PCI functions and store the device list in a static kernel array.

Task progress snapshot

49 of 49 tracked tasks are complete.

Documentation targets

  • Documentation deliverables have not been listed yet.

Deferred follow-on work

  • ACPI AML interpreter and dynamic hardware events
  • PCIe extended config space (MCFG)
  • MSI and MSI-X interrupt routing
  • PCI device power management (D-states)
  • ACPI S-states (sleep, hibernate)
  • PCIe hotplug

Source materials

Important code

Code spotlight cards

Code spotlight

Read RSDP address from BootInfo

kernel/src/main.rs L32-L55

Read RSDP address from BootInfo. Why it matters: The RSDP is the entry point into the ACPI table chain; without it, no hardware can be discovered dynamically. Focus on `boot_info.rsdp_addr` and `kernel_main` in `kernel/src/main.rs` while you trace the snippet.

fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
    serial::init();
    serial::init_logger();

    serial_println!("[m3os] Hello from kernel!");
    log::info!("Kernel initialized");

    // Load GDT/IDT — no IRQs yet.
    arch::init();

    // P9-T001: parse framebuffer info before mm::init consumes boot_info.
    // `mm::init` takes `&'static mut BootInfo` which borrows the whole struct
    // for 'static, so we must extract the raw pointer + layout first.
    let fb_parts: Option<(*mut u8, bootloader_api::info::FrameBufferInfo)> =
        boot_info.framebuffer.as_mut().map(|fb| {
            let info = fb.info();
            // SAFETY: boot_info is &'static mut so the framebuffer memory is
            // valid for the kernel lifetime.  We extract a raw pointer here
            // and hand it to fb::init_from_parts after mm::init returns;
            // no other code accesses the framebuffer between these two points.
            let ptr: *mut u8 = fb.buffer_mut().as_mut_ptr();
            (ptr, info)
        });

Code spotlight

Define RSDP structures

kernel/src/acpi/mod.rs L21-L35

Define RSDP structures. Why it matters: The RSDP v1/v2 structures must be correctly defined to locate the root system descriptor table. Focus on `RsdpDescriptor` and `RsdpDescriptorV2` in `kernel/src/acpi/mod.rs` while you trace the snippet.

/// ACPI 1.0 RSDP (Root System Description Pointer), 20 bytes.
#[repr(C, packed)]
#[derive(Clone, Copy)]
struct RsdpDescriptor {
    signature: [u8; 8],
    checksum: u8,
    oem_id: [u8; 6],
    revision: u8,
    rsdt_address: u32,
}

/// ACPI 2.0+ RSDP extension, 36 bytes total.
#[repr(C, packed)]
#[derive(Clone, Copy)]
struct RsdpDescriptorV2 {

Code spotlight

Read Local APIC base address

kernel/src/arch/x86_64/apic.rs See file

Read Local APIC base address. Why it matters: The LAPIC base address is needed to memory-map its control registers. Focus on `IA32_APIC_BASE` in `kernel/src/arch/x86_64/apic.rs` while you trace the snippet.

Key files

How real OSes differ

  • Production kernels use ACPI's AML bytecode interpreter to handle dynamic hardware configuration (hotplug, power transitions, embedded controller queries). PCI enumeration extends to PCIe extended config space (4 KB per function) and uses MCFG tables for MMIO-mapped access. IRQ routing on modern systems runs through MSI/MSI-X rather than the I/O APIC. This phase uses only the static descriptor tables and legacy port-I/O PCI access, which is sufficient for a single-machine QEMU target.

Success criteria

  • The kernel boots using the LAPIC timer for preemption instead of the PIT.
  • Keyboard interrupts are delivered via the I/O APIC without regression.
  • The legacy 8259 PIC is fully masked and disabled.
  • A boot log entry prints the full PCI device list with vendor ID and class codes.
  • ACPI table parsing logs the CPU count and APIC IDs found in the MADT.

Related phases

Keep exploring the roadmap