Roll Your Own Linux, I guess.
Find a file
2025-12-26 04:40:52 +00:00
bootstrap initial 2025-12-25 22:57:16 -05:00
libodin initial 2025-12-25 22:57:16 -05:00
minit initial 2025-12-25 22:57:16 -05:00
staging Remove unused file from staging tree 2025-12-26 04:40:52 +00:00
.gitignore initial 2025-12-25 22:57:16 -05:00
Makefile initial 2025-12-25 22:57:16 -05:00
README.md Update README.md 2025-12-26 04:30:46 +00:00

This repository contains the fruits of my decision to explore Linux in a barebones environment. It exists here primarily for redundancy, but you're more than welcome to poke around with it.

Here are the big moving parts. I'll briefly cover each in its own section.

  • the Linux kernel: I'm using 6.12.63 (LTS) as of the creation of this README; not included in the repo due to size
  • qemu-system-x86_64: probably available from your package manager; see Makefile for flags used
  • bootstrap: a basic init program for the initramfs environment
  • minit: a minimal PID 1 init that's started after switching to the persistent rootfs
  • toybox: modern busybox; it does a lot of heavy lifting right now by way of providing shell utilities
  • libodin: a WIP quasi-libc thing; mostly for convenience procs/wrappers I want available across programs
  • musl: used to avoid shipping glibc into the image
  • root.img: not included due to size; 2GB image file with an ext4 filesystem on it; passed to qemu as /dev/sda

Linux LTS Kernel

As stated above, I'm using 6.12.63 as it was the latest LTS at the time of writing. If you want to build this project yourself, it expects the kernel image to live at $PROJECT_ROOT/linux-6.12.63/arch/x86_64/boot/bzImage, or you can adjust the path in the Makefile accordingly. My version is built with the default options. Nothing fancy here.


bootstrap

Bootstrap's job is to serve as a sort of "pre-init" while the system is still operating from the initial ramdisk image. It starts by mounting several special filesystems such as devtmpfs, proc, and sysfs. After that it mounts the root filesystem on /dev/sda1 to /mnt/newroot and copies directories from the ramdisk to the new root, skipping any existing files. Once that's done, it performs execve to replace itself with toybox's switch_root command. This pivots the root filesystem from the ram disk to /dev/sda1.


minit

This is the "real init", aka PID 1. It mounts devpts, then enters a loop. It forks and opens tty1 before spawning a bash shell attached to it. It then loops forever and reaps any orphaned PIDs. There is plumbing there for graceful reboots but this does not yet work inside this environment.


toybox

Toybox is a BusyBox alternative. Mostly chosen because when I tried to go get BusyBox, the site wouldn't load. You can read more about it here.


libodin

I'd like to eventually build libodin out into a libc replacement for Odin, but that is currently outside the scope of this project. For now its job is to provide reusable procs for various system tasks such as mounting a filesystem, reading the mounts table, or opening a tty.


musl

It's musl. Not much to say here. Find it here.


root.img

The virtual hard disk I'm using was created manually (because learning is fun). Below is a rough overview (not a tutorial) of the steps I used to create it.

  • fallocate to create the empty 2GB file
  • fdisk to partition the file
  • fdisk again to find the byte offset of the start of the first partition
  • losetup to attach that partition (unmounted) as a loop device
  • mkfs.ext4 to format the partition
  • losetup again to detach the loop device

After that, you have essentially a bare hard disk image that can be passed to qemu.