About initramfs

#### Beyond Linux® From Scratch (System V Edition) - Version r13.0-1374

### Chapter 5. File Systems and Disk Management

- Prev

  File Systems and Disk Management

- Next

  btrfs-progs-7.1

- Up
- Home

# About initramfs

The only purpose of an initramfs is to mount the root filesystem. The initramfs is a complete set of directories that you would find on a normal root filesystem. 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 kernel. The kernel checks for the presence of the initramfs and, if found, mounts it as / and runs /init. The init program is typically a shell script. Note that the boot process takes longer, possibly significantly longer, if an initramfs is used.

For most distributions, kernel modules are the biggest reason to have an initramfs. In a general distribution, there are many unknowns such as file system types and disk layouts. In a way, this is the opposite of LFS where the system capabilities and layout are known and a custom kernel is normally built. In this situation, an initramfs is rarely needed.

There are only four primary reasons to have an initramfs in the LFS environment: loading the rootfs from a network, loading it from an LVM logical volume, having an encrypted rootfs where a password is required, or for the convenience of specifying the rootfs as a LABEL or UUID. Anything else usually means that the kernel was not configured properly.

## Building an initramfs

If you do decide to build an initramfs, the following scripts will provide a basis to do it. The scripts will allow specifying a rootfs via partition UUID or partition LABEL or a rootfs on an LVM logical volume. They do not support an encrypted root file system or mounting the rootfs over a network card. For a more complete capability see the LFS Hints or dracut.

