Categories
Filesystem Kernel Linux RAM disk ZFS

Analyzing Ubuntu Initial Ramdisk

While investigating ZFS snapshot problems I was looking into Ubuntu 24.04 initial RAM disks (initrd/initramfs).

So here’s what I found.

Unpacking RAM disk

Over the years many different ways to pack a RAM disk were used (things like cpio archives, squashfs, and so on).

Since it has been a long time since I was forced to take a look into an initramfs image, I started the (obvious) UNIX way using file to detect what kind of file I was looking at:

linux # file /boot/initrd.img-6.11.0-17-generic
/boot/initrd.img-6.11.0-17-generic: ASCII cpio archive (SVR4 with no CRC)

Ok, so looks like a simple cpio archive, however when trying to unpack all you get is this:

linux # cpio -i --verbose < /boot/initrd.img-6.11.0-17-generic 
.
kernel
kernel/x86
kernel/x86/microcode
kernel/x86/microcode/AuthenticAMD.bin
151 blocks

That doesn’t look right … so here comes a little internet search and reveals unmkinitramfs (part of Ubuntu’s initramfs-tools-core package). Using that tool we get a lot more files:

linux # unmkinitramfs -v /boot/initrd.img-6.11.0-17-generic .
<... starts with the files listed above, but much more follows ...>
early
<...>
early2
<...>
early3
<...>
main
<...>

Analyzing content

While the first two early* directories contain microcode for AMD and Intel machines the third one contains device firmware and kernel modules.

The most interesting part is however contained in the main directory. This one contains the root file system used to prepare the system boot.

Entry point (the first command executed after booting the kernel and unpacking the RAM disk) is /init. As this happens to be a bash script, it’s relatively easy to get an impression how it works.

First steps include making the typical (kernel) pseudo file systems available:

  • /sys
  • /proc
  • /dev, /dev/pts

Then it parses the kernel command line (/proc/cmdline) to decide about further steps to execute.

Executed scripts are

  • /scripts/local
  • /scripts/nfs
  • /scripts/${BOOT} (where ${BOOT} is the value of kernel’s boot= command line)
linux # find scripts/ -maxdepth 1 -type f 
scripts/functions
scripts/local
scripts/nfs
scripts/zfs

So in my case besides the two scripts included anyway (local,nfs) there’s only functions (basic functions used by all kinds of scripts) and zfs (which looks promising when looking for ZFS related problems).

Taking a look at those scripts shows, that they only include function definitions – no code that’ll be executed during inclusion.

This is also explained at the beginning of scripts/zfs (and in more detail at a later point):

linux # cat scripts/zfs
# ZFS boot stub for initramfs-tools.
#
# In the initramfs environment, the /init script sources this stub to
# override the default functions in the /scripts/local script.
#
# Enable this by passing boot=zfs on the kernel command line.
#
# $quiet, $root, $rpool, $bootfs come from the cmdline
<...>
        # ----------------------------------------------------------------
        # P A R S E   C O M M A N D   L I N E   O P T I O N S

        # This part is the really ugly part - there's so many options and permutations
        # 'out there', and if we should make this the 'primary' source for ZFS initrd
        # scripting, we need/should support them all.
        #
        # Supports the following kernel command line argument combinations
        # (in this order - first match win):
        #
        #       rpool=<pool>                    (tries to finds bootfs automatically)
        #       bootfs=<pool>/<dataset>         (uses this for rpool - first part)
        #       rpool=<pool> bootfs=<pool>/<dataset>
        #       -B zfs-bootfs=<pool>/<fs>       (uses this for rpool - first part)
        #       rpool=rpool                     (default if none of the above is used)
        #       root=<pool>/<dataset>           (uses this for rpool - first part)
        #       root=ZFS=<pool>/<dataset>       (uses this for rpool - first part, without 'ZFS=')
        #       root=zfs:AUTO                   (tries to detect both pool and rootfs)
        #       root=zfs:<pool>/<dataset>       (uses this for rpool - first part, without 'zfs:')
        #
        # Option <dataset> could also be <snapshot>
        # Option <pool> could also be <guid>
<...>

Leave a Reply

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