deploy: one-shot boot without the bootloader's help (terra runs Limine)

`install <config> localhost` assumed systemd-boot. terra's CachyOS boots
Limine, so it stopped at "/boot/loader/entries doesn't exist" — the check
added in e538788 doing its job, but with nowhere to go.

Limine cannot help here at all: `bootctl status` lists it as
`✗ One-shot entry control`, and CachyOS's pacman hooks regenerate
limine.conf regardless. So drop below the bootloader entirely and use the
firmware's own BootNext, pointing at a temporary UEFI entry that
EFI-stub-boots the installer kernel straight off the ESP. That keeps the
property which makes this safe to attempt: BootNext is spent by that one
boot, so a failed try still comes back on the normal bootloader.

  - picked at runtime: systemd-boot loader entry when $BOOT/loader/entries
    exists, else arm_efi_bootnext(). jupiter/neptun and terra-after-install
    keep the systemd-boot path.
  - `efibootmgr --create-only`, NOT `--create`: the latter pushes the entry
    to the front of BootOrder, which would make a wiped installer the
    permanent default if the install died halfway.
  - the EFI stub loads initrd= from the volume it was loaded from, so this
    mode stages on --print-esp-path rather than --print-boot-path.
  - stale entries from an earlier attempt are removed before adding one, and
    homelab-auto-install.service deletes the entry as soon as it boots, so
    nothing lingers in NVRAM pointing at a reformatted partition.
  - label matching is EXACT ("Homelab Installer"); a prefix match would have
    deleted this box's Windows or Limine entry.

Verified against terra's real NVRAM (read-only): the label parser picks out
Limine/UEFI OS/Windows by exact name and rejects prefixes, and both branches
run end-to-end under stubs — BootNext mode emits the right --disk/--part,
loader path and initrd= cmdline, systemd-boot mode still writes its entry and
never calls efibootmgr.

