LIBRENEITOR

How to create an initramfs after you compile a linux kernel

Thu, Jul 4, 2019

What it is the initramfs? How can you build it?

As a summary 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 gziped cpio file that contains the initial file system that the kernel needs to run. And that includes the /init script.

I strongly recommend you to read this two files from the Linux kernel source code, even if you don’t fully understand them:

What 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.

Other thing I realized is that you need to build your initramfs your own. 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 command I listed before while talking about BusyBox. Also it creates the basic file systems so we can start playing around.

gen_init_cpio

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 an 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.