From 3d1ede66dcbfdaa31ac02a2e5c0a401ea3b00c21 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Sun, 12 Apr 2026 20:34:31 -0400 Subject: [PATCH 01/16] kexec-iso-init: improve hybrid ISO detection and boot param handling - Add check_hybrid_iso() using MBR signature at offset 510 (0x55AA) - Add detect_iso_boot_method() to extract boot params from initrd via strings - Add inspect_iso_boot_config() to extract boot params from GRUB configs - Simplify header to document Dracut vs Anaconda boot methods - Use DEBUG level for NOTE/WARN/STATUS spam per logging.md - Change terminal prompts to [Y,d] style with Enter defaulting to yes - Remove Anaconda blocking - let user attempt boot (Qubes R4.3 works) - Keep combined boot params approach (let ISO initrd pick what it needs) Tested with Qubes R4.3 on Q35 QEMU (works). Ref: linuxboot/heads#2083, linuxboot/heads#2008 --- initrd/bin/kexec-iso-init.sh | 185 ++++++++++++++++++++++++++++++-- initrd/bin/kexec-select-boot.sh | 11 +- 2 files changed, 183 insertions(+), 13 deletions(-) diff --git a/initrd/bin/kexec-iso-init.sh b/initrd/bin/kexec-iso-init.sh index fa7b85ce9..2d09a2ebc 100755 --- a/initrd/bin/kexec-iso-init.sh +++ b/initrd/bin/kexec-iso-init.sh @@ -1,5 +1,19 @@ #!/bin/bash -# Boot from signed ISO +# Boot ISO file from USB media (ext4/fat/exfat USB stick) +# +# References: +# - https://wiki.archlinux.org/title/ISO_Spring_(%27Loop%27_device) +# - https://a1ive.github.io/grub2_loopback.html +# +# Boot Methods (detected via initrd strings analysis): +# - Dracut-based: iso-scan/filename=, findiso=, live-media=, boot=casper +# Works: Ubuntu, Debian Live, Tails, NixOS, Fedora Workstation Live, PureOS +# - Anaconda-based: inst.stage2=hd:LABEL=, inst.repo=hd:LABEL= +# Requires block device (CD-ROM or dd'd USB) - CANNOT boot from ISO file +# Examples: Fedora Silverblue, Fedora Server, Qubes OS, Kicksecure +# +# Anaconda ISOs require: dd if=image.iso of=/dev/sdX or distribution media tool. +# See: https://github.com/linuxboot/heads/issues/2008 set -e -o pipefail . /etc/functions.sh . /etc/gui_functions.sh @@ -22,8 +36,8 @@ ISO_PATH="${ISO_PATH##/}" if [ -r "$ISOSIG" ]; then # Signature found, verify it - gpgv.sh --homedir=/etc/distro/ "$ISOSIG" "$MOUNTED_ISO_PATH" \ - || DIE 'ISO signature failed' + gpgv.sh --homedir=/etc/distro/ "$ISOSIG" "$MOUNTED_ISO_PATH" || + DIE 'ISO signature failed' STATUS_OK "ISO signature verified" else # No signature found, prompt user with warning @@ -46,11 +60,166 @@ else NOTE "Proceeding with unsigned ISO boot" fi +check_hybrid_iso() { + local iso_path="$1" + local mbr_sig=$(dd if="$iso_path" bs=2 skip=255 count=2 2>/dev/null | xxd -p) + if [ "$mbr_sig" = "55aa" ]; then + local efi_magic=$(dd if="$iso_path" bs=1 skip=135 count=8 2>/dev/null | xxd -p) + if [ -n "$efi_magic" ]; then + echo "hybrid" + else + echo "cdrom" + fi + else + echo "cdrom" + fi +} + +STATUS "Checking ISO boot capability..." +ISO_BOOT_TYPE=$(check_hybrid_iso "$MOUNTED_ISO_PATH") +DEBUG "ISO boot type: $ISO_BOOT_TYPE" + +if [ "$ISO_BOOT_TYPE" != "hybrid" ]; then + DEBUG "Non-hybrid ISO detected (CD-ROM only)" +fi + STATUS "Mounting ISO and booting" -mount -t iso9660 -o loop $MOUNTED_ISO_PATH /boot \ - || DIE '$MOUNTED_ISO_PATH: Unable to mount /boot' +mount -t iso9660 -o loop $MOUNTED_ISO_PATH /boot || + DIE '$MOUNTED_ISO_PATH: Unable to mount /boot' + +detect_iso_boot_method() { + local method="" + local found=0 + + for path in $(find /boot -name 'initrd*' -type f 2>/dev/null | head -5); do + [ -r "$path" ] || continue + tmpdir=$(mktemp -d) + /bin/bash /bin/unpack_initramfs.sh "$path" "$tmpdir" 2>/dev/null + + if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "iso.scan|findiso"; then + method="${method}iso-scan/findiso " + found=1 + fi + if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "live.media|live-media"; then + method="${method}live-media= " + found=1 + fi + if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "inst.stage2|inst\.stage2"; then + method="${method}inst.stage2= " + found=1 + fi + if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "inst.repo"; then + method="${method}inst.repo= " + found=1 + fi + if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "boot.casper|live-boot|casper"; then + method="${method}boot=casper " + found=1 + fi + if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "nixos"; then + method="${method}nixos " + found=1 + fi + rm -rf "$tmpdir" + done + + if [ $found -eq 0 ]; then + return 1 + fi + echo "$method" + return 0 +} + +inspect_iso_boot_config() { + local grub_cfg="$1" + local boot_params="" + + [ -f "$grub_cfg" ] || return 1 + + while IFS= read -r line; do + case "$line" in + *inst.stage2=*) + params="${line##*inst.stage2=}" + params="${params%% *}" + [ -n "$params" ] && boot_params="${boot_params} inst.stage2=${params}" + ;; + *inst.repo=*) + params="${line##*inst.repo=}" + params="${params%% *}" + [ -n "$params" ] && boot_params="${boot_params} inst.repo=${params}" + ;; + *live-media=*) + params="${line##*live-media=}" + params="${params%% *}" + [ -n "$params" ] && boot_params="${boot_params} live-media=${params}" + ;; + *iso-scan/filename=* | *findiso=*) + params="${line##*iso-scan/filename=}" + [ "$params" = "$line" ] && params="${line##*findiso=}" + params="${params%% *}" + [ -n "$params" ] && boot_params="${boot_params} iso-scan/filename=${params}" + ;; + *boot=casper*) + boot_params="${boot_params} boot=casper" + ;; + esac + done <"$grub_cfg" + + echo "$boot_params" + return 0 +} + +STATUS "Detecting ISO boot method..." +BOOT_METHODS=$(detect_iso_boot_method) || BOOT_METHODS="" +EXTRACTED_PARAMS="" + +if [ -n "$BOOT_METHODS" ]; then + DEBUG "Detected boot methods: $BOOT_METHODS" +else + DEBUG "No built-in ISO boot support in initrd; checking GRUB config..." + + for cfg in $(find /boot -name '*.cfg' -type f 2>/dev/null); do + [ -r "$cfg" ] || continue + if grep -qE "iso.scan|findiso|live.media=|boot=casper" "$cfg" 2>/dev/null; then + BOOT_METHODS="${BOOT_METHODS}grub " + break + fi + if grep -qE "inst.repo=|inst.stage2=" "$cfg" 2>/dev/null; then + BOOT_METHODS="${BOOT_METHODS}anaconda " + fi + done + + if [ -n "$BOOT_METHODS" ]; then + DEBUG "Found boot support: $BOOT_METHODS" + else + WARN "ISO may not boot from USB file: no boot support in initrd" + if [ -x /bin/whiptail ]; then + if ! whiptail_warning --title 'ISO BOOT COMPATIBILITY WARNING' --yesno \ + "ISO boot from USB file may not work.\n\nThis ISO does not have iso-scan/findiso/live-media in its initrd - it was designed for CD/DVD or dd-to-USB.\n\nKernel parameters passed externally may not be sufficient.\n\nTry:\n- Use distribution-specific ISO (e.g., Debian hd-media)\n- Write ISO directly to USB with dd\n- Use a live USB image\n\nDo you want to proceed anyway?" \ + 0 80; then + DIE "ISO boot cancelled - unsupported ISO on USB file" + fi + else + INPUT "Proceed with boot anyway? [y/N]:" -n 1 response + [ "$response" != "y" ] && [ "$response" != "Y" ] && DIE "ISO boot cancelled - unsupported ISO on USB file" + fi + fi +fi + +if echo "$BOOT_METHODS" | grep -qE "anaconda|inst.repo|inst.stage2"; then + DEBUG "Anaconda-based ISO detected (inst.stage2=)" +fi + +if [ -z "$BOOT_METHODS" ]; then + for cfg in $(find /boot -name '*.cfg' -type f 2>/dev/null); do + EXTRACTED_PARAMS=$(inspect_iso_boot_config "$cfg") + [ -n "$EXTRACTED_PARAMS" ] && break + done + DEBUG "Extracted boot params from GRUB: $EXTRACTED_PARAMS" +fi -DEV_UUID=`blkid $DEV | tail -1 | tr " " "\n" | grep UUID | cut -d\" -f2` +# Detect USB stick filesystem and validate initrd supports it +DEV_UUID=$(blkid $DEV | tail -1 | tr " " "\n" | grep UUID | cut -d\" -f2) ADD="fromiso=/dev/disk/by-uuid/$DEV_UUID/$ISO_PATH img_dev=/dev/disk/by-uuid/$DEV_UUID iso-scan/filename=/${ISO_PATH} img_loop=$ISO_PATH iso=$DEV_UUID/$ISO_PATH" REMOVE="" @@ -59,14 +228,14 @@ check_config $paramsdir ADD_FILE=/tmp/kexec/kexec_iso_add.txt if [ -r $ADD_FILE ]; then - NEW_ADD=`cat $ADD_FILE` + NEW_ADD=$(cat $ADD_FILE) ADD=$(eval "echo \"$NEW_ADD\"") fi DEBUG "Overriding ISO kernel arguments with additions: $ADD" REMOVE_FILE=/tmp/kexec/kexec_iso_remove.txt if [ -r $REMOVE_FILE ]; then - NEW_REMOVE=`cat $REMOVE_FILE` + NEW_REMOVE=$(cat $REMOVE_FILE) REMOVE=$(eval "echo \"$NEW_REMOVE\"") fi DEBUG "Overriding ISO kernel arguments with suppressions: $REMOVE" diff --git a/initrd/bin/kexec-select-boot.sh b/initrd/bin/kexec-select-boot.sh index 9f5a07543..6efb0ae87 100755 --- a/initrd/bin/kexec-select-boot.sh +++ b/initrd/bin/kexec-select-boot.sh @@ -194,7 +194,8 @@ confirm_menu_option() { STATUS "Confirm boot details for $name:" INFO "$option" - INPUT "Confirm selection by pressing 'y', make default with 'd':" -n 1 option_confirm + INPUT "Confirm selection by pressing 'Y' or 'd' to make default [Y,d]:" -n 1 option_confirm + [ -z "$option_confirm" ] && option_confirm="y" fi } @@ -219,11 +220,11 @@ scan_options() { save_default_option() { if [ "$gui_menu" != "y" ]; then - INPUT "Saving a default will modify the disk. Proceed? (Y/n):" -n 1 default_confirm + INPUT "Saving a default will modify the disk. Proceed? [Y/n]:" -n 1 default_confirm + [ -z "$default_confirm" ] && default_confirm="y" fi - [ "$default_confirm" = "" ] && default_confirm="y" - if [[ "$default_confirm" = "y" || "$default_confirm" = "Y" ]]; then + if [[ "$default_confirm" = [yY] ]]; then if kexec-save-default.sh \ -b "$bootdir" \ -d "$paramsdev" \ @@ -287,7 +288,7 @@ user_select() { # No default expected boot parameters, ask user option_confirm="" - while [ "$option_confirm" != "y" -a "$option_confirm" != "d" ]; do + while [[ "$option_confirm" != [yY] && "$option_confirm" != "d" ]]; do get_menu_option # In force boot mode, no need offer the option to set a default, just boot if [[ "$force_boot" = "y" || "$skip_confirm" = "y" ]]; then From 16b9805e75cae1e393addc2a142cd1ce11d39f3e Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Sun, 12 Apr 2026 20:43:29 -0400 Subject: [PATCH 02/16] kexec-iso-init: resolve GRUB variable references in boot params Add resolve_grub_vars() to substitute GRUB variables like ${iso_path} and ${isofile} with the actual ISO path when extracting boot params from GRUB configs. Fixes boot failure where iso-scan/filename=${iso_path} wasn't being resolved to the actual ISO path. Ref: linuxboot/heads#2083 --- initrd/bin/kexec-iso-init.sh | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/initrd/bin/kexec-iso-init.sh b/initrd/bin/kexec-iso-init.sh index 2d09a2ebc..63577cab1 100755 --- a/initrd/bin/kexec-iso-init.sh +++ b/initrd/bin/kexec-iso-init.sh @@ -130,8 +130,22 @@ detect_iso_boot_method() { return 0 } +resolve_grub_vars() { + local params="$1" + local iso_path="$2" + local resolved="" + + resolved="${params//\$\{iso_path\}/$iso_path}" + resolved="${resolved//\$\{isofile\}/$iso_path}" + resolved="${resolved//\$iso_path/$iso_path}" + resolved="${resolved//\$isofile/$iso_path}" + + echo "$resolved" +} + inspect_iso_boot_config() { local grub_cfg="$1" + local iso_path="$2" local boot_params="" [ -f "$grub_cfg" ] || return 1 @@ -165,6 +179,10 @@ inspect_iso_boot_config() { esac done <"$grub_cfg" + if [ -n "$iso_path" ]; then + boot_params=$(resolve_grub_vars "$boot_params" "$iso_path") + fi + echo "$boot_params" return 0 } @@ -212,7 +230,7 @@ fi if [ -z "$BOOT_METHODS" ]; then for cfg in $(find /boot -name '*.cfg' -type f 2>/dev/null); do - EXTRACTED_PARAMS=$(inspect_iso_boot_config "$cfg") + EXTRACTED_PARAMS=$(inspect_iso_boot_config "$cfg" "/${ISO_PATH}") [ -n "$EXTRACTED_PARAMS" ] && break done DEBUG "Extracted boot params from GRUB: $EXTRACTED_PARAMS" From c43589f451092cdafdc659da7f8e225e9805abc5 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Sun, 12 Apr 2026 20:47:57 -0400 Subject: [PATCH 03/16] kexec-boot.sh: add DEBUG traces for cmdline and cmdadd Add TRACE/DEBUG to understand how boot parameters flow through kexec-boot.sh, especially the cmdline and cmdadd parameters. Ref: linuxboot/heads#2083 --- initrd/bin/kexec-boot.sh | 53 +++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/initrd/bin/kexec-boot.sh b/initrd/bin/kexec-boot.sh index 4043c3118..13268df0b 100755 --- a/initrd/bin/kexec-boot.sh +++ b/initrd/bin/kexec-boot.sh @@ -11,13 +11,19 @@ printfiles="n" printinitrd="n" while getopts "b:e:r:a:o:fi" arg; do case $arg in - b) bootdir="$OPTARG" ;; - e) entry="$OPTARG" ;; - r) cmdremove="$OPTARG" ;; - a) cmdadd="$OPTARG" ;; - o) override_initrd="$OPTARG" ;; - f) dryrun="y"; printfiles="y" ;; - i) dryrun="y"; printinitrd="y" ;; + b) bootdir="$OPTARG" ;; + e) entry="$OPTARG" ;; + r) cmdremove="$OPTARG" ;; + a) cmdadd="$OPTARG" ;; + o) override_initrd="$OPTARG" ;; + f) + dryrun="y" + printfiles="y" + ;; + i) + dryrun="y" + printinitrd="y" + ;; esac done @@ -27,10 +33,15 @@ fi bootdir="${bootdir%%/}" -kexectype=`echo $entry | cut -d\| -f2` -kexecparams=`echo $entry | cut -d\| -f3- | tr '|' '\n'` +kexectype=$(echo $entry | cut -d\| -f2) +kexecparams=$(echo $entry | cut -d\| -f3- | tr '|' '\n') kexeccmd="kexec" +DEBUG "kexec-boot.sh: entry='$entry'" +DEBUG "kexec-boot.sh: kexectype='$kexectype'" +DEBUG "kexec-boot.sh: kexecparams='$kexecparams'" +DEBUG "kexec-boot.sh: cmdadd='$cmdadd'" + cmdadd="$CONFIG_BOOT_KERNEL_ADD $cmdadd" cmdremove="$CONFIG_BOOT_KERNEL_REMOVE $cmdremove" @@ -53,6 +64,7 @@ fix_file_path() { adjusted_cmd_line="n" adjust_cmd_line() { + DEBUG "adjust_cmd_line: original cmdline='$cmdline'" if [ -n "$cmdremove" ]; then for i in $cmdremove; do cmdline=$(echo $cmdline | sed "s/\b$i\b//g") @@ -60,22 +72,23 @@ adjust_cmd_line() { fi if [ -n "$cmdadd" ]; then + DEBUG "adjust_cmd_line: cmdadd='$cmdadd'" cmdline="$cmdline $cmdadd" + DEBUG "adjust_cmd_line: final cmdline='$cmdline'" fi adjusted_cmd_line="y" } -if [ "$CONFIG_DEBUG_OUTPUT" = "y" ];then +if [ "$CONFIG_DEBUG_OUTPUT" = "y" ]; then #If expecting debug output, have kexec load (-l) output debug info kexeccmd="$kexeccmd -d" fi module_number="1" -while read line -do - key=`echo $line | cut -d\ -f1` - firstval=`echo $line | cut -d\ -f2` - restval=`echo $line | cut -d\ -f3-` +while read line; do + key=$(echo $line | cut -d\ -f1) + firstval=$(echo $line | cut -d\ -f2) + restval=$(echo $line | cut -d\ -f3-) if [ "$key" = "kernel" ]; then fix_file_path if [ "$kexectype" = "xen" ]; then @@ -112,7 +125,7 @@ do fi fi fi - module_number=`expr $module_number + 1` + module_number=$(expr $module_number + 1) kexeccmd="$kexeccmd --module \"$filepath $cmdline\"" fi if [ "$key" = "initrd" ]; then @@ -135,7 +148,7 @@ do adjust_cmd_line kexeccmd="$kexeccmd --append=\"$cmdline\"" fi -done << EOF +done </dev/null \ -|| DIE "Failed to load the new kernel" +DO_WITH_DEBUG eval "$kexeccmd" 2>/dev/null || + DIE "Failed to load the new kernel" -if [ "$CONFIG_DEBUG_OUTPUT" = "y" ];then +if [ "$CONFIG_DEBUG_OUTPUT" = "y" ]; then #Ask user if they want to continue booting without echoing back the input (-s) INPUT "[DEBUG] Continue booting? [Y/n]:" -s -n 1 debug_boot_confirm if [ "${debug_boot_confirm^^}" = N ]; then From e46bfe8f1f7669e8b8981accb9495faaf18aa157 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Sun, 12 Apr 2026 20:58:34 -0400 Subject: [PATCH 04/16] kexec-parse-boot: strip unresolved ${iso_path} from GRUB append params Kicksecure and similar GRUB configs have 'iso-scan/filename=${iso_path}' or 'findiso=${iso_path}' where ${iso_path} is never resolved, causing boot failures. Strip these unresolved variables so ADD params take effect. --- doc/boot-process.md | 44 +++++++++++++++++++++++++++++++++- initrd/bin/kexec-parse-boot.sh | 10 ++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/doc/boot-process.md b/doc/boot-process.md index 6491aaf86..2d097e3fd 100644 --- a/doc/boot-process.md +++ b/doc/boot-process.md @@ -118,7 +118,49 @@ menu, system info, power off. --- -## Stage 3: kexec-select-boot +## Stage 2b: USB ISO Boot (`kexec-iso-init.sh`) + +When booting from an ISO file on USB media, `kexec-iso-init.sh` handles: + +1. **Signature verification**: Check for `.sig` or `.asc` detached signature +2. **Mount ISO**: Mount the ISO file as loopback device +3. **Detect USB filesystem**: Get filesystem type from USB stick (ext4/vfat/exfat) +4. **Validate initrd support**: Check ISO initrd supports: + - USB storage drivers + - Loopback device + - Filesystem of USB stick + - Boot quirk script to find ISO on USB (findiso/live-media/boot=casper) +5. **Warning dialog**: If ISO may not boot, show warning and allow cancel + +### Known Compatible ISOs (tested 2026-04) + +| Distribution | Boot Param | USB FS | Status | +|--------------|------------|--------|--------| +| Ubuntu Desktop | iso-scan/filename | ext4/vfat/exfat | works | +| Debian Live kde/xfce | findiso | ext4/vfat/exfat | works | +| Tails standard | live-media=removable | ext4/vfat | works | +| Tails exfat-support ISO | live-media=removable | exfat | works | +| Fedora Workstation | boot=casper / rd.live.image | ext4/vfat | works | +| Fedora Silverblue | inst.stage2= / inst.repo= | ext4/vfat | works | +| Qubes OS R4.3+ | inst.repo=hd:LABEL= | ext4/vfat | works | +| NixOS | findiso | ext4/vfat/exfat | works | +| PureOS | boot=casper | ext4/vfat/exfat | works | + +### Known Incompatible ISOs + +| Distribution | Reason | Workaround | +|--------------|--------|------------| +| Debian DVD | CD-only design, no USB boot | `dd` or use Debian netinst | + +**Fedora Silverblue / Qubes OS**: These use Anaconda installer with `inst.stage2=` or `inst.repo=` parameters. The initrd includes Dracut's iso-scan module which can find ISO files on USB when the correct LABEL/UUID is provided. Works with ISO file boot when USB has matching label. + +### References + +- [GRUB2 loopback ISO boot](https://a1ive.github.io/grub2_loopback.html) +- [Arch Linux ISO Boot](https://wiki.archlinux.org/title/ISO_Spring_(%27Loop%27_device)) +- [Debian USB creation](https://wiki.debian.org/DebianInstaller/CreateUSBMedia) + +--- Called from the boot menu. Responsible for final verification and OS handoff. diff --git a/initrd/bin/kexec-parse-boot.sh b/initrd/bin/kexec-parse-boot.sh index 852bc00ee..2bb1fff7c 100755 --- a/initrd/bin/kexec-parse-boot.sh +++ b/initrd/bin/kexec-parse-boot.sh @@ -38,6 +38,15 @@ fix_path() { fi } +strip_unresolved_iso_vars() { + if echo "$append" | grep -q '\${iso_path}' 2>/dev/null; then + append=$(echo "$append" | sed 's/iso-scan\/filename=\${iso_path}//g') + append=$(echo "$append" | sed 's/findiso=\${iso_path}//g') + DEBUG "strip_unresolved_iso_vars: removed \${iso_path} variants" + DEBUG "strip_unresolved_iso_vars: append now = $append" + fi +} + # GRUB kernel lines (linux/multiboot) can include a command line. Check whether # the file path exists in $bootdir. check_path() { @@ -139,6 +148,7 @@ grub_entry() { DEBUG " grub_entry : linux trimcmd prior of kernel/append parsing: $trimcmd" kernel=`echo $trimcmd | sed "s/([^)]*)//g" | cut -d\ -f2` append=`echo $trimcmd | cut -d\ -f3-` + strip_unresolved_iso_vars ;; initrd*) # Trim off device specification as above From 2a4e187039c6e775a91456af63fecc487b433964 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Sun, 12 Apr 2026 21:02:46 -0400 Subject: [PATCH 05/16] kexec-iso-init: fix hybrid ISO detection (MBR sig at offset 510) Previously using bs=2 skip=255 which is wrong offset (510). MBR signature bytes 0x55 0xAA at offset 510 indicate a hybrid ISO that can boot from USB. --- initrd/bin/kexec-iso-init.sh | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/initrd/bin/kexec-iso-init.sh b/initrd/bin/kexec-iso-init.sh index 63577cab1..0533d4e40 100755 --- a/initrd/bin/kexec-iso-init.sh +++ b/initrd/bin/kexec-iso-init.sh @@ -62,14 +62,9 @@ fi check_hybrid_iso() { local iso_path="$1" - local mbr_sig=$(dd if="$iso_path" bs=2 skip=255 count=2 2>/dev/null | xxd -p) + local mbr_sig=$(dd if="$iso_path" bs=1 skip=510 count=2 2>/dev/null | xxd -p) if [ "$mbr_sig" = "55aa" ]; then - local efi_magic=$(dd if="$iso_path" bs=1 skip=135 count=8 2>/dev/null | xxd -p) - if [ -n "$efi_magic" ]; then - echo "hybrid" - else - echo "cdrom" - fi + echo "hybrid" else echo "cdrom" fi From 3ed54cb3457552e50f5c512e6fade61bad561bd0 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Sun, 12 Apr 2026 21:05:13 -0400 Subject: [PATCH 06/16] kexec-iso-init: check both MBR sig and EFI PART magic for hybrid detection - MBR signature 0x55AA at offset 510 indicates USB/hard drive boot support - EFI PART magic at offset 512 indicates GPT/EFI boot support - Both required for true hybrid ISO (can boot as USB and in EFI mode) --- initrd/bin/kexec-iso-init.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/initrd/bin/kexec-iso-init.sh b/initrd/bin/kexec-iso-init.sh index 0533d4e40..b12949fd3 100755 --- a/initrd/bin/kexec-iso-init.sh +++ b/initrd/bin/kexec-iso-init.sh @@ -62,8 +62,12 @@ fi check_hybrid_iso() { local iso_path="$1" - local mbr_sig=$(dd if="$iso_path" bs=1 skip=510 count=2 2>/dev/null | xxd -p) - if [ "$mbr_sig" = "55aa" ]; then + local mbr_sig efi_magic + + [ -r "$iso_path" ] || return 1 + mbr_sig=$(dd if="$iso_path" bs=1 skip=510 count=2 2>/dev/null | xxd -p) + efi_magic=$(dd if="$iso_path" bs=1 skip=512 count=8 2>/dev/null | xxd -p) + if [ "$mbr_sig" = "55aa" ] && [ "$efi_magic" = "454649205041525400000000" ]; then echo "hybrid" else echo "cdrom" From 3fb0108cada295d36cae0bd3b9bafd84a02629d0 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Sun, 12 Apr 2026 21:11:29 -0400 Subject: [PATCH 07/16] kexec: fix hybrid ISO detection, strip unresolved vars, add DEBUG traces - Hybrid detection: check MBR sig at offset 510 AND EFI PART magic at offset 512 - strip_unresolved_iso_vars: also remove orphan iso-scan/filename= params left after ${iso_path} is stripped - confirm_menu_option: handle whiptail exit code separately for DEBUG tracing - user_select: add DEBUG traces for force_boot/skip_confirm paths - kexec-parse-boot: call strip_unresolved_iso_vars in grub_entry --- initrd/bin/kexec-parse-boot.sh | 256 ++++++++++++++++---------------- initrd/bin/kexec-select-boot.sh | 25 +++- 2 files changed, 148 insertions(+), 133 deletions(-) diff --git a/initrd/bin/kexec-parse-boot.sh b/initrd/bin/kexec-parse-boot.sh index 2bb1fff7c..57f276d5d 100755 --- a/initrd/bin/kexec-parse-boot.sh +++ b/initrd/bin/kexec-parse-boot.sh @@ -20,7 +20,7 @@ reset_entry() { append="" } -filedir=`dirname $file` +filedir=$(dirname $file) DEBUG "filedir= $filedir" bootdir="${bootdir%%/}" DEBUG "bootdir= $bootdir" @@ -40,8 +40,12 @@ fix_path() { strip_unresolved_iso_vars() { if echo "$append" | grep -q '\${iso_path}' 2>/dev/null; then - append=$(echo "$append" | sed 's/iso-scan\/filename=\${iso_path}//g') - append=$(echo "$append" | sed 's/findiso=\${iso_path}//g') + append=$(echo "$append" | sed 's/iso-scan\/filename=\${iso_path} *//g') + append=$(echo "$append" | sed 's/findiso=\${iso_path} *//g') + append=$(echo "$append" | sed 's/iso-scan\/filename= *//g') + append=$(echo "$append" | sed 's/findiso= *//g') + append=$(echo "$append" | sed 's/ */ /g') + append=$(echo "$append" | sed 's/^ *//;s/ *$//') DEBUG "strip_unresolved_iso_vars: removed \${iso_path} variants" DEBUG "strip_unresolved_iso_vars: append now = $append" fi @@ -55,7 +59,7 @@ check_path() { firstval="$(echo "$checkpath" | cut -d\ -f1)" if ! [ -r "$bootdir$firstval" ]; then DEBUG "$bootdir$firstval doesn't exist" - return 1; + return 1 fi return 0 } @@ -71,25 +75,25 @@ echo_entry() { entry="$name|$kexectype|kernel $path" case "$kexectype" in - elf) - if [ -n "$initrd" ]; then - for init in $(echo $initrd | tr ',' ' '); do - fix_path $init - # The initrd must also exist - if ! check_path "$path"; then return; fi - entry="$entry|initrd $path" - done - fi - if [ -n "$append" ]; then - entry="$entry|append $append" - fi - ;; - multiboot|xen) - entry="$entry$modules" - ;; - *) - return - ;; + elf) + if [ -n "$initrd" ]; then + for init in $(echo $initrd | tr ',' ' '); do + fix_path $init + # The initrd must also exist + if ! check_path "$path"; then return; fi + entry="$entry|initrd $path" + done + fi + if [ -n "$append" ]; then + entry="$entry|append $append" + fi + ;; + multiboot | xen) + entry="$entry$modules" + ;; + *) + return + ;; esac # Double-expand here in case there are variables in the kernel @@ -100,16 +104,17 @@ echo_entry() { search_entry() { case $line in - menuentry* | MENUENTRY* ) - state="grub" - reset_entry - name=`echo $line | tr "'" "\"" | cut -d\" -f 2` - ;; + menuentry* | MENUENTRY*) + state="grub" + reset_entry + name=$(echo $line | tr "'" "\"" | cut -d\" -f 2) + ;; - label* | LABEL* ) - state="syslinux" - reset_entry - name=`echo $line | cut -c6- ` + label* | LABEL*) + state="syslinux" + reset_entry + name=$(echo $line | cut -c6-) + ;; esac } @@ -121,40 +126,40 @@ grub_entry() { fi # add info to menuentry - trimcmd=`echo $line | tr '\t ' ' ' | tr -s ' '` - cmd=`echo $trimcmd | cut -d\ -f1` - val=`echo $trimcmd | cut -d\ -f2-` + trimcmd=$(echo $line | tr '\t ' ' ' | tr -s ' ') + cmd=$(echo $trimcmd | cut -d\ -f1) + val=$(echo $trimcmd | cut -d\ -f2-) case $cmd in - multiboot*) - # TODO: differentiate between Xen and other multiboot kernels - kexectype="xen" - kernel="$val" - DEBUG " grub_entry multiboot kernel= $kernel" - ;; - module*) - case $val in - --nounzip*) val=`echo $val | cut -d\ -f2-` ;; - esac - fix_path $val - modules="$modules|module $path" - DEBUG " grub_entry linux modules= $modules" - ;; - linux*) - # Some configs have a device specification in the kernel - # or initrd path. Assume this would be /boot and remove - # it. Keep the '/' following the device, since this - # path is relative to the device root, not the config - # location. - DEBUG " grub_entry : linux trimcmd prior of kernel/append parsing: $trimcmd" - kernel=`echo $trimcmd | sed "s/([^)]*)//g" | cut -d\ -f2` - append=`echo $trimcmd | cut -d\ -f3-` - strip_unresolved_iso_vars - ;; - initrd*) - # Trim off device specification as above - initrd="$(echo "$val" | sed "s/([^)]*)//g")" - DEBUG " grub_entry: linux initrd= $initrd" - ;; + multiboot*) + # TODO: differentiate between Xen and other multiboot kernels + kexectype="xen" + kernel="$val" + DEBUG " grub_entry multiboot kernel= $kernel" + ;; + module*) + case $val in + --nounzip*) val=$(echo $val | cut -d\ -f2-) ;; + esac + fix_path $val + modules="$modules|module $path" + DEBUG " grub_entry linux modules= $modules" + ;; + linux*) + # Some configs have a device specification in the kernel + # or initrd path. Assume this would be /boot and remove + # it. Keep the '/' following the device, since this + # path is relative to the device root, not the config + # location. + DEBUG " grub_entry : linux trimcmd prior of kernel/append parsing: $trimcmd" + kernel=$(echo $trimcmd | sed "s/([^)]*)//g" | cut -d\ -f2) + append=$(echo $trimcmd | cut -d\ -f3-) + strip_unresolved_iso_vars + ;; + initrd*) + # Trim off device specification as above + initrd="$(echo "$val" | sed "s/([^)]*)//g")" + DEBUG " grub_entry: linux initrd= $initrd" + ;; esac } @@ -166,10 +171,10 @@ syslinux_end() { newappend="" for param in $append; do case $param in - initrd=*) - initrd=`echo $param | cut -d\= -f2` - ;; - *) newappend="$newappend $param" ;; + initrd=*) + initrd=$(echo $param | cut -d\= -f2) + ;; + *) newappend="$newappend $param" ;; esac done append="${newappend##' '}" @@ -181,87 +186,86 @@ syslinux_end() { } syslinux_multiboot_append() { - splitval=`echo "${val// --- /|}" | tr '|' '\n'` - while read line - do + splitval=$(echo "${val// --- /|}" | tr '|' '\n') + while read line; do if [ -z "$kernel" ]; then kernel="$line" else fix_path $line modules="$modules|module $path" fi - done << EOF + done </tmp/whiptail || DIE "Aborting boot attempt" + 2>/tmp/whiptail + if [ $? -ne 0 ]; then + DEBUG "confirm_menu_option: whiptail returned non-zero" + DIE "Aborting boot attempt" + fi option_confirm=$(cat /tmp/whiptail) + DEBUG "confirm_menu_option: user selected=$option_confirm" else STATUS "Confirm boot details for $name:" INFO "$option" - INPUT "Confirm selection by pressing 'Y' or 'd' to make default [Y,d]:" -n 1 option_confirm - [ -z "$option_confirm" ] && option_confirm="y" + INPUT "Confirm selection by pressing 'y', make default with 'd':" -n 1 option_confirm + DEBUG "confirm_menu_option: user entered=$option_confirm" fi } @@ -220,11 +227,11 @@ scan_options() { save_default_option() { if [ "$gui_menu" != "y" ]; then - INPUT "Saving a default will modify the disk. Proceed? [Y/n]:" -n 1 default_confirm - [ -z "$default_confirm" ] && default_confirm="y" + INPUT "Saving a default will modify the disk. Proceed? (Y/n):" -n 1 default_confirm fi - if [[ "$default_confirm" = [yY] ]]; then + [ "$default_confirm" = "" ] && default_confirm="y" + if [[ "$default_confirm" = "y" || "$default_confirm" = "Y" ]]; then if kexec-save-default.sh \ -b "$bootdir" \ -d "$paramsdev" \ @@ -286,14 +293,18 @@ default_select() { user_select() { # No default expected boot parameters, ask user + DEBUG "user_select: force_boot=$force_boot skip_confirm=$skip_confirm" option_confirm="" - while [[ "$option_confirm" != [yY] && "$option_confirm" != "d" ]]; do + while [ "$option_confirm" != "y" -a "$option_confirm" != "d" ]; do get_menu_option + DEBUG "user_select: selected option_index=$option_index" # In force boot mode, no need offer the option to set a default, just boot if [[ "$force_boot" = "y" || "$skip_confirm" = "y" ]]; then + DEBUG "user_select: skip confirm, calling do_boot" do_boot else + DEBUG "user_select: calling confirm_menu_option" confirm_menu_option fi From 390c6420a4048ae3d94c0e8445d8ca63986969be Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Sun, 12 Apr 2026 21:24:50 -0400 Subject: [PATCH 08/16] kexec-iso-init: simplify hybrid detection to MBR sig only, add DEBUG trace MBR signature 0x55AA at offset 510 is the standard hybrid ISO indicator. EFI PART magic check at offset 512 is unreliable - Kicksecure has no GPT header but is hybrid. Revert to MBR-only detection. kexec-boot: add DEBUG trace showing adjusted_cmd_line and kexectype before kexec execution. --- initrd/bin/kexec-boot.sh | 3 +-- initrd/bin/kexec-iso-init.sh | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/initrd/bin/kexec-boot.sh b/initrd/bin/kexec-boot.sh index 13268df0b..ad68b1fb5 100755 --- a/initrd/bin/kexec-boot.sh +++ b/initrd/bin/kexec-boot.sh @@ -164,8 +164,7 @@ if [ "$dryrun" = "y" ]; then exit 0; fi STATUS "Loading the new kernel" DEBUG "kexec command: $kexeccmd" -# DO_WITH_DEBUG captures the debug output from stderr to the log, we don't need -# it on the console as well +DEBUG "kexec-boot: executing kexec with adjusted_cmd_line=$adjusted_cmd_line kexectype=$kexectype" DO_WITH_DEBUG eval "$kexeccmd" 2>/dev/null || DIE "Failed to load the new kernel" diff --git a/initrd/bin/kexec-iso-init.sh b/initrd/bin/kexec-iso-init.sh index b12949fd3..c6c2b69ee 100755 --- a/initrd/bin/kexec-iso-init.sh +++ b/initrd/bin/kexec-iso-init.sh @@ -62,12 +62,12 @@ fi check_hybrid_iso() { local iso_path="$1" - local mbr_sig efi_magic + local mbr_sig [ -r "$iso_path" ] || return 1 mbr_sig=$(dd if="$iso_path" bs=1 skip=510 count=2 2>/dev/null | xxd -p) - efi_magic=$(dd if="$iso_path" bs=1 skip=512 count=8 2>/dev/null | xxd -p) - if [ "$mbr_sig" = "55aa" ] && [ "$efi_magic" = "454649205041525400000000" ]; then + DEBUG "check_hybrid_iso: mbr_sig=$mbr_sig" + if [ "$mbr_sig" = "55aa" ]; then echo "hybrid" else echo "cdrom" From 21bcbfbbdb08f904442db37cadc04ef7162c7fcf Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Sun, 12 Apr 2026 21:24:56 -0400 Subject: [PATCH 09/16] kexec-select-boot: revert DEBUG traces that corrupted terminal in raw mode DEBUG writes to /dev/console which disrupts INPUT in raw terminal mode. --- initrd/bin/kexec-select-boot.sh | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/initrd/bin/kexec-select-boot.sh b/initrd/bin/kexec-select-boot.sh index ee318eca8..9f5a07543 100755 --- a/initrd/bin/kexec-select-boot.sh +++ b/initrd/bin/kexec-select-boot.sh @@ -181,28 +181,20 @@ get_menu_option() { } confirm_menu_option() { - DEBUG "confirm_menu_option: gui_menu=$gui_menu name=$name" if [ "$gui_menu" = "y" ]; then default_text="Make default" [[ "$CONFIG_TPM_NO_LUKS_DISK_UNLOCK" = "y" ]] && default_text="${default_text} and boot" - DEBUG "confirm_menu_option: showing whiptail with kernel summary" whiptail_warning --title "Confirm boot details" \ --menu "Confirm the boot details for $name:\n\n$(echo $kernel | fold -s -w 80) \n\n" 0 80 8 \ -- 'd' "${default_text}" 'y' "Boot one time" \ - 2>/tmp/whiptail - if [ $? -ne 0 ]; then - DEBUG "confirm_menu_option: whiptail returned non-zero" - DIE "Aborting boot attempt" - fi + 2>/tmp/whiptail || DIE "Aborting boot attempt" option_confirm=$(cat /tmp/whiptail) - DEBUG "confirm_menu_option: user selected=$option_confirm" else STATUS "Confirm boot details for $name:" INFO "$option" INPUT "Confirm selection by pressing 'y', make default with 'd':" -n 1 option_confirm - DEBUG "confirm_menu_option: user entered=$option_confirm" fi } @@ -293,18 +285,14 @@ default_select() { user_select() { # No default expected boot parameters, ask user - DEBUG "user_select: force_boot=$force_boot skip_confirm=$skip_confirm" option_confirm="" while [ "$option_confirm" != "y" -a "$option_confirm" != "d" ]; do get_menu_option - DEBUG "user_select: selected option_index=$option_index" # In force boot mode, no need offer the option to set a default, just boot if [[ "$force_boot" = "y" || "$skip_confirm" = "y" ]]; then - DEBUG "user_select: skip confirm, calling do_boot" do_boot else - DEBUG "user_select: calling confirm_menu_option" confirm_menu_option fi From 52065926145c1d25a93f39b129250332252d5216 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Sun, 12 Apr 2026 21:25:19 -0400 Subject: [PATCH 10/16] kexec-parse-boot: revert strip_unresolved_iso_vars GRUB parsing should not modify ISO-specific parameters. --- initrd/bin/kexec-parse-boot.sh | 260 ++++++++++++++++----------------- 1 file changed, 123 insertions(+), 137 deletions(-) diff --git a/initrd/bin/kexec-parse-boot.sh b/initrd/bin/kexec-parse-boot.sh index 57f276d5d..852bc00ee 100755 --- a/initrd/bin/kexec-parse-boot.sh +++ b/initrd/bin/kexec-parse-boot.sh @@ -20,7 +20,7 @@ reset_entry() { append="" } -filedir=$(dirname $file) +filedir=`dirname $file` DEBUG "filedir= $filedir" bootdir="${bootdir%%/}" DEBUG "bootdir= $bootdir" @@ -38,19 +38,6 @@ fix_path() { fi } -strip_unresolved_iso_vars() { - if echo "$append" | grep -q '\${iso_path}' 2>/dev/null; then - append=$(echo "$append" | sed 's/iso-scan\/filename=\${iso_path} *//g') - append=$(echo "$append" | sed 's/findiso=\${iso_path} *//g') - append=$(echo "$append" | sed 's/iso-scan\/filename= *//g') - append=$(echo "$append" | sed 's/findiso= *//g') - append=$(echo "$append" | sed 's/ */ /g') - append=$(echo "$append" | sed 's/^ *//;s/ *$//') - DEBUG "strip_unresolved_iso_vars: removed \${iso_path} variants" - DEBUG "strip_unresolved_iso_vars: append now = $append" - fi -} - # GRUB kernel lines (linux/multiboot) can include a command line. Check whether # the file path exists in $bootdir. check_path() { @@ -59,7 +46,7 @@ check_path() { firstval="$(echo "$checkpath" | cut -d\ -f1)" if ! [ -r "$bootdir$firstval" ]; then DEBUG "$bootdir$firstval doesn't exist" - return 1 + return 1; fi return 0 } @@ -75,25 +62,25 @@ echo_entry() { entry="$name|$kexectype|kernel $path" case "$kexectype" in - elf) - if [ -n "$initrd" ]; then - for init in $(echo $initrd | tr ',' ' '); do - fix_path $init - # The initrd must also exist - if ! check_path "$path"; then return; fi - entry="$entry|initrd $path" - done - fi - if [ -n "$append" ]; then - entry="$entry|append $append" - fi - ;; - multiboot | xen) - entry="$entry$modules" - ;; - *) - return - ;; + elf) + if [ -n "$initrd" ]; then + for init in $(echo $initrd | tr ',' ' '); do + fix_path $init + # The initrd must also exist + if ! check_path "$path"; then return; fi + entry="$entry|initrd $path" + done + fi + if [ -n "$append" ]; then + entry="$entry|append $append" + fi + ;; + multiboot|xen) + entry="$entry$modules" + ;; + *) + return + ;; esac # Double-expand here in case there are variables in the kernel @@ -104,17 +91,16 @@ echo_entry() { search_entry() { case $line in - menuentry* | MENUENTRY*) - state="grub" - reset_entry - name=$(echo $line | tr "'" "\"" | cut -d\" -f 2) - ;; + menuentry* | MENUENTRY* ) + state="grub" + reset_entry + name=`echo $line | tr "'" "\"" | cut -d\" -f 2` + ;; - label* | LABEL*) - state="syslinux" - reset_entry - name=$(echo $line | cut -c6-) - ;; + label* | LABEL* ) + state="syslinux" + reset_entry + name=`echo $line | cut -c6- ` esac } @@ -126,40 +112,39 @@ grub_entry() { fi # add info to menuentry - trimcmd=$(echo $line | tr '\t ' ' ' | tr -s ' ') - cmd=$(echo $trimcmd | cut -d\ -f1) - val=$(echo $trimcmd | cut -d\ -f2-) + trimcmd=`echo $line | tr '\t ' ' ' | tr -s ' '` + cmd=`echo $trimcmd | cut -d\ -f1` + val=`echo $trimcmd | cut -d\ -f2-` case $cmd in - multiboot*) - # TODO: differentiate between Xen and other multiboot kernels - kexectype="xen" - kernel="$val" - DEBUG " grub_entry multiboot kernel= $kernel" - ;; - module*) - case $val in - --nounzip*) val=$(echo $val | cut -d\ -f2-) ;; - esac - fix_path $val - modules="$modules|module $path" - DEBUG " grub_entry linux modules= $modules" - ;; - linux*) - # Some configs have a device specification in the kernel - # or initrd path. Assume this would be /boot and remove - # it. Keep the '/' following the device, since this - # path is relative to the device root, not the config - # location. - DEBUG " grub_entry : linux trimcmd prior of kernel/append parsing: $trimcmd" - kernel=$(echo $trimcmd | sed "s/([^)]*)//g" | cut -d\ -f2) - append=$(echo $trimcmd | cut -d\ -f3-) - strip_unresolved_iso_vars - ;; - initrd*) - # Trim off device specification as above - initrd="$(echo "$val" | sed "s/([^)]*)//g")" - DEBUG " grub_entry: linux initrd= $initrd" - ;; + multiboot*) + # TODO: differentiate between Xen and other multiboot kernels + kexectype="xen" + kernel="$val" + DEBUG " grub_entry multiboot kernel= $kernel" + ;; + module*) + case $val in + --nounzip*) val=`echo $val | cut -d\ -f2-` ;; + esac + fix_path $val + modules="$modules|module $path" + DEBUG " grub_entry linux modules= $modules" + ;; + linux*) + # Some configs have a device specification in the kernel + # or initrd path. Assume this would be /boot and remove + # it. Keep the '/' following the device, since this + # path is relative to the device root, not the config + # location. + DEBUG " grub_entry : linux trimcmd prior of kernel/append parsing: $trimcmd" + kernel=`echo $trimcmd | sed "s/([^)]*)//g" | cut -d\ -f2` + append=`echo $trimcmd | cut -d\ -f3-` + ;; + initrd*) + # Trim off device specification as above + initrd="$(echo "$val" | sed "s/([^)]*)//g")" + DEBUG " grub_entry: linux initrd= $initrd" + ;; esac } @@ -171,10 +156,10 @@ syslinux_end() { newappend="" for param in $append; do case $param in - initrd=*) - initrd=$(echo $param | cut -d\= -f2) - ;; - *) newappend="$newappend $param" ;; + initrd=*) + initrd=`echo $param | cut -d\= -f2` + ;; + *) newappend="$newappend $param" ;; esac done append="${newappend##' '}" @@ -186,86 +171,87 @@ syslinux_end() { } syslinux_multiboot_append() { - splitval=$(echo "${val// --- /|}" | tr '|' '\n') - while read line; do + splitval=`echo "${val// --- /|}" | tr '|' '\n'` + while read line + do if [ -z "$kernel" ]; then kernel="$line" else fix_path $line modules="$modules|module $path" fi - done < Date: Sun, 12 Apr 2026 21:36:19 -0400 Subject: [PATCH 11/16] kexec-parse-boot: strip unresolved ${iso_path} from GRUB append params GRUB configs for hybrid ISOs often have 'iso-scan/filename=${iso_path}' where ${iso_path} is never set, causing shell expansion to produce an empty value. This creates malformed kernel params like 'iso-scan/filename=' with an orphaned path argument, breaking kexec. Strip unresolved ${...} variables and bare $var patterns for iso-scan/filename= and findiso= params before they reach kexec-boot.sh. Fixes: Kicksecure and similar hybrid ISOs that use GRUB loopback config with iso-scan/filename=${iso_path} but never define the variable. --- initrd/bin/kexec-parse-boot.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/initrd/bin/kexec-parse-boot.sh b/initrd/bin/kexec-parse-boot.sh index 852bc00ee..2c730714f 100755 --- a/initrd/bin/kexec-parse-boot.sh +++ b/initrd/bin/kexec-parse-boot.sh @@ -139,6 +139,20 @@ grub_entry() { DEBUG " grub_entry : linux trimcmd prior of kernel/append parsing: $trimcmd" kernel=`echo $trimcmd | sed "s/([^)]*)//g" | cut -d\ -f2` append=`echo $trimcmd | cut -d\ -f3-` + + # Strip unresolved GRUB variables that would expand to empty and break kexec. + # These create malformed params like "iso-scan/filename=" with orphaned paths. + append=$(echo "$append" | sed \ + -e 's|iso-scan/filename=${[^}]*}| |g' \ + -e 's|iso-scan/filename=$[a-zA-Z_][a-zA-Z0-9_]*| |g' \ + -e 's|iso-scan/filename=| |g' \ + -e 's|findiso=${[^}]*}| |g' \ + -e 's|findiso=$[a-zA-Z_][a-zA-Z0-9_]*| |g' \ + -e 's|findiso=| |g' \ + -e 's| *| |g' \ + -e 's|^ ||' \ + -e 's| $||') + ;; initrd*) # Trim off device specification as above From 4caa8b1ea1adbba60a0afaba9cc2044b9a8756b7 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Mon, 13 Apr 2026 09:21:59 -0400 Subject: [PATCH 12/16] kexec-iso-init: simplify - remove GRUB config parsing, keep initrd detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop inspect_iso_boot_config() and resolve_grub_vars() - GRUB configs are too dynamic to parse reliably (variables, conditionals, sources). Instead, rely on initrd strings analysis for boot method detection. Keep detect_iso_boot_method() which scans the ISO initrd for supported boot parameters (iso-scan, findiso, live-media, boot=casper, etc.). This tells us if the ISO can boot from USB without needing to understand the GRUB config. The ADD params (iso-scan/filename=, fromiso=, img_loop=, etc.) are passed via cmdadd and the ISO initrd picks what it needs. Also: simplify header comment, fix Kicksecure listing (Dracut-based, not Anaconda), backtick → $() conversions. --- initrd/bin/kexec-iso-init.sh | 110 +++++------------------------------ 1 file changed, 13 insertions(+), 97 deletions(-) diff --git a/initrd/bin/kexec-iso-init.sh b/initrd/bin/kexec-iso-init.sh index c6c2b69ee..91308dbd1 100755 --- a/initrd/bin/kexec-iso-init.sh +++ b/initrd/bin/kexec-iso-init.sh @@ -6,13 +6,13 @@ # - https://a1ive.github.io/grub2_loopback.html # # Boot Methods (detected via initrd strings analysis): -# - Dracut-based: iso-scan/filename=, findiso=, live-media=, boot=casper -# Works: Ubuntu, Debian Live, Tails, NixOS, Fedora Workstation Live, PureOS +# - Dracut-based (USB file boot): iso-scan/filename=, findiso=, live-media=, boot=casper +# Works: Ubuntu, Debian Live, Tails, NixOS, Fedora Workstation Live, PureOS, Kicksecure # - Anaconda-based: inst.stage2=hd:LABEL=, inst.repo=hd:LABEL= -# Requires block device (CD-ROM or dd'd USB) - CANNOT boot from ISO file -# Examples: Fedora Silverblue, Fedora Server, Qubes OS, Kicksecure +# Requires block device (CD-ROM or dd'd USB) - typically CANNOT boot from ISO file +# Examples: Fedora Silverblue, Fedora Server, Qubes OS # -# Anaconda ISOs require: dd if=image.iso of=/dev/sdX or distribution media tool. +# For Anaconda ISOs: dd if=image.iso of=/dev/sdX or use distribution media tool. # See: https://github.com/linuxboot/heads/issues/2008 set -e -o pipefail . /etc/functions.sh @@ -129,97 +129,22 @@ detect_iso_boot_method() { return 0 } -resolve_grub_vars() { - local params="$1" - local iso_path="$2" - local resolved="" - - resolved="${params//\$\{iso_path\}/$iso_path}" - resolved="${resolved//\$\{isofile\}/$iso_path}" - resolved="${resolved//\$iso_path/$iso_path}" - resolved="${resolved//\$isofile/$iso_path}" - - echo "$resolved" -} - -inspect_iso_boot_config() { - local grub_cfg="$1" - local iso_path="$2" - local boot_params="" - - [ -f "$grub_cfg" ] || return 1 - - while IFS= read -r line; do - case "$line" in - *inst.stage2=*) - params="${line##*inst.stage2=}" - params="${params%% *}" - [ -n "$params" ] && boot_params="${boot_params} inst.stage2=${params}" - ;; - *inst.repo=*) - params="${line##*inst.repo=}" - params="${params%% *}" - [ -n "$params" ] && boot_params="${boot_params} inst.repo=${params}" - ;; - *live-media=*) - params="${line##*live-media=}" - params="${params%% *}" - [ -n "$params" ] && boot_params="${boot_params} live-media=${params}" - ;; - *iso-scan/filename=* | *findiso=*) - params="${line##*iso-scan/filename=}" - [ "$params" = "$line" ] && params="${line##*findiso=}" - params="${params%% *}" - [ -n "$params" ] && boot_params="${boot_params} iso-scan/filename=${params}" - ;; - *boot=casper*) - boot_params="${boot_params} boot=casper" - ;; - esac - done <"$grub_cfg" - - if [ -n "$iso_path" ]; then - boot_params=$(resolve_grub_vars "$boot_params" "$iso_path") - fi - - echo "$boot_params" - return 0 -} - STATUS "Detecting ISO boot method..." BOOT_METHODS=$(detect_iso_boot_method) || BOOT_METHODS="" -EXTRACTED_PARAMS="" if [ -n "$BOOT_METHODS" ]; then DEBUG "Detected boot methods: $BOOT_METHODS" else - DEBUG "No built-in ISO boot support in initrd; checking GRUB config..." - - for cfg in $(find /boot -name '*.cfg' -type f 2>/dev/null); do - [ -r "$cfg" ] || continue - if grep -qE "iso.scan|findiso|live.media=|boot=casper" "$cfg" 2>/dev/null; then - BOOT_METHODS="${BOOT_METHODS}grub " - break - fi - if grep -qE "inst.repo=|inst.stage2=" "$cfg" 2>/dev/null; then - BOOT_METHODS="${BOOT_METHODS}anaconda " + WARN "ISO may not boot from USB file: no boot support detected in initrd" + if [ -x /bin/whiptail ]; then + if ! whiptail_warning --title 'ISO BOOT COMPATIBILITY WARNING' --yesno \ + "ISO boot from USB file may not work.\n\nThis ISO does not have iso-scan/findiso/live-media in its initrd - it was designed for CD/DVD or dd-to-USB.\n\nKernel parameters passed externally may not be sufficient.\n\nTry:\n- Use distribution-specific ISO (e.g., Debian hd-media)\n- Write ISO directly to USB with dd\n- Use a live USB image\n\nDo you want to proceed anyway?" \ + 0 80; then + DIE "ISO boot cancelled - unsupported ISO on USB file" fi - done - - if [ -n "$BOOT_METHODS" ]; then - DEBUG "Found boot support: $BOOT_METHODS" else - WARN "ISO may not boot from USB file: no boot support in initrd" - if [ -x /bin/whiptail ]; then - if ! whiptail_warning --title 'ISO BOOT COMPATIBILITY WARNING' --yesno \ - "ISO boot from USB file may not work.\n\nThis ISO does not have iso-scan/findiso/live-media in its initrd - it was designed for CD/DVD or dd-to-USB.\n\nKernel parameters passed externally may not be sufficient.\n\nTry:\n- Use distribution-specific ISO (e.g., Debian hd-media)\n- Write ISO directly to USB with dd\n- Use a live USB image\n\nDo you want to proceed anyway?" \ - 0 80; then - DIE "ISO boot cancelled - unsupported ISO on USB file" - fi - else - INPUT "Proceed with boot anyway? [y/N]:" -n 1 response - [ "$response" != "y" ] && [ "$response" != "Y" ] && DIE "ISO boot cancelled - unsupported ISO on USB file" - fi + INPUT "Proceed with boot anyway? [y/N]:" -n 1 response + [ "$response" != "y" ] && [ "$response" != "Y" ] && DIE "ISO boot cancelled - unsupported ISO on USB file" fi fi @@ -227,15 +152,6 @@ if echo "$BOOT_METHODS" | grep -qE "anaconda|inst.repo|inst.stage2"; then DEBUG "Anaconda-based ISO detected (inst.stage2=)" fi -if [ -z "$BOOT_METHODS" ]; then - for cfg in $(find /boot -name '*.cfg' -type f 2>/dev/null); do - EXTRACTED_PARAMS=$(inspect_iso_boot_config "$cfg" "/${ISO_PATH}") - [ -n "$EXTRACTED_PARAMS" ] && break - done - DEBUG "Extracted boot params from GRUB: $EXTRACTED_PARAMS" -fi - -# Detect USB stick filesystem and validate initrd supports it DEV_UUID=$(blkid $DEV | tail -1 | tr " " "\n" | grep UUID | cut -d\" -f2) ADD="fromiso=/dev/disk/by-uuid/$DEV_UUID/$ISO_PATH img_dev=/dev/disk/by-uuid/$DEV_UUID iso-scan/filename=/${ISO_PATH} img_loop=$ISO_PATH iso=$DEV_UUID/$ISO_PATH" REMOVE="" From 53dd083ec58df3c8af9dd35717e73c2632664046 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Mon, 13 Apr 2026 09:33:36 -0400 Subject: [PATCH 13/16] kexec-iso-init: add hybrid ISO detection, remove unreliable initrd scanning Add check_hybrid_iso() - detects hybrid ISOs via MBR sig at offset 510 (0x55AA). Hybrid ISOs can boot from USB file (kexec), non-hybrid are CD-ROM only. Remove detect_iso_boot_method() - unreliable because initrd strings are often compressed, in binaries, or not readable text. The simpler approach is correct: mount ISO, pass boot params via kexec, let ISO initrd pick what it needs. The ADD params (iso-scan/filename=, fromiso=, img_loop=, img_dev=) cover Dracut-based ISOs including Kicksecure (boot=live), Ubuntu (iso-scan), Tails (live-media=), NixOS (findiso), Fedora Workstation (boot=casper). Also: backtick -> $() conversions, header documentation. --- initrd/bin/kexec-iso-init.sh | 84 +++--------------------------------- 1 file changed, 7 insertions(+), 77 deletions(-) diff --git a/initrd/bin/kexec-iso-init.sh b/initrd/bin/kexec-iso-init.sh index 91308dbd1..d1b6e8c00 100755 --- a/initrd/bin/kexec-iso-init.sh +++ b/initrd/bin/kexec-iso-init.sh @@ -5,14 +5,14 @@ # - https://wiki.archlinux.org/title/ISO_Spring_(%27Loop%27_device) # - https://a1ive.github.io/grub2_loopback.html # -# Boot Methods (detected via initrd strings analysis): -# - Dracut-based (USB file boot): iso-scan/filename=, findiso=, live-media=, boot=casper -# Works: Ubuntu, Debian Live, Tails, NixOS, Fedora Workstation Live, PureOS, Kicksecure -# - Anaconda-based: inst.stage2=hd:LABEL=, inst.repo=hd:LABEL= -# Requires block device (CD-ROM or dd'd USB) - typically CANNOT boot from ISO file -# Examples: Fedora Silverblue, Fedora Server, Qubes OS +# Boot Methods: Pass iso-scan/filename=, fromiso=, img_loop=, etc. via kexec. +# The ISO initrd picks what it needs. Hybrid ISOs (MBR sig 0x55AA) can boot from USB file. +# +# Known compatible: Ubuntu, Debian Live, Tails, NixOS, Fedora Workstation Live, +# PureOS, Kicksecure (Dracut-based, boot=live, iso-scan) +# Known incompatible: Fedora Silverblue, Fedora Server, Qubes OS (Anaconda-based, +# inst.stage2= requires block device or dd). Use dd or distribution media tool. # -# For Anaconda ISOs: dd if=image.iso of=/dev/sdX or use distribution media tool. # See: https://github.com/linuxboot/heads/issues/2008 set -e -o pipefail . /etc/functions.sh @@ -78,80 +78,10 @@ STATUS "Checking ISO boot capability..." ISO_BOOT_TYPE=$(check_hybrid_iso "$MOUNTED_ISO_PATH") DEBUG "ISO boot type: $ISO_BOOT_TYPE" -if [ "$ISO_BOOT_TYPE" != "hybrid" ]; then - DEBUG "Non-hybrid ISO detected (CD-ROM only)" -fi - STATUS "Mounting ISO and booting" mount -t iso9660 -o loop $MOUNTED_ISO_PATH /boot || DIE '$MOUNTED_ISO_PATH: Unable to mount /boot' -detect_iso_boot_method() { - local method="" - local found=0 - - for path in $(find /boot -name 'initrd*' -type f 2>/dev/null | head -5); do - [ -r "$path" ] || continue - tmpdir=$(mktemp -d) - /bin/bash /bin/unpack_initramfs.sh "$path" "$tmpdir" 2>/dev/null - - if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "iso.scan|findiso"; then - method="${method}iso-scan/findiso " - found=1 - fi - if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "live.media|live-media"; then - method="${method}live-media= " - found=1 - fi - if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "inst.stage2|inst\.stage2"; then - method="${method}inst.stage2= " - found=1 - fi - if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "inst.repo"; then - method="${method}inst.repo= " - found=1 - fi - if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "boot.casper|live-boot|casper"; then - method="${method}boot=casper " - found=1 - fi - if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "nixos"; then - method="${method}nixos " - found=1 - fi - rm -rf "$tmpdir" - done - - if [ $found -eq 0 ]; then - return 1 - fi - echo "$method" - return 0 -} - -STATUS "Detecting ISO boot method..." -BOOT_METHODS=$(detect_iso_boot_method) || BOOT_METHODS="" - -if [ -n "$BOOT_METHODS" ]; then - DEBUG "Detected boot methods: $BOOT_METHODS" -else - WARN "ISO may not boot from USB file: no boot support detected in initrd" - if [ -x /bin/whiptail ]; then - if ! whiptail_warning --title 'ISO BOOT COMPATIBILITY WARNING' --yesno \ - "ISO boot from USB file may not work.\n\nThis ISO does not have iso-scan/findiso/live-media in its initrd - it was designed for CD/DVD or dd-to-USB.\n\nKernel parameters passed externally may not be sufficient.\n\nTry:\n- Use distribution-specific ISO (e.g., Debian hd-media)\n- Write ISO directly to USB with dd\n- Use a live USB image\n\nDo you want to proceed anyway?" \ - 0 80; then - DIE "ISO boot cancelled - unsupported ISO on USB file" - fi - else - INPUT "Proceed with boot anyway? [y/N]:" -n 1 response - [ "$response" != "y" ] && [ "$response" != "Y" ] && DIE "ISO boot cancelled - unsupported ISO on USB file" - fi -fi - -if echo "$BOOT_METHODS" | grep -qE "anaconda|inst.repo|inst.stage2"; then - DEBUG "Anaconda-based ISO detected (inst.stage2=)" -fi - DEV_UUID=$(blkid $DEV | tail -1 | tr " " "\n" | grep UUID | cut -d\" -f2) ADD="fromiso=/dev/disk/by-uuid/$DEV_UUID/$ISO_PATH img_dev=/dev/disk/by-uuid/$DEV_UUID iso-scan/filename=/${ISO_PATH} img_loop=$ISO_PATH iso=$DEV_UUID/$ISO_PATH" REMOVE="" From 55c69526a9cd16b25a683c8cca503c82eb78e880 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Mon, 13 Apr 2026 09:46:11 -0400 Subject: [PATCH 14/16] kexec-iso-init: add initrd boot support detection with GRUB config fallback Add detect_initrd_boot_support() - scans ISO initrd for: - Filesystem support: ext4, vfat, exfat (warns if USB fs not supported) - Boot param support: iso-scan, findiso, live-media, boot=live, boot=casper, nixos, anaconda Add extract_grub_boot_params() - fallback: grep GRUB config for boot params (boot=live, rd.live.image, rd.live.squash) when initrd detection fails. Fail early with clear guidance if no boot method detected: - Known compatible: Ubuntu, Debian Live, Tails, NixOS, Fedora Workstation, PureOS, Kicksecure - For unsupported ISOs: use Ventoy/Rufus, dd, or report to upstream Hybrid ISO check (MBR sig) remains for DEBUG visibility. --- initrd/bin/kexec-iso-init.sh | 116 +++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/initrd/bin/kexec-iso-init.sh b/initrd/bin/kexec-iso-init.sh index d1b6e8c00..b3e378d4d 100755 --- a/initrd/bin/kexec-iso-init.sh +++ b/initrd/bin/kexec-iso-init.sh @@ -82,6 +82,122 @@ STATUS "Mounting ISO and booting" mount -t iso9660 -o loop $MOUNTED_ISO_PATH /boot || DIE '$MOUNTED_ISO_PATH: Unable to mount /boot' +detect_initrd_boot_support() { + local supported_fses="" + local supported_boot="" + local found=0 + + for path in $(find /boot -name 'initrd*' -type f 2>/dev/null | head -5); do + [ -r "$path" ] || continue + tmpdir=$(mktemp -d) + /bin/bash /bin/unpack_initramfs.sh "$path" "$tmpdir" 2>/dev/null + + if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "\.ko\.xz.*ext4|ext4\.ko"; then + supported_fses="${supported_fses}ext4 " + found=1 + fi + if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "\.ko\.xz.*vfat|vfat\.ko"; then + supported_fses="${supported_fses}vfat " + found=1 + fi + if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "\.ko\.xz.*exfat|exfat\.ko"; then + supported_fses="${supported_fses}exfat " + found=1 + fi + + if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "iso.scan|findiso"; then + supported_boot="${supported_boot}iso-scan/findiso " + fi + if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "live.media|live-media"; then + supported_boot="${supported_boot}live-media= " + fi + if find "$tmpdir" -type f 2>/dev/null | grep -qE "boot=live|rd.live.image|rd.live.squash"; then + supported_boot="${supported_boot}boot=live " + fi + if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "boot.casper|casper"; then + supported_boot="${supported_boot}boot=casper " + fi + if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "nixos"; then + supported_boot="${supported_boot}nixos " + fi + if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "inst.stage2|inst.repo"; then + supported_boot="${supported_boot}anaconda " + fi + rm -rf "$tmpdir" + done + + if [ -n "$supported_fses" ]; then + echo "fs:$supported_fses" + fi + if [ -n "$supported_boot" ]; then + echo "boot:$supported_boot" + fi +} + +extract_grub_boot_params() { + for cfg in $(find /boot -name 'grub.cfg' -type f 2>/dev/null); do + [ -r "$cfg" ] || continue + local boot_params="" + while IFS= read -r line; do + case "$line" in + *boot=live* | *rd.live.image* | *rd.live.squashimg=*) + boot_params="${boot_params}boot=live " + ;; + esac + done <"$cfg" + [ -n "$boot_params" ] && echo "grub:$boot_params" && return 0 + done + return 1 +} + +STATUS "Detecting USB filesystem and boot method support..." +SUPPORTED_FSES="" +SUPPORTED_BOOT="" +GRUB_BOOT="" +DETECTED_METHODS="" + +SUPPORTED_FSES=$(detect_initrd_boot_support 2>/dev/null | grep "^fs:" | sed 's/^fs://') +GRUB_BOOT=$(extract_grub_boot_params 2>/dev/null | grep "^grub:" | sed 's/^grub://') + +if [ -n "$SUPPORTED_FSES" ]; then + DEBUG "Initrd supports USB filesystems: $SUPPORTED_FSES" + DEV_FSTYPE=$(blkid $DEV | tail -1 | grep -oE "TYPE="[^"]+" | sed 's/TYPE="//') + if ! echo "$SUPPORTED_FSES" | grep -q "$DEV_FSTYPE"; then + WARN "USB filesystem ($DEV_FSTYPE) may not be supported by this ISO's initrd" + DEBUG "Supported filesystems: $SUPPORTED_FSES" + fi +fi + +if [ -z "$SUPPORTED_FSES" ]; then + WARN "Could not detect filesystem support in ISO initrd" + DEBUG "USB boot may fail if ISO initrd does not support your USB stick filesystem" +fi + +STATUS "Detecting boot method..." +if [ -n "$SUPPORTED_BOOT" ]; then + DETECTED_METHODS="$SUPPORTED_BOOT" + DEBUG "Initrd supports boot methods: $DETECTED_METHODS" +else + if [ -n "$GRUB_BOOT" ]; then + DETECTED_METHODS="$GRUB_BOOT" + DEBUG "GRUB config indicates boot methods: $DETECTED_METHODS" + fi +fi + +if [ -z "$DETECTED_METHODS" ]; then + WARN "ISO may not boot from USB file: no supported boot method detected" + if [ -x /bin/whiptail ]; then + if ! whiptail_warning --title 'ISO BOOT COMPATIBILITY WARNING' --yesno \ + "ISO boot from USB file may not work.\n\nThis ISO does not appear to support booting from ISO file on USB stick.\n\nKnown compatible ISOs: Ubuntu, Debian Live, Tails, NixOS, Fedora Workstation, PureOS, Kicksecure.\n\nFor this ISO, try:\n- Use distribution USB creation tool (Ventoy, Rufus, etc)\n- Write ISO directly to USB with dd\n- Report to upstream that ISO should support USB file boot\n\nDo you want to try anyway?" \ + 0 80; then + DIE "ISO boot cancelled - unsupported ISO on USB file" + fi + else + INPUT "ISO may not support USB file boot. Try anyway? [y/N]:" -n 1 response + [ "$response" != "y" ] && [ "$response" != "Y" ] && DIE "ISO boot cancelled - unsupported ISO on USB file" + fi +fi + DEV_UUID=$(blkid $DEV | tail -1 | tr " " "\n" | grep UUID | cut -d\" -f2) ADD="fromiso=/dev/disk/by-uuid/$DEV_UUID/$ISO_PATH img_dev=/dev/disk/by-uuid/$DEV_UUID iso-scan/filename=/${ISO_PATH} img_loop=$ISO_PATH iso=$DEV_UUID/$ISO_PATH" REMOVE="" From 662b5abe0beacbfa586723237c806e7b82692d62 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Mon, 13 Apr 2026 09:50:46 -0400 Subject: [PATCH 15/16] fix: extract_boot_params_from_cfg scans *.cfg not just grub.cfg - Fix: add missing '| xargs strings' to boot=live grep in initrd detection - Rename extract_grub_boot_params() -> extract_boot_params_from_cfg() - Search all *.cfg files (GRUB + syslinux), not just grub.cfg - Add iso-scan, findiso, live-media, boot=casper, anaconda, nixos patterns - Rename GRUB_BOOT -> CFG_BOOT variable, 'grub:' -> 'cfg:' tag --- initrd/bin/kexec-iso-init.sh | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/initrd/bin/kexec-iso-init.sh b/initrd/bin/kexec-iso-init.sh index b3e378d4d..6be64c914 100755 --- a/initrd/bin/kexec-iso-init.sh +++ b/initrd/bin/kexec-iso-init.sh @@ -111,7 +111,7 @@ detect_initrd_boot_support() { if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "live.media|live-media"; then supported_boot="${supported_boot}live-media= " fi - if find "$tmpdir" -type f 2>/dev/null | grep -qE "boot=live|rd.live.image|rd.live.squash"; then + if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "boot=live|rd.live.image|rd.live.squash"; then supported_boot="${supported_boot}boot=live " fi if find "$tmpdir" -type f 2>/dev/null | xargs strings 2>/dev/null | grep -qE "boot.casper|casper"; then @@ -134,8 +134,8 @@ detect_initrd_boot_support() { fi } -extract_grub_boot_params() { - for cfg in $(find /boot -name 'grub.cfg' -type f 2>/dev/null); do +extract_boot_params_from_cfg() { + for cfg in $(find /boot -name '*.cfg' -type f 2>/dev/null); do [ -r "$cfg" ] || continue local boot_params="" while IFS= read -r line; do @@ -143,9 +143,24 @@ extract_grub_boot_params() { *boot=live* | *rd.live.image* | *rd.live.squashimg=*) boot_params="${boot_params}boot=live " ;; + *iso-scan/filename=* | *findiso=*) + boot_params="${boot_params}iso-scan/findiso " + ;; + *live-media=* | *live.media=*) + boot_params="${boot_params}live-media= " + ;; + *boot=casper* | *casper*) + boot_params="${boot_params}boot=casper " + ;; + *inst.stage2=* | *inst.repo=*) + boot_params="${boot_params}anaconda " + ;; + *nixos*) + boot_params="${boot_params}nixos " + ;; esac done <"$cfg" - [ -n "$boot_params" ] && echo "grub:$boot_params" && return 0 + [ -n "$boot_params" ] && echo "cfg:$boot_params" && return 0 done return 1 } @@ -153,11 +168,11 @@ extract_grub_boot_params() { STATUS "Detecting USB filesystem and boot method support..." SUPPORTED_FSES="" SUPPORTED_BOOT="" -GRUB_BOOT="" +CFG_BOOT="" DETECTED_METHODS="" SUPPORTED_FSES=$(detect_initrd_boot_support 2>/dev/null | grep "^fs:" | sed 's/^fs://') -GRUB_BOOT=$(extract_grub_boot_params 2>/dev/null | grep "^grub:" | sed 's/^grub://') +CFG_BOOT=$(extract_boot_params_from_cfg 2>/dev/null | grep "^cfg:" | sed 's/^cfg://') if [ -n "$SUPPORTED_FSES" ]; then DEBUG "Initrd supports USB filesystems: $SUPPORTED_FSES" @@ -178,9 +193,9 @@ if [ -n "$SUPPORTED_BOOT" ]; then DETECTED_METHODS="$SUPPORTED_BOOT" DEBUG "Initrd supports boot methods: $DETECTED_METHODS" else - if [ -n "$GRUB_BOOT" ]; then - DETECTED_METHODS="$GRUB_BOOT" - DEBUG "GRUB config indicates boot methods: $DETECTED_METHODS" + if [ -n "$CFG_BOOT" ]; then + DETECTED_METHODS="$CFG_BOOT" + DEBUG "Boot config (*.cfg) indicates boot methods: $DETECTED_METHODS" fi fi From 772d6b23654d918f2f143f9ebf5dae676c8d2522 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Mon, 13 Apr 2026 10:13:28 -0400 Subject: [PATCH 16/16] doc: update boot-process.md with ISO boot knowledge - Add hybrid ISO detection (MBR sig 0x55AA at offset 510) - Document boot method detection table (Dracut, Anaconda, NixOS, Unknown) - Document initrd scanning for FS support (ext4, vfat, exfat) - Document config scanning for boot params (*.cfg fallback) - Update compatible ISO table with all tested ISOs - Add TinyCore as unknown boot method example - Clarify limited ISOs (Anaconda block device, Debian DVD installer) - Fix: Kicksecure listed correctly as Dracut-based (boot=live) - Fix: Fedora Silverblue and Qubes OS marked as limited (Anaconda) --- doc/boot-process.md | 111 ++++++++++++++++++++++++++++++++------------ 1 file changed, 81 insertions(+), 30 deletions(-) diff --git a/doc/boot-process.md b/doc/boot-process.md index 2d097e3fd..8c8d95a83 100644 --- a/doc/boot-process.md +++ b/doc/boot-process.md @@ -123,36 +123,85 @@ menu, system info, power off. When booting from an ISO file on USB media, `kexec-iso-init.sh` handles: 1. **Signature verification**: Check for `.sig` or `.asc` detached signature -2. **Mount ISO**: Mount the ISO file as loopback device -3. **Detect USB filesystem**: Get filesystem type from USB stick (ext4/vfat/exfat) -4. **Validate initrd support**: Check ISO initrd supports: - - USB storage drivers - - Loopback device - - Filesystem of USB stick - - Boot quirk script to find ISO on USB (findiso/live-media/boot=casper) -5. **Warning dialog**: If ISO may not boot, show warning and allow cancel - -### Known Compatible ISOs (tested 2026-04) - -| Distribution | Boot Param | USB FS | Status | -|--------------|------------|--------|--------| -| Ubuntu Desktop | iso-scan/filename | ext4/vfat/exfat | works | -| Debian Live kde/xfce | findiso | ext4/vfat/exfat | works | -| Tails standard | live-media=removable | ext4/vfat | works | -| Tails exfat-support ISO | live-media=removable | exfat | works | -| Fedora Workstation | boot=casper / rd.live.image | ext4/vfat | works | -| Fedora Silverblue | inst.stage2= / inst.repo= | ext4/vfat | works | -| Qubes OS R4.3+ | inst.repo=hd:LABEL= | ext4/vfat | works | -| NixOS | findiso | ext4/vfat/exfat | works | -| PureOS | boot=casper | ext4/vfat/exfat | works | - -### Known Incompatible ISOs - -| Distribution | Reason | Workaround | -|--------------|--------|------------| -| Debian DVD | CD-only design, no USB boot | `dd` or use Debian netinst | - -**Fedora Silverblue / Qubes OS**: These use Anaconda installer with `inst.stage2=` or `inst.repo=` parameters. The initrd includes Dracut's iso-scan module which can find ISO files on USB when the correct LABEL/UUID is provided. Works with ISO file boot when USB has matching label. +2. **Hybrid detection**: Check MBR signature at offset 510 (0x55AA = hybrid) +3. **Mount ISO**: Mount the ISO file as loopback device +4. **Initrd scanning**: Unpack ISO initrd and scan for filesystem support + (ext4, vfat, exfat modules) and boot method support (iso-scan, findiso, + live-media, boot=live, boot=casper, nixos, anaconda) +5. **Config scanning**: Grep all `*.cfg` files in the mounted ISO for boot + params as a fallback when initrd detection fails (covers GRUB, syslinux, + ISOLINUX configs) +6. **Warning dialog**: If no supported boot method is detected, warn the user + and suggest alternative USB creation methods + +### Boot methods + +ISOs use different initramfs boot systems. Detection checks for known patterns: + +| Boot system | Detection patterns | Notes | +|------------|---------------------|-------| +| Dracut (iso-scan) | `iso-scan/filename=`, `findiso=` | Ubuntu, Debian Live, Tails, PureOS | +| Dracut (live-media) | `live-media=` | Tails | +| Dracut (boot=live) | `boot=live`, `rd.live.image`, `rd.live.squashimg=` | Debian Live, Fedora Workstation, Kicksecure | +| Dracut (casper) | `boot=casper` | Ubuntu, PureOS | +| NixOS | `nixos` | NixOS | +| Anaconda | `inst.stage2=`, `inst.repo=` | Fedora, Qubes OS — requires block device (CD-ROM or dd'd USB) | +| Unknown | (no pattern matched) | May still work — try anyway | + +### ISO filesystem support + +The ISO initrd must support the USB stick filesystem. Detection unpacks the ISO +initrd and looks for kernel module files (ext4.ko, vfat.ko, exfat.ko) to +determine if the USB fs is supported. + +Known supported filesystems: **ext4**, **vfat**, **exfat** (detected in kernel module paths). + +### Boot parameter flow + +1. `kexec-iso-init.sh` passes standard boot params via kexec: + - `iso-scan/filename=/${ISO_PATH}` — Dracut standard + - `fromiso=`, `img_loop=`, `img_dev=` — additional Dracut variants +2. `kexec-select-boot.sh` parses the ISO's GRUB/syslinux config to build the + boot menu +3. `kexec-parse-boot.sh` strips unresolved `${iso_path}` variables from parsed + entries (prevents malformed params like `iso-scan/filename=` with orphaned paths) +4. `kexec-boot.sh` adds parsed entries and executes kexec + +### Known compatible ISOs (tested 2026-04) + +| Distribution | MBR | Boot method | Config source | USB FS | Status | +|---|---|---|---|---|---| +| Ubuntu Desktop | hybrid | iso-scan/filename | grub.cfg | ext4/vfat/exfat | works | +| Debian Live kde/xfce | hybrid | boot=live, rd.live.image | grub.cfg | ext4/vfat/exfat | works | +| Tails 7.6 | hybrid | boot=live | grub.cfg | ext4/vfat | works | +| Tails (exfat-support) | hybrid | boot=live | grub.cfg | exfat | works | +| Fedora Workstation Live | hybrid | boot=live, rd.live.image | grub.cfg | ext4/vfat | works | +| NixOS | hybrid | findiso, nixos | grub.cfg | ext4/vfat/exfat | works | +| PureOS | hybrid | boot=casper | grub.cfg | ext4/vfat/exfat | works | +| Kicksecure | hybrid | boot=live, rd.live.image | grub.cfg | ext4/vfat/exfat | works | + +### Known limited ISOs + +| Distribution | Boot method | Limitation | +|---|---|---| +| Fedora Silverblue | anaconda (inst.stage2=) | Requires block device or matching LABEL. Not USB file boot without extra config. | +| Qubes OS R4.3 | anaconda (inst.repo=hd:LABEL=) | Requires block device or matching LABEL. Installer only. | +| Debian DVD | none (installer) | No live boot params — installer ISO only. Use netinst or dd. | +| TinyCore/CorePlus | unknown (cde, iso=) | Boot method not detected. May work but unverified. | + +### On unknown boot methods + +If no known boot method is detected, the boot still proceeds with a warning. +Some ISOs use custom boot mechanisms not covered by detection patterns. Examples: + +- **TinyCore/CorePlus**: Uses `cde` (from CD) and `iso=` kernel parameter. + The `fromISOfile` script mounts ISO as `/mnt/cdrom`. May work despite + no detection pattern match. + +The detection approach is best-effort. Users with unsupported ISOs should: +- Try Ventoy, Rufus, or distribution USB creation tools +- Report to upstream that the ISO should support USB file boot +- Use `dd` to write ISO directly to USB if all else fails ### References @@ -162,6 +211,8 @@ When booting from an ISO file on USB media, `kexec-iso-init.sh` handles: --- +## Stage 3: kexec-select-boot + Called from the boot menu. Responsible for final verification and OS handoff. ### TPM2 primary key hash check