initramfs
The initramfs is a complete set of directories that you would find on a normal root filesystem embedded into the Linux kernel. It is bundled into a single cpio archive and compressed with one of several compression algorithms. At boot time, the boot loader loads the kernel and the initramfs image into memory and starts the kerne. If a kernel has been built to include an initramfs it mounts that instead and ignores the 'root=' kernel parameter.
Using a small root filesystem via initramfs is a great way to do Linux kernel development or work with a system that does not yet have any via-able storage available. See buildroot for an example of using a small ~1.5MiB buildroot rootfs attached to a kernel.
Note that the Linux kernel runs /init
, /sbin/init
, or /bin/sh
(searched in that order) as PID1 from the initramfs (if it exists). If you want to run a different executable as PID1 you can use the init=
kernel parameter. The init application must be executable and statically linked or it will move on to the next item in the search path.
The word 'initramfs' and 'initrd' can be used interchangeably here.
see also:
external initramfs
This is a ramdisk image (cpio, or compressed cpio if you have enabled ramdisk compression in your kernel) that you pass to the kernel when booting. If doing this with U-Boot its the 2nd (middle) arg to bootm/booti/bootz.
To support this you need the following kernel config:
- CONFIG_INITRAMFS_SOURCE="" (No source defined!)
- CONFIG_INITRAMFS_COMPRESSION_GZIP=y (optional) - or some other compression algorithm
- CONFIG_DEVTMPFS=y (optional) - to get devtmpfs support and provide a dynamic /dev
Note that bootm
expects a uImage and uramdisk which have U-Boot wrappers (created with the mkimage
tool from the u-boot-tools
package) around the images. U-Boot must have CONFIG_LEGACY_IMAGE_FORMAT enabled for this. Note that arm64 does not support compressed kernel Images so you must use booti for arm64 (or bootm with a FIT image).
Note that booti
requires passing the size of the ramdisk by specifying <rdaddr>:<filesize and U-Boot must have CONFIG_SUPPORT_RAW_INITRD enabled. You can also use a uramdisk with booti as long as you have CONFIG_LEGACY_IMAGE_FORMAT enabled.
The benefit of using an external initramfs is that you do not have to rebuild the kernel to update the root filesystem.
Many Linux based Operating Systems using an initramfs as a shim to load kernel modules that are required to mount the root filesystem allowing them to use kernels that are very small and modular. Android and Ubuntu are two very popular Linux based Operating Systems that use this technique.
Example booting with an initramfs:
- Venice: IMX8MM GW7300-00:
tftpboot $fdt_addr_r venice/imx8mm-venice-gw73xx-0x && \ tftpboot $kernel_addr_r venice/Image && \ tftpboot $ramdisk_addr_r venice/cpio && \ booti $kernel_addr_r $ramdisk_addr_r:$filesize $fdt_addr_r
Note that the ramdisk was loaded last so that $filesize represents it's size as compared to the other two objects loaded
internal initramfs
An internal initramfs is when you embed an initramfs into the kernel via CONFIG_INITRAMFS_SOURCE
which can point to a directory path, an existing cpio.gz, or a text file with some directives.
Note that your bootloader (ie U-Boot) may have a limit on the size of a kernel. For U-Boot this is defined by CONFIG_SYS_BOOTM_LEN which you may need to increase if either the static kernel or the initramfs gets too large.
To build a kernel with an initramfs configure the following kernel items:
- CONFIG_INITRAMFS_SOURCE="/path/to/rootfs.cpio"
- CONFIG_INITRAMFS_COMPRESSION_GZIP=y (optional) - or some other compression algorithm
- CONFIG_DEVTMPFS=y (optional) - to get devtmpfs support and provide a dynamic /dev
Building an initramfs
There are several ways to create an initramfs
Using an initramfs_list config file
Here is a simple example of building an initramfs with a stand-alone application and some basic dev nodes as the only components in the rootfs using an initramfs_list conifg file and the Linux kernels gen_initramfs_list.sh
script. This assumes you have a CROSS_COMPILE environment setup and a built linux kernel:
- cd to your built Linux directory (where the gen_initramfs.sh script needs to run from as it uses some built objects):
cd venice/bsp . ./setup-environment # setup your cross toolchain cd linux
- create a simple application that is statically linked such that it has no dependencies:
cat << EOF > hello.c #include <stdio.h> #include <unistd.h> int main(int argc, char *argv[]) { printf("Hello world!\n"); sleep(999999999); } EOF # cross compile it static so it has no runtime dependencies ${CROSS_COMPILE}gcc -static hello.c -o hello
- create an initramfs template and use the Linux gen_initramfs.sh script to create the cpio. Note that you need to run this relative to the linux directory
# create list of files cat << EOF > initramfs dir /dev 755 0 0 nod /dev/console 644 0 0 c 5 1 nod /dev/loop0 644 0 0 b 7 0 dir /bin 755 1000 1000 dir /proc 755 0 0 dir /sys 755 0 0 dir /mnt 755 0 0 file /init hello 755 0 0 EOF # generate a cpio from it ./usr/gen_initramfs.sh initramfs | gzip -9 > cpio
Note that we use gzip compression here (which is completely unnecessary for such a small cpio) so your kernel must have CONFIG_INITRAMFS_COMPRESSION_GZIP=y
Take a look at the Linux gen_initramfs.sh script to see how it works for more info
Creating from scratch using a script and pre-built static busybox
You can create an initramfs from scratch fairly easy using a prebuilt status busybox as your base
# specify a location for our rootfs ROOTFS=./rootfs # create rootfs dir mkdir -p $ROOTFS for i in bin dev etc lib mnt proc sbin sys tmp var; do \ mkdir $ROOTFS/$i; done # download a prebuilt static aarch64 binary of busybox and make it executable wget http://mirror.archlinuxarm.org/aarch64/extra/busybox-1.36.1-1-aarch64.pkg.tar.xz tar xvf busybox*.tar.xz usr/bin/busybox mv usr/bin/busybox $ROOTFS/bin # busybox takes the role of a large list of standard linux tools so anything we want or need here that it provides can by symlinked; note we use 'mount' below ln -sf busybox $ROOTFS/bin/mount # create a /init that mounts basic pseudo filesystems then executes a shell cat <<\EOF > $ROOTFS/init #!/bin/busybox sh mount -t devtmpfs devtmpfs /dev mount -t proc proc /proc mount -t sysfs sysfs /sys mount -t tmpfs tmpfs /tmp echo "Hello busybox!" sh EOF chmod +x $ROOTFS/init # create a compressed cpio archive (cd $ROOTFS; find . | cpio -ov --format=newc) | gzip -9 > cpio
Note that we use gzip compression here (which is completely unnecessary for such a small cpio) so your kernel must have CONFIG_INITRAMFS_COMPRESSION_GZIP=y
buildroot
For a more complete and easy to build root filesystem take a look at buildroot.
Example:
# clone buildroot git clone http://github.com/buildroot/buildroot.git cd buildroot # create the most basic ARM64 rootfs with a gzip cpio filesystem cat << EOF > configs/minimal_arm64_ramdisk_defconfig # arm64 arch BR2_aarch64=y # filesystem options BR2_TARGET_ROOTFS_CPIO=y BR2_TARGET_ROOTFS_CPIO_GZIP=y EOF # build it make minimal_arm64_ramdisk_defconfig make ls -l output/images/rootfs.cpio.gz
While the above creates a full features Linux if you want to add your own bootstraping script in front you can simply override /sbin/init or add your own /init (as its looked for first) to the buildroot output/target directory and rebuild the image:
# add a simple /init script cat <<EOF >output/target/init #!/bin/busybox sh mount -t devtmpfs devtmpfs /dev mount -t proc proc /proc mount -t sysfs sysfs /sys mount -t tmpfs tmpfs /tmp echo "Hello buildroot!" sh EOF chmod +x output/target/init # build again to regenerate the rootfs make ls -l output/images/rootfs.cpio.gz
rebuilding or modifying an initramfs
You can modify an external initramfs fairly easily with the cpio
tool. You merely need to uncompress it (if compressed) and use the extract parameter. You will want to do this as root in order to preserve file ownership of root files.
Example:
- extract files from an existing gzipped cpio:
# uncompress it (if compressed) and use {{{cpio -i}}} to extract it: # -i extract # -d create directories # -m preserve mtime # -v verbose mkdir rootfs_mod (cd rootfs_mod; gzip -cd ../cpio | sudo cpio -idmv)
- modify it; for example overwrite or create your own /init:
cat <<EOF >rootfs_mod/init #!/bin/busybox sh mount -t devtmpfs devtmpfs /dev mount -t proc proc /proc mount -t sysfs sysfs /sys mount -t tmpfs tmpfs /tmp echo "Hello world!" sh EOF chmod +x rootfs_mod/init
- re-create it
(cd rootfs_mod; find . | cpio -ov --format=newc) | gzip -9 > cpio