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?
5 Answers
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.
Edit /etc/default/raspberrypi-kernel to comment out INITRD and uncomment RPI_INITRD instead.
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 [ "$RPI_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++$' && currenttype="+"
echo $currentversion | grep -Pq '^\d+.\d+.\d+-v[78]+$' && currenttype="${currentversion#*-}"
get $newtype from $version
newtype="<no newtype>"
echo $version | grep -Pq '^\d+.\d+.\d++$' && newtype="+"
echo $version | grep -Pq '^\d+.\d+.\d+-v[78]+$' && 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} followkernel"
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
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.
- 501
- 1
- 5
- 11
I've used a different solution, adding a hook in the /etc/kernel/postinst.d directory. It has been working for a while, but I am about to make a miniscule change that I will include here that hasn't been tested because I want to include the ability to run the v8 kernel. So I added this script as /etc/kernel/postinst.d/rebuild
#!/bin/sh -e
# Rebuild initramfs{7,8}.img after kernel upgrade to include new kernel’s modules.
#
# Exit if rebuild cannot be performed or not needed.
[ -x /usr/sbin/mkinitramfs ] || exit 0
# Exit if not building kernel for this Raspberry Pi’s hardware version (assume armv7l or aarch64).
version="$1"
case "${version}" in
*-v7+) arch=7;;
*-v8+) arch=8;;
*) exit 0
esac
[ -f /boot/initramfs$arch.img ] && lsinitramfs /boot/initramfs$arch.img | grep -q "/$version$" && exit 0 # Already in initramfs.
# Rebuild.
mkinitramfs -o /boot/initramfs$arch.img "$version"
making it executable. I don't touch the the /etc/default/raspberrypi-kernel file at all.
I do edit /boot/config.txt to either include initramfs initramfs7.img followkernel or initramfs initramfs8.img followkernel and arm_64bit=1. I don't mind making that small distinction which I think is necessary so often I can undo the arm_64bit flag if I want. It only
- 111
- 3
the script called during the kernel installation must be slightly edited because otherwise it will exit with error code 1 if grep doesn't match
#!/bin/sh -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>"
if [ `echo $currentversion | grep -P '^\d+\.\d+\.\d+\+$'` ]; then
[ $? -eq 0 ] && currenttype="+"
else [ `echo $currentversion | grep -P '^\d+\.\d+\.\d+-v[78]l?\+$'` ];
[ $? -eq 0 ] && currenttype="${currentversion#*-}"
fi
# get $newtype from $version
newtype="<no newtype>"
if [ `echo $version | grep -P '^\d+\.\d+\.\d+\+$'` ]; then
[ $? -eq 0 ] && newtype="+"
else [ `echo $version | grep -P '^\d+\.\d+\.\d+-v[78]l?\+$'` ];
[ $? -eq 0 ] && newtype="${version#*-}"
fi
# 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 -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} followkernel"
echo >&2 $(basename "$0"): insert \'"$INITRD_ENTRY"\' into /boot/config.txt
/bin/sed -i "1i $INITRD_ENTRY" /boot/config.txt
- 11
- 2
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:
- Add the modules you want to include to
/etc/initramfs-tools/modules. - Add any support scripts you need to run during boot to '/etc/initramfs-tools/scripts/...'.
- Run
mkinitramfs -o /boot/initrdto build theinitramfs - Add
initramfs initrd followkernelto your/boot/config.txtto enable it. - 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,484
- 8
- 14