Project 1: The Do-Nothing Bootloader and Kernel
Build a bootable image that prints a message directly to video memory and halts, proving you control the very first instructions after power-on.
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | Advanced |
| Time Estimate | Weekend |
| Main Programming Language | Assembly and C (Alternatives: Rust, Zig) |
| Alternative Programming Languages | Rust, Zig |
| Coolness Level | See REFERENCE.md (Level 4) |
| Business Potential | See REFERENCE.md (Level 1) |
| Prerequisites | x86 basics, hex, binary layout, basic assembly |
| Key Topics | Boot chain, real mode, firmware handoff |
1. Learning Objectives
By completing this project, you will:
- Explain the boot chain from firmware to your first instruction.
- Describe why the boot signature and sector layout are critical.
- Understand how early code writes to screen memory without an OS.
- Validate a boot image using deterministic checks.
2. All Theory Needed (Per-Concept Breakdown)
Boot Chain and Early Execution Environment
Fundamentals The boot chain is a strict sequence of handoffs that moves a machine from power-on to the first instruction you control. Firmware initializes hardware, locates a boot target, and loads a small boot sector into memory. That boot sector is not a program in the usual sense; it is a raw block that must fit a specific format. The bootloader then loads a larger kernel image and supplies metadata such as the memory layout and boot parameters. In early boot, there is no OS, no drivers, and no libraries, so you must interact with hardware directly. This project focuses on the moment where firmware hands off to your code, proving that you can reason about and shape the machine before the OS exists.
Deep Dive Boot is a chain of trust and control, not a single event. Firmware is responsible for initializing the CPU, memory controllers, and essential devices. On UEFI systems, firmware exposes structured services and tables, while legacy BIOS follows a simpler, sector-based handoff. Regardless of platform, the firmware decides which storage device to boot from and loads the first boot sector into a fixed memory address. That sector must end with the boot signature (0x55 0xAA) so the firmware recognizes it as valid.
The boot sector is tiny and highly constrained. You cannot rely on a standard library or any kernel service. The CPU is typically in a basic execution mode with limited addressing features, and only a subset of hardware behavior is guaranteed. This is why early boot code is often written in assembly and why it is careful about memory addresses. The bootloader’s task is to transition from this minimal environment into a more capable one by loading the kernel image and preparing the system state the kernel expects.
The Linux kernel expects metadata to be passed in a specific format depending on architecture. For x86, the boot protocol describes where the kernel image should be loaded, how the command line is passed, and where the memory map is located. These details are not optional. If the bootloader miscommunicates memory layout, the kernel can overwrite itself or attempt to use non-existent RAM. If the command line is malformed, the kernel may fail to find the root filesystem. The kernel then initializes memory management, interrupt handling, and basic drivers before launching early user space.
Understanding early boot is essential because debugging in this phase is different. There are no system logs on disk yet. Many errors will manifest as a blank screen, a hang, or an early panic with minimal output. The typical debugging tools do not exist. You must rely on deterministic output (for example, writing to video memory) and careful inspection of the boot image. This project trains you to validate a boot sector by checking exact bytes and fixed offsets, which is a transferable skill for embedded systems, OS development, and low-level debugging.
Another key idea is that the boot process is modular. You can choose a minimal bootloader for learning, or a full-featured bootloader that supports menus and filesystem parsing. You can build an initramfs to supply drivers before the real root filesystem is mounted. The point is that each stage has clear inputs and outputs. Once you can describe those inputs and outputs, you can reason about boot failures, customize the boot flow, or build a minimal system image for special-purpose deployments.
How this fit on projects You will apply this concept in §3.1 and §4.1 to design the boot image and in §5.10 Phase 1 to validate the boot flow. It also connects to P10-container-runtime.md where PID 1 behavior matters.
Definitions & key terms
- Firmware: Code that initializes hardware and starts the boot chain.
- Boot sector: The first disk sector loaded by firmware in legacy flows.
- Boot signature: The 0x55 0xAA marker that indicates a bootable sector.
- Real mode: Early x86 execution mode with limited addressing.
- Boot protocol: Contract describing how a bootloader passes data to the kernel.
Mental model diagram
Power on
|
v
Firmware
|
v
Boot sector (512 bytes)
|
v
Bootloader stage
|
v
Kernel image
|
v
Early user space
How it works
- Firmware loads the boot sector into memory.
- Firmware jumps to the boot sector entry point.
- Boot sector code loads or chains to a larger loader.
- Loader places the kernel image in memory and passes metadata.
- Kernel initializes and starts PID 1.
Minimal concrete example
Boot log (conceptual):
[boot] firmware OK
[boot] boot signature valid
[boot] jump to 0x7C00
[boot] write text to screen
[boot] halt
Common misconceptions
- “The kernel is the first code that runs.” Firmware runs first.
- “The boot sector can be any size.” It must be 512 bytes in legacy flows.
- “The kernel can discover everything on its own.” It needs boot metadata.
Check-your-understanding questions
- Why does the boot sector need the signature 0x55 0xAA?
- What happens if the bootloader uses the wrong load address?
- Why is early boot code typically written in assembly?
- What problem does the boot protocol solve?
Check-your-understanding answers
- Firmware checks it to confirm the sector is bootable.
- The CPU may execute garbage or overwrite critical memory.
- No OS services exist, and the environment is extremely constrained.
- It defines a stable contract for passing metadata to the kernel.
Real-world applications
- Building minimal boot images for embedded systems.
- Debugging boot failures in cloud images.
- Understanding secure boot chains.
Where you’ll apply it
- See §3.1 What You Will Build and §4.1 High-Level Design in this file.
- Also used in: P10-container-runtime.md
References
- Linux x86 boot protocol: https://docs.kernel.org/arch/x86/boot.html
- UEFI specification: https://uefi.org/specs/UEFI/2.10/
- “How Linux Works” by Brian Ward - Ch. 1-3
Key insights Boot is a chain of explicit contracts, not a mysterious black box.
Summary If you can control the first instruction and verify the boot signature, you can reason about the entire boot pipeline.
Homework/Exercises to practice the concept
- Draw a diagram of your machine’s boot chain.
- Identify which stage owns the kernel command line.
Solutions to the homework/exercises
- Firmware -> boot sector -> loader -> kernel -> PID 1.
- The bootloader or firmware passes the command line to the kernel.
3. Project Specification
3.1 What You Will Build
A bootable disk image that contains a minimal boot sector. The boot sector writes a fixed message to video memory and halts. It does not load a full kernel or start user space. It is intentionally minimal to make the boot handoff observable and deterministic.
3.2 Functional Requirements
- Bootable sector: The image must be recognized by firmware and executed.
- Deterministic output: The same message appears every boot.
- Clean halt: Execution stops without reboot loops.
3.3 Non-Functional Requirements
- Performance: Boot within 1 second in QEMU.
- Reliability: Output must be identical on each run.
- Usability: Clear instructions to build and run.
3.4 Example Usage / Output
Run QEMU and observe a black window with the text:
HELLO FROM BARE METAL
3.5 Data Formats / Schemas / Protocols
- Boot sector layout: 512 bytes with signature 0x55 0xAA at the last two bytes.
- Text output: character and attribute bytes in VGA text buffer layout.
3.6 Edge Cases
- Wrong image size (not 512 bytes).
- Missing boot signature.
- Message not aligned with expected video memory layout.
3.7 Real World Outcome
This is the golden reference for success.
3.7.1 How to Run (Copy/Paste)
- Build: Assemble and write a 512-byte image named
os-image.binin the project root. - Run:
qemu-system-x86_64 -drive format=raw,file=os-image.bin - Working directory: project root.
3.7.2 Golden Path Demo (Deterministic)
Use a fixed message and fixed screen position so output is identical on every run.
3.7.3 If CLI: Exact terminal transcript
$ qemu-system-x86_64 -drive format=raw,file=os-image.bin
# QEMU window opens
# Screen shows:
HELLO FROM BARE METAL
# Exit by closing the QEMU window
Failure demo (deterministic):
$ qemu-system-x86_64 -drive format=raw,file=bad-image.bin
# QEMU shows: "No bootable device"
# Exit code: 1
Exit codes:
- 0 on successful boot and manual close.
- 1 if QEMU reports no bootable device.
4. Solution Architecture
4.1 High-Level Design
[boot sector image]
|
v
firmware loads 512 bytes -> jump to entry -> write to VGA buffer -> halt
4.2 Key Components
| Component | Responsibility | Key Decisions |
|---|---|---|
| Boot sector | First code executed | Must fit 512 bytes and include signature |
| VGA writer | Writes text to screen | Use text mode memory buffer |
| Halt loop | Stops execution | Use a tight loop to prevent runaway |
4.4 Data Structures (No Full Code)
- Boot sector layout: header region + message bytes + signature bytes.
- Message buffer: fixed-length ASCII string.
4.4 Algorithm Overview
Key Algorithm: Boot and print
- Assume control at entry point.
- Write characters and color bytes into VGA buffer.
- Halt by looping indefinitely.
Complexity Analysis:
- Time: O(n) for n characters.
- Space: O(1) extra memory.
5. Implementation Guide
5.1 Development Environment Setup
# Install QEMU and an assembler
# Verify you can run qemu-system-x86_64
5.2 Project Structure
project-root/
├── boot/
│ └── boot-sector.asm
├── build/
│ └── os-image.bin
└── README.md
5.3 The Core Question You’re Answering
“What does the CPU execute before any OS services exist, and how can I see it?”
5.4 Concepts You Must Understand First
Stop and research these before coding:
- Boot signature and sector size
- Why the firmware checks the last two bytes.
- Book Reference: “How Linux Works” - Ch. 1
- Real mode addressing
- What limits exist in early boot.
- Book Reference: “Operating Systems: Three Easy Pieces” - Intro
5.5 Questions to Guide Your Design
- How will you guarantee the image is exactly 512 bytes?
- How will you verify the signature bytes without running QEMU?
5.6 Thinking Exercise
Trace the First Instruction
Write a step-by-step explanation of the first three instructions executed after firmware jumps to your code.
5.7 The Interview Questions They’ll Ask
- “What is the boot signature and why is it required?”
- “What is the difference between firmware and a bootloader?”
- “Why do bootloaders often use assembly?”
- “What does ‘real mode’ mean?”
5.8 Hints in Layers
Hint 1: Fixed Layout Treat the boot sector as a fixed-size binary block.
Hint 2: Text Mode Each character uses two bytes in VGA text memory.
Hint 3: Deterministic Output Use a constant message and fixed screen offset.
Hint 4: Debugging
Validate the last two bytes with hexdump or xxd.
5.9 Books That Will Help
| Topic | Book | Chapter |
|---|---|---|
| Boot process | “How Linux Works” | Ch. 1-2 |
| CPU modes | “Operating Systems: Three Easy Pieces” | Intro |
5.10 Implementation Phases
Phase 1: Foundation (2-4 hours)
Goals:
- Produce a 512-byte boot image.
- Add a valid boot signature.
Tasks:
- Create the minimal boot sector layout.
- Validate the signature bytes.
Checkpoint: hexdump shows 0x55 0xAA at the end.
Phase 2: Core Functionality (2-4 hours)
Goals:
- Write a fixed message to VGA memory.
Tasks:
- Encode message bytes.
- Write to the VGA buffer location.
Checkpoint: Message appears in QEMU.
Phase 3: Polish & Edge Cases (1-2 hours)
Goals:
- Ensure deterministic output.
- Provide a failure demo.
Tasks:
- Test with bad signatures to confirm failure behavior.
- Document exit codes.
Checkpoint: Success and failure demos match expected output.
5.11 Key Implementation Decisions
| Decision | Options | Recommendation | Rationale |
|---|---|---|---|
| Boot mode | BIOS vs UEFI | BIOS (legacy) | Simpler and easier to validate |
| Output method | VGA memory vs BIOS calls | VGA memory | Deterministic and explicit |
6. Testing Strategy
6.1 Test Categories
| Category | Purpose | Examples |
|---|---|---|
| Unit Tests | Validate image size and signature | Signature check script |
| Integration Tests | Boot in QEMU | Visual output test |
| Edge Case Tests | Missing signature | Failure demo |
6.2 Critical Test Cases
- Valid image: QEMU boots and prints message.
- Invalid signature: QEMU reports no bootable device.
- Wrong size: Image fails to boot.
6.3 Test Data
Expected message: HELLO FROM BARE METAL
Expected boot signature: 0x55 0xAA
7. Common Pitfalls & Debugging
7.1 Frequent Mistakes
| Pitfall | Symptom | Solution |
|---|---|---|
| Wrong signature | No bootable device | Fix last two bytes |
| Wrong image size | Boot fails | Pad to 512 bytes |
| Wrong VGA address | Blank screen | Use correct text buffer address |
7.2 Debugging Strategies
- Binary inspection: Use hexdump to validate layout.
- Minimal changes: Change one byte at a time to isolate issues.
7.3 Performance Traps
Not applicable; the workload is tiny and deterministic.
8. Extensions & Challenges
8.1 Beginner Extensions
- Add a second line of text.
- Change text color attributes.
8.2 Intermediate Extensions
- Add a simple loading animation.
- Read input from keyboard controller.
8.3 Advanced Extensions
- Load a second-stage bootloader.
- Switch to protected mode and display a new message.
9. Real-World Connections
9.1 Industry Applications
- Embedded systems: Custom boot flows for appliances.
- Cloud images: Debugging early boot failures.
9.2 Related Open Source Projects
- GRUB: https://www.gnu.org/software/grub/ - Full-featured bootloader.
- syslinux: https://wiki.syslinux.org/ - Lightweight bootloader suite.
9.3 Interview Relevance
- Boot sequences and firmware handoffs are common OS interview topics.
10. Resources
10.1 Essential Reading
- “How Linux Works” by Brian Ward - Boot overview and early user space
- Linux x86 boot protocol - docs.kernel.org
10.2 Video Resources
- “How Computers Boot” - public lecture (search title)
10.3 Tools & Documentation
- QEMU: https://www.qemu.org/ - emulator for boot testing
- UEFI Spec: https://uefi.org/specs/UEFI/2.10/ - firmware reference
10.4 Related Projects in This Series
- Project 2: The Syscall Tracer - starts after user space exists
- Project 10: The Poor Man’s Docker - relies on PID 1 semantics
11. Self-Assessment Checklist
11.1 Understanding
- I can explain the firmware -> bootloader -> kernel chain
- I can explain why the boot signature matters
- I understand why early code is so constrained
11.2 Implementation
- All functional requirements are met
- All test cases pass
- The image is deterministic and documented
11.3 Growth
- I can describe this project in a job interview
- I documented lessons learned
- I can identify a next extension to try
12. Submission / Completion Criteria
Minimum Viable Completion:
- Bootable image prints a fixed message
- Correct boot signature and size
- Deterministic output in QEMU
Full Completion:
- All minimum criteria plus:
- Failure demo documented with exit code
- Clear build/run instructions
Excellence (Going Above & Beyond):
- Add a second-stage loader or protected mode transition
- Document the boot protocol metadata you would pass to a kernel