Boot a Linux Kernel Without GRUB (GenesisOS Tutorial)
8/14/2025 • devlog
Most Linux guides assume you need GRUB as a bootloader. You don’t. If your kernel is built with the EFI stub, you can boot it directly with systemd‑boot.
This tutorial shows how to set up a GRUB‑less boot for your own OS: place a kernel + initramfs on an EFI System Partition (ESP), add a systemd‑boot entry, and run it in QEMU. For GenesisOS, I generated the kernel and initramfs with Buildroot, but any toolchain works (distro kernel, Yocto, hand‑rolled, etc.).
1) What you need
- A Linux kernel compiled with
CONFIG_EFI_STUB=y(and your usual options). - An initramfs (cpio archive, often compressed like
.gz/.xz). - An EFI System Partition (ESP) (FAT32).
- systemd‑boot (tiny EFI boot manager).
- OVMF (UEFI firmware) and QEMU (for testing).
If you use Buildroot (like GenesisOS): you’ll get
bzImageandrootfs.cpio.gzinoutput/images/.
2) Create/prepare an ESP
Make a small FAT32 partition (e.g., 128–256MB) and mount it somewhere (e.g., /mnt/esp). Your final ESP must have this structure:
/EFI/BOOT/BOOTX64.EFI # systemd-boot binary
/loader/loader.conf # global config
/loader/entries/your-os.conf # boot entry
/bzImage # kernel (any filename is okay)
/rootfs.cpio.gz # initramfs (any filename is okay)
Install systemd‑boot into the ESP (from a Linux host with systemd):
sudo bootctl --path=/mnt/esp install
Create the loader config:
# /mnt/esp/loader/loader.conf
default your-os
timeout 3
console-mode max
Create the boot entry:
# /mnt/esp/loader/entries/your-os.conf
title Your OS
linux /bzImage
initrd /rootfs.cpio.gz
options root=/dev/vda2 rw quiet
The
linuxandinitrdpaths are relative to the ESP root. Name them however you like; just keep the entry consistent.
3) Compile the kernel with EFI stub
Ensure the kernel has:
CONFIG_EFI=yCONFIG_EFI_STUB=y- Your usual drivers (storage, framebuffer/GPU, etc.)
If you’re using Buildroot (GenesisOS does): set kernel + cpio rootfs in your defconfig. After make, you’ll have bzImage and rootfs.cpio.gz ready to copy to the ESP.
4) (Optional) Automate ESP staging during your build
Rather than copying by hand, you can generate the ESP layout during your build. Here’s a post‑build script that assumes Buildroot variables; adapt paths if you’re not using Buildroot:
#!/bin/sh
# post-build.sh
set -e
ESP_DIR="$TARGET_DIR/boot/efi"
mkdir -p "$ESP_DIR/EFI/BOOT" "$ESP_DIR/loader/entries"
# Copy artifacts (adjust if you don't use Buildroot)
cp "$BINARIES_DIR/bzImage" "$ESP_DIR/bzImage"
cp "$BINARIES_DIR/rootfs.cpio.gz" "$ESP_DIR/rootfs.cpio.gz"
# Install systemd-boot if available on the build host/runtime
if command -v bootctl >/dev/null 2>&1; then
bootctl --path="$ESP_DIR" install
fi
# Configs
cat > "$ESP_DIR/loader/loader.conf" <<EOF
default your-os
timeout 3
console-mode max
EOF
cat > "$ESP_DIR/loader/entries/your-os.conf" <<'EOF'
title Your OS
linux /bzImage
initrd /rootfs.cpio.gz
options root=/dev/vda2 rw quiet
EOF
Hook it up (Buildroot example):
# external/external.mk
BR2_ROOTFS_POST_BUILD_SCRIPT += $(BR2_EXTERNAL_MYOS_PATH)/post-build.sh
If later build steps overwrite files, add a post‑image hook and re‑apply changes after images are finalized.
5) Boot in QEMU (UEFI)
Use OVMF for UEFI and pass a disk image that contains your ESP + rootfs partitions (or an ESP‑only image for early testing):
qemu-system-x86_64
-machine q35,accel=kvm
-m 2G
-cpu host
-bios /usr/share/OVMF/OVMF_CODE.fd
-drive file=disk.img,if=virtio,format=raw
-device virtio-vga-gl
-display sdl,gl=on
-serial mon:stdio
Notes:
OVMF_CODE.fdsupplies UEFI firmware so systemd‑boot can run.virtio-vga-gl+-display sdl,gl=onenables KMS/modesetting in the guest.- If
virtio-vga-glisn’t available, try:-device virtio-gpu-pci,virgl=on -display gtk,gl=on
You should see systemd‑boot → your entry → kernel runs with the initramfs.
6) Troubleshooting
Black screen / stuck at VGA
• Ensure you used a virtio GPU device and enabled GL.
• Verify kernel has DRM/KMS drivers (e.g., CONFIG_DRM_VIRTIO_GPU).
systemd‑boot doesn’t list my entry
• Check ESP paths and case sensitivity.
• Ensure /loader/entries/*.conf exists and has title + linux lines.
Kernel not launching (‘Unsupported’)
• Your kernel likely lacks CONFIG_EFI_STUB=y. Rebuild with EFI stub.
Modules not found after boot
• Run depmod at the right stage (post‑image), or embed needed drivers in the kernel until your userspace is ready to run depmod.
Wrong initrd path
• Paths are relative to ESP root. If you nest files, update the entry accordingly.
7) Why skip GRUB?
- Simplicity: UEFI → systemd‑boot → EFI‑stub kernel.
- Reproducibility: All artifacts generated by your build; fewer moving parts.
- Speed: Minimal bootloader logic; straight into your kernel.
8) Summary checklist
- Kernel compiled with
CONFIG_EFI_STUB=y - Initramfs built (cpio, compressed ok)
- FAT32 ESP with systemd‑boot installed (
bootctl --path=... install) -
/loader/loader.conf+/loader/entries/*.confcreated -
linux=/path/to/kernelandinitrd=/path/to/initramfscorrect - QEMU launched with OVMF and a modern GPU device
GenesisOS notes (what I used)
- Artifacts produced by Buildroot:
bzImage,rootfs.cpio.gz. - ESP staged automatically via a post‑build hook.
- QEMU GPU:
virtio-vga-glfor modesetting + VirGL. - No GRUB anywhere in the boot path.
Happy booting — and welcome to the GRUB‑less club.