How to create an initramfs after you compile a linux kernel
You build an initramfs
by putting all the OS-required files in a folder, you convert everything in there to a single .cpio
, and finally you gzip
that. From your perspective, the initramfs
is a gzip
ed cpio
file that contains the initial file system that the kernel needs to run. And that includes the /init
script.
I strongly recommend you read these two files from the Linux kernel source code, even if you don’t fully understand them:
Documentation/early-user-space/README
: Why do we need an early user space, what it is, and the different tools to compile it with the kernel.Documentation/filesystems/ramfs-rootfs-initramfs.txt
: Explanation of the ram filesystems you will be using while booting.
I wanted to create an initramfs
with everything I could need for testing my kernel with qemu
without needing to recompile the kernel. For this reason, I didn’t change any option while compiling the kernel.
When we first boot, we need at least some tools to start working. This includes the init
process and some tools like ls
, mount
, mv
, etc. To get those user-space tools you can use BusyBox. BusyBox has many useful commands available for just 1.1MB:
acpid, add-shell, addgroup, adduser, adjtimex, arch, arp, arping, ash, awk,
base64, basename, bc, beep, blkdiscard, blkid, blockdev, bootchartd, brctl,
bunzip2, bzcat, bzip2, cal, cat, chat, chattr, chgrp, chmod, chown, chpasswd,
chpst, chroot, chrt, chvt, cksum, clear, cmp, comm, conspy, cp, cpio, crond,
crontab, cryptpw, cttyhack, cut, date, dc, dd, deallocvt, delgroup, deluser,
depmod, devmem, df, dhcprelay, diff, dirname, dmesg, dnsd, dnsdomainname,
dos2unix, dpkg, dpkg-deb, du, dumpkmap, dumpleases, echo, ed, egrep, eject,
env, envdir, envuidgid, ether-wake, expand, expr, factor, fakeidentd,
fallocate, false, fatattr, fbset, fbsplash, fdflush, fdformat, fdisk,
fgconsole, fgrep, find, findfs, flock, fold, free, freeramdisk, fsck,
fsck.minix, fsfreeze, fstrim, fsync, ftpd, ftpget, ftpput, fuser, getopt,
getty, grep, groups, gunzip, gzip, halt, hd, hdparm, head, hexdump, hexedit,
hostid, hostname, httpd, hush, hwclock, i2cdetect, i2cdump, i2cget, i2cset,
i2ctransfer, id, ifconfig, ifdown, ifenslave, ifplugd, ifup, inetd, init,
insmod, install, ionice, iostat, ip, ipaddr, ipcalc, ipcrm, ipcs, iplink,
ipneigh, iproute, iprule, iptunnel, kbd_mode, kill, killall, killall5, klogd,
last, less, link, linux32, linux64, linuxrc, ln, loadfont, loadkmap, logger,
login, logname, logread, losetup, lpd, lpq, lpr, ls, lsattr, lsmod, lsof,
lspci, lsscsi, lsusb, lzcat, lzma, lzop, makedevs, makemime, man, md5sum,
mdev, mesg, microcom, mkdir, mkdosfs, mke2fs, mkfifo, mkfs.ext2, mkfs.minix,
mkfs.vfat, mknod, mkpasswd, mkswap, mktemp, modinfo, modprobe, more, mount,
mountpoint, mpstat, mt, mv, nameif, nanddump, nandwrite, nbd-client, nc,
netstat, nice, nl, nmeter, nohup, nologin, nproc, nsenter, nslookup, ntpd,
nuke, od, openvt, partprobe, passwd, paste, patch, pgrep, pidof, ping, ping6,
pipe_progress, pivot_root, pkill, pmap, popmaildir, poweroff, powertop,
printenv, printf, ps, pscan, pstree, pwd, pwdx, raidautorun, rdate, rdev,
readahead, readlink, readprofile, realpath, reboot, reformime, remove-shell,
renice, reset, resize, resume, rev, rm, rmdir, rmmod,route, rpm, rpm2cpio,
rtcwake, run-init, run-parts, runlevel, runsv, runsvdir, rx, script,
scriptreplay, sed, sendmail, seq, setarch, setconsole, setfattr, setfont,
setkeycodes, setlogcons, setpriv, setserial, setsid, setuidgid, sh, sha1sum,
sha256sum, sha3sum, sha512sum, showkey, shred, shuf, slattach, sleep, smemcap,
softlimit, sort, split, ssl_client, start-stop-daemon, stat, strings, stty,
su, sulogin, sum, sv, svc, svlogd, svok, swapoff, swapon, switch_root, sync,
sysctl, syslogd, tac, tail, tar, taskset, tc, tcpsvd, tee, telnet, telnetd,
test, tftp, tftpd, time, timeout, top, touch, tr, traceroute, traceroute6,
true, truncate, ts, tty, ttysize, tunctl, ubiattach, ubidetach, ubimkvol,
ubirename, ubirmvol, ubirsvol, ubiupdatevol, udhcpc, udhcpc6, udhcpd, udpsvd,
uevent, umount, uname, unexpand, uniq, unix2dos, unlink, unlzma,unshare,
unxz, unzip, uptime, users, usleep, uudecode, uuencode, vconfig, vi, vlock,
volname, w, wall, watch, watchdog, wc, wget, which, who, whoami, whois,
xargs, xxd, xz, xzcat, yes, zcat, zcip
I’m listing all those programs because for some reason in the past I thought that all those basic programs would be available for me after compiling the kernel via some sort of magic. But they are not. You need to compile/download them first separately.
Another thing I realized is that you need to build your own initramfs
. I mean, you need to choose which software you want when your kernel starts. I used this script to create my initramfs
:
#!/bin/bash
ARCH="x86_64"
BB_VER="1.31.0"
# Dirs
mkdir -p root
cd root
mkdir -p bin dev etc lib mnt proc sbin sys tmp var
cd -
# Utils
if [ ! -f "root/bin/busybox" ]; then
curl -L "https://www.busybox.net/downloads/binaries/${BB_VER}-defconfig-multiarch-musl/busybox-${ARCH}" >root/bin/busybox
fi
cd root/bin
chmod +x busybox
ln -s busybox mount
ln -s busybox sh
cd -
# Init process
cat >>root/init << EOF
#!/bin/busybox sh
/bin/busybox --install -s /bin
mount -t devtmpfs devtmpfs /dev
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t tmpfs tmpfs /tmp
setsid cttyhack sh
exec /bin/sh
EOF
chmod +x root/init
# initramfs creation
cd root
find . | cpio -ov --format=newc | gzip -9 >../initramfs
cd -
This script will output an initramfs
containing an /init
bash script, some directories, and the BusyBox binary. The basic directories are created bin
, dev
, etc
, lib
, mnt
, proc
, sbin
, sys
, tmp
, var
.
When BusyBox is downloaded I created also two symbolic links for mount
and sh
, all the commands that I will be using in the init
script. The init
script installs all the others required symbolic links for all the commands I listed before while talking about BusyBox. Also, it creates the basic file systems so we can start playing around.
In my case, I used a simple bash
script, but you may want to use user/gen_init_cpio
(is in the linux kernel source tree) for that. It’s an already-made script for creating initramfs
from a file that describes the file system structure. It can be called like this:
$ usr/gen_init_cpio descriptor | gzip >initramfs
And the descriptor may look like this:
file /init my-init.sh 07555 0 0
dir /bin 0755 0 0
nod /dev/zero 0666 0 0 c 1 5
file /bin/busybox /bin/busybox 0755 0 0
To learn more about the syntax just call the command without any arguments: usr/gen_init_cpio
.