How to chainload another kernel with kexec inside a LUKS2 + LVM2 partition?

I have a Debian 11 installation with the following partition layout:

path format mount point
/dev/nvme0n1p7 ext4 (no encryption) /boot (Debian 11)
/dev/nvme0n1p8 dm-crypt LUKS2 LVM2 (named vg_main)
/dev/mapper/vg_main-lv_swap swap
/dev/mapper/vg_main-lv_debian ext4 / (Debian 11)
/dev/mapper/vg_main-lv_ubuntu ext4 / (Ubuntu 22.04)

The /boot for Ubuntu, lives inside its root file system (/dev/mapper/vg_main-lv_ubuntu).
I’d like to kexec the Ubuntu kernel after booting the Debian kernel that lives in the unencrypted /boot partition that unlocks the LUKS2 partition.

I’d like to use the systemd kexec strategy described here.

Is there a way to pass any specific kernel parameter to Debian 11 (that I will do in a specially created GRUB2 entry for this) to tell systemd to simple kexec the Ubuntu 22.04 kernel?


Solution:

Worked as per @telcoM suggestion, with just few adjustments:

/etc/systemd/system/ubuntu-kexec.target

[Unit]
Description=Ubuntu kexec target
Requires=sysinit.target ubuntu-kexec.service
After=sysinit.target ubuntu-kexec.service
AllowIsolate=yes

/etc/systemd/system/ubuntu-kexec.service

[Unit]
Description=Ubuntu kexec service
DefaultDependencies=no
Requires=sysinit.target
After=sysinit.target
Before=shutdown.target umount.target final.target

[Service]
Type=oneshot
ExecStart=/usr/bin/mount -o defaults,ro /dev/mapper/vg_main-lv_ubuntu /mnt
ExecStart=/usr/sbin/kexec -l /mnt/boot/vmlinuz --initrd=/mnt/boot/initrd.img --command-line="root=/dev/mapper/vg_main-lv_ubuntu resume=UUID=[MY-UUID-HERE] ro quiet splash"
ExecStart=/usr/bin/systemctl kexec

[Install]
WantedBy=ubuntu-kexec.target
Asked By: Eduardo

||

You might want to set up a ubuntu-kexec.target which would be essentially a stripped-down version of multi-user.target, with basically:

[Unit]
Description=Kexec an Ubuntu kernel from within an encrypted partition
Requires=basic.target    #You might get by with just sysinit.target here
Conflicts=rescue.service rescue.target
Wants=ubuntu-kexec.service
After=basic.target rescue.service rescue.target ubuntu-kexec.service
AllowIsolate=yes

This would invoke a ubuntu-kexec.service, which you would create to run your kexec command.

The kernel parameter would then be: systemd.unit=ubuntu-kexec.target, similar to how rescue.target or emergency.target can be invoked when necessary.

The idea is that ubuntu-kexec.target will pull in basic.target (or even just sysinit.target) to get the filesystems mounted, and then pull in the ubuntu-kexec.service which runs the actual kexec command line.

As far as I know, you can specify just one systemd.unit= option, and since you need to specify "boot as usual up to sysinit.target/basic.target, then pull in ubuntu-kexec.service, you’ll need a unit of type *.target to specify all the necessary details.

Answered By: telcoM
Categories: Answers Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.