wiki:linux/initramfs

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:

  1. 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
    
  2. 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
    
  3. 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:

  1. 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)
    
  2. 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
    
  3. re-create it
    (cd rootfs_mod; find . | cpio -ov --format=newc) | gzip -9 > cpio
    
Last modified 9 months ago Last modified on 02/27/2024 12:05:32 AM
Note: See TracWiki for help on using the wiki.