5

I need to load driver on early stage booting. For development I use LVM (logical volume manager) so I can easily revert test setups to the default image from a snapshot. For this I have to load the lvm driver before accessing the root partition. This is done with an init ramdisk (initrd or initramfs). This is also good to support custom kernel. But unlike Debian, Raspbian does not support initramfs out of the box. How can I use an init ramdisk?

6

I have found that there is a setup in

rpi ~$ cat /etc/default/raspberrypi-kernel
# Defaults for raspberrypi-kernel

# Uncomment the following line to enable generation of
# /boot/initrd.img-KVER files (requires initramfs-tools)

#INITRD=Yes

# Uncomment the following line to enable generation of
# /boot/initrd(7).img files (requires rpi-initramfs-tools)

#RPI_INITRD=Yes

The comments there are the only documentation I have found. In particular I can't find anything about rpi-initramfs-tools. They are simply not available. So I tested with INITRD=Yes by uncommenting it because initramfs-tools are installed by default. Then to create an initramfs I execute

rpi ~$ sudo update-initramfs -c -k $(uname -r)

and it works like a charm. You must also do it to generate the first initramfs. It produces one for example /boot/initrd.img-4.14.71-v7+. From now on you can simply update with:

rpi ~$ sudo update-initramfs -u
ln: failed to create hard link '/boot/initrd.img-4.14.71-v7+.dpkg-bak' => '/boot/initrd.img-4.14.71-v7+': Operation not permitted
update-initramfs: Generating /boot/initrd.img-4.14.71-v7+