README/CLAUDE.md corrected: terra runs Limine, not systemd-boot.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-24 02:35:33 +02:00
co-authored by Claude Opus 4.8
parent e538788907
commit c87fd3b1f2
4 changed files with 148 additions and 17 deletions
+105 -11
View File
@@ -132,6 +132,72 @@ disks_backing() {
lsblk -rnso NAME,TYPE "$1" 2>/dev/null | awk '$2 == "disk" { print "/dev/" $1 }'
}
# Label of the temporary UEFI boot entry arm_efi_bootnext() creates. Also the
# key the ISO uses to delete it again once it has booted (see flake.nix).
EFI_LABEL="Homelab Installer"
# Boot numbers of every UEFI entry with exactly this label, one per line.
# efibootmgr prints `Boot0002* Limine<TAB>HD(1,GPT,...)/\EFI\...`, so the
# label runs from past the "Boot####* " prefix up to the first TAB.
# (Character classes spelled out rather than {4}: mawk predates ERE intervals.)
efi_entries_named() {
efibootmgr 2>/dev/null | awk -v want="$1" '
/^Boot[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]/ {
num = substr($0, 5, 4)
rest = substr($0, 9)
sub(/^\*/, "", rest); sub(/^ +/, "", rest)
split(rest, parts, "\t")
if (parts[1] == want) print num
}'
}
# Arm a genuine one-shot boot of the staged installer WITHOUT any help from the
# bootloader: create a UEFI boot entry that EFI-stub-boots the kernel straight
# off the ESP, and point BootNext at it.
#
# Needed because "boot this once, then go back to normal" is not something
# every bootloader can do. systemd-boot has it; terra's CachyOS runs Limine,
# which reports `One-shot entry control: ✗` and has no equivalent, and whose
# limine.conf is regenerated by pacman hooks anyway. BootNext is a firmware
# feature, so it works underneath all of them — and the firmware clears it
# after that one boot, which is what keeps the "a failed attempt still comes
# back on the normal bootloader" property that makes this safe to try.
arm_efi_bootnext() {
local esp="$1" cmdline="$2"
local esp_src esp_disk esp_part num n
need efibootmgr
esp_src="$(findmnt -no SOURCE --nofsroot --target "$esp")" \
|| die "couldn't resolve $esp to a device"
esp_disk="$(disks_backing "$esp_src" | head -1 || true)"
esp_part="$(cat "/sys/class/block/$(basename "$esp_src")/partition" 2>/dev/null || true)"
{ [ -n "$esp_disk" ] && [ -n "$esp_part" ]; } \
|| die "couldn't work out the disk + partition number of the ESP ($esp -> $esp_src)"
# Clear anything left by an earlier attempt first, so repeated runs don't
# slowly fill NVRAM with dead entries pointing at a wiped partition.
for n in $(efi_entries_named "$EFI_LABEL"); do
echo ">> removing stale UEFI entry Boot$n ($EFI_LABEL)"
efibootmgr -q -B -b "$n"
done
# --create-only, NOT --create: the latter also pushes the entry to the front
# of BootOrder, which would make a wiped installer the permanent default if
# anything went wrong. This way the entry is reachable through BootNext and
# nothing else, i.e. exactly once.
#
# The EFI stub loads `initrd=` off the volume it was itself loaded from, so
# the path is relative to the ESP root and uses backslashes.
efibootmgr -q --create-only --disk "$esp_disk" --part "$esp_part" \
--label "$EFI_LABEL" \
--loader '\homelab-installer\bzImage' \
--unicode "initrd=\\homelab-installer\\initrd $cmdline"
num="$(efi_entries_named "$EFI_LABEL" | head -1)"
[ -n "$num" ] || die "efibootmgr did not create a '$EFI_LABEL' entry"
efibootmgr -q --bootnext "$num"
echo ">> UEFI BootNext -> Boot$num ($EFI_LABEL); BootOrder untouched"
}
# Sets tb / cpio / bbox — the kexec tarball plus the static cpio+gzip that
# kexec-run.sh needs on PATH to rebuild its initrd.
#
@@ -194,15 +260,30 @@ local_install_prepare_and_reboot() {
need stat
need df
# Where to stage the installer, and how to make the box boot it exactly once.
#
# systemd-boot keeps its entries on $BOOT — the XBOOTLDR partition when there
# is one, the ESP otherwise — which is not always /boot. Hardcoding /boot on
# a box that mounts its ESP elsewhere just creates a directory on the root
# filesystem, and then reboots into an entry the firmware never sees.
local boot
boot="$(bootctl --print-boot-path 2>/dev/null)" \
|| die "bootctl couldn't locate the boot partition — is systemd-boot installed here?"
[ -d "$boot/loader/entries" ] \
|| die "$boot/loader/entries doesn't exist — systemd-boot isn't installed on this box"
# filesystem and then reboots into an entry the firmware never sees.
#
# No systemd-boot (terra's CachyOS runs Limine) means no `bootctl set-oneshot`,
# so fall back to the firmware's own BootNext — see arm_efi_bootnext(). That
# path EFI-stub-boots the kernel directly, which requires it to sit on the ESP
# itself rather than on a separate XBOOTLDR.
local boot boot_mode esp
esp="$(bootctl --print-esp-path 2>/dev/null)" \
|| die "bootctl couldn't locate the ESP — is this box actually UEFI-booted?"
boot="$(bootctl --print-boot-path 2>/dev/null || echo "$esp")"
if [ -d "$boot/loader/entries" ]; then
boot_mode=systemd-boot
else
boot_mode=efi-bootnext
boot="$esp"
need efibootmgr
echo ">> no systemd-boot entries at $boot/loader/entries — arming the firmware's"
echo " own BootNext instead (bootloader in charge here: $(bootctl status 2>/dev/null | awk '/Product:/ {$1=""; print substr($0,2); exit}' || echo unknown))"
fi
# No default/auto-picked location — the wrong disk here is destroyed
# mid-install (see the OS-disk check below), so this always asks rather
@@ -273,7 +354,7 @@ local_install_prepare_and_reboot() {
echo " -> $osdisk_real ** WIPED, unattended, after the reboot **"
# Unquoted on purpose: collapses the one-per-line list onto one line.
echo " staging: $stagedir (on $(echo $stage_disks))"
echo " entry: $boot/loader/entries/homelab-installer.conf (one-shot)"
echo " one-shot: $boot_mode"
read -rp ">> type 'yes' to build the installer, reboot into it and wipe $osdisk_real: " ok
[ "$ok" = yes ] || die "aborted"
fi
@@ -330,15 +411,28 @@ local_install_prepare_and_reboot() {
mnt_point="$(findmnt -no TARGET --target "$stagedir")"
iso_relpath="$(printf '/%s/%s' "${stagedir#"$mnt_point"}" homelab-installer.iso | tr -s /)"
cat >"$boot/loader/entries/homelab-installer.conf" <<EOF
# Identical either way — only the mechanism that gets the kernel booted with
# it differs.
local cmdline
cmdline="nohibernate root=fstab loglevel=4 lsm=landlock,yama,bpf findiso=$iso_relpath homelab.install=$config homelab.keypart=$boot_partuuid"
case "$boot_mode" in
systemd-boot)
cat >"$boot/loader/entries/homelab-installer.conf" <<EOF
title Homelab Installer ($config, findiso)
linux /homelab-installer/bzImage
initrd /homelab-installer/initrd
options nohibernate root=fstab loglevel=4 lsm=landlock,yama,bpf findiso=$iso_relpath homelab.install=$config homelab.keypart=$boot_partuuid
options $cmdline
EOF
bootctl set-oneshot homelab-installer.conf
echo ">> systemd-boot one-shot entry armed"
;;
efi-bootnext)
arm_efi_bootnext "$boot" "$cmdline"
;;
esac
echo ">> one-shot boot into the installer, then rebooting — it will finish this install itself"
bootctl set-oneshot homelab-installer.conf
echo ">> rebooting into the installer — it will finish this install itself"
systemctl reboot
}