Creating a boot ramdisk

A Linux boot ramdisk, strictly speaking, an initial RAM filesystem or initramfs, is a compressed cpio archive. cpio is an old Unix archive format, similar to TAR and ZIP but easier to decode and so requiring less code in the kernel. You need to configure your kernel with CONFIG_BLK_DEV_INITRD to support initramfs.

In fact, there are three different ways to create a boot ramdisk: as a standalone cpio archive, as a cpio archive embedded in the kernel image, and as a device table which the kernel build system processes as part of the build. The first option gives the most flexibility because we can mix and match kernels and ramdisks to our hearts content. However, it means that you have two files to deal with instead of one and not all bootloaders have the facility to load a separate ramdisk. I will show you how to build one into the kernel later.

The following sequence of instructions creates the archive, compresses it and adds a U-Boot header ready for loading onto the target:

Note that we ran cpio with the option --owner root:root. This is a quick fix for the file ownership problem mentioned earlier, making everything in the cpio file UID and GID 0.

The final size of the uRamdisk file is ~ 2.9 MiB, with no kernel modules. Add to that 4.4 MiB for the kernel zImage file, and 440 KiB for U-Boot and this gives a total of 7.7 MiB of storage needed to boot this board. We are a little way off the 1.44 MiB floppy that started it all off. If size was a real problem, you could use one of these options:

The simplest thing we can do is to run a shell on the console so that we can interact with the device. We can do that by adding rdinit=/bin/sh to the kernel command line. Now, you can boot the device.

In some cases, it is preferable to build the ramdisk into the kernel image, for example, if the bootloader cannot handle a ramdisk file. To do this, change the kernel configuration and set CONFIG_INITRAMFS_SOURCE to the full path of the cpio archive you created earlier. If you are using menuconfig, it is in General setup | Initramfs source file(s). Note that it has to be the uncompressed cpio file ending in .cpio; not the gzipped version. Then, build the kernel. You should see that it is larger than before.

Booting is the same as before, except that there is no ramdisk file. For QEMU, the command is like this:

For the BeagleBone Black, enter these commands into U-Boot:

Of course, you must remember to rebuild the kernel each time you change the contents of the ramdisk and regenerate the .cpio file.

An interesting way to build the ramdisk into the kernel image is by using a device table to generate a cpio archive. A device table is a text file which lists the files, directories, device nodes, and links that go into the archive. The overwhelming advantage is that you can create entries in the cpio file that are owned by root, or any other UID, without having root privileges yourself. You can even create device nodes. All this is possible because the archive is just a data file. It is only when it is expanded by Linux at boot time that real files and directories get created, using the attributes you have specified.

Here is a device table for our simple rootfs, but missing most of the symbolic links to busybox to make it manageable:

The syntax is fairly obvious:

The kernel provides a tool that reads this file and creates a cpio archive. The source is in usr/gen_init_cpio.c. There is a handy script in scripts/gen_initramfs_list.sh that creates a device table from a given directory, which saves a lot of typing.

To complete, the task, you need to set CONFIG_INITRAMFS_SOURCE to point to the device table file and then build the kernel. Everything else is the same as before.

There is an older format for a Linux ramdisk, known as initrd. It was the only format available before Linux 2.6 and is still needed if you are using the mmu-less variant of Linux, uCLinux. It is pretty obscure and I will not cover it here. There is more information in the kernel source, in Documentation/initrd.txt.