As you see you will get a warning (it's not an error) from ln. The boot partition has a fat filesystem that does not support links but it doesn't matter. The only problem is that it needs an entry in /boot/config.txt like

initramfs initrd.img-4.14.71-v7+ (without equal sign)

Otherwise the boot loader cannot find the ramdisk and boot up fails.

With Raspbian we have also two kernel images, for example /boot/initrd.img-4.14.71 and /boot/initrd.img-4.14.71-v7+ and by default update-initramfs generates an init ramddisk for each kernel but that will not fit onto the limited space of the boot partition. So we have also to ensure that we only generate an initramfs for the running kernel.

Managing initramfs is done with the script /etc/kernel/postinst.d/initramfs-tools. We have to modify this script as follows. But before move the original script to a save place:

rpi ~$ sudo mv /etc/kernel/postinst.d/initramfs-tools ~

Then create a file

rpi ~$ sudo editor /etc/kernel/postinst.d/rpi-initramfs-tools

with this content:

#!/bin/bash -e
# Environment variables are set by the calling script

version="$1"
bootopt=""

command -v update-initramfs >/dev/null 2>&1 || exit 0

# passing the kernel version is required
if [ -z "${version}" ]; then
        echo >&2 "W: initramfs-tools: ${DPKG_MAINTSCRIPT_PACKAGE:-kernel package} did not pass a version number"
        exit 2
fi

# exit if kernel does not need an initramfs
if [ "$INITRD" = 'No' ]; then
        # delete initramfs entries in /boot/config.txt
        /bin/sed -i '/^initramfs /d' /boot/config.txt
        exit 0
fi

# there are only two kernel types: with and without postfix "-v7+" or "-v8+"
currentversion="$(uname -r)"

# get §currenttype from $currentversion
currenttype="<no currenttype>"
echo $currentversion | grep -Pq '^\d+\.\d+\.\d+\+$'
[ $? -eq 0 ] && currenttype="+"
echo $currentversion | grep -Pq '^\d+\.\d+\.\d+-v[78]\+$'
[ $? -eq 0 ] && currenttype="${currentversion#*-}"

# get $newtype from $version
newtype="<no newtype>"
echo $version | grep -Pq '^\d+\.\d+\.\d+\+$'
[ $? -eq 0 ] && newtype="+"
echo $version | grep -Pq '^\d+\.\d+\.\d+-v[78]\+$'
[ $? -eq 0 ] && newtype="${version#*-}"

# we do nothing if the new kernel is not for the same kernel type then the current
if [ "$newtype" != "$currenttype" ]; then
        exit 0
fi

# absolute file name of kernel image may be passed as a second argument;
# create the initrd in the same directory
if [ -n "$2" ]; then
        bootdir=$(dirname "$2")
        bootopt="-b ${bootdir}"
fi

# avoid running multiple times
if [ -n "$DEB_MAINT_PARAMS" ]; then
        eval set -- "$DEB_MAINT_PARAMS"
        if [ -z "$1" ] || [ "$1" != "configure" ]; then
                exit 0
        fi
fi

# we're good - create initramfs.  update runs do_bootloader
INITRAMFS_TOOLS_KERNEL_HOOK=1 update-initramfs -c -t -k "${version}" ${bootopt} >&2

# delete initramfs entries in /boot/config.txt
/bin/sed -i '/^initramfs /d' /boot/config.txt

# insert initramfs entry in /boot/config.txt
INITRD_ENTRY="initramfs initrd.img-${version}"
echo >&2 $(basename "$0"): insert \'"$INITRD_ENTRY"\' into /boot/config.txt
/bin/sed -i "1i $INITRD_ENTRY" /boot/config.txt

Make the script executable:

rpi ~$ sudo chmod 755 /etc/kernel/postinst.d/rpi-initramfs-tools

The extensions in the script ensures that an initramfs is only created for the current running kernel and that there is managed an entry in /boot/config.txt. If you like to see the changes you can do it with diff --ignore-tab-expansion ~/initramfs-tools /etc/kernel/postinst.d/rpi-initramfs-tools.

For manual updates we create:

rpi ~$ sudo editor /usr/local/sbin/update-rpi-initramfs

with this content:

#!/bin/bash
# This script calls default update-initramfs
# and then insert a 'initramfs' entry into /boot/config.txt if necessary

# should return e.g. "update-initramfs: Generating /boot/initrd.img-4.14.79-v7+"
# or                 "update-initramfs: Deleting /boot/initrd.img-4.14.71-v7+"
MSG=$(/usr/sbin/update-initramfs "$@")
RETCODE=$?
echo $MSG

if [[ $RETCODE -ne 0 ]]; then
        echo >&2 ATTENTION! Check \'initramfs\' entry in /boot/config.txt
        exit "$RETCODE"
fi

CMP="update-initramfs: Deleting *"
if [[ $MSG == $CMP ]]; then
        # delete initramfs entries in /boot/config.txt
        /bin/sed -i '/^initramfs /d' /boot/config.txt
        echo $(basename "$0"): deleted all \'initramfs\' entries from /boot/config.txt
        exit 0
fi

CMP="update-initramfs: Generating *"
if [[ $MSG == $CMP ]]; then
        # delete initramfs entries in /boot/config.txt
        /bin/sed -i '/^initramfs /d' /boot/config.txt

        # exit if kernel does not need an initramfs
        source /etc/default/raspberrypi-kernel
        if [ "${INITRD,,}" != 'yes' ]; then
                echo $(basename "$0"): no entry in /boot/config.txt \(see INITRD in /etc/default/raspberrypi-kernel\)
                exit 0
        fi

        # insert initramfs entry in /boot/config.txt
        VERSION=$(basename "$MSG")
        INITRD_ENTRY="initramfs $VERSION"
        echo $(basename "$0"): insert \'"$INITRD_ENTRY"\' into /boot/config.txt
        /bin/sed -i "1i $INITRD_ENTRY" /boot/config.txt

        exit 0
fi

echo >&2 ATTENTION! Check 'initramfs' entry in /boot/config.txt
exit 1

Set permissions:

rpi ~$ sudo chmod 755 /usr/local/sbin/update-rpi-initramfs

From now on you should only use update-rpi-initramfs instead of update-initramfs, for example:

rpi ~$ sudo update-rpi-initramfs -u

This ensures that you always have the right entry in /boot/config.txt.


References:
[1] Package maintainer scripts and hooks

0

Not tested this but have used a similar method to add overlayfs modules and a custom scripts to the the initramfs to enable RO operation. If all you want is to add the LVM modules to the initramfs I think all you need to do is:

  1. Add the modules you want to include to /etc/initramfs-tools/modules.
  2. Add any support scripts you need to run during boot to '/etc/initramfs-tools/scripts/...'.
  3. Run mkinitramfs -o /boot/initrd to build the initramfs
  4. Add initramfs initrd followkernel to your /boot/config.txt to enable it.
  5. Reboot.

EDIT: As Ingo pointed out in the comments this method does not automatically recompile the initramfs as a part of any system updates. You'd have to manually re-run mkinitramfs -o /boot/initrd to compile a new image with, for example, a new LVM module to match a newly installed kernel. So, might not be suitable for what you need.

  • 1
    Hi Roger, many thanks for your feedback. I'm using an initramfs for a long time. [..] But it is a little bit dangerous because automatic updating a initramfs isn't supported by Raspbian. More details about my motivation to make this triggered update you can read at Custom initramfs. Adding support scripts in etc/initramfs-tools/scripts/ doesn't trigger update-initramfs on kernel updates. – Ingo Jan 14 at 12:46
  • If the followkernel option is that what it looks like it would be a very useful hint to simplify my scripts. I will look at it. Btw. I also use overlayfs for How do I make the OS reset itself every time it boots up? but I don't need an initframfs for it because overlayfs is part of the kernel. – Ingo Jan 14 at 13:21
  • @Ingo That's a fair comment about this method not automatically rebuilding the initramfs. Wasn't a consideration for what we were doing as the final disk image was more or less static once burned to the SD card (we'd not expect our users to run apt-get). I'll amend my answer to clarify that point. Was not aware that unionfs was in the stock kernel but made extensive use of the initramfs scripts anyway to delay the boot whilst a UPS charged up and so on. – Roger Jones Jan 14 at 15:23
  • I have tested initramfs initrd followkernel. It has no effect. The bootloader does not use an initramfs if initrd does not exactly match the name in /boot/, e.g. initrd.img-4.14.79-v7+. I have considered to rename the initrd to a generic name but then I have no control over tne version. Don't k now what's better ... – Ingo Jan 14 at 15:26
  • @ingo Sorry, didn't read your answer properly and you're already using sed to edit config.txt. I believe that initramfs-tools exports the version as a variable, can you edit your sed command to change the initramfs line to contain the new version instead? Something like sed -i "s/^initramfs\\ initrd.*/initramfs\\ initrd.img-${version}/" /boot/config.txt? manpages.ubuntu.com/manpages/xenial/man8/initramfs-tools.8.html – Roger Jones Jan 14 at 15:55
0

I can offer an alternative solution.

Instead of renaming the initramfs-image and modifying the existing /etc/kernel/postinst.d/* files, I added an initramfs hook that modifies /boot/config.txt so that it will load the initramfs with its current name.

Therefore, there is no need to rename the initramfs any longer after creation, and it can use the standard name chosen by the initramfs generation scripts (i. e. initrd.img-$KERNELRELEASE).

The script is customizable by setting variables for all important path name components at its beginning, so that all customizable settings are in one place.

The script also avoids updating config.txt if it already contains the current name of the actual initramfs image, saving the life endurance of your SD card a little.

Before you install the hook, make sure your /boot/config.txt contains the line which the hook attempts to update.

That line must have a format like the last one in the following section from my config.txt:

# Important: This value will be updated by initramfs generation scripts.
#
# Special syntax: Do not use "=" for assignment here.
initramfs initrd.img-4.19.42-v7+ followkernel

I named the hook script

/etc/initramfs-tools/hooks/update_initrd_ref_qaumv1g34z54324pbel831evd

which contains a UUID at the end of its name, so there is no danger of name collisions with existing or future scripts of the same name. However, the name does not really matter; feel free to rename it as long as you leave it in the same directory.

And here are the contents of that script:

#! /bin/sh -e
# Update reference to $INITRD in $BOOTCFG, making the kernel use the new
# initrd after the next reboot.
BOOTLDR_DIR=/boot
BOOTCFG=$BOOTLDR_DIR/config.txt
INITRD_PFX=initrd.img-
INITRD=$INITRD_PFX$version

case $1 in
    prereqs) echo; exit
esac

FROM="^ *\\(initramfs\\) \\+$INITRD_PFX.\\+ \\+\\(followkernel\\) *\$"
INTO="\\1 $INITRD \\2"

T=`umask 077 && mktemp --tmpdir genramfs_XXXXXXXXXX.tmp`
trap "rm -- \"$T\"" 0

sed "s/$FROM/$INTO/" "$BOOTCFG" > "$T"

# Update file only if necessary.
if ! cmp -s "$BOOTCFG" "$T"
then
    cat "$T" > "$BOOTCFG"
fi

Note that this is a script, so you need to

$ chmod +x /etc/initramfs-tools/hooks/update_initrd_ref_qaumv1g34z54324pbel831evd

after creating it.

This is actually all there is to it - run

$ update-initramfs -u

as usual, and the contents of /boot/config.txt should "automagically" match the name of your /boot/initrd.img-* file.

  • Thanks for the suggestion. I will test it in detail. Just had only a glance at it. Does this script will be triggered automatically on kernel updates like on debian by default? – Ingo Jul 3 at 19:52
  • @Ingo Yes, it will/does. – Guenther Brunthaler Jul 4 at 11:43
  • As long as you have initramfs-tools installed. – Guenther Brunthaler Jul 4 at 11:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.