GitHub - lopespm/zig-minimal-kernel-x86: Minimal x86 Kernel - built in Zig

4 min read Original article ↗

Minimal x86 Kernel - built in Zig

A minimal bare-metal kernel written entirely in Zig (zero assembly files). It boots on an x86 (i386) machine via the Multiboot 1 protocol and prints a coloured greeting to the VGA text-mode display, then halts the CPU.

The project is designed to be cross-compiled from any host (including Apple Silicon Macs) and tested instantly with QEMU. It can be booted directly via QEMU's built-in Multiboot loader or through a GRUB rescue ISO.

What it does

  1. QEMU loads the ELF binary using its built-in Multiboot 1 support.
  2. The CPU starts in 32-bit protected mode at the _start entry point.
  3. _start sets up a 16 KiB stack and jumps to kmain.
  4. kmain clears the VGA text buffer and writes a message to the screen.
  5. The CPU enters an infinite hlt loop.

Preconditions

Tool Version Install
Zig 0.14.0+ ziglang.org/download or brew install zig
QEMU any recent brew install qemu / nix-env -iA nixpkgs.qemu
Docker any recent docker.com (only needed for GRUB ISO builds)

Zig bundles its own LLVM back-end and linker, so cross-compilation to x86-freestanding-none works out of the box on any host OS and architecture (macOS ARM, Linux x86_64, etc.). Docker is only required for building the GRUB ISO, since grub-pc-bin is an x86 Linux package.

How to run

Option 1: Direct Multiboot (no GRUB)

# Build the kernel (produces zig-out/bin/kernel)
zig build

# Boot it in QEMU (opens a graphical VGA window)
zig build run

# Or use the helper script (curses mode, auto-kills after a few seconds)
chmod +x run.sh
./run.sh

To run QEMU manually with custom flags:

qemu-system-i386 -kernel zig-out/bin/kernel

You should see this:

Screenshot 2026-02-17 at 23 58 16

Option 2: GRUB rescue ISO

This builds a bootable ISO with a GRUB menu using Docker, then launches it in QEMU:

chmod +x run-grub.sh
./run-grub.sh

You should see this:

Screenshot 2026-02-19 at 00 29 04

The script performs the following steps:

  1. Builds the kernel with zig build
  2. Creates an iso/boot/grub/ directory structure with a grub.cfg
  3. Runs grub-mkrescue inside a Docker container (--platform linux/amd64) to produce zig-kernel.iso
  4. Boots the ISO with qemu-system-i386 -cdrom zig-kernel.iso

Project structure

zig-kernel/
├── build.zig          Zig build script (target, linker, QEMU run step)
├── build.zig.zon      Package manifest
├── linker.ld          Linker script (section layout, entry point)
├── run.sh             Quick-test shell script (direct Multiboot via QEMU)
├── run-grub.sh        Full GRUB ISO build + QEMU boot script
└── src/
    └── main.zig       Entire kernel: Multiboot header, VGA driver, kmain

System diagram

 HOST (macOS ARM / any OS)                    EMULATED x86 MACHINE (QEMU)
 ─────────────────────────                    ──────────────────────────────

 ┌──────────────┐   zig build    ┌────────────────────┐
 │  src/main.zig│───────────────▶│  zig-out/bin/kernel│  (i386 ELF binary)
 │  linker.ld   │  cross-compile │  Multiboot 1 magic │
 │  build.zig   │  x86-free-     │  at offset 0       │
 └──────────────┘  standing-none └─────────┬──────────┘
                                          │
                               qemu-system-i386 -kernel
                                          │
                                          ▼
                               ┌──────────────────────┐
                               │      QEMU / TCG      │
                               │  (x86 CPU emulation) │
                               └─────────┬────────────┘
                                          │
                    ┌─────────────────────┼───────────────────────┐
                    │   Emulated i386 hardware                    │
                    │                     │                       │
                    │   1. Multiboot      │                       │
                    │      loader reads   ▼                       │
                    │      ELF, puts   ┌───────────┐              │
                    │      CPU in      │  _start   │  32-bit      │
                    │      protected   │  (naked)  │  protected   │
                    │      mode        └────┬──────┘  mode        │
                    │                       │                     │
                    │              set up   │ stack               │
                    │                       ▼                     │
                    │                 ┌──────────┐                │
                    │                 │  kmain   │                │
                    │                 └────┬─────┘                │
                    │                      │                      │
                    │          ┌───────────┼───────────┐          │
                    │          │           │           │          │
                    │          ▼           ▼           ▼          │
                    │   clearScreen()  print(...)   hlt loop      │
                    │          │           │                      │
                    │          ▼           ▼                      │
                    │   ┌──────────────────────────────────┐      │
                    │   │  VGA Text Buffer at 0xB8000      │      │
                    │   │  80×25 grid, 16-bit per cell     │      │
                    │   │  (ASCII byte + colour attribute) │      │
                    │   └──────────────────────────────────┘      │
                    │                     │                       │
                    └─────────────────────┼───────────────────────┘
                                          │
                                          ▼
                               ┌──────────────────────┐
                               │   QEMU VGA Window    │
                               │                      │
                               │  ════════════════    │
                               │  Hello from the      │
                               │    Zig Kernel!       │
                               │  ════════════════    │
                               │                      │
                               └──────────────────────┘

Key technical details

  • Target: x86-freestanding-none — 32-bit, no OS, no libc
  • Boot protocol: Multiboot 1 — a 12-byte header (magic 0x1BADB002, flags, checksum) placed in the first 8 KiB of the ELF
  • VGA output: Direct memory-mapped I/O to 0xB8000 using Zig's volatile pointer semantics — no drivers, no BIOS calls
  • Red zone: Disabled — the System V ABI red zone would be corrupted by hardware interrupts
  • SSE/AVX: Disabled — avoids the need to save/restore FPU state
  • No assembly files: The Multiboot header is a Zig extern struct exported to a linker section; the entry point uses inline asm volatile