To install these scripts, run the following commands as the `root` user:

    cat > /usr/sbin/mkinitramfs << "EOF"
    #!/bin/bash
    # This file based in part on the mkinitramfs script for the LFS LiveCD
    # written by Alexander E. Patrakov and Jeremy Huntwork.

    copy()
    {
      local file

      if [ "$2" = "lib" ]; then
        file=$(PATH=/usr/lib type -p $1)
      else
        file=$(type -p $1)
      fi

      if [ -n "$file" ] ; then
        cp $file $WDIR/usr/$2
      else
        echo "Missing required file: $1 for directory $2"
        rm -rf $WDIR
        exit 1
      fi
    }

    if [ -z $1 ] ; then
      INITRAMFS_FILE=initrd.img-no-kmods
    else
      KERNEL_VERSION=$1
      INITRAMFS_FILE=initrd.img-$KERNEL_VERSION
    fi

    if [ -n "$KERNEL_VERSION" ] && [ ! -d "/usr/lib/modules/$1" ] ; then
      echo "No modules directory named $1"
      exit 1
    fi

    printf "Creating $INITRAMFS_FILE... "

    binfiles="sh cat cp dd killall ls mkdir mknod mount "
    binfiles="$binfiles umount sed sleep ln rm uname"
    binfiles="$binfiles readlink basename"

    # Systemd installs udevadm in /bin. Other udev implementations have it in /sbin
    if [ -x /usr/bin/udevadm ] ; then binfiles="$binfiles udevadm"; fi

    sbinfiles="modprobe blkid switch_root"

    # Optional files and locations
    for f in mdadm mdmon udevd udevadm; do
      if [ -x /usr/sbin/$f ] ; then sbinfiles="$sbinfiles $f"; fi
    done

    # Add lvm if present (cannot be done with the others because it
    # also needs dmsetup
    if [ -x /usr/sbin/lvm ] ; then sbinfiles="$sbinfiles lvm dmsetup"; fi

    unsorted=$(mktemp /tmp/unsorted.XXXXXXXXXX)

    DATADIR=/usr/share/mkinitramfs
    INITIN=init.in

    # Create a temporary working directory
    WDIR=$(mktemp -d /tmp/initrd-work.XXXXXXXXXX)

    # Create base directory structure
    mkdir -p $WDIR/{dev,run,sys,proc,usr/{bin,lib/{firmware,modules},sbin}}
    mkdir -p $WDIR/etc/{modprobe.d,udev/rules.d}
    touch $WDIR/etc/modprobe.d/modprobe.conf
    ln -s usr/bin  $WDIR/bin
    ln -s usr/lib  $WDIR/lib
    ln -s usr/sbin $WDIR/sbin
    ln -s lib      $WDIR/lib64

    # Create necessary device nodes
    mknod -m 640 $WDIR/dev/console c 5 1
    mknod -m 664 $WDIR/dev/null    c 1 3

    # Install the udev configuration files
    if [ -f /etc/udev/udev.conf ]; then
      cp /etc/udev/udev.conf $WDIR/etc/udev/udev.conf
    fi

    for file in $(find /etc/udev/rules.d/ -type f) ; do
      cp $file $WDIR/etc/udev/rules.d
    done

    # Install any firmware present
    cp -a /usr/lib/firmware $WDIR/usr/lib

    # Copy the RAID configuration file if present
    if [ -f /etc/mdadm.conf ] ; then
      cp /etc/mdadm.conf $WDIR/etc
    fi

    # Install the init file
    install -m0755 $DATADIR/$INITIN $WDIR/init

    if [  -n "$KERNEL_VERSION" ] ; then
      if [ -x /usr/bin/kmod ] ; then
        binfiles="$binfiles kmod"
      else
        binfiles="$binfiles lsmod"
        sbinfiles="$sbinfiles insmod"
      fi
    fi

    # Install basic binaries
    for f in $binfiles ; do
      ldd /usr/bin/$f | sed "s/\t//" | cut -d " " -f1 >> $unsorted
      copy /usr/bin/$f bin
    done

    for f in $sbinfiles ; do
      ldd /usr/sbin/$f | sed "s/\t//" | cut -d " " -f1 >> $unsorted
      copy $f sbin
    done

    # Add udevd libraries if not in /usr/sbin
    if [ -x /usr/lib/udev/udevd ] ; then
      ldd /usr/lib/udev/udevd | sed "s/\t//" | cut -d " " -f1 >> $unsorted
    elif [ -x /usr/lib/systemd/systemd-udevd ] ; then
      ldd /usr/lib/systemd/systemd-udevd | sed "s/\t//" | cut -d " " -f1 >> $unsorted
    fi

    # Add module symlinks if appropriate
    if [ -n "$KERNEL_VERSION" ] && [ -x /usr/bin/kmod ] ; then
      ln -s kmod $WDIR/usr/bin/lsmod
      ln -s kmod $WDIR/usr/bin/insmod
    fi

    # Add lvm symlinks if appropriate
    # Also copy the lvm.conf file
    if  [ -x /usr/sbin/lvm ] ; then
      ln -s lvm $WDIR/usr/sbin/lvchange
      ln -s lvm $WDIR/usr/sbin/lvrename
      ln -s lvm $WDIR/usr/sbin/lvextend
      ln -s lvm $WDIR/usr/sbin/lvcreate
      ln -s lvm $WDIR/usr/sbin/lvdisplay
      ln -s lvm $WDIR/usr/sbin/lvscan

      ln -s lvm $WDIR/usr/sbin/pvchange
      ln -s lvm $WDIR/usr/sbin/pvck
      ln -s lvm $WDIR/usr/sbin/pvcreate
      ln -s lvm $WDIR/usr/sbin/pvdisplay
      ln -s lvm $WDIR/usr/sbin/pvscan

      ln -s lvm $WDIR/usr/sbin/vgchange
      ln -s lvm $WDIR/usr/sbin/vgcreate
      ln -s lvm $WDIR/usr/sbin/vgscan
      ln -s lvm $WDIR/usr/sbin/vgrename
      ln -s lvm $WDIR/usr/sbin/vgck
      # Conf file(s)
      cp -a /etc/lvm $WDIR/etc
    fi

    # Install libraries
    sort $unsorted | uniq | while read library ; do
    # linux-vdso and linux-gate are pseudo libraries and do not correspond to a file
    # libsystemd-shared is in /lib/systemd, so it is not found by copy, and
    # it is copied below anyway
      if [[ "$library" == linux-vdso.so.1 ]] ||
         [[ "$library" == linux-gate.so.1 ]] ||
         [[ "$library" == libsystemd-shared* ]]; then
        continue
      fi

      copy $library lib
    done

    if [ -d /usr/lib/udev ]; then
      cp -a /usr/lib/udev $WDIR/usr/lib
    fi
    if [ -d /usr/lib/systemd ]; then
      cp -a /usr/lib/systemd $WDIR/usr/lib
    fi
    if [ -d /usr/lib/elogind ]; then
      cp -a /usr/lib/elogind $WDIR/usr/lib
    fi

    # Install the kernel modules if requested
    if [ -n "$KERNEL_VERSION" ]; then
      find \
         /usr/lib/modules/$KERNEL_VERSION/kernel/{crypto,fs,lib}                      \
         /usr/lib/modules/$KERNEL_VERSION/kernel/drivers/{block,ata,nvme,md,firewire} \
         /usr/lib/modules/$KERNEL_VERSION/kernel/drivers/{scsi,message,pcmcia,virtio} \
         /usr/lib/modules/$KERNEL_VERSION/kernel/drivers/usb/{host,storage}           \
         -type f 2> /dev/null | cpio --make-directories -p --quiet $WDIR

      cp /usr/lib/modules/$KERNEL_VERSION/modules.{builtin,order} \
                $WDIR/usr/lib/modules/$KERNEL_VERSION
      if [ -f /usr/lib/modules/$KERNEL_VERSION/modules.builtin.modinfo ]; then
        cp /usr/lib/modules/$KERNEL_VERSION/modules.builtin.modinfo \
                $WDIR/usr/lib/modules/$KERNEL_VERSION
      fi

      depmod -b $WDIR $KERNEL_VERSION
    fi

    ( cd $WDIR ; find . | cpio -o -H newc --quiet | gzip -9 ) > $INITRAMFS_FILE

    # Prepare early loading of microcode if available
    if ls /usr/lib/firmware/intel-ucode/* >/dev/null 2>&1 ||
       ls /usr/lib/firmware/amd-ucode/*   >/dev/null 2>&1; then

    # first empty WDIR to reuse it
      rm -r $WDIR/*

      DSTDIR=$WDIR/kernel/x86/microcode
      mkdir -p $DSTDIR

      if [ -d /usr/lib/firmware/amd-ucode ]; then
        cat /usr/lib/firmware/amd-ucode/microcode_amd*.bin > $DSTDIR/AuthenticAMD.bin
      fi

      if [ -d /usr/lib/firmware/intel-ucode ]; then
        cat /usr/lib/firmware/intel-ucode/* > $DSTDIR/GenuineIntel.bin
      fi

      ( cd $WDIR; find . | cpio -o -H newc --quiet ) > microcode.img
      cat microcode.img $INITRAMFS_FILE > tmpfile
      mv tmpfile $INITRAMFS_FILE
      rm microcode.img
    fi

    # Remove the temporary directories and files
    rm -rf $WDIR $unsorted
    printf "done.\n"

    EOF

    chmod 0755 /usr/sbin/mkinitramfs

    mkdir -p /usr/share/mkinitramfs &&
    cat > /usr/share/mkinitramfs/init.in << "EOF"
    #!/bin/sh

    PATH=/usr/bin:/usr/sbin
    export PATH

    problem()
    {
       printf "Encountered a problem!\n\nDropping you to a shell.\n\n"
       sh
    }

    no_device()
    {
       printf "The device %s, which is supposed to contain the\n" $1
       printf "root file system, does not exist.\n"
       printf "Please fix this problem and exit this shell.\n\n"
    }

    no_mount()
    {
       printf "Could not mount device %s\n" $1
       printf "Sleeping forever. Please reboot and fix the kernel command line.\n\n"
       printf "Maybe the device is formatted with an unsupported file system?\n\n"
       printf "Or maybe filesystem type autodetection went wrong, in which case\n"
       printf "you should add the rootfstype=... parameter to the kernel command line.\n\n"
       printf "Available partitions:\n"
    }

    do_mount_root()
    {
       mkdir /.root
       [ -n "$rootflags" ] && rootflags="$rootflags,"
       rootflags="$rootflags$ro"

       case "$root" in
          /dev/*    ) device=$root ;;
          UUID=*    ) eval $root; device="/dev/disk/by-uuid/$UUID" ;;
          PARTUUID=*) eval $root; device="/dev/disk/by-partuuid/$PARTUUID" ;;
          LABEL=*   ) eval $root; device="/dev/disk/by-label/$LABEL" ;;
          ""        ) echo "No root device specified." ; problem ;;
       esac

       while [ ! -b "$device" ] ; do
           no_device $device
           problem
       done

       if ! mount -n -t "$rootfstype" -o "$rootflags" "$device" /.root ; then
           no_mount $device
           cat /proc/partitions
           while true ; do sleep 10000 ; done
       else
           echo "Successfully mounted device $root"
       fi
    }

    do_try_resume()
    {
       case "$resume" in
          UUID=* ) eval $resume; resume="/dev/disk/by-uuid/$UUID"  ;;
          LABEL=*) eval $resume; resume="/dev/disk/by-label/$LABEL" ;;
       esac

       if $noresume || ! [ -b "$resume" ]; then return; fi

       ls -lH "$resume" | ( read x x x x maj min x
           echo -n ${maj%,}:$min > /sys/power/resume )
    }

    init=/sbin/init
    root=
    rootdelay=
    rootfstype=auto
    ro="ro"
    rootflags=
    device=
    resume=
    noresume=false

    mount -n -t devtmpfs devtmpfs /dev
    mount -n -t proc     proc     /proc
    mount -n -t sysfs    sysfs    /sys
    mount -n -t tmpfs    tmpfs    /run

    read -r cmdline < /proc/cmdline

    for param in $cmdline ; do
      case $param in
        init=*      ) init=${param#init=}             ;;
        root=*      ) root=${param#root=}             ;;
        rootdelay=* ) rootdelay=${param#rootdelay=}   ;;
        rootfstype=*) rootfstype=${param#rootfstype=} ;;
        rootflags=* ) rootflags=${param#rootflags=}   ;;
        resume=*    ) resume=${param#resume=}         ;;
        noresume    ) noresume=true                   ;;
        ro          ) ro="ro"                         ;;
        rw          ) ro="rw"                         ;;
      esac
    done

    # udevd location depends on version
    if [ -x /sbin/udevd ]; then
      UDEVD=/sbin/udevd
    elif [ -x /lib/udev/udevd ]; then
      UDEVD=/lib/udev/udevd
    elif [ -x /lib/systemd/systemd-udevd ]; then
      UDEVD=/lib/systemd/systemd-udevd
    else
      echo "Cannot find udevd nor systemd-udevd"
      problem
    fi

    ${UDEVD} --daemon --resolve-names=never
    udevadm trigger
    udevadm settle

    if [ -f /etc/mdadm.conf ] ; then mdadm -As                       ; fi
    if [ -x /sbin/vgchange  ] ; then /sbin/vgchange -a y > /dev/null ; fi
    if [ -n "$rootdelay"    ] ; then sleep "$rootdelay"              ; fi

    do_try_resume # This function will not return if resuming from disk
    do_mount_root

    killall -w ${UDEVD##*/}

    exec switch_root /.root "$init" "$@"

    EOF

