Skip to content

Commit

Permalink
Support for bootloaders that follows BLS
Browse files Browse the repository at this point in the history
Use sdbootutil to generate boot loader entries, install new kernels and
update the initrd for new modules.

Signed-off-by: Alberto Planas <[email protected]>
  • Loading branch information
aplanas committed May 16, 2024
1 parent 2e74477 commit 5ba5b23
Show file tree
Hide file tree
Showing 4 changed files with 259 additions and 62 deletions.
99 changes: 99 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,102 @@ that sysctl settings which are recommended for the currently running kernel
are applied by **systemd-sysctl.service** at boot time. These settings are
shipped in the file `/boot/sysctl.conf-$(uname -r)`, which is part of the
kernel package.


# BLS and ESP

There scripts are generating boot entries (via perl-Bootloader), new
initrds (via dracut) and updating the kernel module dependency lists
(via depmod). If we are in a system using the boot entries defined in
the bootloader specification (BLS), then we need to take special
considerations.

The tool `sdbootutil` is the one responsible of synchronizing the
content in the rootfs with the ESP (kernel, bootloader, shim, boot
entries, initrd, etc), so certain actions should be delegated to it.

To complicate a bit more the situation, the transactional systems
(like MicroOS) cannot now access the ESP from inside the transaction
as they will break the atomicity update operation. It is the
`snapper` plugin provided by `sdbootutil` the one that will call the
scripts in the correct moment (when setting the new default snapshot).

This impose that `weak-modules2`, `regenerate-initrd-posttrans`, the
`kernel-scriptlets` and the snapper plugin now need to work in
coordination, reordering certain actions depending on the kind of
system. The next table will try to summarize those interaction:


| Model | Operation | Element | Done |
|------------------|-----------|------------|--------------------------------|
| Traditional | Kernel | initrd | wm2 (rpm-script/post[un]) |
| | | depmod | wm2 (rpm-script/post[un]) |
| | | boot entry | rpm-script/post[un] |
| | | | |
| | KMP | initrd | wm2 (inkmp-script/post[un]) |
| | | depmod | wm2 (inkmp-script/post[un]) |
| | | | |
| | Dracut | initrd | regenerate-initrd-posttrans[1] |
|------------------|-----------|------------|--------------------------------|
| MicroOS[2] | Kernel | initrd | wm2 (rpm-script/post[un]) |
| | | depmod | wm2 (rpm-script/post[un]) |
| | | boot entry | rpm-script/post[un] |
| | | | |
| | KMP | initrd | wm2 (inkmp-script/post[un]) |
| | | depmod | wm2 (inkmp-script/post[un]) |
| | | | |
| | Dracut | initrd | regenerate-initrd-posttrans |
|------------------|-----------|------------|--------------------------------|
| Tumbleweed + BLS | Kernel | initrd | wm2 (rpm-script/post[un])[3] |
| | | depmod | wm2 (rpm-script/post[un]) |
| | | boot entry | rpm-script/post[un] |
| | | | |
| | KMP | initrd | wm2 (inkmp-script/post[un]) |
| | | depmod | wm2 (inkmp-script/post[un]) |
| | | | |
| | Dracut | initrd | regenerate-initrd-posttrans |
|------------------|-----------|------------|--------------------------------|
| MicroOS + BLS | Kernel | initrd | snapper plugin[4] |
| | | depmod | wm2 (rpm-script/post[un]) |
| | | boot entry | snapper plugin |
| | | | |
| | KMP | initrd | snapper plugin[5] |
| | | depmod | wm2 (rpm-script/post[un]) |
| | | | |
| | Dracut | initrd | snapper plugin |
|------------------|-----------|------------|--------------------------------|
| Tumbleweed + BLS | Kernel | initrd | wm2 (rpm-script/post[un])[6] |
| (no btrfs) | | depmod | wm2 (rpm-script/post[un]) |
| | | boot entry | rpm-script/post[un] |
| | | | |
| | KMP | initrd | wm2 (inkmp-script/post[un]) |
| | | depmod | wm2 (inkmp-script/post[un]) |
| | | | |
| | Dracut | initrd | regenerate-initrd-posttrans |
|------------------|-----------|------------|--------------------------------|

Notes:

[1] Triggered by the `%regenerate_initrd_post[trans]` macros

[2] In MicroOS the kernel in /boot is inside the transaction, so gets
discarded if the snapshot is dropped.

