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.
Learning goal
Understand how ACPI tables describe the hardware topology to the OS.
Category
infrastructure
Components touched
Builds on
Unlocked by this phase
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
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
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
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
Phase 14
CompleteShell And Tools
Turn the minimal shell from Phase 9 into a genuinely useful interactive environment: pipes, I/O redirection, job control, environment variables, and a small set of core utilities that exercise the filesystem and process model.
Phase 16
CompleteNetwork
Add a minimal but real TCP/IP stack so the OS can send and receive packets over a network. A virtio-net NIC in QEMU is the hardware target. The end goal is a working `ping` and a simple TCP connection.