How to Embed an Initramfs Into the Kernel-Embedded Linux Course In Hyderabad

PREV_LEC | NEXT_LEC

Embed an Initramfs Into the Kernel
A lecture in EmbeddedPathashala’s free Linux kernel development course — baking a root filesystem directly into the kernel image, and building reproducible archives with a device table.
Free Linux Kernel Development Course
Chapter 5: Root Filesystem
Lecture 11

In the previous lecture of this free embedded Linux course, we built a standalone initramfs and handed it to the bootloader as a separate file. That works well when your bootloader supports loading a second image — but plenty of minimal or legacy bootloaders can only load one file: the kernel itself. This lecture covers the fix: baking the initramfs directly into the kernel image, plus a more reproducible way to build that archive using a device table instead of a live rootfs directory.

CONFIG_INITRAMFS_SOURCE
embedded initramfs
device table
gen_init_cpio
free linux kernel development course
free linux device drivers course

What You Will Learn

Configuring CONFIG_INITRAMFS_SOURCE
Booting a kernel with an embedded initramfs
Why a device table beats a live rootfs directory for reproducibility
The device table syntax: dir, file, nod, slink
Generating device nodes without root privileges
Where the legacy initrd format still applies

Prerequisites

A working standalone initramfs from the previous lecture
A kernel source tree you can configure and rebuild
Familiarity with menuconfig

Why Embed the Initramfs at All

A standalone archive is the fastest thing to iterate on, but it has one real limitation: it depends on the bootloader being able to load and pass a second file to the kernel. Some minimal bootloaders, some secure-boot chains, and some legacy boot ROMs simply don’t support that. Embedding the initramfs solves this by letting the kernel build process fold the cpio archive into the kernel image itself — one file, one load, no second image to manage.

The trade-off is iteration speed: any change to the rootfs now requires a kernel rebuild, not just a fresh cpio archive.

Standalone vs Embedded Initramfs
Standalone: Bootloader loads Kernel + cpio separately
Embedded: Bootloader loads one Kernel Image (cpio baked inside)

Configuring CONFIG_INITRAMFS_SOURCE

The kernel build system can pull in an initramfs source at build time through a single Kconfig option. In menuconfig, it lives under:

General setup ---> Initramfs source file(s)

Set it to the full path of your uncompressed cpio archive — the .cpio file, not the .cpio.gz. The kernel build handles compression itself:

$ cd ~/ep-rootfs
$ find . | cpio -H newc -ov --owner root:root > ../ep-initramfs.cpio
$ cd ~/linux
$ scripts/config --set-str CONFIG_INITRAMFS_SOURCE "/home/you/ep-initramfs.cpio"
$ make -j$(nproc) Image

You’ll notice the resulting kernel Image is measurably larger than a build with no embedded initramfs — that size difference is exactly your rootfs, now living inside the kernel binary.

Booting an Embedded-Initramfs Kernel

Booting is nearly identical to the standalone case, except there’s no separate ramdisk file to load. Under QEMU:

$ qemu-system-aarch64 -M virt -cpu cortex-a72 -m 512M -nographic \
  -kernel Image \
  -append "console=ttyAMA0 rdinit=/bin/sh"

Notice the -initrd flag is simply gone — the kernel Image already contains everything it needs. On a U-Boot-based board, the equivalent boot sequence drops the ramdisk load and address entirely:

=> fatload mmc 0:1 0x40200000 Image
=> fatload mmc 0:1 0x43000000 ep-board.dtb
=> setenv bootargs console=ttyS0,115200 rdinit=/bin/sh
=> booti 0x40200000 - 0x43000000

Remember: every time the rootfs content changes, you must regenerate the .cpio file and rebuild the kernel. There’s no shortcut here — that’s the price of embedding.

A Better Way to Build the Archive: Device Tables

So far we’ve built the cpio archive from a live directory tree on our build host. That works, but it has an awkward dependency: creating device nodes with mknod, or making files owned by root, normally requires root privileges on your build machine. A device table solves this elegantly — it’s a plain text file that describes the archive’s contents (files, directories, symlinks, device nodes) without your build host ever needing to actually create those entries with those permissions.

The kernel provides a small C tool, usr/gen_init_cpio.c, that reads a device table and emits a cpio archive directly — as an unprivileged user, entries can carry any UID, GID, or device major/minor number you specify in the table, because the archive is just a data file until Linux expands it at boot.

Device Table Syntax

Entry type Syntax Meaning
dir dir <name> <mode> <uid> <gid> Create a directory
file file <name> <location> <mode> <uid> <gid> Copy a file from the build host into the archive at the given path
nod nod <name> <mode> <uid> <gid> <dev_type> <maj> <min> Create a character or block device node
slink slink <name> <target> <mode> <uid> <gid> Create a symbolic link

Here’s an original, minimal device table for an EmbeddedPathashala demo rootfs — small enough to read in one pass, deliberately leaving out the full set of BusyBox applet symlinks:

