How initramfs works
The basic initramfs is the root filesystem image used for booting the kernel provided as a compressed cpio archive.
This basic initramfs image may be prepended with an uncompressed cpio archive holding the microcode data loaded very early in the boot process.
At boot time, the kernel performs the followings:
- If an uncompressed cpio archive exists at the start of the initramfs, extract and load the microcode from it to CPU.
- If an uncompressed cpio archive exists at the start of the initramfs, skip that and set the rest of file as the basic initramfs. Otherwise, treat the whole initramfs as the basic initramfs.
- unpack the basic initramfs by treating it as compressed (gzip by default on Debian, LZ4 on Ubuntu) cpio archive into a RAM-based disk.
- mount and use the RAM-based disk as the initial root filesystem.
Much of the kernel initialization and bootstrap code can then be moved into this disk and run in user mode. Tasks like finding the real root disk, boot-time networking setup, handling of initrd-style ramdisks, ACPI setup, etc, will be shifted out of the kernel in this way.
An obvious advantage of this scheme is that the size of the kernel code itself can shrink. That does not free memory for a running system, since the Linux kernel already dumps initialization code when it is no longer needed. But a smaller code base for the kernel itself makes the whole thing a little easier to maintain, and that is always a good thing. But the real advantages of initramfs are:
- Customizing the early boot process becomes much easier. Anybody who needs to change how the system boot can now do so with user-space code; patching the kernel itself will no longer be required.
- Moving the initialization code into user space makes it easier to write that code - it has a full C library, memory protection, etc.
- user-space code is required to deal with the kernel via system calls. This requirement will flush a lot of in-kernel "magic" currently used by the initialization code; the result will be cleaner, safer code.
- A small C library ("klibc") will be merged to support initramfs applications.
- A small kinit application will be created with klibc. In the beginning, it will only do enough work to show that the mechanism is functioning properly.
- The Initrd (initial ramdisk) subsystem will be moved into kinit, and out of the kernel itself.
- The mounting of the root filesystem will be moved to user space. A lot of code for dealing with things like NFS-mounted root filesystems will go away.
The kernel has currently 3 ways to mount the root filesystem:
- all required device and filesystem drivers compiled into the kernel, no initrd. init/main.c:init() will call prepare_namespace() to mount the final root filesystem, based on the root= option and optional init= to run some other init binary than listed at the end of init/main.c:init().
some device and filesystem drivers built as modules and stored in an Initrd. The initrd must contain a binary '/linuxrc' which is supposed to load these driver modules. It is also possible to mount the final root filesystem via linuxrc and use the pivot_root syscall. The initrd is mounted and executed via prepare_namespace().
- using initramfs. The call to prepare_namespace() must be skipped. This means that a binary must do all the work. Said binary can be stored into initramfs either via modifying usr/gen_init_cpio.c or via the new initrd format, an cpio archive. It must be called "/init". This binary is responsible to do all the things prepare_namespace() would do.
To remain backwards compatibility, the /init binary will only run if it comes via an initramfs cpio archive. If this is not the case, init/main.c:init() will run prepare_namespace() to mount the final root and exec one of the predefined init binaries
How to inspect initramfs
The initramfs-tools-core package provides lsinitramfs to list files inside the initramfs and unmkinitramfs to extract files from the initramfs.
Alternatively, you can do the following (assuming path/to/initrd already exists as empty working directory):
If the output of file -L /initrd.img is ASCII cpio archive (SVR4 with no CRC), consider the initrd image to have microcode prepended.
extract the microcode by cd path/to/initrd/; cpio -i </initrd.img and read the STDERR display (in my case 48).
extract the basic initrd by cd path/to/initrad/; dd if=/initrd.img of=initrd.img bs=512 skip=48; zcat initrd.img |cpio -i (adjust skip values matching the cpio STDERR output).
Otherwise, extract the initrd directly by cd path/to/initrad/; zcat /initrd.img |cpio -i
The microcode cpio archive size can be independently checked by cpio -t </initrd.img >/dev/null
(This manual method somehow worked for me at one point. But this is not reliable. /usr/bin/lsinitramfs has a comment "cpio won't tell us the true size".)
Please note Debian currently use gzip as the compression method and the above method assumes so. Ubuntu seems to start using LZ4 as of March 2018. initramfs-tools in Debian supports LZ4 since Debian buster (893845).
The initramfs chapter in the Debian Kernel Handbook
Ramfs, rootfs and initramfs section of the Linux kernel documentation