## Using an initramfs

### Required Runtime Dependency

cpio-2.15

### Other Runtime Dependencies

LVM2-2.03.42 and/or mdadm-4.6 must be installed before generating the initramfs, if the system partition uses them.

To build an initramfs, run the following as the `root` user:

    mkinitramfs [KERNEL VERSION]

The optional argument is the directory where the appropriate kernel modules are located. This must be a subdirectory of `/lib/modules`. If no modules are specified, then the initramfs is named initrd.img-no-kmods. If a kernel version is specified, the initrd is named initrd.img-$KERNEL_VERSION and is only appropriate for the specific kernel specified. The output file will be placed in the current directory.

If early loading of microcode is needed (see the section called “Microcode updates for CPUs”), you can install the appropriate blob or container in `/lib/firmware`. It will be automatically added to the initrd when running mkinitramfs.

After generating the initrd, copy it to the `/boot` directory.

Now edit `/boot/grub/grub.cfg` and add a new menuentry. Below are several examples.

    # Generic initramfs and root fs identified by UUID
    menuentry "LFS Dev (LFS-7.0-Feb14) initrd, Linux 3.0.4"
    {
      linux  /vmlinuz-3.0.4-lfs-20120214 root=UUID=54b934a9-302d-415e-ac11-4988408eb0a8 ro
      initrd /initrd.img-no-kmods
    }

    # Generic initramfs and root fs on LVM partition
    menuentry "LFS Dev (LFS-7.0-Feb18) initrd lvm, Linux 3.0.4"
    {
      linux  /vmlinuz-3.0.4-lfs-20120218 root=/dev/mapper/myroot ro
      initrd /initrd.img-no-kmods
    }

    # Specific initramfs and root fs identified by LABEL
    menuentry "LFS Dev (LFS-7.1-Feb20) initrd label, Linux 3.2.6"
    {
      linux  /vmlinuz-3.2.6-lfs71-120220 root=LABEL=lfs71 ro
      initrd /initrd.img-3.2.6-lfs71-120220
    }

Finally, reboot the system and select the desired system.

- Prev

  File Systems and Disk Management

- Next

  btrfs-progs-7.1

- Up
- Home