Phase 01
Boot Foundation
Boot the kernel through UEFI, print useful output over serial, and halt cleanly. This is the first end-to-end proof that the workspace, toolchain, boot path, and panic path all work together.
Learning goal
Understand `#![no_std]` and `#![no_main]`.
Category
foundations
Components touched
Builds on
Unlocked by this phase
Why this phase matters
Boot the kernel through UEFI, print useful output over serial, and halt cleanly. This is the first end-to-end proof that the workspace, toolchain, boot path, and panic path all work together.
How it works in m3OS
The boot path has one job: get from a host-side Cargo command to running Rust code in a no_std kernel with enough early diagnostics to understand what happened.
In m³OS, that means:
How it builds on earlier phases
This phase establishes the first teachable slice of the system and becomes the baseline for everything that follows.
Implementation outline
- Set up the kernel crate and host-side
xtask. - Define the target and runner configuration used for OS builds.
- Initialize COM1 serial and expose simple print macros.
- Install a logger backend that writes through serial.
- Add a minimal
kernel_mainentry point andhltloop. - Add a panic handler that emits enough context to debug early failures.
Task progress snapshot
20 of 20 tracked tasks are complete.
Documentation targets
- Documentation deliverables have not been listed yet.
Deferred follow-on work
- framebuffer output
- test harness integration beyond basic smoke testing
- real hardware boot support beyond the documented QEMU path
Source materials
Important code
Code spotlight cards
Code spotlight
Implement a minimal kernel_main entry point
Implement a minimal kernel_main entry point. Why it matters: This is the first code that runs after the bootloader hands off control; it must reach a stable halt loop. Focus on `kernel_main` and `entry_point!(kernel_main)` in `kernel/src/main.rs` while you trace the snippet.
entry_point!(kernel_main);
fn kernel_main(_boot_info: &'static mut BootInfo) -> ! {
serial::init();
serial::init_logger();
serial_println!("[ostest] Hello from kernel!");
log::info!("Kernel initialized");
hlt_loop();
}
fn hlt_loop() -> ! {
loop {
x86_64::instructions::hlt();
}
} Code spotlight
Add serial initialization and print macros
Add serial initialization and print macros. Why it matters: Serial output is the primary debug channel in headless QEMU; without it there is no visibility into boot progress. Focus on `init` and `serial_print!` in `kernel/src/serial.rs` while you trace the snippet.
pub fn init() {
let mut serial_port = unsafe { SerialPort::new(COM1_PORT) };
serial_port.init();
*SERIAL1.lock() = Some(serial_port);
} Code spotlight
Implement cargo xtask image
Implement cargo xtask image. Why it matters: Produces a bootable disk image that QEMU (or real hardware) can load via UEFI. Focus on `xtask/src/main.rs` while you trace the snippet.
Key files
- xtask/src/main.rs — The host-side logic lives in xtask/src/main.rs.
- kernel/src/main.rs — The kernel entry point lives in kernel/src/main.rs.
- kernel/src/serial.rs — The serial implementation in kernel/src/serial.rs provides:
How real OSes differ
- Production kernels usually support many boot environments, richer hardware discovery, multiple log sinks, and more defensive boot diagnostics. A learning project can keep the boot path narrow and explicit so the reader can see every step without being buried in platform variation.
Success criteria
- The kernel boots in QEMU through UEFI.
- Serial output includes a clear startup message.
- Panics print a message and halt instead of silently freezing.
- Image generation is predictable and reproducible.
Related phases