[3] Could be done in the snapper plugin, but it is done in
weak-modules2 like the rest of the systems, but calling
`sdbootutil` and forcing the no reuse of the initrd. Indirectly
creates the boot entry. The initrd name is selected from the
current default boot entry

[4] When adding or removing a kernel, the `set_default_snapshot` will
regenerate boot entries for all the remaining kernels in the
snapshot. This will synchronize also the initrds (but can leave
old initrds in the ESP). Also, wm2 will create a mark in
`/run/regenerate-initrd`.

[5] A direct call to `regenerate-initrd-posttrans` inside the
transaction will drop the call and kept the
`/run/regenerate-initrd` directory. A second call (from the
snapper plugin) will complete it.

[6] `sdbootutil` partially understand BLS systems without snapshots.
151 changes: 96 additions & 55 deletions kernel-scriptlets/rpm-script
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ nvr="$name"-"$version"-"$release"
modules_dir=/usr/lib/modules/$kernelrelease-$flavor
system_map=${modules_dir}/System.map

if [ -e "/usr/bin/sdbootutil" ] && /usr/bin/sdbootutil is-installed; then
is_sdbootutil=1
fi

trigger_purge_kernels() {
[ -z "$KERNEL_PACKAGE_SCRIPT_DEBUG" ] || echo Triggering purge-kernels >&2
touch /boot/do_purge_kernels
Expand All @@ -84,9 +88,26 @@ message_install_bl () {
echo "available bootloader for your platform (e.g. grub, lilo, zipl, ...)."
}

update_bootloader() {
update_bootloader_entry() {
if [ ! -e /.buildenv ] ; then
if [ -f /etc/fstab ] ; then
if [ -n "$is_sdbootutil" ]; then
# Transactional systems use the snapper plugins to add new
# bootloader entries, and this same hook is used in normal
# Tumbleweed installations that uses btrfs.
#
# For transactional systems we should not mangle with the
# /boot inside the transaction, to keep the atomicity
# promise.
#
# For non-transactional ones, we will generate the boot
# entries here, ignoring the snapper plugin, so we have a
# chance of implementing FDE in case that the system is
# not using btrfs.
if [ -z "$TRANSACTIONAL_UPDATE" ]; then
/usr/bin/sdbootutil --image="$image" add-kernel "$kernelrelease-$flavor"
/usr/bin/sdbootutil set-default-snapshot
fi
elif [ -f /etc/fstab ] ; then
# only run the bootloader if the usual bootloader configuration
# files are there -- this is different on every architecture
initrd=initrd-"$kernelrelease"-"$flavor"
Expand Down Expand Up @@ -115,6 +136,65 @@ update_bootloader() {
fi
}

check_space_in_boot() {
# see bug #259303
# this script runs when the kernel gets updated with YaST
# YaST calls rpm always with -U
# -U replaces all packages with the new one
# rpm removes the files from the old packages after the postinstall script ran
# this will double the required space below /boot
# remove the files from the old packages to make room for the new initrd
# rpm may complain about low disk space if /boot/vmlinux does not fit
#
# When sdbootutil is used, the kernel and initrd should be living
# in sysroot, and transferred into /boot later by the snapper
# plugin (that calls sdbootutil add-all-kernels)
#
if [ "$YAST_IS_RUNNING" != "" ]; then
mydf="$( POSIXLY_CORRECT=1 df -P /boot/ | awk '/^(\/|-[[:blank:]])/{ print $4}' )"
if test "$mydf" != "" ; then
echo "Free diskspace below /boot: $mydf blocks"
# echo "512 byte blocks: $(( 2 * 1024 * 20 ))"
if test "$mydf" -lt "40960" ; then
echo "make room for new kernel '"$flavor"' because there are less than 20MB available."
# disabled because it breaks patch rpms
#rm -fv /boot/"$image"-*-"$flavor"
[ -n "$is_sdbootutil" ] || rm -fv /boot/initrd-*-"$flavor"
fi
fi
fi
}

copy_or_link_legacy_files() {
# compat stuff for /boot.
# if /boot and /usr are not separate partitions we can just link
# the kernel there to save space. Otherwise copy.
if mountpoint -q /boot || mountpoint -q /usr; then
copy_or_link="cp -a --remove-destination"
separate_boot='1'
else
copy_or_link="ln -sf"
separate_boot=""
fi

for x in "$image" sysctl.conf System.map config; do
if [ "$separate_boot" = 1 ] || [ ! -e /boot/$x-"$kernelrelease"-"$flavor" ]; then
$copy_or_link .."$modules_dir"/$x /boot/$x-"$kernelrelease"-"$flavor" || script_rc=$?
if [ -e "$modules_dir"/.$x.hmac ]; then
$copy_or_link .."$modules_dir"/.$x.hmac /boot/.$x-"$kernelrelease"-"$flavor".hmac || script_rc=$?
fi
fi
done

for x in /boot/"$image" /boot/initrd; do
rm -f "$x"
ln -s "${x##*/}-$kernelrelease-$flavor" "$x"
done
rm -f /boot/.vmlinuz.hmac
[ ! -e "/boot/.vmlinuz-$kernelrelease-$flavor.hmac" ] ||
ln -s ".vmlinuz-$kernelrelease-$flavor.hmac" /boot/.vmlinuz.hmac
}

[ -z "$KERNEL_PACKAGE_SCRIPT_DEBUG" ] || \
echo "$op" name: "$name" version: "$version" release: "$release" \
kernelrelease: "$kernelrelease" flavor: "$flavor" variant: "$variant" \
Expand All @@ -124,27 +204,7 @@ script_rc=0

case $op in
pre)
# see bug #259303
# this script runs when the kernel gets updated with YaST
# YaST calls rpm always with -U
# -U replaces all packages with the new one
# rpm removes the files from the old packages after the postinstall script ran
# this will double the required space below /boot
# remove the files from the old packages to make room for the new initrd
# rpm may complain about low disk space if /boot/vmlinux does not fit
if [ "$YAST_IS_RUNNING" != "" ]; then
mydf="$( POSIXLY_CORRECT=1 df -P /boot/ | awk '/^(\/|-[[:blank:]])/{ print $4}' )"
if test "$mydf" != "" ; then
echo "Free diskspace below /boot: $mydf blocks"
# echo "512 byte blocks: $(( 2 * 1024 * 20 ))"
if test "$mydf" -lt "40960" ; then
echo "make room for new kernel '"$flavor"' because there are less than 20MB available."
# disabled because it breaks patch rpms
#rm -fv /boot/"$image"-*-"$flavor"
rm -fv /boot/initrd-*-"$flavor"
fi
fi
fi
[ -n "$is_sdbootutil" ] || check_space_in_boot

# On AArch64 we switched from 64k PAGE_SIZE to 4k PAGE_SIZE. Unfortunately
# btrfs can only use file systems created with the same PAGE_SIZE. So we
Expand Down Expand Up @@ -189,38 +249,12 @@ EOF
;;
post)
# Flag to trigger /etc/init.d/purge-kernels on next reboot (fate#312018)
# ... but avoid the first installion (bsc#1180058)
# ... but avoid the first installation (bsc#1180058)
if [ "$1" -gt 1 ]; then
trigger_purge_kernels || script_rc=$?
fi

# compat stuff for /boot.
# if /boot and /usr are not speparate partitions we can just link
# the kernel there to save space. Otherwise copy.
if mountpoint -q /boot || mountpoint -q /usr; then
copy_or_link="cp -a --remove-destination"
separate_boot='1'
else
copy_or_link="ln -sf"
separate_boot=""
fi

for x in "$image" sysctl.conf System.map config; do
if [ "$separate_boot" = 1 ] || [ ! -e /boot/$x-"$kernelrelease"-"$flavor" ]; then
$copy_or_link .."$modules_dir"/$x /boot/$x-"$kernelrelease"-"$flavor" || script_rc=$?
if [ -e "$modules_dir"/.$x.hmac ]; then
$copy_or_link .."$modules_dir"/.$x.hmac /boot/.$x-"$kernelrelease"-"$flavor".hmac || script_rc=$?
fi
fi
done

for x in /boot/"$image" /boot/initrd; do
rm -f "$x"
ln -s "${x##*/}-$kernelrelease-$flavor" "$x"
done
rm -f /boot/.vmlinuz.hmac
[ ! -e "/boot/.vmlinuz-$kernelrelease-$flavor.hmac" ] ||
ln -s ".vmlinuz-$kernelrelease-$flavor.hmac" /boot/.vmlinuz.hmac
[ -n "$is_sdbootutil" ] || copy_or_link_legacy_files

# Add symlinks of compatible modules to /lib/modules/$krel/weak-updates/,
# run depmod and dracut
Expand All @@ -231,7 +265,7 @@ EOF
script_rc=1
fi

[ "$INITRD_IN_POSTTRANS" ] || update_bootloader
[ "$INITRD_IN_POSTTRANS" ] || update_bootloader_entry
[ -z "$certs" ] || /usr/lib/module-init-tools/kernel-scriptlets/cert-$op --ca-check 1 --certs "$certs" "$@" || script_rc=$?
;;
preun)
Expand All @@ -258,8 +292,15 @@ EOF
run_wm2 --remove-kernel "$kernelrelease"-"$flavor"
fi

