Part 2 - The Minimal way of kernel

Part 2 - The Minimal Way of My Kernel

In this installment, I’ll walk you through the bare essentials of booting and running a tiny bare-metal Rust kernel. I’ve tweaked and annotated Phil Opple’s outline to reflect my own experiments—and added callouts where pitfalls might hide for begineers.


The Boot Process

When you hit power, your hardware embarks on a well-orchestrated journey called the boot process. Here’s how it goes:

Tip

Keep your motherboard manual nearby—firmware behaviors can vary slightly between BIOS and UEFI implementations.

Step 1: Power-On Self Test (POST)

Step 2: CPU & Hardware Initialization

Warning

Real mode has only 1 MB of accessible memory! You’ll need to switch modes for anything complex.

Step 3: Finding a Boot Device

In this guide, we’ll stick to BIOS and leverage the bootimage tool so we don’t reinvent the bootloader wheel.


The Multiboot Standard

The Free Software Foundation’s Multiboot spec (used by GRUB) defines how to load a 32-bit protected-mode kernel. It requires extra work for 64-bit, and its docs are terse.

Important

We’ll defer full Multiboot support until later—bootimage gives us a simpler path now.


A Minimal Kernel

Goal: Build a disk image that prints “Hello from KaranOS!” to the screen using a freestanding Rust program.

1. Rust Nightly

We need Rust nightly for unstable features used in OS dev.

rustup override set nightly
rustc --version  # should end in "-nightly"
Tip

Nightly changes fast—pin your toolchain in rust-toolchain.toml if you hit unexpected breakages.

2. Custom Target JSON

Create x86_64-karan_os.json in your project root:

{
  "llvm-target": "x86_64-unknown-none",
  "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-" +
                 "i64:64-i128:128-f80:128-n8:16:32:64-S128",
  "arch": "x86_64",
  "target-endian": "little",
  "target-pointer-width": "64",
  "target-c-int-width": "32",
  "os": "none",
  "executables": true,
  "linker-flavor": "ld.lld",
  "linker": "rust-lld",
  "panic-strategy": "abort",
  "disable-redzone": true,
  "features": "-mmx,-sse,+soft-float"
}
Warning

A single missing comma or brace will break cargo build—watch your JSON syntax carefully.

3. Building the Kernel

Start with a minimal src/main.rs:

#![no_std]
#![no_main]

use core::panic::PanicInfo;

#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
    loop {}
}

#[no_mangle]
pub extern "C" fn _start() -> ! {
    loop {}
}
cargo build --target x86_64-karan_os.json

The build will complain because core isn’t prebuilt for our target. Fix it via .cargo/config.toml:

[build]
target = "x86_64-karan_os.json"

[unstable]
build-std = ["core", "compiler_builtins"]
build-std-features = ["compiler-builtins-mem"]

And install sources:

rustup component add rust-src
Tip

build-std forces recompilation of core crates—expect longer compile times.

4. Writing Text to VGA

We’ll display “Hello from KaranOS!” on the VGA text buffer at 0xb8000.

#![no_std]
#![no_main]

use core::panic::PanicInfo;

#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
    loop {}
}

static MESSAGE: &[u8] = b"Hello from KaranOS!";

#[no_mangle]
pub extern "C" fn _start() -> ! {
    let buffer = 0xb8000 as *mut u8;
    for (i, &byte) in MESSAGE.iter().enumerate() {
        unsafe {
            *buffer.offset((i * 2) as isize) = byte;
            // 0x1f = bright white on blue background
            *buffer.offset((i * 2 + 1) as isize) = 0x1f;
        }
    }
    loop {}
}
Tip

We’re using unsafe here for raw pointer writes. In the next post we’ll wrap this in a safe VGA writer.

5. Bootimage & Booting

Add to Cargo.toml:

[dependencies]
bootloader = "0.9"

Install and prepare:

cargo install bootimage
rustup component add llvm-tools-preview

Generate the image:

cargo bootimage

Run in QEMU:

qemu-system-x86_64 \
  -drive format=raw,file=target/x86_64-karan_os/debug/bootimage-karan_os.bin
Warning

When writing to real hardware via dd, double-check your of= target or you might overwrite your main OS!


What’s Next?

In Part 3, we’ll build a safe abstraction for VGA text and introduce a rudimentary println! macro. Stay tuned!