dir /proc 0755 0 0
dir /sys 0755 0 0
dir /dev 0755 0 0
nod /dev/console 0600 0 0 c 5 1
nod /dev/null 0666 0 0 c 1 3
nod /dev/ttyAMA0 0600 0 0 c 204 64
dir /bin 0755 0 0
file /bin/busybox /home/you/ep-rootfs/bin/busybox 0755 0 0
slink /bin/sh /bin/busybox 0777 0 0
dir /ep_init 0755 0 0
file /ep_init/ep_init.sh /home/you/ep-rootfs/ep_init.sh 0755 0 0

Building the archive from this table is a single invocation of the kernel’s generator tool:

$ cd ~/linux
$ usr/gen_init_cpio ep-device-table.txt > ../ep-initramfs.cpio

If typing every entry by hand feels tedious, the kernel source also ships a helper script that reverse-engineers a device table from an existing directory tree:

$ scripts/gen_initramfs_list.sh ~/ep-rootfs > ep-device-table.txt

From here, the rest of the workflow is identical to the CONFIG_INITRAMFS_SOURCE steps above: point the Kconfig option at your generated .cpio (or directly at the device table file — the build system accepts either), then build the kernel.

Why This Matters for Reproducible Builds

Device tables turn your initramfs contents into something version-controllable and reviewable in a pull request, the same way you’d review any other source file — no more “works on my machine because my UID happens to be right.” This is exactly the kind of discipline that separates a one-off hack from a CI-friendly embedded Linux build pipeline, which is why it’s worth internalizing early in any free linux device drivers course.

A Note on the Legacy initrd Format

You may occasionally see references to an older ramdisk mechanism called initrd, distinct from initramfs. initrd was the only mechanism available before Linux 2.6, works by loading a block-device image rather than a cpio stream, and today matters almost exclusively for MMU-less kernel variants such as uCLinux. Unless you’re specifically targeting that class of hardware, initramfs is the correct and modern choice, and this course won’t cover initrd further.

Common Mistakes and Troubleshooting

Symptom Likely Cause Fix
CONFIG_INITRAMFS_SOURCE build fails silently Pointed at the .gz file instead of the raw .cpio Always point the Kconfig option at the uncompressed archive
Kernel Image size didn’t change after rebuild Kconfig option wasn’t actually picked up (stale .config) Re-run make olddefconfig or check with scripts/config --state
gen_init_cpio can’t find a file entry The <location> path in the device table is wrong or relative Use an absolute path to the file on your build host
Device node permissions look wrong after boot Wrong major/minor number in the nod entry Cross-check against Documentation/admin-guide/devices.txt or your driver’s registration code

Best Practices

Use standalone initramfs during active rootfs development
Switch to embedded initramfs only once the rootfs content is stable
Prefer a device table over a live directory for anything going into version control
Never hand-edit UIDs/GIDs directly on your build host as root just to avoid a device table
Keep gen_initramfs_list.sh output as a starting point, then hand-review it

Summary and Key Takeaways

Embedding an initramfs into the kernel image trades iteration speed for boot simplicity — one file instead of two, which matters when your bootloader can only load a single image. CONFIG_INITRAMFS_SOURCE is the Kconfig hook that pulls an uncompressed cpio archive into the kernel build. Device tables give you a reproducible, unprivileged, version-controllable way to describe that archive’s contents, using the kernel’s own gen_init_cpio tool. The older initrd format is now a historical footnote relevant mainly to MMU-less Linux. Together with the previous lecture’s standalone workflow, you now have both major approaches this free embedded systems course covers for getting a root filesystem in front of a booting kernel.

Frequently Asked Questions

Do I point CONFIG_INITRAMFS_SOURCE at the compressed or uncompressed cpio?

Always the uncompressed .cpio file. The kernel build system compresses it itself as part of building the kernel image.

Can CONFIG_INITRAMFS_SOURCE point directly at a device table instead of a cpio file?

Yes. The kernel build system accepts a device table file directly as the initramfs source, or a directory, or a pre-built cpio archive — it detects which one you’ve given it.

Why use a device table instead of just building the cpio from a directory?

A device table lets you specify ownership and device nodes without needing root privileges on your build host, and it’s plain text, so it’s easy to review and version-control.

What tool actually turns a device table into a cpio archive?

The kernel’s own gen_init_cpio tool, built from usr/gen_init_cpio.c in the kernel source tree.

Is there a shortcut to generate a device table from an existing rootfs?

Yes, the kernel ships scripts/gen_initramfs_list.sh, which inspects a directory tree and emits an equivalent device table you can then hand-edit.

Do I still need to know about the old initrd format?

Only if you’re working with MMU-less Linux variants like uCLinux. For virtually all modern embedded Linux work, initramfs has fully replaced it.

 

Continue the Free Linux Kernel Development Course

With both initramfs workflows covered, the next lectures move into real init systems and service management.

PREV_LEC | NEXT_LEC

Leave a Reply

Your email address will not be published. Required fields are marked *