# can't check $1 as kernel is usually multiversion. So need to check if
# that particular kernel was actually removed from disk.
if [ -n "$is_sdbootutil" ] && [ ! -e /.buildenv ] \
&& [ ! -e /lib/modules/"$kernelrelease-$flavor/$image" ]; then
if [ -z "$TRANSACTIONAL_UPDATE" ]; then
/usr/bin/sdbootutil --image="$image" remove-kernel "$kernelrelease-$flavor"
fi
# remove fstab check once perl-Bootloader can cope with it
if [ -f /etc/fstab ]; then
elif [ -f /etc/fstab ]; then
if [ -x /usr/lib/bootloader/bootloader_entry ]; then
/usr/lib/bootloader/bootloader_entry \
remove \
Expand All @@ -273,10 +314,10 @@ EOF
[ -z "$certs" ] || /usr/lib/module-init-tools/kernel-scriptlets/cert-$op --ca-check 1 --certs "$certs" "$@"
;;
posttrans)
if test -x /usr/lib/module-init-tools/regenerate-initrd-posttrans; then
if [ -x /usr/lib/module-init-tools/regenerate-initrd-posttrans ]; then
/bin/bash -c 'set +e; /usr/lib/module-init-tools/regenerate-initrd-posttrans' || script_rc=$?
fi
[ ! "$INITRD_IN_POSTTRANS" ] || update_bootloader
[ ! "$INITRD_IN_POSTTRANS" ] || update_bootloader_entry
;;
*)
echo Unknown scriptlet "$op" >&2
Expand Down
33 changes: 30 additions & 3 deletions regenerate-initrd-posttrans
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,29 @@ if [ ! -x "$DRACUT" ]; then
exit 0
fi

if [ -e "/usr/bin/sdbootutil" ] && /usr/bin/sdbootutil is-installed; then
is_sdbootutil=1
fi

dir=/run/regenerate-initrd

if ! test -d "$dir"; then
exit 0
fi

# If we are inside a transaction and using a separate /boot/efi
# partition (ESP) then we cannot touch it, as we will escape the
# atomicity promise. We need to delay the call to this script after
# the transaction has been completed. The component that will call
# again regenerate-initrd-posttrans to generate the new initrd is the
# sdbootutil snapper plugin, but we need to signal it from inside the
# transaction via /dev/shm
if [ -n "$is_sdbootutil" ] && [ -n "$TRANSACTIONAL_UPDATE" ] && [ -z "$TRANSACTIONAL_UPDATE_INITRD" ]; then
mkdir -p /dev/shm/transactional-update$dir
cp -a $dir/* /dev/shm/transactional-update$dir
exit 0
fi

for f in "$dir"/*; do
case $f in
"$dir/*")
Expand All @@ -38,8 +56,13 @@ done

if test -e "$dir/all"; then
rm "$dir"/*
[ "$SKIP_REGENERATE_INITRD_ALL" = 1 ] ||
"$DRACUT" -f --regenerate-all
[ "$SKIP_REGENERATE_INITRD_ALL" = 1 ] || {
if [ -n "$is_sdbootutil" ] && [ -z "$TRANSACTIONAL_UPDATE" ]; then
/usr/bin/sdbootutil --no-reuse-initrd add-all-kernels
else
"$DRACUT" -f --regenerate-all
fi
}
exit
fi
err=0
Expand All @@ -59,8 +82,12 @@ for f in "$dir"/*; do
echo $0: skipping invalid kernel version "$dir/$kver"
continue
}
if ! "$DRACUT" -f --kver "$kver"; then
if [ -n "$is_sdbootutil" ] && [ -z "$TRANSACTIONAL_UPDATE" ]; then
if ! /usr/bin/sdbootutil --no-reuse-initrd add-kernel "$kver"; then
err=$?
fi
elif ! "$DRACUT" -f --kver "$kver"; then
err=$?
fi
done
exit $err
Loading

0 comments on commit 5ba5b23

Please sign in to comment.