From e02b6e88c71642ecc063ae7aefbd09b9be80d50e Mon Sep 17 00:00:00 2001 From: Marta Lewandowska Date: Mon, 19 May 2025 13:21:50 -0600 Subject: [PATCH 01/11] Phase 1 of the bootloader updates proposal implementation https://fedoraproject.org/wiki/Changes/BootLoaderUpdatesPhase1 Signed-off-by: Marta Lewandoska Signed-off-by: Nicolas Frayer Signed-off-by: Leo Sandoval --- gen_grub_cfgstub | 1 + grub.macros | 26 +++++++++++++++++--------- grub2.spec | 25 ++++++++++++++++++++----- 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/gen_grub_cfgstub b/gen_grub_cfgstub index c73ee533..a6a97123 100644 --- a/gen_grub_cfgstub +++ b/gen_grub_cfgstub @@ -27,3 +27,4 @@ configfile \$prefix/grub.cfg EOF mv ${EFI_HOME}/grub.cfg.stb ${EFI_HOME}/grub.cfg +chmod 0600 ${EFI_HOME}/grub.cfg diff --git a/grub.macros b/grub.macros index a2cab587..dfc9707d 100644 --- a/grub.macros +++ b/grub.macros @@ -1,6 +1,12 @@ # vim:filetype=spec # Modules always contain just 32-bit code +%global evr %{epoch}:%{version}-%{release} %global _libdir %{_exec_prefix}/lib + +%global os_id %(eval echo $(grep ^ID= /etc/os-release | sed -e 's/^ID=//' -e 's/rhel/redhat/')) +%global grub_evr_dir %{_libdir}/efi/grub2/%{evr} +%global grub_efi_dir %{grub_evr_dir}/EFI/%{os_id} + %global _binaries_in_noarch_packages_terminate_build 0 #%%undefine _missing_build_ids_terminate_build %{expand:%%{!?buildsubdir:%%global buildsubdir grub-%{tarversion}}} @@ -280,8 +286,6 @@ %global grubelfname core.elf %endif -%global evr %{epoch}:%{version}-%{release} - %ifarch x86_64 %global with_efi_common 1 %global with_legacy_modules 1 @@ -704,8 +708,8 @@ find . '(' -iname gdb_grub \\\ ')' \\\ -exec cp {} $RPM_BUILD_ROOT/usr/lib/grub/%{grubaltefiarch}/ \\\; \ find $RPM_BUILD_ROOT -type f -iname "*.mod*" -exec chmod a-x {} '\;' \ -install -m 700 %{2} $RPM_BUILD_ROOT%{efi_esp_dir}/%{2} \ -install -m 700 %{3} $RPM_BUILD_ROOT%{efi_esp_dir}/%{3} \ +install -m 700 %{2} $RPM_BUILD_ROOT%{grub_efi_dir}/%{2} \ +install -m 700 %{3} $RPM_BUILD_ROOT%{grub_efi_dir}/%{3} \ %{expand:%%do_install_protected_file grub2-%{alt_package_arch}} \ cd .. \ %{nil} @@ -722,8 +726,8 @@ fi \ find $RPM_BUILD_ROOT -iname "*.module" -exec chmod a-x {} '\;' \ ln -s ../boot/grub2/grub.cfg \\\ $RPM_BUILD_ROOT%{_sysconfdir}/grub2-efi.cfg \ -install -m 700 %{2} $RPM_BUILD_ROOT%{efi_esp_dir}/%{2} \ -install -m 700 %{3} $RPM_BUILD_ROOT%{efi_esp_dir}/%{3} \ +install -m 700 %{2} $RPM_BUILD_ROOT%{grub_efi_dir}/%{2} \ +install -m 700 %{3} $RPM_BUILD_ROOT%{grub_efi_dir}/%{3} \ %ifarch %{arm} \ install -D -m 700 %{2} $RPM_BUILD_ROOT%{efi_esp_boot}/BOOTARM.EFI \ %endif \ @@ -826,6 +830,7 @@ cp docs/grub.info $RPM_BUILD_ROOT%{_infodir}/grub2.info \ cp docs/grub-dev.info \\\ $RPM_BUILD_ROOT%{_infodir}/grub2-dev.info \ install -d -m 0700 ${RPM_BUILD_ROOT}%{efi_esp_dir}/ \ +install -d -m 0700 ${RPM_BUILD_ROOT}%{grub_efi_dir}/ \ install -d -m 0700 ${RPM_BUILD_ROOT}/boot/grub2/ \ install -d -m 0700 ${RPM_BUILD_ROOT}/boot/loader/entries \ install -d -m 0700 ${RPM_BUILD_ROOT}/boot/grub2/themes/system \ @@ -873,7 +878,8 @@ ln -s ../boot/grub2/grub.cfg \\\ %defattr(-,root,root,-) \ %config(noreplace) %{_sysconfdir}/grub2.cfg \ %config(noreplace) %{_sysconfdir}/grub2-efi.cfg \ -%attr(0700,root,root) %verify(not mtime) %{efi_esp_dir}/%{2} \ +%ghost %attr(0700,root,root) %{efi_esp_dir}/%{2} \ +%attr(0700,root,root) %{grub_efi_dir}/%{2} \ %ifarch %{arm} \ %attr(0700,root,root) %verify(not mtime) %{efi_esp_boot}/BOOTARM.EFI \ %endif \ @@ -881,7 +887,8 @@ ln -s ../boot/grub2/grub.cfg \\\ %attr(0700,root,root)/usr/bin/gen_grub_cfgstub \ %dir %attr(0700,root,root)/boot/loader/entries \ %ghost %config(noreplace) %attr(0600,root,root)/boot/grub2/grub.cfg \ -%ghost %config(noreplace) %verify(not mtime) %attr(0700,root,root)%{efi_esp_dir}/grub.cfg \ +%ghost %config(noreplace) %attr(0700,root,root)%{efi_esp_dir}/grub.cfg \ +%ghost %config(noreplace) %attr(0600,root,root)%{grub_efi_dir}/grub.cfg \ %config(noreplace) %verify(not size mode md5 mtime) /boot/grub2/grubenv \ %attr(0644,root,root) %config(noreplace) /etc/dnf/protected.d/grub2-%{1}.conf \ %{expand:%if 0%{?without_efi_modules} \ @@ -899,7 +906,8 @@ ln -s ../boot/grub2/grub.cfg \\\ \ %{expand:%%files %{1}-cdboot} \ %defattr(-,root,root,-) \ -%attr(0700,root,root) %verify(not mtime) %{efi_esp_dir}/%{3} \ +%ghost %attr(0700,root,root) %{efi_esp_dir}/%{3} \ +%attr(0700,root,root) %{grub_efi_dir}/%{3} \ %attr(0700,root,root)/boot/grub2/fonts \ %{nil} diff --git a/grub2.spec b/grub2.spec index edd26b40..3d9b3d2a 100644 --- a/grub2.spec +++ b/grub2.spec @@ -17,7 +17,7 @@ Name: grub2 Epoch: 1 Version: 2.12 -Release: 40%{?dist} +Release: 41%{?dist} Summary: Bootloader with support for Linux, Multiboot and more License: GPL-3.0-or-later URL: http://www.gnu.org/software/grub/ @@ -387,7 +387,7 @@ fi %posttrans common set -eu -EFI_HOME=%{efi_esp_dir} +EFI_HOME=%{grub_efi_dir} GRUB_HOME=/boot/grub2 ESP_PATH=/boot/efi @@ -421,6 +421,17 @@ if test -f ${EFI_HOME}/grubenv; then mv --force ${EFI_HOME}/grubenv ${GRUB_HOME}/grubenv fi +%if 0%{with_efi_arch} +%posttrans efi-%{efiarch} +set -eu + +# On image mode, bootupd takes care of installing bootloader updates to the ESP +if [[ ! -e "/run/ostree-booted" ]]; then + cp -a %{grub_efi_dir}/. %{efi_esp_dir} || : +fi + +%endif + %files common -f grub.lang %dir %{_libdir}/grub/ %dir %{_datarootdir}/grub/ @@ -435,8 +446,8 @@ fi %dir /boot/grub2/themes/system %attr(0700,root,root) %dir /boot/grub2 %exclude /boot/grub2/* -%dir %attr(0700,root,root) %{efi_esp_dir} -%exclude %{efi_esp_dir}/* +%dir %attr(0700,root,root) %{grub_efi_dir} +%exclude %{grub_efi_dir}/* %ghost %config(noreplace) %verify(not size mode md5 mtime) /boot/grub2/grubenv %license COPYING %doc THANKS @@ -598,11 +609,15 @@ fi %endif %changelog +* Mon Aug 11 2025 Marta Lewandowska - 2.12-41 +- Phase 1 of the bootloader updates proposal implementation +- https://fedoraproject.org/wiki/Changes/BootLoaderUpdatesPhase1 + * Wed Aug 6 2025 Jan Stancek - 2.12-40 - 55-set-boot-entry.install: fix initrd check - Resolves: #2386118 -* Tue Jul 15 2025 FeRD (Frank Dana) - 2.12-39 +* Fri Aug 1 2025 FeRD (Frank Dana) - 2.12-39 - kernel-install: Suppress warnings about missing /etc/default/grub file when attempting to grep its contents From f6bc25c3e72a962cb59f568ec5fcc4dd374a9be2 Mon Sep 17 00:00:00 2001 From: Jan Stancek Date: Mon, 18 Aug 2025 15:56:46 +0200 Subject: [PATCH 02/11] 55-set-boot-entry.install: move to 95 When KERNEL_INSTALL_STAGING_AREA is used (for example with initrd_generator=mkosi-initrd), initrd gets installed to /boot only later at 90-loaderentry.install. Move the plugin to 95 so it runs after. Related: #2389020 Signed-off-by: Jan Stancek --- 55-set-boot-entry.install => 95-set-boot-entry.install | 4 ++-- grub2.spec | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) rename 55-set-boot-entry.install => 95-set-boot-entry.install (89%) diff --git a/55-set-boot-entry.install b/95-set-boot-entry.install similarity index 89% rename from 55-set-boot-entry.install rename to 95-set-boot-entry.install index be505e30..9f92f9f4 100755 --- a/55-set-boot-entry.install +++ b/95-set-boot-entry.install @@ -25,11 +25,11 @@ case "$COMMAND" in if [ -e "${ROOTPREFIX}${LINUX}" -a -n "${INITRD}" ]; then if [ ! -e "${ROOTPREFIX}${INITRD}" ]; then - echo "Error: ${INITRD} not found" + echo "Error: ${ROOTPREFIX}${INITRD} not found." exit 1 fi if ! lsinitrd ${ROOTPREFIX}${INITRD} > /dev/null; then - echo "Error: ${INITRD} appears to be corrupt." + echo "Error: ${ROOTPREFIX}${INITRD} appears to be corrupt." exit 1 fi fi diff --git a/grub2.spec b/grub2.spec index 3d9b3d2a..a1a7400c 100644 --- a/grub2.spec +++ b/grub2.spec @@ -17,7 +17,7 @@ Name: grub2 Epoch: 1 Version: 2.12 -Release: 41%{?dist} +Release: 42%{?dist} Summary: Bootloader with support for Linux, Multiboot and more License: GPL-3.0-or-later URL: http://www.gnu.org/software/grub/ @@ -36,7 +36,7 @@ Source10: 20-grub.install Source11: grub.patches Source12: sbat.csv.in Source13: gen_grub_cfgstub -Source14: 55-set-boot-entry.install +Source14: 95-set-boot-entry.install %include %{SOURCE1} @@ -437,7 +437,7 @@ fi %dir %{_datarootdir}/grub/ %attr(0700,root,root) %dir %{_sysconfdir}/grub.d %{_prefix}/lib/kernel/install.d/20-grub.install -%{_prefix}/lib/kernel/install.d/55-set-boot-entry.install +%{_prefix}/lib/kernel/install.d/95-set-boot-entry.install %{_prefix}/lib/kernel/install.d/99-grub-mkconfig.install %dir %{_datarootdir}/grub %exclude %{_datarootdir}/grub/* @@ -609,6 +609,10 @@ fi %endif %changelog +* Wed Aug 20 2025 Jan Stancek - 2.12-42 +- move 55-set-boot-entry.install to 95 +- Related: #2389020 + * Mon Aug 11 2025 Marta Lewandowska - 2.12-41 - Phase 1 of the bootloader updates proposal implementation - https://fedoraproject.org/wiki/Changes/BootLoaderUpdatesPhase1 From 730ce277473cc673793f79437426c1403ea47621 Mon Sep 17 00:00:00 2001 From: Nicolas Frayer Date: Tue, 19 Aug 2025 12:05:25 +0200 Subject: [PATCH 03/11] gen_grub_cfgstub: Removed output messages Signed-off-by: Nicolas Frayer --- gen_grub_cfgstub | 4 ---- grub2.spec | 5 ++++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/gen_grub_cfgstub b/gen_grub_cfgstub index a6a97123..8f77c0f5 100644 --- a/gen_grub_cfgstub +++ b/gen_grub_cfgstub @@ -15,10 +15,6 @@ EFI_HOME=$2 BOOT_UUID=$(grub2-probe --target=fs_uuid "${GRUB_HOME}") GRUB_DIR=$(grub2-mkrelpath "${GRUB_HOME}") -echo "Generating grub stub config for drive " "${BOOT_UUID}" -echo "GRUB_DIR=" "${GRUB_DIR}" -echo "EFI_HOME=" "${EFI_HOME}" - cat << EOF > "${EFI_HOME}"/grub.cfg.stb search --no-floppy --root-dev-only --fs-uuid --set=dev ${BOOT_UUID} set prefix=(\$dev)${GRUB_DIR} diff --git a/grub2.spec b/grub2.spec index a1a7400c..0e804eac 100644 --- a/grub2.spec +++ b/grub2.spec @@ -17,7 +17,7 @@ Name: grub2 Epoch: 1 Version: 2.12 -Release: 42%{?dist} +Release: 43%{?dist} Summary: Bootloader with support for Linux, Multiboot and more License: GPL-3.0-or-later URL: http://www.gnu.org/software/grub/ @@ -609,6 +609,9 @@ fi %endif %changelog +* Tue Aug 19 2025 Nicolas Frayer - 2.12-43 +- gen_grub_cfgstub: Removed output messages + * Wed Aug 20 2025 Jan Stancek - 2.12-42 - move 55-set-boot-entry.install to 95 - Related: #2389020 From b9b1d1e384bc49e29f4398d0e3ac176c584684a0 Mon Sep 17 00:00:00 2001 From: Leo Sandoval Date: Mon, 15 Sep 2025 14:06:19 -0600 Subject: [PATCH 04/11] Include function name on debug and error print functions With the following change, we see standard (grub_dprintf) and error (grub_error) logs with the function name embedded (see below) into the log which is particular useful when debugging commands/efi/tpm.c:grub_tpm_measure:281:tpm: log_event, pcr = 8, size = 0xb, Including one more field on the print log impacts the binary sizes and in turn their respective packages, the latter with an increase of 20k approximately (compared to 2.12-43) for most the the packages. Signed-off-by: Leo Sandoval --- ...unction-name-on-debug-print-function.patch | 102 ++++++++++++++++++ grub.patches | 3 +- grub2.spec | 7 +- 3 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 0383-Include-function-name-on-debug-print-function.patch diff --git a/0383-Include-function-name-on-debug-print-function.patch b/0383-Include-function-name-on-debug-print-function.patch new file mode 100644 index 00000000..e38ad7f3 --- /dev/null +++ b/0383-Include-function-name-on-debug-print-function.patch @@ -0,0 +1,102 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Leo Sandoval +Date: Fri, 11 Apr 2025 15:31:53 -0600 +Subject: [PATCH] Include function name on debug and error print functions + +Together with the line number, the debug log gives more context when +debugging. + +Signed-off-by: Leo Sandoval +--- + grub-core/kern/err.c | 4 ++-- + grub-core/kern/misc.c | 4 ++-- + include/grub/err.h | 6 +++--- + include/grub/misc.h | 5 +++-- + 4 files changed, 10 insertions(+), 9 deletions(-) + +diff --git a/grub-core/kern/err.c b/grub-core/kern/err.c +index aebfe0cf83..ba04b57fb7 100644 +--- a/grub-core/kern/err.c ++++ b/grub-core/kern/err.c +@@ -38,14 +38,14 @@ static int grub_error_stack_assert; + #endif + + grub_err_t +-grub_error (grub_err_t n, const char *file, const int line, const char *fmt, ...) ++grub_error (grub_err_t n, const char *file, const char *function, const int line, const char *fmt, ...) + { + va_list ap; + int m; + + grub_errno = n; + +- m = grub_snprintf (grub_errmsg, sizeof (grub_errmsg), "%s:%d:", file, line); ++ m = grub_snprintf (grub_errmsg, sizeof (grub_errmsg), "%s:%s:%d:", file, function, line); + if (m < 0) + m = 0; + +diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c +index 69bd655f3d..541c7bb80c 100644 +--- a/grub-core/kern/misc.c ++++ b/grub-core/kern/misc.c +@@ -248,7 +248,7 @@ grub_debug_enabled (const char * condition) + } + + void +-grub_real_dprintf (const char *file, const int line, const char *condition, ++grub_real_dprintf (const char *file, const char *function, const int line, const char *condition, + const char *fmt, ...) + { + va_list args; +@@ -272,7 +272,7 @@ grub_real_dprintf (const char *file, const int line, const char *condition, + else + last_had_cr = 0; + #endif +- grub_printf ("%s:%d:%s: ", file, line, condition); ++ grub_printf ("%s:%s:%d:%s: ", file, function, line, condition); + va_start (args, fmt); + grub_vprintf (fmt, args); + va_end (args); +diff --git a/include/grub/err.h b/include/grub/err.h +index 7530f58b29..6379a6baf8 100644 +--- a/include/grub/err.h ++++ b/include/grub/err.h +@@ -88,10 +88,10 @@ struct grub_error_saved + extern grub_err_t EXPORT_VAR(grub_errno); + extern char EXPORT_VAR(grub_errmsg)[GRUB_MAX_ERRMSG]; + +-grub_err_t EXPORT_FUNC(grub_error) (grub_err_t n, const char *file, const int line, const char *fmt, ...) +- __attribute__ ((format (GNU_PRINTF, 4, 5))); ++grub_err_t EXPORT_FUNC(grub_error) (grub_err_t n, const char *file, const char *function, const int line, const char *fmt, ...) ++ __attribute__ ((format (GNU_PRINTF, 5, 6))); + +-#define grub_error(n, fmt, ...) grub_error (n, __FILE__, __LINE__, fmt, ##__VA_ARGS__) ++#define grub_error(n, fmt, ...) grub_error (n, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__) + + + void EXPORT_FUNC(grub_fatal) (const char *fmt, ...) __attribute__ ((noreturn)); +diff --git a/include/grub/misc.h b/include/grub/misc.h +index 0429339ef3..2b9096a0b3 100644 +--- a/include/grub/misc.h ++++ b/include/grub/misc.h +@@ -43,7 +43,7 @@ + #define CONCAT(a, b) CONCAT_(a, b) + #endif + +-#define grub_dprintf(condition, ...) grub_real_dprintf(GRUB_FILE, __LINE__, condition, __VA_ARGS__) ++#define grub_dprintf(condition, ...) grub_real_dprintf(GRUB_FILE, __FUNCTION__, __LINE__, condition, __VA_ARGS__) + + void *EXPORT_FUNC(grub_memmove) (void *dest, const void *src, grub_size_t n); + char *EXPORT_FUNC(grub_strcpy) (char *dest, const char *src); +@@ -420,9 +420,10 @@ int EXPORT_FUNC(grub_puts_) (const char *s); + int EXPORT_FUNC(grub_debug_is_enabled) (void); + int EXPORT_FUNC(grub_debug_enabled) (const char *condition); + void EXPORT_FUNC(grub_real_dprintf) (const char *file, ++ const char *function, + const int line, + const char *condition, +- const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 4, 5))); ++ const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 5, 6))); + int EXPORT_FUNC(grub_printf) (const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 1, 2))); + int EXPORT_FUNC(grub_printf_) (const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 1, 2))); + void EXPORT_FUNC(grub_qdprintf) (const char *condition, diff --git a/grub.patches b/grub.patches index f236c38a..66e0b693 100644 --- a/grub.patches +++ b/grub.patches @@ -378,4 +378,5 @@ Patch0378: 0378-cryptocheck-Add-quiet-option.patch Patch0379: 0379-loader-efi-chainloader-Fix-double-free.patch Patch0380: 0380-loader-efi-chainloader-Fix-null-pointer-dereference.patch Patch0381: 0381-osdep-linux-getroot-Detect-DDF-container-similar-to-.patch -Patch0382: 0382-Set-correctly-the-memory-attributes-for-the-kernel-P.patch \ No newline at end of file +Patch0382: 0382-Set-correctly-the-memory-attributes-for-the-kernel-P.patch +Patch0383: 0383-Include-function-name-on-debug-print-function.patch \ No newline at end of file diff --git a/grub2.spec b/grub2.spec index 0e804eac..4563828d 100644 --- a/grub2.spec +++ b/grub2.spec @@ -17,7 +17,7 @@ Name: grub2 Epoch: 1 Version: 2.12 -Release: 43%{?dist} +Release: 44%{?dist} Summary: Bootloader with support for Linux, Multiboot and more License: GPL-3.0-or-later URL: http://www.gnu.org/software/grub/ @@ -609,7 +609,10 @@ fi %endif %changelog -* Tue Aug 19 2025 Nicolas Frayer - 2.12-43 +* Mon Sep 15 2025 Leo Sandoval - 2.12-44 +- Include function name on debug print function + +* Fri Aug 29 2025 Nicolas Frayer - 2.12-43 - gen_grub_cfgstub: Removed output messages * Wed Aug 20 2025 Jan Stancek - 2.12-42 From 4614a09b9fba8c2ebd3f52f39d9a49ecb12c4c59 Mon Sep 17 00:00:00 2001 From: Leo Sandoval Date: Wed, 15 Oct 2025 15:11:39 -0600 Subject: [PATCH 05/11] Include upstream blsuki related patches Signed-off-by: Leo Sandoval --- 0001-kern-misc-Implement-grub_strtok.patch | 109 ++ ...g-command-to-parse-Boot-Loader-Speci.patch | 1354 +++++++++++++++++ ...ge-offset-type-for-grub_util_write_i.patch | 43 + ...blsuki-Check-for-mounted-boot-in-emu.patch | 342 +++++ ...ommand-to-load-Unified-Kernel-Image-.patch | 822 ++++++++++ ...p-Tweaks-in-preparation-for-libtasn1.patch | 168 ++ grub.macros | 2 +- grub.patches | 9 +- grub2.spec | 5 +- 9 files changed, 2851 insertions(+), 3 deletions(-) create mode 100644 0001-kern-misc-Implement-grub_strtok.patch create mode 100644 0002-blsuki-Add-blscfg-command-to-parse-Boot-Loader-Speci.patch create mode 100644 0003-util-misc.c-Change-offset-type-for-grub_util_write_i.patch create mode 100644 0004-blsuki-Check-for-mounted-boot-in-emu.patch create mode 100644 0005-blsuki-Add-uki-command-to-load-Unified-Kernel-Image-.patch create mode 100644 0006-posix_wrap-Tweaks-in-preparation-for-libtasn1.patch diff --git a/0001-kern-misc-Implement-grub_strtok.patch b/0001-kern-misc-Implement-grub_strtok.patch new file mode 100644 index 00000000..e9e0e1ec --- /dev/null +++ b/0001-kern-misc-Implement-grub_strtok.patch @@ -0,0 +1,109 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Alec Brown +Date: Tue, 12 Aug 2025 03:45:32 +0000 +Subject: [PATCH] kern/misc: Implement grub_strtok() + +Add the functions grub_strtok() and grub_strtok_r() to help parse strings into +tokens separated by characters in the "delim" parameter. These functions are +present in gnulib but calling them directly from the gnulib code is quite +challenging since the call "#include " would include the header file +grub-core/lib/posix_wrap/string.h instead of grub-core/lib/gnulib/string.h, +where strtok() and strtok_r() are declared. Since this overlap is quite +problematic, the simpler solution was to implement the code in the GRUB based +on gnulib's implementation. For more information on these functions, visit the +Linux Programmer's Manual, man strtok. + +Signed-off-by: Alec Brown +Reviewed-by: Daniel Kiper +--- + grub-core/kern/misc.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++ + include/grub/misc.h | 3 +++ + 2 files changed, 65 insertions(+) + +diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c +index 69bd655f3d..c69fe7fb19 100644 +--- a/grub-core/kern/misc.c ++++ b/grub-core/kern/misc.c +@@ -453,6 +453,68 @@ grub_strword (const char *haystack, const char *needle) + return 0; + } + ++char * ++grub_strtok_r (char *s, const char *delim, char **save_ptr) ++{ ++ char *token; ++ const char *c; ++ bool is_delim; ++ ++ if (s == NULL) ++ s = *save_ptr; ++ ++ /* Scan leading delimiters. */ ++ while (*s != '\0') ++ { ++ is_delim = false; ++ for (c = delim; *c != '\0'; c++) ++ { ++ if (*s == *c) ++ { ++ is_delim = true; ++ break; ++ } ++ } ++ if (is_delim == true) ++ s++; ++ else ++ break; ++ } ++ ++ if (*s == '\0') ++ { ++ *save_ptr = s; ++ return NULL; ++ } ++ ++ /* Find the end of the token. */ ++ token = s; ++ while (*s != '\0') ++ { ++ for (c = delim; *c != '\0'; c++) ++ { ++ if (*s == *c) ++ { ++ *s = '\0'; ++ *save_ptr = s + 1; ++ return token; ++ } ++ } ++ s++; ++ } ++ ++ *save_ptr = s; ++ return token; ++} ++ ++char * ++grub_strtok (char *s, const char *delim) ++{ ++ static char *last; ++ ++ return grub_strtok_r (s, delim, &last); ++} ++ + int + grub_isspace (int c) + { +diff --git a/include/grub/misc.h b/include/grub/misc.h +index 0429339ef3..626f0c3535 100644 +--- a/include/grub/misc.h ++++ b/include/grub/misc.h +@@ -134,6 +134,9 @@ char *EXPORT_FUNC(grub_strchr) (const char *s, int c); + char *EXPORT_FUNC(grub_strrchr) (const char *s, int c); + int EXPORT_FUNC(grub_strword) (const char *s, const char *w); + ++char *EXPORT_FUNC(grub_strtok_r) (char *s, const char *delim, char **save_ptr); ++char *EXPORT_FUNC(grub_strtok) (char *s, const char *delim); ++ + /* Copied from gnulib. + Written by Bruno Haible , 2005. */ + static inline char * diff --git a/0002-blsuki-Add-blscfg-command-to-parse-Boot-Loader-Speci.patch b/0002-blsuki-Add-blscfg-command-to-parse-Boot-Loader-Speci.patch new file mode 100644 index 00000000..e2bf790a --- /dev/null +++ b/0002-blsuki-Add-blscfg-command-to-parse-Boot-Loader-Speci.patch @@ -0,0 +1,1354 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Tue, 12 Aug 2025 03:45:33 +0000 +Subject: [PATCH] blsuki: Add blscfg command to parse Boot Loader Specification + snippets + +The BootLoaderSpec (BLS) defines a scheme where different bootloaders can +share a format for boot items and a configuration directory that accepts +these common configurations as drop-in files. + +The BLS Specification: https://uapi-group.org/specifications/specs/boot_loader_specification/ + +Signed-off-by: Peter Jones +Signed-off-by: Javier Martinez Canillas +Signed-off-by: Will Thompson +Signed-off-by: Alec Brown +Reviewed-by: Daniel Kiper +--- + bootstrap.conf | 1 + + docs/grub.texi | 74 ++- + grub-core/Makefile.core.def | 12 + + grub-core/commands/blscfg.c | 2 +- + grub-core/commands/blsuki.c | 1028 ++++++++++++++++++++++++++++++++++++++++ + grub-core/commands/legacycfg.c | 4 +- + grub-core/commands/menuentry.c | 7 +- + grub-core/normal/main.c | 4 + + include/grub/menu.h | 14 + + include/grub/normal.h | 2 +- + 10 files changed, 1140 insertions(+), 8 deletions(-) + create mode 100644 grub-core/commands/blsuki.c + +diff --git a/bootstrap.conf b/bootstrap.conf +index 60de0597c5..799cdb1a35 100644 +--- a/bootstrap.conf ++++ b/bootstrap.conf +@@ -24,6 +24,7 @@ gnulib_modules=" + argp + base64 + error ++ filevercmp + fnmatch + getdelim + getline +diff --git a/docs/grub.texi b/docs/grub.texi +index ae01e8c994..336f8e87ed 100644 +--- a/docs/grub.texi ++++ b/docs/grub.texi +@@ -3275,6 +3275,7 @@ These variables have special meaning to GRUB. + + @menu + * biosnum:: ++* blsuki_save_default:: + * check_appended_signatures:: + * check_signatures:: + * chosen:: +@@ -3339,6 +3340,11 @@ this. + For an alternative approach which also changes BIOS drive mappings for the + chain-loaded system, @pxref{drivemap}. + ++@node blsuki_save_default ++@subsection blsuki_save_default ++ ++If this variable is set, menu entries generated from BLS config files ++(@pxref{blscfg}) will be set as the default boot entry when selected. + + @node check_appended_signatures + @subsection check_appended_signatures +@@ -3346,7 +3352,6 @@ chain-loaded system, @pxref{drivemap}. + This variable controls whether GRUB enforces appended signature validation on + certain loaded files. @xref{Using appended signatures}. + +- + @node check_signatures + @subsection check_signatures + +@@ -4355,6 +4360,7 @@ you forget a command, you can run the command @command{help} + * background_image:: Load background image for active terminal + * badram:: Filter out bad regions of RAM + * blocklist:: Print a block list ++* blscfg:: Load Boot Loader Specification menu entries + * boot:: Start up your operating system + * cat:: Show the contents of a file + * clear:: Clear the screen +@@ -4542,6 +4548,72 @@ Print a block list (@pxref{Block list syntax}) for @var{file}. + @end deffn + + ++@node blscfg ++@subsection blscfg ++ ++@deffn Command blscfg [@option{-p|--path} dir] [@option{-f|--enable-fallback}] [@option{-d|--show-default}] [@option{-n|--show-non-default}] [@option{-e|--entry} file] ++Load Boot Loader Specification (BLS) entries into the GRUB menu. Boot entries ++generated from @command{blscfg} won't interfere with entries from @file{grub.cfg} appearing in ++the GRUB menu. Also, entries generated from @command{blscfg} exists only in memory and ++don't update @file{grub.cfg}. ++ ++By default, the BLS entries are stored in the @file{/loader/entries} directory in the ++boot partition. If BLS entries are stored elsewhere, the @option{--path} option can be ++used to check a different directory instead of the default location. If no BLS ++entries are found while using the @option{--path} option, the @option{--enable-fallback} option ++can be used to check for entries in the default location. ++ ++The @option{--show-default} option allows the default boot entry to be added to the ++GRUB menu from the BLS entries. ++ ++The @option{--show-non-default} option allows non-default boot entries to be added to ++the GRUB menu from the BLS entries. ++ ++The @option{--entry} option allows specific boot entries to be added to the GRUB menu ++from the BLS entries. ++ ++The @option{--entry}, @option{--show-default}, and @option{--show-non-default} options ++are used to filter which BLS entries are added to the GRUB menu. If none are ++used, all entries in the default location or the location specified by @option{--path} ++will be added to the GRUB menu. ++ ++A BLS config file example: ++@example ++# /boot/loader/entries/6a9857a393724b7a981ebb5b8495b9ea-3.8.0-2.fc19.x86_64.conf ++title Fedora 19 (Rawhide) ++sort-key fedora ++machine-id 6a9857a393724b7a981ebb5b8495b9ea ++version 3.8.0-2.fc19.x86_64 ++options root=UUID=6d3376e4-fc93-4509-95ec-a21d68011da2 quiet ++architecture x64 ++linux /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/linux ++initrd /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/initrd ++@end example ++ ++For more information on BLS entry keys as well as other information on BLS, ++see: @uref{https://uapi-group.org/specifications/specs/boot_loader_specification/, The Boot Loader Specification}. For the GRUB, there are a few additional ++BLS entry keys based on the @command{menuentry} command (@pxref{menuentry}). ++ ++The @code{grub_class} key may be used any number of times to group menu entries into ++classes. Menu themes may display different classes using different styles. ++ ++The @code{grub_users} key grants specific users access to specific menu ++entries. @xref{Security}. ++ ++The @code{grub_hotkey} key associates a hotkey with a menu entry. ++@var{key} may be a single letter, or one of the aliases @samp{backspace}, ++@samp{tab}, or @samp{delete}. ++ ++The @code{grub_args} key can be used for any other argument to be passed as positonal ++parameters when the list of commands generated from the BLS config file are ++executed. ++ ++Variable expansion using the @samp{$} character (@xref{Shell-like scripting}) may be ++used with BLS config files for the GRUB but might not be compatible with other ++bootloaders. ++@end deffn ++ ++ + @node boot + @subsection boot + +diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def +index 95fd18d2dd..5e0e757344 100644 +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -884,6 +884,18 @@ module = { + enable = i386_xen_pvh; + }; + ++module = { ++ name = blsuki; ++ common = commands/blsuki.c; ++ common = lib/gnulib/filevercmp.c; ++ enable = powerpc_ieee1275; ++ enable = efi; ++ enable = i386_pc; ++ enable = emu; ++ cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)'; ++ cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB)'; ++}; ++ + module = { + name = boot; + common = commands/boot.c; +diff --git a/grub-core/commands/blscfg.c b/grub-core/commands/blscfg.c +index 5d931b0c9b..38913d6964 100644 +--- a/grub-core/commands/blscfg.c ++++ b/grub-core/commands/blscfg.c +@@ -955,7 +955,7 @@ static void create_entry (struct bls_entry *entry) + clinux, options ? " " : "", options ? options : "", + initrd ? initrd : "", dt ? dt : ""); + +- grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, src, 0, &index, entry); ++ grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, src, 0, &index, entry, NULL); + grub_dprintf ("blscfg", "Added entry %d id:\"%s\"\n", index, id); + + finish: +diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c +new file mode 100644 +index 0000000000..9440adb100 +--- /dev/null ++++ b/grub-core/commands/blsuki.c +@@ -0,0 +1,1028 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2025 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++GRUB_MOD_LICENSE ("GPLv3+"); ++ ++#define GRUB_BLS_CONFIG_PATH "/loader/entries/" ++ ++#define BLS_EXT_LEN (sizeof (".conf") - 1) ++ ++/* ++ * It is highly unlikely to ever receive a large amount of keyval pairs. A ++ * limit of 10000 is more than enough. ++ */ ++#define BLSUKI_KEYVALS_MAX 10000 ++ ++static const struct grub_arg_option bls_opt[] = ++ { ++ {"path", 'p', 0, "Specify path to find BLS entries.", N_("DIR"), ARG_TYPE_PATHNAME}, ++ {"enable-fallback", 'f', 0, "Fallback to the default BLS path if --path fails to find BLS entries.", 0, ARG_TYPE_NONE}, ++ {"show-default", 'd', 0, "Allow the default BLS entry to be added to the GRUB menu.", 0, ARG_TYPE_NONE}, ++ {"show-non-default", 'n', 0, "Allow the non-default BLS entries to be added to the GRUB menu.", 0, ARG_TYPE_NONE}, ++ {"entry", 'e', 0, "Allow specific BLS entries to be added to the GRUB menu.", N_("FILE"), ARG_TYPE_FILE}, ++ {0, 0, 0, 0, 0, 0} ++ }; ++ ++struct keyval ++{ ++ const char *key; ++ char *val; ++}; ++ ++struct read_entry_info ++{ ++ const char *devid; ++ const char *dirname; ++ grub_file_t file; ++}; ++ ++struct find_entry_info ++{ ++ const char *dirname; ++ const char *devid; ++ grub_device_t dev; ++ grub_fs_t fs; ++}; ++ ++static grub_blsuki_entry_t *entries; ++ ++#define FOR_BLSUKI_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries) ++ ++/* ++ * This function will add a new keyval pair to a list of keyvals stored in the ++ * entry parameter. ++ */ ++static grub_err_t ++blsuki_add_keyval (grub_blsuki_entry_t *entry, char *key, char *val) ++{ ++ char *k, *v; ++ struct keyval **kvs, *kv; ++ grub_size_t size; ++ int new_n = entry->nkeyvals + 1; ++ ++ if (new_n > BLSUKI_KEYVALS_MAX) ++ return grub_error (GRUB_ERR_BAD_NUMBER, "too many keyval pairs"); ++ ++ if (entry->keyvals_size == 0) ++ { ++ size = sizeof (struct keyval *); ++ kvs = grub_malloc (size); ++ if (kvs == NULL) ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't allocate space for BLS key values"); ++ ++ entry->keyvals = kvs; ++ entry->keyvals_size = size; ++ } ++ else if (entry->keyvals_size < new_n * sizeof (struct keyval *)) ++ { ++ size = entry->keyvals_size * 2; ++ kvs = grub_realloc (entry->keyvals, size); ++ if (kvs == NULL) ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't reallocate space for BLS key values"); ++ ++ entry->keyvals = kvs; ++ entry->keyvals_size = size; ++ } ++ ++ kv = grub_malloc (sizeof (struct keyval)); ++ if (kv == NULL) ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value"); ++ ++ k = grub_strdup (key); ++ if (k == NULL) ++ { ++ grub_free (kv); ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value"); ++ } ++ ++ v = grub_strdup (val); ++ if (v == NULL) ++ { ++ grub_free (k); ++ grub_free (kv); ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value"); ++ } ++ ++ kv->key = k; ++ kv->val = v; ++ entry->keyvals[entry->nkeyvals] = kv; ++ entry->nkeyvals = new_n; ++ ++ return GRUB_ERR_NONE; ++} ++ ++/* ++ * Find the value of the key named by keyname. If there are allowed to be ++ * more than one, pass a pointer set to -1 to the last parameter the first ++ * time, and pass the same pointer through each time after, and it'll return ++ * them in sorted order as defined in the BLS fragment file. ++ */ ++static char * ++blsuki_get_val (grub_blsuki_entry_t *entry, const char *keyname, int *last) ++{ ++ int idx, start = (last != NULL) ? (*last + 1) : 0; ++ struct keyval *kv = NULL; ++ char *ret = NULL; ++ ++ for (idx = start; idx < entry->nkeyvals; idx++) ++ { ++ kv = entry->keyvals[idx]; ++ ++ if (grub_strcmp (keyname, kv->key) == 0) ++ { ++ ret = kv->val; ++ break; ++ } ++ } ++ ++ if (last != NULL) ++ { ++ if (idx == entry->nkeyvals) ++ *last = -1; ++ else ++ *last = idx; ++ } ++ ++ return ret; ++} ++ ++/* ++ * Add a new grub_blsuki_entry_t struct to the entries list and sort it's ++ * position on the list. ++ */ ++static grub_err_t ++blsuki_add_entry (grub_blsuki_entry_t *entry) ++{ ++ grub_blsuki_entry_t *e, *last = NULL; ++ int rc; ++ ++ if (entries == NULL) ++ { ++ grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename); ++ entries = entry; ++ return GRUB_ERR_NONE; ++ } ++ ++ FOR_BLSUKI_ENTRIES (e) ++ { ++ rc = filevercmp (entry->filename, e->filename); ++ if (rc == 0) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("duplicate file: `%s'"), entry->filename); ++ ++ if (rc > 0) ++ { ++ grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename); ++ grub_list_push (GRUB_AS_LIST_P (&e), GRUB_AS_LIST (entry)); ++ if (entry->next == entries) ++ { ++ entries = entry; ++ entry->prev = NULL; ++ } ++ else if (last != NULL) ++ last->next = entry; ++ ++ return GRUB_ERR_NONE; ++ } ++ last = e; ++ } ++ ++ if (last != NULL) ++ { ++ grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename); ++ last->next = entry; ++ entry->prev = &last; ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++/* ++ * This function parses each line of a BLS config file to obtain the key value ++ * pairs that will be used to setup the GRUB menu entries. The key value pair ++ * will be stored in a list in the entry parameter. ++ */ ++static grub_err_t ++bls_parse_keyvals (grub_file_t f, grub_blsuki_entry_t *entry) ++{ ++ grub_err_t err = GRUB_ERR_NONE; ++ ++ for (;;) ++ { ++ char *line, *key, *val; ++ ++ line = grub_file_getline (f); ++ if (line == NULL) ++ break; ++ ++ key = grub_strtok_r (line, " \t", &val); ++ if (key == NULL) ++ { ++ grub_free (line); ++ break; ++ } ++ if (*key == '#') ++ { ++ grub_free (line); ++ continue; ++ } ++ ++ while (*val == ' ' || *val == '\t') ++ val++; ++ ++ if (*val == '\0') ++ { ++ grub_free (line); ++ break; ++ } ++ ++ err = blsuki_add_keyval (entry, key, val); ++ grub_free (line); ++ if (err != GRUB_ERR_NONE) ++ break; ++ } ++ ++ return err; ++} ++ ++/* ++ * If a file hasn't already been opened, this function opens a BLS config file ++ * and initializes entry data before parsing keyvals and adding the entry to ++ * the list of BLS entries. ++ */ ++static int ++blsuki_read_entry (const char *filename, ++ const struct grub_dirhook_info *dirhook_info __attribute__ ((__unused__)), ++ void *data) ++{ ++ grub_size_t path_len = 0, filename_len; ++ grub_err_t err; ++ char *p = NULL; ++ grub_file_t f = NULL; ++ grub_blsuki_entry_t *entry; ++ struct read_entry_info *info = (struct read_entry_info *) data; ++ ++ grub_dprintf ("blsuki", "filename: \"%s\"\n", filename); ++ ++ filename_len = grub_strlen (filename); ++ ++ if (info->file != NULL) ++ f = info->file; ++ else ++ { ++ if (filename_len < BLS_EXT_LEN || ++ grub_strcmp (filename + filename_len - BLS_EXT_LEN, ".conf") != 0) ++ return 0; ++ ++ p = grub_xasprintf ("(%s)%s/%s", info->devid, info->dirname, filename); ++ ++ f = grub_file_open (p, GRUB_FILE_TYPE_CONFIG); ++ grub_free (p); ++ if (f == NULL) ++ goto finish; ++ } ++ ++ entry = grub_zalloc (sizeof (*entry)); ++ if (entry == NULL) ++ goto finish; ++ ++ /* ++ * If a file is opened before this function, the filename may have a path. ++ * Since the filename is used for the ID of the GRUB menu entry, we can ++ * remove the path. ++ */ ++ if (info->file != NULL) ++ { ++ char *slash; ++ ++ slash = grub_strrchr (filename, '/'); ++ if (slash != NULL) ++ path_len = slash - filename + 1; ++ } ++ filename_len -= path_len; ++ ++ entry->filename = grub_strndup (filename + path_len, filename_len); ++ if (entry->filename == NULL) ++ { ++ grub_free (entry); ++ goto finish; ++ } ++ ++ err = bls_parse_keyvals (f, entry); ++ ++ if (err == GRUB_ERR_NONE) ++ blsuki_add_entry (entry); ++ else ++ grub_free (entry); ++ ++ finish: ++ if (f != NULL) ++ grub_file_close (f); ++ ++ return 0; ++} ++ ++/* ++ * This function returns a list of values that had the same key in the BLS ++ * config file. The number of entries in this list is returned by the len ++ * parameter. ++ */ ++static char ** ++blsuki_make_list (grub_blsuki_entry_t *entry, const char *key, int *len) ++{ ++ int last = -1; ++ char *val; ++ int nlist = 0; ++ char **list; ++ ++ list = grub_zalloc (sizeof (char *)); ++ if (list == NULL) ++ return NULL; ++ ++ while (1) ++ { ++ char **new; ++ ++ /* ++ * Since the same key might appear more than once, the 'last' variable ++ * starts at -1 and increments to indicate the last index in the list ++ * we obtained from blsuki_get_val(). ++ */ ++ val = blsuki_get_val (entry, key, &last); ++ if (val == NULL) ++ break; ++ ++ new = grub_realloc (list, (nlist + 2) * sizeof (char *)); ++ if (new == NULL) ++ break; ++ ++ list = new; ++ list[nlist++] = val; ++ list[nlist] = NULL; ++ } ++ ++ if (nlist == 0) ++ { ++ grub_free (list); ++ return NULL; ++ } ++ ++ if (len != NULL) ++ *len = nlist; ++ ++ return list; ++} ++ ++/* ++ * This function appends a field to the end of a buffer. If the field given is ++ * an enviornmental variable, it gets the value stored for that variable and ++ * appends that to the buffer instead. ++ */ ++static char * ++blsuki_field_append (bool is_env_var, char *buffer, const char *start, const char *end) ++{ ++ char *tmp; ++ const char *field; ++ grub_size_t size = 0; ++ ++ tmp = grub_strndup (start, end - start + 1); ++ if (tmp == NULL) ++ return NULL; ++ ++ field = tmp; ++ ++ if (is_env_var == true) ++ { ++ field = grub_env_get (tmp); ++ if (field == NULL) ++ return buffer; ++ } ++ ++ if (grub_add (grub_strlen (field), 1, &size)) ++ return NULL; ++ ++ if (buffer == NULL) ++ buffer = grub_zalloc (size); ++ else ++ { ++ if (grub_add (size, grub_strlen (buffer), &size)) ++ return NULL; ++ ++ buffer = grub_realloc (buffer, size); ++ } ++ ++ if (buffer == NULL) ++ return NULL; ++ ++ tmp = buffer + grub_strlen (buffer); ++ tmp = grub_stpcpy (tmp, field); ++ ++ if (is_env_var == true) ++ tmp = grub_stpcpy (tmp, " "); ++ ++ return buffer; ++} ++ ++/* ++ * This function takes a value string, checks for environmental variables, and ++ * returns the value string with all environmental variables replaced with the ++ * value stored in the variable. ++ */ ++static char * ++blsuki_expand_val (const char *value) ++{ ++ char *buffer = NULL; ++ const char *start = value; ++ const char *end = value; ++ bool is_env_var = false; ++ ++ if (value == NULL) ++ return NULL; ++ ++ while (*value != '\0') ++ { ++ if (*value == '$') ++ { ++ if (start != end) ++ { ++ buffer = blsuki_field_append (is_env_var, buffer, start, end); ++ if (buffer == NULL) ++ return NULL; ++ } ++ ++ is_env_var = true; ++ start = value + 1; ++ } ++ else if (is_env_var == true) ++ { ++ if (grub_isalnum (*value) == 0 && *value != '_') ++ { ++ buffer = blsuki_field_append (is_env_var, buffer, start, end); ++ is_env_var = false; ++ start = value; ++ if (*start == ' ') ++ start++; ++ } ++ } ++ ++ end = value; ++ value++; ++ } ++ ++ if (start != end) ++ { ++ buffer = blsuki_field_append (is_env_var, buffer, start, end); ++ if (buffer == NULL) ++ return NULL; ++ } ++ ++ return buffer; ++} ++ ++/* ++ * This function returns a string with the command to load a linux kernel with ++ * kernel command-line options based on what was specified in the BLS config ++ * file. ++ */ ++static char * ++bls_get_linux (grub_blsuki_entry_t *entry) ++{ ++ char *linux_path; ++ char *linux_cmd = NULL; ++ char *options = NULL; ++ char *tmp; ++ grub_size_t size; ++ ++ linux_path = blsuki_get_val (entry, "linux", NULL); ++ options = blsuki_expand_val (blsuki_get_val (entry, "options", NULL)); ++ ++ if (grub_add (sizeof ("linux "), grub_strlen (linux_path), &size)) ++ { ++ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while calculating linux buffer size"); ++ goto finish; ++ } ++ ++ if (options != NULL) ++ { ++ if (grub_add (size, grub_strlen (options), &size) || ++ grub_add (size, 1, &size)) ++ { ++ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while calculating linux buffer size"); ++ goto finish; ++ } ++ } ++ ++ linux_cmd = grub_malloc (size); ++ if (linux_cmd == NULL) ++ goto finish; ++ ++ tmp = linux_cmd; ++ tmp = grub_stpcpy (tmp, "linux "); ++ tmp = grub_stpcpy (tmp, linux_path); ++ if (options != NULL) ++ { ++ tmp = grub_stpcpy (tmp, " "); ++ tmp = grub_stpcpy (tmp, options); ++ } ++ ++ tmp = grub_stpcpy (tmp, "\n"); ++ ++ finish: ++ grub_free (options); ++ return linux_cmd; ++} ++ ++/* ++ * This function returns a string with the command to load all initrds for a ++ * linux kernel image based on the list provided by the BLS config file. ++ */ ++static char * ++bls_get_initrd (grub_blsuki_entry_t *entry) ++{ ++ char **initrd_list; ++ char *initrd_cmd = NULL; ++ char *tmp; ++ grub_size_t size; ++ int i; ++ ++ initrd_list = blsuki_make_list (entry, "initrd", NULL); ++ if (initrd_list != NULL) ++ { ++ size = sizeof ("initrd"); ++ ++ for (i = 0; initrd_list != NULL && initrd_list[i] != NULL; i++) ++ { ++ if (grub_add (size, 1, &size) || ++ grub_add (size, grub_strlen (initrd_list[i]), &size)) ++ { ++ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd buffer size"); ++ goto finish; ++ } ++ } ++ ++ if (grub_add (size, 1, &size)) ++ { ++ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd buffer size"); ++ goto finish; ++ } ++ ++ initrd_cmd = grub_malloc (size); ++ if (initrd_cmd == NULL) ++ goto finish; ++ ++ tmp = grub_stpcpy (initrd_cmd, "initrd"); ++ for (i = 0; initrd_list != NULL && initrd_list[i] != NULL; i++) ++ { ++ grub_dprintf ("blsuki", "adding initrd %s\n", initrd_list[i]); ++ tmp = grub_stpcpy (tmp, " "); ++ tmp = grub_stpcpy (tmp, initrd_list[i]); ++ } ++ tmp = grub_stpcpy (tmp, "\n"); ++ } ++ ++ finish: ++ grub_free (initrd_list); ++ return initrd_cmd; ++} ++ ++/* ++ * This function returns a string with the command to load a device tree blob ++ * from the BLS config file. ++ */ ++static char * ++bls_get_devicetree (grub_blsuki_entry_t *entry) ++{ ++ char *dt_path; ++ char *dt_cmd = NULL; ++ char *tmp; ++ grub_size_t size; ++ ++ dt_path = blsuki_expand_val (blsuki_get_val (entry, "devicetree", NULL)); ++ if (dt_path != NULL) ++ { ++ if (grub_add (sizeof ("devicetree "), grub_strlen (dt_path), &size) || ++ grub_add (size, 1, &size)) ++ { ++ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating device tree buffer size"); ++ return NULL; ++ } ++ ++ dt_cmd = grub_malloc (size); ++ if (dt_cmd == NULL) ++ return NULL; ++ ++ tmp = dt_cmd; ++ tmp = grub_stpcpy (dt_cmd, "devicetree "); ++ tmp = grub_stpcpy (tmp, dt_path); ++ tmp = grub_stpcpy (tmp, "\n"); ++ } ++ ++ return dt_cmd; ++} ++ ++/* ++ * This function puts together all of the commands generated from the contents ++ * of the BLS config file and creates a new entry in the GRUB boot menu. ++ */ ++static void ++bls_create_entry (grub_blsuki_entry_t *entry) ++{ ++ int argc = 0; ++ const char **argv = NULL; ++ char *title = NULL; ++ char *linux_path = NULL; ++ char *linux_cmd = NULL; ++ char *initrd_cmd = NULL; ++ char *dt_cmd = NULL; ++ char *id = entry->filename; ++ grub_size_t id_len; ++ char *hotkey = NULL; ++ char *users = NULL; ++ char **classes = NULL; ++ char **args = NULL; ++ char *src = NULL; ++ int i; ++ grub_size_t size; ++ bool blsuki_save_default; ++ ++ linux_path = blsuki_get_val (entry, "linux", NULL); ++ if (linux_path == NULL) ++ { ++ grub_dprintf ("blsuki", "Skipping file %s with no 'linux' key.\n", entry->filename); ++ goto finish; ++ } ++ ++ id_len = grub_strlen (id); ++ if (id_len >= BLS_EXT_LEN && grub_strcmp (id + id_len - BLS_EXT_LEN, ".conf") == 0) ++ id[id_len - BLS_EXT_LEN] = '\0'; ++ ++ title = blsuki_get_val (entry, "title", NULL); ++ hotkey = blsuki_get_val (entry, "grub_hotkey", NULL); ++ users = blsuki_expand_val (blsuki_get_val (entry, "grub_users", NULL)); ++ classes = blsuki_make_list (entry, "grub_class", NULL); ++ args = blsuki_make_list (entry, "grub_arg", &argc); ++ ++ argc++; ++ if (grub_mul (argc + 1, sizeof (char *), &size)) ++ { ++ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected creating argv list")); ++ goto finish; ++ } ++ ++ argv = grub_malloc (size); ++ if (argv == NULL) ++ goto finish; ++ ++ argv[0] = (title != NULL) ? title : linux_path; ++ for (i = 1; i < argc; i++) ++ argv[i] = args[i - 1]; ++ argv[argc] = NULL; ++ ++ linux_cmd = bls_get_linux (entry); ++ if (linux_cmd == NULL) ++ goto finish; ++ ++ initrd_cmd = bls_get_initrd (entry); ++ if (grub_errno != GRUB_ERR_NONE) ++ goto finish; ++ ++ dt_cmd = bls_get_devicetree (entry); ++ if (grub_errno != GRUB_ERR_NONE) ++ goto finish; ++ ++ blsuki_save_default = grub_env_get_bool ("blsuki_save_default", false); ++ src = grub_xasprintf ("%s%s%s%s", ++ blsuki_save_default ? "savedefault\n" : "", ++ linux_cmd, initrd_cmd ? initrd_cmd : "", ++ dt_cmd ? dt_cmd : ""); ++ ++ grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, src, 0, entry); ++ ++ finish: ++ grub_free (linux_cmd); ++ grub_free (dt_cmd); ++ grub_free (initrd_cmd); ++ grub_free (classes); ++ grub_free (args); ++ grub_free (argv); ++ grub_free (src); ++} ++ ++/* ++ * This function fills a find_entry_info struct passed in by the info parameter. ++ * If the dirname or devid parameters are set to NULL, the dirname and devid ++ * fields in the info parameter will be set to default values. If info already ++ * has a value in the dev fields, we can compare it to the value passed in by ++ * the devid parameter or the default devid to see if we need to open a new ++ * device. ++ */ ++static grub_err_t ++blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, const char *devid) ++{ ++ grub_device_t dev; ++ grub_fs_t fs; ++ ++ if (info == NULL) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "info parameter is not set"); ++ ++ if (devid == NULL) ++ { ++ devid = grub_env_get ("root"); ++ if (devid == NULL) ++ return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable '%s' isn't set"), "root"); ++ } ++ ++ /* Check that we aren't closing and opening the same device. */ ++ if (info->dev != NULL && grub_strcmp (info->devid, devid) != 0) ++ { ++ grub_device_close (info->dev); ++ info->dev = NULL; ++ } ++ /* If we are using the same device, then we can skip this step and only set the directory. */ ++ if (info->dev == NULL) ++ { ++ grub_dprintf ("blsuki", "opening %s\n", devid); ++ dev = grub_device_open (devid); ++ if (dev == NULL) ++ return grub_errno; ++ ++ grub_dprintf ("blsuki", "probing fs\n"); ++ fs = grub_fs_probe (dev); ++ if (fs == NULL) ++ { ++ grub_device_close (dev); ++ return grub_errno; ++ } ++ ++ info->devid = devid; ++ info->dev = dev; ++ info->fs = fs; ++ } ++ ++ info->dirname = dirname; ++ ++ return GRUB_ERR_NONE; ++} ++ ++/* ++ * This function searches for BLS config files based on the data in the info ++ * parameter. If the fallback option is enabled, the default location will be ++ * checked for BLS config files if the first attempt fails. ++ */ ++static void ++blsuki_find_entry (struct find_entry_info *info, bool enable_fallback) ++{ ++ struct read_entry_info read_entry_info; ++ grub_fs_t dir_fs = NULL; ++ grub_device_t dir_dev = NULL; ++ bool fallback = false; ++ int r; ++ ++ do ++ { ++ read_entry_info.file = NULL; ++ read_entry_info.dirname = info->dirname; ++ ++ grub_dprintf ("blsuki", "scanning dir: %s\n", info->dirname); ++ dir_dev = info->dev; ++ dir_fs = info->fs; ++ read_entry_info.devid = info->devid; ++ ++ r = dir_fs->fs_dir (dir_dev, read_entry_info.dirname, blsuki_read_entry, ++ &read_entry_info); ++ if (r != 0) ++ { ++ grub_dprintf ("blsuki", "blsuki_read_entry returned error\n"); ++ grub_errno = GRUB_ERR_NONE; ++ } ++ ++ /* ++ * If we aren't able to find BLS entries in the directory given by info->dirname, ++ * we can fallback to the default location "/boot/loader/entries/" and see if we ++ * can find the files there. ++ */ ++ if (entries == NULL && fallback == false && enable_fallback == true) ++ { ++ blsuki_set_find_entry_info (info, GRUB_BLS_CONFIG_PATH, NULL); ++ grub_dprintf ("blsuki", "Entries weren't found in %s, fallback to %s\n", ++ read_entry_info.dirname, info->dirname); ++ fallback = true; ++ } ++ else ++ fallback = false; ++ } ++ while (fallback == true); ++} ++ ++static grub_err_t ++blsuki_load_entries (char *path, bool enable_fallback) ++{ ++ grub_size_t len; ++ static grub_err_t r; ++ const char *devid = NULL; ++ char *dir = NULL; ++ struct find_entry_info info = { ++ .dev = NULL, ++ .fs = NULL, ++ .dirname = NULL, ++ }; ++ struct read_entry_info rei = { ++ .devid = NULL, ++ .dirname = NULL, ++ }; ++ ++ if (path != NULL) ++ { ++ len = grub_strlen (path); ++ if (len >= BLS_EXT_LEN && grub_strcmp (path + len - BLS_EXT_LEN, ".conf") == 0) ++ { ++ rei.file = grub_file_open (path, GRUB_FILE_TYPE_CONFIG); ++ if (rei.file == NULL) ++ return grub_errno; ++ ++ /* blsuki_read_entry() closes the file. */ ++ return blsuki_read_entry (path, NULL, &rei); ++ } ++ else if (path[0] == '(') ++ { ++ devid = path + 1; ++ ++ dir = grub_strchr (path, ')'); ++ if (dir == NULL) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid file name `%s'"), path); ++ ++ *dir = '\0'; ++ ++ /* Check if there is more than the devid in the path. */ ++ if (dir + 1 < path + len) ++ dir = dir + 1; ++ } ++ else if (path[0] == '/') ++ dir = path; ++ } ++ ++ if (dir == NULL) ++ dir = (char *) GRUB_BLS_CONFIG_PATH; ++ ++ r = blsuki_set_find_entry_info (&info, dir, devid); ++ if (r == GRUB_ERR_NONE) ++ blsuki_find_entry (&info, enable_fallback); ++ ++ if (info.dev != NULL) ++ grub_device_close (info.dev); ++ ++ return r; ++} ++ ++static bool ++blsuki_is_default_entry (const char *def_entry, grub_blsuki_entry_t *entry, int idx) ++{ ++ const char *title; ++ const char *def_entry_end; ++ long def_idx; ++ ++ if (def_entry == NULL || *def_entry == '\0') ++ return false; ++ ++ if (grub_strcmp (def_entry, entry->filename) == 0) ++ return true; ++ ++ title = blsuki_get_val (entry, "title", NULL); ++ ++ if (title != NULL && grub_strcmp (def_entry, title) == 0) ++ return true; ++ ++ def_idx = grub_strtol (def_entry, &def_entry_end, 0); ++ if (*def_entry_end != '\0' || def_idx < 0 || def_idx > GRUB_INT_MAX) ++ return false; ++ ++ if ((int) def_idx == idx) ++ return true; ++ ++ return false; ++} ++ ++/* ++ * This function creates a GRUB boot menu entry for each BLS entry in the ++ * entries list. ++ */ ++static grub_err_t ++blsuki_create_entries (bool show_default, bool show_non_default, char *entry_id) ++{ ++ const char *def_entry = NULL; ++ grub_blsuki_entry_t *entry = NULL; ++ int idx = 0; ++ ++ def_entry = grub_env_get ("default"); ++ ++ FOR_BLSUKI_ENTRIES(entry) ++ { ++ if (entry->visible == true) ++ { ++ idx++; ++ continue; ++ } ++ if ((show_default == true && blsuki_is_default_entry (def_entry, entry, idx) == true) || ++ (show_non_default == true && blsuki_is_default_entry (def_entry, entry, idx) == false) || ++ (entry_id != NULL && grub_strcmp (entry_id, entry->filename) == 0)) ++ { ++ bls_create_entry (entry); ++ entry->visible = true; ++ } ++ ++ idx++; ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)), ++ char **args __attribute__ ((unused))) ++{ ++ grub_err_t err; ++ struct grub_arg_list *state = ctxt->state; ++ char *path = NULL; ++ char *entry_id = NULL; ++ bool enable_fallback = false; ++ bool show_default = false; ++ bool show_non_default = false; ++ bool all = true; ++ entries = NULL; ++ ++ if (state[0].set) ++ path = state[0].arg; ++ if (state[1].set) ++ enable_fallback = true; ++ if (state[2].set) ++ { ++ show_default = true; ++ all = false; ++ } ++ if (state[3].set) ++ { ++ show_non_default = true; ++ all = false; ++ } ++ if (state[4].set) ++ { ++ entry_id = state[4].arg; ++ all = false; ++ } ++ if (all == true) ++ { ++ show_default = true; ++ show_non_default = true; ++ } ++ ++ err = blsuki_load_entries (path, enable_fallback); ++ if (err != GRUB_ERR_NONE) ++ return err; ++ ++ return blsuki_create_entries (show_default, show_non_default, entry_id); ++} ++ ++static grub_extcmd_t bls_cmd; ++ ++GRUB_MOD_INIT(blsuki) ++{ ++ bls_cmd = grub_register_extcmd ("blscfg", grub_cmd_blscfg, 0, ++ N_("[-p|--path] [-f|--enable-fallback] DIR [-d|--show-default] [-n|--show-non-default] [-e|--entry] FILE"), ++ N_("Import Boot Loader Specification snippets."), ++ bls_opt); ++} ++ ++GRUB_MOD_FINI(blsuki) ++{ ++ grub_unregister_extcmd (bls_cmd); ++} +diff --git a/grub-core/commands/legacycfg.c b/grub-core/commands/legacycfg.c +index 86bcda6954..a5d296769e 100644 +--- a/grub-core/commands/legacycfg.c ++++ b/grub-core/commands/legacycfg.c +@@ -143,7 +143,7 @@ legacy_file (const char *filename) + args[0] = oldname; + grub_normal_add_menu_entry (1, args, NULL, NULL, "legacy", + NULL, NULL, +- entrysrc, 0, NULL, NULL); ++ entrysrc, 0, NULL, NULL, NULL); + grub_free (args); + entrysrc[0] = 0; + grub_free (oldname); +@@ -205,7 +205,7 @@ legacy_file (const char *filename) + args[0] = entryname; + grub_normal_add_menu_entry (1, args, NULL, NULL, NULL, + NULL, NULL, entrysrc, 0, NULL, +- NULL); ++ NULL, NULL); + grub_free (args); + } + +diff --git a/grub-core/commands/menuentry.c b/grub-core/commands/menuentry.c +index b175a1b43b..d6cf329203 100644 +--- a/grub-core/commands/menuentry.c ++++ b/grub-core/commands/menuentry.c +@@ -78,7 +78,7 @@ grub_normal_add_menu_entry (int argc, const char **args, + char **classes, const char *id, + const char *users, const char *hotkey, + const char *prefix, const char *sourcecode, +- int submenu, int *index, struct bls_entry *bls) ++ int submenu, int *index, struct bls_entry *bls, grub_blsuki_entry_t *blsuki) + { + int menu_hotkey = 0; + char **menu_args = NULL; +@@ -196,6 +196,7 @@ grub_normal_add_menu_entry (int argc, const char **args, + (*last)->sourcecode = menu_sourcecode; + (*last)->submenu = submenu; + (*last)->bls = bls; ++ (*last)->blsuki = blsuki; + + menu->size++; + if (index) +@@ -297,7 +298,7 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args) + ctxt->state[2].arg, 0, + ctxt->state[3].arg, + ctxt->extcmd->cmd->name[0] == 's', +- NULL, NULL); ++ NULL, NULL, NULL); + + src = args[argc - 1]; + args[argc - 1] = NULL; +@@ -315,7 +316,7 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args) + users, + ctxt->state[2].arg, prefix, src + 1, + ctxt->extcmd->cmd->name[0] == 's', NULL, +- NULL); ++ NULL, NULL); + + src[len - 1] = ch; + args[argc - 1] = src; +diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c +index 6f7290a897..e038ed14b8 100644 +--- a/grub-core/normal/main.c ++++ b/grub-core/normal/main.c +@@ -75,6 +75,10 @@ grub_normal_free_menu (grub_menu_t menu) + { + entry->bls->visible = 0; + } ++ if (entry->blsuki) ++ { ++ entry->blsuki->visible = 0; ++ } + + grub_free ((void *) entry->id); + grub_free ((void *) entry->users); +diff --git a/include/grub/menu.h b/include/grub/menu.h +index 0acdc2aa6b..43a3c5809b 100644 +--- a/include/grub/menu.h ++++ b/include/grub/menu.h +@@ -20,6 +20,7 @@ + #ifndef GRUB_MENU_HEADER + #define GRUB_MENU_HEADER 1 + ++ + struct bls_entry + { + struct bls_entry *next; +@@ -30,6 +31,18 @@ struct bls_entry + int visible; + }; + ++struct grub_blsuki_entry ++{ ++ struct grub_blsuki_entry *next; ++ struct grub_blsuki_entry **prev; ++ struct keyval **keyvals; ++ grub_size_t keyvals_size; ++ int nkeyvals; ++ char *filename; ++ bool visible; ++}; ++typedef struct grub_blsuki_entry grub_blsuki_entry_t; ++ + struct grub_menu_entry_class + { + char *name; +@@ -73,6 +86,7 @@ struct grub_menu_entry + + /* BLS used to populate the entry */ + struct bls_entry *bls; ++ grub_blsuki_entry_t *blsuki; + }; + typedef struct grub_menu_entry *grub_menu_entry_t; + +diff --git a/include/grub/normal.h b/include/grub/normal.h +index 8839ad85a1..6d2b59db88 100644 +--- a/include/grub/normal.h ++++ b/include/grub/normal.h +@@ -145,7 +145,7 @@ grub_normal_add_menu_entry (int argc, const char **args, char **classes, + const char *id, + const char *users, const char *hotkey, + const char *prefix, const char *sourcecode, +- int submenu, int *index, struct bls_entry *bls); ++ int submenu, int *index, struct bls_entry *bls, grub_blsuki_entry_t *blsuki); + + grub_err_t + grub_normal_set_password (const char *user, const char *password); diff --git a/0003-util-misc.c-Change-offset-type-for-grub_util_write_i.patch b/0003-util-misc.c-Change-offset-type-for-grub_util_write_i.patch new file mode 100644 index 00000000..f80fb5c9 --- /dev/null +++ b/0003-util-misc.c-Change-offset-type-for-grub_util_write_i.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Alec Brown +Date: Tue, 12 Aug 2025 03:45:34 +0000 +Subject: [PATCH] util/misc.c: Change offset type for + grub_util_write_image_at() + +Adding filevercmp support to grub-core/commands/blsuki.c from gnulib will cause +issues with the type of the offset parameter for grub_util_write_image_at() for +emu builds. To fix this issue, we can change the type from off_t to grub_off_t. + +Signed-off-by: Alec Brown +Reviewed-by: Daniel Kiper +--- + include/grub/util/misc.h | 2 +- + util/misc.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/include/grub/util/misc.h b/include/grub/util/misc.h +index e9e0a6724a..bfce065583 100644 +--- a/include/grub/util/misc.h ++++ b/include/grub/util/misc.h +@@ -36,7 +36,7 @@ char *grub_util_read_image (const char *path); + void grub_util_load_image (const char *path, char *buf); + void grub_util_write_image (const char *img, size_t size, FILE *out, + const char *name); +-void grub_util_write_image_at (const void *img, size_t size, off_t offset, ++void grub_util_write_image_at (const void *img, size_t size, grub_off_t offset, + FILE *out, const char *name); + + char *make_system_path_relative_to_its_root (const char *path); +diff --git a/util/misc.c b/util/misc.c +index 0f928e5b49..6e16a68d9a 100644 +--- a/util/misc.c ++++ b/util/misc.c +@@ -101,7 +101,7 @@ grub_util_read_image (const char *path) + } + + void +-grub_util_write_image_at (const void *img, size_t size, off_t offset, FILE *out, ++grub_util_write_image_at (const void *img, size_t size, grub_off_t offset, FILE *out, + const char *name) + { + grub_util_info ("writing 0x%" GRUB_HOST_PRIxLONG_LONG " bytes at offset 0x%" diff --git a/0004-blsuki-Check-for-mounted-boot-in-emu.patch b/0004-blsuki-Check-for-mounted-boot-in-emu.patch new file mode 100644 index 00000000..3f207333 --- /dev/null +++ b/0004-blsuki-Check-for-mounted-boot-in-emu.patch @@ -0,0 +1,342 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Robbie Harwood +Date: Tue, 12 Aug 2025 03:45:35 +0000 +Subject: [PATCH] blsuki: Check for mounted /boot in emu + +Irritatingly, BLS defines paths relative to the mountpoint of the +filesystem which contains its snippets, not / or any other fixed +location. So grub-emu needs to know whether /boot is a separate +filesystem from / and conditionally prepend a path. + +Signed-off-by: Robbie Harwood +Signed-off-by: Alec Brown +Reviewed-by: Daniel Kiper +--- + grub-core/Makefile.core.def | 3 ++ + grub-core/commands/blsuki.c | 83 +++++++++++++++++++++++++++++++++++++---- + grub-core/osdep/linux/getroot.c | 8 ++++ + grub-core/osdep/unix/getroot.c | 4 +- + include/grub/emu/misc.h | 2 +- + 5 files changed, 91 insertions(+), 9 deletions(-) + +diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def +index 5e0e757344..885dd436de 100644 +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -376,6 +376,9 @@ kernel = { + emu = kern/emu/cache_s.S; + emu = kern/emu/hostdisk.c; + emu = osdep/unix/hostdisk.c; ++ emu = osdep/relpath.c; ++ emu = osdep/getroot.c; ++ emu = osdep/unix/getroot.c; + emu = osdep/exec.c; + extra_dist = osdep/unix/exec.c; + emu = osdep/devmapper/hostdisk.c; +diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c +index 9440adb100..ed93a150b1 100644 +--- a/grub-core/commands/blsuki.c ++++ b/grub-core/commands/blsuki.c +@@ -32,6 +32,13 @@ + #include + #include + ++#ifdef GRUB_MACHINE_EMU ++#include ++#define GRUB_BOOT_DEVICE "/boot" ++#else ++#define GRUB_BOOT_DEVICE "" ++#endif ++ + GRUB_MOD_LICENSE ("GPLv3+"); + + #define GRUB_BLS_CONFIG_PATH "/loader/entries/" +@@ -79,6 +86,34 @@ static grub_blsuki_entry_t *entries; + + #define FOR_BLSUKI_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries) + ++/* ++ * BLS appears to make paths relative to the filesystem that snippets are ++ * on, not /. Attempt to cope. ++ */ ++static char *blsuki_update_boot_device (char *tmp) ++{ ++#ifdef GRUB_MACHINE_EMU ++ static int separate_boot = -1; ++ char *ret; ++ ++ if (separate_boot != -1) ++ goto probed; ++ ++ separate_boot = 0; ++ ++ ret = grub_make_system_path_relative_to_its_root (GRUB_BOOT_DEVICE); ++ ++ if (ret != NULL && ret[0] == '\0') ++ separate_boot = 1; ++ ++ probed: ++ if (separate_boot == 0) ++ return tmp; ++#endif ++ ++ return grub_stpcpy (tmp, GRUB_BOOT_DEVICE); ++} ++ + /* + * This function will add a new keyval pair to a list of keyvals stored in the + * entry parameter. +@@ -526,7 +561,7 @@ bls_get_linux (grub_blsuki_entry_t *entry) + linux_path = blsuki_get_val (entry, "linux", NULL); + options = blsuki_expand_val (blsuki_get_val (entry, "options", NULL)); + +- if (grub_add (sizeof ("linux "), grub_strlen (linux_path), &size)) ++ if (grub_add (sizeof ("linux " GRUB_BOOT_DEVICE), grub_strlen (linux_path), &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while calculating linux buffer size"); + goto finish; +@@ -548,6 +583,7 @@ bls_get_linux (grub_blsuki_entry_t *entry) + + tmp = linux_cmd; + tmp = grub_stpcpy (tmp, "linux "); ++ tmp = blsuki_update_boot_device (tmp); + tmp = grub_stpcpy (tmp, linux_path); + if (options != NULL) + { +@@ -582,7 +618,7 @@ bls_get_initrd (grub_blsuki_entry_t *entry) + + for (i = 0; initrd_list != NULL && initrd_list[i] != NULL; i++) + { +- if (grub_add (size, 1, &size) || ++ if (grub_add (size, sizeof (" " GRUB_BOOT_DEVICE) - 1, &size) || + grub_add (size, grub_strlen (initrd_list[i]), &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd buffer size"); +@@ -605,6 +641,7 @@ bls_get_initrd (grub_blsuki_entry_t *entry) + { + grub_dprintf ("blsuki", "adding initrd %s\n", initrd_list[i]); + tmp = grub_stpcpy (tmp, " "); ++ tmp = blsuki_update_boot_device (tmp); + tmp = grub_stpcpy (tmp, initrd_list[i]); + } + tmp = grub_stpcpy (tmp, "\n"); +@@ -630,7 +667,7 @@ bls_get_devicetree (grub_blsuki_entry_t *entry) + dt_path = blsuki_expand_val (blsuki_get_val (entry, "devicetree", NULL)); + if (dt_path != NULL) + { +- if (grub_add (sizeof ("devicetree "), grub_strlen (dt_path), &size) || ++ if (grub_add (sizeof ("devicetree " GRUB_BOOT_DEVICE), grub_strlen (dt_path), &size) || + grub_add (size, 1, &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating device tree buffer size"); +@@ -643,6 +680,7 @@ bls_get_devicetree (grub_blsuki_entry_t *entry) + + tmp = dt_cmd; + tmp = grub_stpcpy (dt_cmd, "devicetree "); ++ tmp = blsuki_update_boot_device (tmp); + tmp = grub_stpcpy (tmp, dt_path); + tmp = grub_stpcpy (tmp, "\n"); + } +@@ -757,7 +795,11 @@ blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, c + + if (devid == NULL) + { ++#ifdef GRUB_MACHINE_EMU ++ devid = "host"; ++#else + devid = grub_env_get ("root"); ++#endif + if (devid == NULL) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable '%s' isn't set"), "root"); + } +@@ -799,10 +841,13 @@ blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, c + * parameter. If the fallback option is enabled, the default location will be + * checked for BLS config files if the first attempt fails. + */ +-static void ++static grub_err_t + blsuki_find_entry (struct find_entry_info *info, bool enable_fallback) + { + struct read_entry_info read_entry_info; ++ char *default_dir = NULL; ++ char *tmp; ++ grub_size_t default_size; + grub_fs_t dir_fs = NULL; + grub_device_t dir_dev = NULL; + bool fallback = false; +@@ -833,7 +878,15 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback) + */ + if (entries == NULL && fallback == false && enable_fallback == true) + { +- blsuki_set_find_entry_info (info, GRUB_BLS_CONFIG_PATH, NULL); ++ default_size = sizeof (GRUB_BOOT_DEVICE) + sizeof (GRUB_BLS_CONFIG_PATH) - 1; ++ default_dir = grub_malloc (default_size); ++ if (default_dir == NULL) ++ return grub_errno; ++ ++ tmp = blsuki_update_boot_device (default_dir); ++ tmp = grub_stpcpy (tmp, GRUB_BLS_CONFIG_PATH); ++ ++ blsuki_set_find_entry_info (info, default_dir, NULL); + grub_dprintf ("blsuki", "Entries weren't found in %s, fallback to %s\n", + read_entry_info.dirname, info->dirname); + fallback = true; +@@ -842,6 +895,9 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback) + fallback = false; + } + while (fallback == true); ++ ++ grub_free (default_dir); ++ return GRUB_ERR_NONE; + } + + static grub_err_t +@@ -851,6 +907,9 @@ blsuki_load_entries (char *path, bool enable_fallback) + static grub_err_t r; + const char *devid = NULL; + char *dir = NULL; ++ char *default_dir = NULL; ++ char *tmp; ++ grub_size_t dir_size; + struct find_entry_info info = { + .dev = NULL, + .fs = NULL, +@@ -892,15 +951,25 @@ blsuki_load_entries (char *path, bool enable_fallback) + } + + if (dir == NULL) +- dir = (char *) GRUB_BLS_CONFIG_PATH; ++ { ++ dir_size = sizeof (GRUB_BOOT_DEVICE) + sizeof (GRUB_BLS_CONFIG_PATH) - 2; ++ default_dir = grub_malloc (dir_size); ++ if (default_dir == NULL) ++ return grub_errno; ++ ++ tmp = blsuki_update_boot_device (default_dir); ++ tmp = grub_stpcpy (tmp, GRUB_BLS_CONFIG_PATH); ++ dir = default_dir; ++ } + + r = blsuki_set_find_entry_info (&info, dir, devid); + if (r == GRUB_ERR_NONE) +- blsuki_find_entry (&info, enable_fallback); ++ r = blsuki_find_entry (&info, enable_fallback); + + if (info.dev != NULL) + grub_device_close (info.dev); + ++ grub_free (default_dir); + return r; + } + +diff --git a/grub-core/osdep/linux/getroot.c b/grub-core/osdep/linux/getroot.c +index 4bf386b590..8bf281c1d8 100644 +--- a/grub-core/osdep/linux/getroot.c ++++ b/grub-core/osdep/linux/getroot.c +@@ -139,6 +139,7 @@ struct mountinfo_entry + char fstype[ESCAPED_PATH_MAX + 1], device[ESCAPED_PATH_MAX + 1]; + }; + ++#ifdef GRUB_UTIL + static char ** + grub_util_raid_getmembers (const char *name, int bootable) + { +@@ -199,6 +200,7 @@ grub_util_raid_getmembers (const char *name, int bootable) + + return devicelist; + } ++#endif + + /* Statting something on a btrfs filesystem always returns a virtual device + major/minor pair rather than the real underlying device, because btrfs +@@ -700,6 +702,7 @@ out: + return ret; + } + ++#ifdef GRUB_UTIL + static char * + get_mdadm_uuid (const char *os_dev) + { +@@ -757,6 +760,7 @@ out: + + return name; + } ++#endif + + static int + grub_util_is_imsm_or_ddf (const char *os_dev) +@@ -1096,6 +1100,7 @@ grub_util_part_to_disk (const char *os_dev, struct stat *st, + return path; + } + ++#ifdef GRUB_UTIL + static char * + grub_util_get_raid_grub_dev (const char *os_dev) + { +@@ -1198,6 +1203,7 @@ grub_util_get_raid_grub_dev (const char *os_dev) + } + return grub_dev; + } ++#endif + + enum grub_dev_abstraction_types + grub_util_get_dev_abstraction_os (const char *os_dev) +@@ -1214,6 +1220,7 @@ grub_util_get_dev_abstraction_os (const char *os_dev) + return GRUB_DEV_ABSTRACTION_NONE; + } + ++#ifdef GRUB_UTIL + int + grub_util_pull_device_os (const char *os_dev, + enum grub_dev_abstraction_types ab) +@@ -1270,6 +1277,7 @@ grub_util_get_grub_dev_os (const char *os_dev) + + return grub_dev; + } ++#endif + + static void *mp = NULL; + static void +diff --git a/grub-core/osdep/unix/getroot.c b/grub-core/osdep/unix/getroot.c +index ee11b02fb6..62581ba601 100644 +--- a/grub-core/osdep/unix/getroot.c ++++ b/grub-core/osdep/unix/getroot.c +@@ -16,8 +16,8 @@ + * along with GRUB. If not, see . + */ + +-#include + #include ++#include + + #include + #include +@@ -567,6 +567,7 @@ grub_guess_root_devices (const char *dir_in) + + #endif + ++#ifdef GRUB_UTIL + void + grub_util_pull_lvm_by_command (const char *os_dev) + { +@@ -663,6 +664,7 @@ out: + free (buf); + free (vgid); + } ++#endif + + /* ZFS has similar problems to those of btrfs (see above). */ + void +diff --git a/include/grub/emu/misc.h b/include/grub/emu/misc.h +index f3a712a8b2..59b8038c60 100644 +--- a/include/grub/emu/misc.h ++++ b/include/grub/emu/misc.h +@@ -39,7 +39,7 @@ void grub_fini_all (void); + void grub_find_zpool_from_dir (const char *dir, + char **poolname, char **poolfs); + +-char *grub_make_system_path_relative_to_its_root (const char *path) ++char *EXPORT_FUNC (grub_make_system_path_relative_to_its_root) (const char *path) + WARN_UNUSED_RESULT; + int + grub_util_device_is_mapped (const char *dev); diff --git a/0005-blsuki-Add-uki-command-to-load-Unified-Kernel-Image-.patch b/0005-blsuki-Add-uki-command-to-load-Unified-Kernel-Image-.patch new file mode 100644 index 00000000..fe91d9e4 --- /dev/null +++ b/0005-blsuki-Add-uki-command-to-load-Unified-Kernel-Image-.patch @@ -0,0 +1,822 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Alec Brown +Date: Tue, 12 Aug 2025 03:45:36 +0000 +Subject: [PATCH] blsuki: Add uki command to load Unified Kernel Image entries + +A Unified Kernel Image (UKI) is a single UEFI PE file that combines +a UEFI boot stub, a Linux kernel image, an initrd, and further resources. +The uki command will locate where the UKI file is and create a GRUB menu +entry to load it. + +The Unified Kernel Image Specification: https://uapi-group.org/specifications/specs/unified_kernel_image/ + +Signed-off-by: Alec Brown +Reviewed-by: Daniel Kiper +--- + docs/grub.texi | 36 +++- + grub-core/commands/blsuki.c | 476 ++++++++++++++++++++++++++++++++++++++++---- + include/grub/menu.h | 2 + + 3 files changed, 474 insertions(+), 40 deletions(-) + +diff --git a/docs/grub.texi b/docs/grub.texi +index 336f8e87ed..531e847f32 100644 +--- a/docs/grub.texi ++++ b/docs/grub.texi +@@ -3344,7 +3344,8 @@ chain-loaded system, @pxref{drivemap}. + @subsection blsuki_save_default + + If this variable is set, menu entries generated from BLS config files +-(@pxref{blscfg}) will be set as the default boot entry when selected. ++(@pxref{blscfg}) or UKI files (@pxref{uki}) will be set as the default boot ++entry when selected. + + @node check_appended_signatures + @subsection check_appended_signatures +@@ -4434,6 +4435,7 @@ you forget a command, you can run the command @command{help} + * true:: Do nothing, successfully + * trust:: Add public key to list of trusted keys + * trust_certificate:: Add an x509 certificate to the list of trusted certificates ++* uki:: Load Unified Kernel Image menu entries + * unset:: Unset an environment variable + @comment * vbeinfo:: List available video modes + * verify_appended:: Verify appended digital signature +@@ -6062,6 +6064,38 @@ Unset the environment variable @var{envvar}. + @end deffn + + ++@node uki ++@subsection uki ++ ++@deffn Command uki [@option{-p|--path} dir] [@option{-f|--enable-fallback}] [@option{-d|--show-default}] [@option{-n|--show-non-default}] [@option{-e|--entry} file] ++Load Unified Kernel Image (UKI) files into the GRUB menu. Boot entries ++generated from @command{uki} won't interfere with entries from @file{grub.cfg} appearing in the ++GRUB menu. Also, entries generated from @command{uki} exists only in memory and don't ++update @file{grub.cfg}. ++ ++By default, the UKI files are stored in the @file{/EFI/Linux} directory in the EFI ++system partition. If UKI files are stored elsewhere, the @option{--path} option can be ++used to check a different directory instead of the default location. If no UKI ++files are found while using the @option{--path} option, the @option{--enable-fallback} option can ++be used to check for files in the default location. ++ ++The @option{--show-default} option allows the default boot entry to be added to the ++GRUB menu from the UKI files. ++ ++The @option{--show-non-default} option allows non-default boot entries to be added to ++the GRUB menu from the UKI files. ++ ++The @option{--entry} option allows specific boot entries to be added to the GRUB menu ++from the UKI files. ++ ++The @option{--entry}, @option{--show-default}, and @option{--show-non-default} options ++are used to filter which UKI files are added to the GRUB menu. If none are ++used, all files in the default location or the location specified by @option{--path} ++will be added to the GRUB menu. ++ ++For more information on UKI, see: @uref{https://uapi-group.org/specifications/specs/unified_kernel_image/, The Unified Kernel Image Specification} ++@end deffn ++ + @ignore + @node vbeinfo + @subsection vbeinfo +diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c +index ed93a150b1..cb00f936a7 100644 +--- a/grub-core/commands/blsuki.c ++++ b/grub-core/commands/blsuki.c +@@ -32,6 +32,12 @@ + #include + #include + ++#ifdef GRUB_MACHINE_EFI ++#include ++#include ++#include ++#endif ++ + #ifdef GRUB_MACHINE_EMU + #include + #define GRUB_BOOT_DEVICE "/boot" +@@ -42,14 +48,28 @@ + GRUB_MOD_LICENSE ("GPLv3+"); + + #define GRUB_BLS_CONFIG_PATH "/loader/entries/" ++#define GRUB_UKI_CONFIG_PATH "/EFI/Linux" + + #define BLS_EXT_LEN (sizeof (".conf") - 1) ++#define UKI_EXT_LEN (sizeof (".efi") - 1) + + /* + * It is highly unlikely to ever receive a large amount of keyval pairs. A + * limit of 10000 is more than enough. + */ + #define BLSUKI_KEYVALS_MAX 10000 ++/* ++ * The only sections we read are ".cmdline" and ".osrel". The ".cmdline" ++ * section has a size limit of 4096 and it would be very unlikely for the size ++ * of the ".osrel" section to be 5 times larger than 4096. ++ */ ++#define UKI_SECTION_SIZE_MAX (5 * 4096) ++ ++enum blsuki_cmd_type ++ { ++ BLSUKI_BLS_CMD, ++ BLSUKI_UKI_CMD, ++ }; + + static const struct grub_arg_option bls_opt[] = + { +@@ -61,6 +81,18 @@ static const struct grub_arg_option bls_opt[] = + {0, 0, 0, 0, 0, 0} + }; + ++#ifdef GRUB_MACHINE_EFI ++static const struct grub_arg_option uki_opt[] = ++ { ++ {"path", 'p', 0, N_("Specify path to find UKI entries."), N_("DIR"), ARG_TYPE_PATHNAME}, ++ {"enable-fallback", 'f', 0, "Fallback to the default BLS path if --path fails to find UKI entries.", 0, ARG_TYPE_NONE}, ++ {"show-default", 'd', 0, N_("Allow the default UKI entry to be added to the GRUB menu."), 0, ARG_TYPE_NONE}, ++ {"show-non-default", 'n', 0, N_("Allow the non-default UKI entries to be added to the GRUB menu."), 0, ARG_TYPE_NONE}, ++ {"entry", 'e', 0, N_("Allow specificUKII entries to be added to the GRUB menu."), N_("FILE"), ARG_TYPE_FILE}, ++ {0, 0, 0, 0, 0, 0} ++ }; ++#endif ++ + struct keyval + { + const char *key; +@@ -71,6 +103,7 @@ struct read_entry_info + { + const char *devid; + const char *dirname; ++ enum blsuki_cmd_type cmd_type; + grub_file_t file; + }; + +@@ -82,7 +115,7 @@ struct find_entry_info + grub_fs_t fs; + }; + +-static grub_blsuki_entry_t *entries; ++static grub_blsuki_entry_t *entries = NULL; + + #define FOR_BLSUKI_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries) + +@@ -181,7 +214,7 @@ blsuki_add_keyval (grub_blsuki_entry_t *entry, char *key, char *val) + * Find the value of the key named by keyname. If there are allowed to be + * more than one, pass a pointer set to -1 to the last parameter the first + * time, and pass the same pointer through each time after, and it'll return +- * them in sorted order as defined in the BLS fragment file. ++ * them in sorted order. + */ + static char * + blsuki_get_val (grub_blsuki_entry_t *entry, const char *keyname, int *last) +@@ -310,20 +343,212 @@ bls_parse_keyvals (grub_file_t f, grub_blsuki_entry_t *entry) + return err; + } + ++#ifdef GRUB_MACHINE_EFI ++/* ++ * This function searches for the .cmdline, .osrel, and .linux sections of a ++ * UKI. We only need to store the data for the .cmdline and .osrel sections, ++ * but we also need to verify that the .linux section exists. ++ */ ++static grub_err_t ++uki_parse_keyvals (grub_file_t f, grub_blsuki_entry_t *entry) ++{ ++ struct grub_msdos_image_header *dos = NULL; ++ struct grub_pe_image_header *pe = NULL; ++ grub_off_t section_offset = 0; ++ struct grub_pe32_section_table *section = NULL; ++ struct grub_pe32_coff_header *coff_header = NULL; ++ char *val = NULL; ++ char *key = NULL; ++ const char *target[] = {".cmdline", ".osrel", ".linux", NULL}; ++ bool has_linux = false; ++ grub_err_t err = GRUB_ERR_NONE; ++ ++ dos = grub_zalloc (sizeof (*dos)); ++ if (dos == NULL) ++ return grub_errno; ++ if (grub_file_read (f, dos, sizeof (*dos)) < (grub_ssize_t) sizeof (*dos)) ++ { ++ err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read UKI image header"); ++ goto finish; ++ } ++ if (dos->msdos_magic != GRUB_DOS_MAGIC) ++ { ++ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "plain image kernel is not supported"); ++ goto finish; ++ } ++ ++ grub_dprintf ("blsuki", "PE/COFF header @ %08x\n", dos->pe_image_header_offset); ++ pe = grub_zalloc (sizeof (*pe)); ++ if (pe == NULL) ++ { ++ err = grub_errno; ++ goto finish; ++ } ++ if (grub_file_seek (f, dos->pe_image_header_offset) == (grub_off_t) -1 || ++ grub_file_read (f, pe, sizeof (*pe)) != sizeof (*pe)) ++ { ++ err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read COFF image header"); ++ goto finish; ++ } ++ if (pe->optional_header.magic != GRUB_PE32_NATIVE_MAGIC) ++ { ++ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "non-native image not supported"); ++ goto finish; ++ } ++ ++ coff_header = &(pe->coff_header); ++ section_offset = dos->pe_image_header_offset + sizeof (*pe); ++ ++ for (int i = 0; i < coff_header->num_sections; i++) ++ { ++ section = grub_zalloc (sizeof (*section)); ++ if (section == NULL) ++ { ++ err = grub_errno; ++ goto finish; ++ } ++ ++ if (grub_file_seek (f, section_offset) == (grub_off_t) -1 || ++ grub_file_read (f, section, sizeof (*section)) != sizeof (*section)) ++ { ++ err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read section header"); ++ goto finish; ++ } ++ ++ key = grub_strndup (section->name, 8); ++ if (key == NULL) ++ { ++ err = grub_errno; ++ goto finish; ++ } ++ ++ for (int j = 0; target[j] != NULL; j++) ++ { ++ if (grub_strcmp (key, target[j]) == 0) ++ { ++ /* ++ * We don't need to read the contents of the .linux PE section, but we ++ * should verify that the section exists. ++ */ ++ if (grub_strcmp (key, ".linux") == 0) ++ { ++ has_linux = true; ++ break; ++ } ++ ++ if (section->raw_data_size > UKI_SECTION_SIZE_MAX) ++ { ++ err = grub_error (GRUB_ERR_BAD_NUMBER, "UKI section size is larger than expected"); ++ goto finish; ++ } ++ ++ val = grub_zalloc (section->raw_data_size); ++ if (val == NULL) ++ { ++ err = grub_errno; ++ goto finish; ++ } ++ ++ if (grub_file_seek (f, section->raw_data_offset) == (grub_off_t) -1 || ++ grub_file_read (f, val, section->raw_data_size) != (grub_ssize_t) section->raw_data_size) ++ { ++ err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read section"); ++ goto finish; ++ } ++ ++ err = blsuki_add_keyval (entry, key, val); ++ if (err != GRUB_ERR_NONE) ++ goto finish; ++ ++ break; ++ } ++ } ++ ++ section_offset += sizeof (*section); ++ grub_free (section); ++ grub_free (val); ++ grub_free (key); ++ section = NULL; ++ val = NULL; ++ key = NULL; ++ } ++ ++ if (has_linux == false) ++ err = grub_error (GRUB_ERR_NO_KERNEL, "UKI is missing the '.linux' section"); ++ ++ finish: ++ grub_free (dos); ++ grub_free (pe); ++ grub_free (section); ++ grub_free (val); ++ grub_free (key); ++ return err; ++} ++ ++/* ++ * This function obtains the keyval pairs when the .osrel data is input into ++ * the osrel_ptr parameter and returns the keyval pair. Since we are using ++ * grub_strtok_r(), the osrel_ptr will be updated to the following line of ++ * osrel. This function returns NULL when it reaches the end of osrel. ++ */ ++static char * ++uki_read_osrel (char **osrel_ptr, char **val_ret) ++{ ++ char *key, *val; ++ grub_size_t val_size; ++ ++ for (;;) ++ { ++ key = grub_strtok_r (NULL, "\n\r", osrel_ptr); ++ if (key == NULL) ++ return NULL; ++ ++ /* Remove leading white space */ ++ while (*key == ' ' || *key == '\t') ++ key++; ++ ++ /* Skip commented lines */ ++ if (*key == '#') ++ continue; ++ ++ /* Split key/value */ ++ key = grub_strtok_r (key, "=", &val); ++ if (key == NULL || *val == '\0') ++ continue; ++ ++ /* Remove quotes from value */ ++ val_size = grub_strlen (val); ++ if ((*val == '\"' && val[val_size - 1] == '\"') || ++ (*val == '\'' && val[val_size - 1] == '\'')) ++ { ++ val[val_size - 1] = '\0'; ++ val++; ++ } ++ ++ *val_ret = val; ++ break; ++ } ++ ++ return key; ++} ++#endif ++ + /* + * If a file hasn't already been opened, this function opens a BLS config file +- * and initializes entry data before parsing keyvals and adding the entry to +- * the list of BLS entries. ++ * or UKI and initializes entry data before parsing keyvals and adding the entry ++ * to the list of BLS or UKI entries. + */ + static int + blsuki_read_entry (const char *filename, + const struct grub_dirhook_info *dirhook_info __attribute__ ((__unused__)), + void *data) + { +- grub_size_t path_len = 0, filename_len; +- grub_err_t err; ++ grub_size_t path_len = 0, ext_len = 0, filename_len; ++ grub_err_t err = GRUB_ERR_NONE; + char *p = NULL; ++ const char *ext = NULL; + grub_file_t f = NULL; ++ enum grub_file_type file_type = 0; + grub_blsuki_entry_t *entry; + struct read_entry_info *info = (struct read_entry_info *) data; + +@@ -331,17 +556,31 @@ blsuki_read_entry (const char *filename, + + filename_len = grub_strlen (filename); + ++ if (info->cmd_type == BLSUKI_BLS_CMD) ++ { ++ ext = ".conf"; ++ ext_len = BLS_EXT_LEN; ++ file_type = GRUB_FILE_TYPE_CONFIG; ++ } ++#ifdef GRUB_MACHINE_EFI ++ else if (info->cmd_type == BLSUKI_UKI_CMD) ++ { ++ ext = ".efi"; ++ ext_len = UKI_EXT_LEN; ++ file_type = GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE; ++ } ++#endif ++ + if (info->file != NULL) + f = info->file; + else + { +- if (filename_len < BLS_EXT_LEN || +- grub_strcmp (filename + filename_len - BLS_EXT_LEN, ".conf") != 0) ++ if (filename_len < ext_len || ++ grub_strcmp (filename + filename_len - ext_len, ext) != 0) + return 0; + + p = grub_xasprintf ("(%s)%s/%s", info->devid, info->dirname, filename); +- +- f = grub_file_open (p, GRUB_FILE_TYPE_CONFIG); ++ f = grub_file_open (p, file_type); + grub_free (p); + if (f == NULL) + goto finish; +@@ -373,7 +612,26 @@ blsuki_read_entry (const char *filename, + goto finish; + } + +- err = bls_parse_keyvals (f, entry); ++ entry->dirname = grub_strdup (info->dirname); ++ if (entry->dirname == NULL) ++ { ++ grub_free (entry); ++ goto finish; ++ } ++ ++ entry->devid = grub_strdup (info->devid); ++ if (entry->devid == NULL) ++ { ++ grub_free (entry); ++ goto finish; ++ } ++ ++ if (info->cmd_type == BLSUKI_BLS_CMD) ++ err = bls_parse_keyvals (f, entry); ++#ifdef GRUB_MACHINE_EFI ++ else if (info->cmd_type == BLSUKI_UKI_CMD) ++ err = uki_parse_keyvals (f, entry); ++#endif + + if (err == GRUB_ERR_NONE) + blsuki_add_entry (entry); +@@ -389,7 +647,7 @@ blsuki_read_entry (const char *filename, + + /* + * This function returns a list of values that had the same key in the BLS +- * config file. The number of entries in this list is returned by the len ++ * config file or UKI. The number of entries in this list is returned by the len + * parameter. + */ + static char ** +@@ -764,7 +1022,7 @@ bls_create_entry (grub_blsuki_entry_t *entry) + linux_cmd, initrd_cmd ? initrd_cmd : "", + dt_cmd ? dt_cmd : ""); + +- grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, src, 0, entry); ++ grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, src, 0, NULL, NULL, entry); + + finish: + grub_free (linux_cmd); +@@ -776,6 +1034,70 @@ bls_create_entry (grub_blsuki_entry_t *entry) + grub_free (src); + } + ++#ifdef GRUB_MACHINE_EFI ++/* ++ * This function puts together the section data received from the UKI and ++ * generates a new entry in the GRUB boot menu. ++ */ ++static void ++uki_create_entry (grub_blsuki_entry_t *entry) ++{ ++ const char **argv = NULL; ++ char *id = entry->filename; ++ char *title = NULL; ++ char *options = NULL; ++ char *osrel, *osrel_line; ++ char *key = NULL; ++ char *value = NULL; ++ char *src = NULL; ++ bool blsuki_save_default; ++ ++ /* ++ * Although .osrel is listed as optional in the UKI specification, the .osrel ++ * section is needed to generate the GRUB menu entry title. ++ */ ++ osrel = blsuki_get_val (entry, ".osrel", NULL); ++ if (osrel == NULL) ++ { ++ grub_dprintf ("blsuki", "Skipping file %s with no '.osrel' key.\n", entry->filename); ++ goto finish; ++ } ++ ++ osrel_line = osrel; ++ while ((key = uki_read_osrel (&osrel_line, &value)) != NULL) ++ { ++ if (grub_strcmp ("PRETTY_NAME", key) == 0) ++ { ++ title = value; ++ break; ++ } ++ } ++ ++ options = blsuki_get_val (entry, ".cmdline", NULL); ++ ++ argv = grub_zalloc (2 * sizeof (char *)); ++ if (argv == NULL) ++ goto finish; ++ argv[0] = title; ++ ++ blsuki_save_default = grub_env_get_bool ("blsuki_save_default", false); ++ src = grub_xasprintf ("%schainloader (%s)%s/%s%s%s\n", ++ blsuki_save_default ? "savedefault\n" : "", ++ entry->devid, entry->dirname, ++ entry->filename, ++ (options != NULL) ? " " : "", ++ (options != NULL) ? options : ""); ++ ++ grub_normal_add_menu_entry (1, argv, NULL, id, NULL, NULL, NULL, src, 0, NULL, NULL, entry); ++ ++ finish: ++ grub_free (argv); ++ grub_free (src); ++ grub_free (options); ++ grub_free (osrel); ++} ++#endif ++ + /* + * This function fills a find_entry_info struct passed in by the info parameter. + * If the dirname or devid parameters are set to NULL, the dirname and devid +@@ -785,7 +1107,7 @@ bls_create_entry (grub_blsuki_entry_t *entry) + * device. + */ + static grub_err_t +-blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, const char *devid) ++blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, const char *devid, enum blsuki_cmd_type cmd_type) + { + grub_device_t dev; + grub_fs_t fs; +@@ -795,10 +1117,23 @@ blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, c + + if (devid == NULL) + { ++ if (cmd_type == BLSUKI_BLS_CMD) ++ { + #ifdef GRUB_MACHINE_EMU +- devid = "host"; ++ devid = "host"; + #else +- devid = grub_env_get ("root"); ++ devid = grub_env_get ("root"); ++#endif ++ } ++#ifdef GRUB_MACHINE_EFI ++ else if (cmd_type == BLSUKI_UKI_CMD) ++ { ++ grub_efi_loaded_image_t *image = grub_efi_get_loaded_image (grub_efi_image_handle); ++ ++ if (image == NULL) ++ return grub_error (GRUB_ERR_BAD_DEVICE, N_("unable to find boot device")); ++ devid = grub_efidisk_get_device_name (image->device_handle); ++ } + #endif + if (devid == NULL) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable '%s' isn't set"), "root"); +@@ -837,15 +1172,16 @@ blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, c + } + + /* +- * This function searches for BLS config files based on the data in the info +- * parameter. If the fallback option is enabled, the default location will be +- * checked for BLS config files if the first attempt fails. ++ * This function searches for BLS config files and UKIs based on the data in the ++ * info parameter. If the fallback option is enabled, the default location will ++ * be checked for BLS config files or UKIs if the first attempt fails. + */ + static grub_err_t +-blsuki_find_entry (struct find_entry_info *info, bool enable_fallback) ++blsuki_find_entry (struct find_entry_info *info, bool enable_fallback, enum blsuki_cmd_type cmd_type) + { + struct read_entry_info read_entry_info; + char *default_dir = NULL; ++ const char *cmd_dir = NULL; + char *tmp; + grub_size_t default_size; + grub_fs_t dir_fs = NULL; +@@ -862,6 +1198,7 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback) + dir_dev = info->dev; + dir_fs = info->fs; + read_entry_info.devid = info->devid; ++ read_entry_info.cmd_type = cmd_type; + + r = dir_fs->fs_dir (dir_dev, read_entry_info.dirname, blsuki_read_entry, + &read_entry_info); +@@ -874,19 +1211,27 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback) + /* + * If we aren't able to find BLS entries in the directory given by info->dirname, + * we can fallback to the default location "/boot/loader/entries/" and see if we +- * can find the files there. ++ * can find the files there. If we can't find UKI entries, fallback to ++ * "/EFI/Linux" on the EFI system partition. + */ + if (entries == NULL && fallback == false && enable_fallback == true) + { +- default_size = sizeof (GRUB_BOOT_DEVICE) + sizeof (GRUB_BLS_CONFIG_PATH) - 1; ++ if (cmd_type == BLSUKI_BLS_CMD) ++ cmd_dir = GRUB_BLS_CONFIG_PATH; ++#ifdef GRUB_MACHINE_EFI ++ else if (cmd_type == BLSUKI_UKI_CMD) ++ cmd_dir = GRUB_UKI_CONFIG_PATH; ++#endif ++ ++ default_size = sizeof (GRUB_BOOT_DEVICE) + grub_strlen (cmd_dir); + default_dir = grub_malloc (default_size); + if (default_dir == NULL) + return grub_errno; + + tmp = blsuki_update_boot_device (default_dir); +- tmp = grub_stpcpy (tmp, GRUB_BLS_CONFIG_PATH); ++ tmp = grub_stpcpy (tmp, cmd_dir); + +- blsuki_set_find_entry_info (info, default_dir, NULL); ++ blsuki_set_find_entry_info (info, default_dir, NULL, cmd_type); + grub_dprintf ("blsuki", "Entries weren't found in %s, fallback to %s\n", + read_entry_info.dirname, info->dirname); + fallback = true; +@@ -901,15 +1246,17 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback) + } + + static grub_err_t +-blsuki_load_entries (char *path, bool enable_fallback) ++blsuki_load_entries (char *path, bool enable_fallback, enum blsuki_cmd_type cmd_type) + { +- grub_size_t len; ++ grub_size_t len, ext_len = 0; + static grub_err_t r; + const char *devid = NULL; + char *dir = NULL; + char *default_dir = NULL; + char *tmp; ++ const char *cmd_dir = NULL; + grub_size_t dir_size; ++ const char *ext = NULL; + struct find_entry_info info = { + .dev = NULL, + .fs = NULL, +@@ -918,12 +1265,26 @@ blsuki_load_entries (char *path, bool enable_fallback) + struct read_entry_info rei = { + .devid = NULL, + .dirname = NULL, ++ .cmd_type = cmd_type, + }; + + if (path != NULL) + { ++ if (cmd_type == BLSUKI_BLS_CMD) ++ { ++ ext = ".conf"; ++ ext_len = BLS_EXT_LEN; ++ } ++#ifdef GRUB_MACHINE_EFI ++ else if (cmd_type == BLSUKI_UKI_CMD) ++ { ++ ext = ".efi"; ++ ext_len = UKI_EXT_LEN; ++ } ++#endif ++ + len = grub_strlen (path); +- if (len >= BLS_EXT_LEN && grub_strcmp (path + len - BLS_EXT_LEN, ".conf") == 0) ++ if (len >= ext_len && grub_strcmp (path + len - ext_len, ext) == 0) + { + rei.file = grub_file_open (path, GRUB_FILE_TYPE_CONFIG); + if (rei.file == NULL) +@@ -952,19 +1313,26 @@ blsuki_load_entries (char *path, bool enable_fallback) + + if (dir == NULL) + { +- dir_size = sizeof (GRUB_BOOT_DEVICE) + sizeof (GRUB_BLS_CONFIG_PATH) - 2; ++ if (cmd_type == BLSUKI_BLS_CMD) ++ cmd_dir = GRUB_BLS_CONFIG_PATH; ++#ifdef GRUB_MACHINE_EFI ++ else if (cmd_type == BLSUKI_UKI_CMD) ++ cmd_dir = GRUB_UKI_CONFIG_PATH; ++#endif ++ ++ dir_size = sizeof (GRUB_BOOT_DEVICE) + grub_strlen (cmd_dir); + default_dir = grub_malloc (dir_size); + if (default_dir == NULL) + return grub_errno; + + tmp = blsuki_update_boot_device (default_dir); +- tmp = grub_stpcpy (tmp, GRUB_BLS_CONFIG_PATH); ++ tmp = grub_stpcpy (tmp, cmd_dir); + dir = default_dir; + } + +- r = blsuki_set_find_entry_info (&info, dir, devid); ++ r = blsuki_set_find_entry_info (&info, dir, devid, cmd_type); + if (r == GRUB_ERR_NONE) +- r = blsuki_find_entry (&info, enable_fallback); ++ r = blsuki_find_entry (&info, enable_fallback, cmd_type); + + if (info.dev != NULL) + grub_device_close (info.dev); +@@ -1002,11 +1370,11 @@ blsuki_is_default_entry (const char *def_entry, grub_blsuki_entry_t *entry, int + } + + /* +- * This function creates a GRUB boot menu entry for each BLS entry in the +- * entries list. ++ * This function creates a GRUB boot menu entry for each BLS or UKI entry in ++ * the entries list. + */ + static grub_err_t +-blsuki_create_entries (bool show_default, bool show_non_default, char *entry_id) ++blsuki_create_entries (bool show_default, bool show_non_default, char *entry_id, enum blsuki_cmd_type cmd_type) + { + const char *def_entry = NULL; + grub_blsuki_entry_t *entry = NULL; +@@ -1025,7 +1393,12 @@ blsuki_create_entries (bool show_default, bool show_non_default, char *entry_id) + (show_non_default == true && blsuki_is_default_entry (def_entry, entry, idx) == false) || + (entry_id != NULL && grub_strcmp (entry_id, entry->filename) == 0)) + { +- bls_create_entry (entry); ++ if (cmd_type == BLSUKI_BLS_CMD) ++ bls_create_entry (entry); ++#ifdef GRUB_MACHINE_EFI ++ else if (cmd_type == BLSUKI_UKI_CMD) ++ uki_create_entry (entry); ++#endif + entry->visible = true; + } + +@@ -1036,8 +1409,7 @@ blsuki_create_entries (bool show_default, bool show_non_default, char *entry_id) + } + + static grub_err_t +-grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)), +- char **args __attribute__ ((unused))) ++blsuki_cmd (grub_extcmd_context_t ctxt, enum blsuki_cmd_type cmd_type) + { + grub_err_t err; + struct grub_arg_list *state = ctxt->state; +@@ -1074,24 +1446,50 @@ grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)), + show_non_default = true; + } + +- err = blsuki_load_entries (path, enable_fallback); ++ err = blsuki_load_entries (path, enable_fallback, cmd_type); + if (err != GRUB_ERR_NONE) + return err; + +- return blsuki_create_entries (show_default, show_non_default, entry_id); ++ return blsuki_create_entries (show_default, show_non_default, entry_id, cmd_type); ++} ++ ++static grub_err_t ++grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)), ++ char **args __attribute__ ((unused))) ++{ ++ return blsuki_cmd (ctxt, BLSUKI_BLS_CMD); + } + + static grub_extcmd_t bls_cmd; + ++#ifdef GRUB_MACHINE_EFI ++static grub_err_t ++grub_cmd_uki (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)), ++ char **args __attribute__ ((unused))) ++{ ++ return blsuki_cmd (ctxt, BLSUKI_UKI_CMD); ++} ++ ++static grub_extcmd_t uki_cmd; ++#endif ++ + GRUB_MOD_INIT(blsuki) + { + bls_cmd = grub_register_extcmd ("blscfg", grub_cmd_blscfg, 0, + N_("[-p|--path] [-f|--enable-fallback] DIR [-d|--show-default] [-n|--show-non-default] [-e|--entry] FILE"), + N_("Import Boot Loader Specification snippets."), + bls_opt); ++#ifdef GRUB_MACHINE_EFI ++ uki_cmd = grub_register_extcmd ("uki", grub_cmd_uki, 0, ++ N_("[-p|--path] DIR [-f|--enable-fallback] [-d|--show-default] [-n|--show-non-default] [-e|--entry] FILE"), ++ N_("Import Unified Kernel Images"), uki_opt); ++#endif + } + + GRUB_MOD_FINI(blsuki) + { + grub_unregister_extcmd (bls_cmd); ++#ifdef GRUB_MACHINE_EFI ++ grub_unregister_extcmd (uki_cmd); ++#endif + } +diff --git a/include/grub/menu.h b/include/grub/menu.h +index 43a3c5809b..0e4978dc3b 100644 +--- a/include/grub/menu.h ++++ b/include/grub/menu.h +@@ -39,6 +39,8 @@ struct grub_blsuki_entry + grub_size_t keyvals_size; + int nkeyvals; + char *filename; ++ char *dirname; ++ char *devid; + bool visible; + }; + typedef struct grub_blsuki_entry grub_blsuki_entry_t; diff --git a/0006-posix_wrap-Tweaks-in-preparation-for-libtasn1.patch b/0006-posix_wrap-Tweaks-in-preparation-for-libtasn1.patch new file mode 100644 index 00000000..d5932723 --- /dev/null +++ b/0006-posix_wrap-Tweaks-in-preparation-for-libtasn1.patch @@ -0,0 +1,168 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Daniel Axtens +Date: Fri, 15 Nov 2024 15:34:29 +0800 +Subject: [PATCH] posix_wrap: Tweaks in preparation for libtasn1 + +Cc: Vladimir Serbinenko +Signed-off-by: Daniel Axtens +Signed-off-by: Gary Lin +Reviewed-by: Daniel Kiper +Tested-by: Stefan Berger +--- + grub-core/lib/posix_wrap/c-ctype.h | 114 +++++++++++++++++++++++++++++++++++++ + grub-core/lib/posix_wrap/string.h | 21 +++++++ + 2 files changed, 135 insertions(+) + create mode 100644 grub-core/lib/posix_wrap/c-ctype.h + +diff --git a/grub-core/lib/posix_wrap/c-ctype.h b/grub-core/lib/posix_wrap/c-ctype.h +new file mode 100644 +index 0000000000..5f8fc8ce3f +--- /dev/null ++++ b/grub-core/lib/posix_wrap/c-ctype.h +@@ -0,0 +1,114 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2024 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#ifndef GRUB_POSIX_C_CTYPE_H ++#define GRUB_POSIX_C_CTYPE_H 1 ++ ++#include ++ ++static inline bool ++c_isspace (int c) ++{ ++ return !!grub_isspace (c); ++} ++ ++static inline bool ++c_isdigit (int c) ++{ ++ return !!grub_isdigit (c); ++} ++ ++static inline bool ++c_islower (int c) ++{ ++ return !!grub_islower (c); ++} ++ ++static inline bool ++c_isascii (int c) ++{ ++ return !(c & ~0x7f); ++} ++ ++static inline bool ++c_isupper (int c) ++{ ++ return !!grub_isupper (c); ++} ++ ++static inline bool ++c_isxdigit (int c) ++{ ++ return !!grub_isxdigit (c); ++} ++ ++static inline bool ++c_isprint (int c) ++{ ++ return !!grub_isprint (c); ++} ++ ++static inline bool ++c_iscntrl (int c) ++{ ++ return !grub_isprint (c); ++} ++ ++static inline bool ++c_isgraph (int c) ++{ ++ return grub_isprint (c) && !grub_isspace (c); ++} ++ ++static inline bool ++c_isalnum (int c) ++{ ++ return grub_isalpha (c) || grub_isdigit (c); ++} ++ ++static inline bool ++c_ispunct (int c) ++{ ++ return grub_isprint (c) && !grub_isspace (c) && !c_isalnum (c); ++} ++ ++static inline bool ++c_isalpha (int c) ++{ ++ return !!grub_isalpha (c); ++} ++ ++static inline bool ++c_isblank (int c) ++{ ++ return c == ' ' || c == '\t'; ++} ++ ++static inline int ++c_tolower (int c) ++{ ++ return grub_tolower (c); ++} ++ ++static inline int ++c_toupper (int c) ++{ ++ return grub_toupper (c); ++} ++ ++#endif +diff --git a/grub-core/lib/posix_wrap/string.h b/grub-core/lib/posix_wrap/string.h +index 1adb450b5a..d3e400d500 100644 +--- a/grub-core/lib/posix_wrap/string.h ++++ b/grub-core/lib/posix_wrap/string.h +@@ -84,6 +84,27 @@ memchr (const void *s, int c, grub_size_t n) + return grub_memchr (s, c, n); + } + ++static inline char * ++strncat (char *dest, const char *src, grub_size_t n) ++{ ++ const char *end; ++ char *str = dest; ++ grub_size_t src_len; ++ ++ dest += grub_strlen (dest); ++ ++ end = grub_memchr (src, '\0', n); ++ if (end != NULL) ++ src_len = (grub_size_t) (end - src); ++ else ++ src_len = n; ++ ++ dest[src_len] = '\0'; ++ grub_memcpy (dest, src, src_len); ++ ++ return str; ++} ++ + #define memcmp grub_memcmp + #define memcpy grub_memcpy + #define memmove grub_memmove diff --git a/grub.macros b/grub.macros index dfc9707d..f6455473 100644 --- a/grub.macros +++ b/grub.macros @@ -518,7 +518,7 @@ install -m 644 %{1}.conf ${RPM_BUILD_ROOT}/etc/dnf/protected.d/ \ rm -f %{1}.conf \ %{nil} -%global grub_modules " all_video boot blscfg btrfs \\\ +%global grub_modules " all_video boot blscfg btrfs blsuki \\\ cat configfile cryptodisk \\\ echo ext2 f2fs fat font \\\ gcry_rijndael gcry_rsa gcry_serpent \\\ diff --git a/grub.patches b/grub.patches index 66e0b693..330fb13a 100644 --- a/grub.patches +++ b/grub.patches @@ -379,4 +379,11 @@ Patch0379: 0379-loader-efi-chainloader-Fix-double-free.patch Patch0380: 0380-loader-efi-chainloader-Fix-null-pointer-dereference.patch Patch0381: 0381-osdep-linux-getroot-Detect-DDF-container-similar-to-.patch Patch0382: 0382-Set-correctly-the-memory-attributes-for-the-kernel-P.patch -Patch0383: 0383-Include-function-name-on-debug-print-function.patch \ No newline at end of file +Patch0383: 0383-Include-function-name-on-debug-print-function.patch +Patch0384: 0001-kern-misc-Implement-grub_strtok.patch +Patch0385: 0002-blsuki-Add-blscfg-command-to-parse-Boot-Loader-Speci.patch +Patch0386: 0003-util-misc.c-Change-offset-type-for-grub_util_write_i.patch +Patch0387: 0004-blsuki-Check-for-mounted-boot-in-emu.patch +Patch0388: 0005-blsuki-Add-uki-command-to-load-Unified-Kernel-Image-.patch +Patch0389: 0006-posix_wrap-Tweaks-in-preparation-for-libtasn1.patch + diff --git a/grub2.spec b/grub2.spec index 4563828d..c63f940a 100644 --- a/grub2.spec +++ b/grub2.spec @@ -17,7 +17,7 @@ Name: grub2 Epoch: 1 Version: 2.12 -Release: 44%{?dist} +Release: 45%{?dist} Summary: Bootloader with support for Linux, Multiboot and more License: GPL-3.0-or-later URL: http://www.gnu.org/software/grub/ @@ -609,6 +609,9 @@ fi %endif %changelog +* Wed Oct 15 2025 Leo Sandoval - 2.12-45 +- Include upstream blsuki related patches + * Mon Sep 15 2025 Leo Sandoval - 2.12-44 - Include function name on debug print function From f4663b0ddc6e7b05b81bff630061838b77e725bc Mon Sep 17 00:00:00 2001 From: Leo Sandoval Date: Mon, 27 Oct 2025 11:37:40 -0600 Subject: [PATCH 06/11] Fix blsuki patch enumeration Signed-off-by: Leo Sandoval --- ...tch => 0384-kern-misc-Implement-grub_strtok.patch | 0 ...d-blscfg-command-to-parse-Boot-Loader-Speci.patch | 0 ....c-Change-offset-type-for-grub_util_write_i.patch | 0 ...> 0387-blsuki-Check-for-mounted-boot-in-emu.patch | 0 ...d-uki-command-to-load-Unified-Kernel-Image-.patch | 0 ...six_wrap-Tweaks-in-preparation-for-libtasn1.patch | 0 grub.patches | 12 ++++++------ 7 files changed, 6 insertions(+), 6 deletions(-) rename 0001-kern-misc-Implement-grub_strtok.patch => 0384-kern-misc-Implement-grub_strtok.patch (100%) rename 0002-blsuki-Add-blscfg-command-to-parse-Boot-Loader-Speci.patch => 0385-blsuki-Add-blscfg-command-to-parse-Boot-Loader-Speci.patch (100%) rename 0003-util-misc.c-Change-offset-type-for-grub_util_write_i.patch => 0386-util-misc.c-Change-offset-type-for-grub_util_write_i.patch (100%) rename 0004-blsuki-Check-for-mounted-boot-in-emu.patch => 0387-blsuki-Check-for-mounted-boot-in-emu.patch (100%) rename 0005-blsuki-Add-uki-command-to-load-Unified-Kernel-Image-.patch => 0388-blsuki-Add-uki-command-to-load-Unified-Kernel-Image-.patch (100%) rename 0006-posix_wrap-Tweaks-in-preparation-for-libtasn1.patch => 0389-posix_wrap-Tweaks-in-preparation-for-libtasn1.patch (100%) diff --git a/0001-kern-misc-Implement-grub_strtok.patch b/0384-kern-misc-Implement-grub_strtok.patch similarity index 100% rename from 0001-kern-misc-Implement-grub_strtok.patch rename to 0384-kern-misc-Implement-grub_strtok.patch diff --git a/0002-blsuki-Add-blscfg-command-to-parse-Boot-Loader-Speci.patch b/0385-blsuki-Add-blscfg-command-to-parse-Boot-Loader-Speci.patch similarity index 100% rename from 0002-blsuki-Add-blscfg-command-to-parse-Boot-Loader-Speci.patch rename to 0385-blsuki-Add-blscfg-command-to-parse-Boot-Loader-Speci.patch diff --git a/0003-util-misc.c-Change-offset-type-for-grub_util_write_i.patch b/0386-util-misc.c-Change-offset-type-for-grub_util_write_i.patch similarity index 100% rename from 0003-util-misc.c-Change-offset-type-for-grub_util_write_i.patch rename to 0386-util-misc.c-Change-offset-type-for-grub_util_write_i.patch diff --git a/0004-blsuki-Check-for-mounted-boot-in-emu.patch b/0387-blsuki-Check-for-mounted-boot-in-emu.patch similarity index 100% rename from 0004-blsuki-Check-for-mounted-boot-in-emu.patch rename to 0387-blsuki-Check-for-mounted-boot-in-emu.patch diff --git a/0005-blsuki-Add-uki-command-to-load-Unified-Kernel-Image-.patch b/0388-blsuki-Add-uki-command-to-load-Unified-Kernel-Image-.patch similarity index 100% rename from 0005-blsuki-Add-uki-command-to-load-Unified-Kernel-Image-.patch rename to 0388-blsuki-Add-uki-command-to-load-Unified-Kernel-Image-.patch diff --git a/0006-posix_wrap-Tweaks-in-preparation-for-libtasn1.patch b/0389-posix_wrap-Tweaks-in-preparation-for-libtasn1.patch similarity index 100% rename from 0006-posix_wrap-Tweaks-in-preparation-for-libtasn1.patch rename to 0389-posix_wrap-Tweaks-in-preparation-for-libtasn1.patch diff --git a/grub.patches b/grub.patches index 330fb13a..98c50a8b 100644 --- a/grub.patches +++ b/grub.patches @@ -380,10 +380,10 @@ Patch0380: 0380-loader-efi-chainloader-Fix-null-pointer-dereference.patch Patch0381: 0381-osdep-linux-getroot-Detect-DDF-container-similar-to-.patch Patch0382: 0382-Set-correctly-the-memory-attributes-for-the-kernel-P.patch Patch0383: 0383-Include-function-name-on-debug-print-function.patch -Patch0384: 0001-kern-misc-Implement-grub_strtok.patch -Patch0385: 0002-blsuki-Add-blscfg-command-to-parse-Boot-Loader-Speci.patch -Patch0386: 0003-util-misc.c-Change-offset-type-for-grub_util_write_i.patch -Patch0387: 0004-blsuki-Check-for-mounted-boot-in-emu.patch -Patch0388: 0005-blsuki-Add-uki-command-to-load-Unified-Kernel-Image-.patch -Patch0389: 0006-posix_wrap-Tweaks-in-preparation-for-libtasn1.patch +Patch0384: 0384-kern-misc-Implement-grub_strtok.patch +Patch0385: 0385-blsuki-Add-blscfg-command-to-parse-Boot-Loader-Speci.patch +Patch0386: 0386-util-misc.c-Change-offset-type-for-grub_util_write_i.patch +Patch0387: 0387-blsuki-Check-for-mounted-boot-in-emu.patch +Patch0388: 0388-blsuki-Add-uki-command-to-load-Unified-Kernel-Image-.patch +Patch0389: 0389-posix_wrap-Tweaks-in-preparation-for-libtasn1.patch From 6467d85178d4ce4522a126aee3ba0e8ea27adf4c Mon Sep 17 00:00:00 2001 From: Tom spot Callaway Date: Tue, 4 Nov 2025 16:20:54 -0500 Subject: [PATCH 07/11] rebuild for new fuse3 --- grub2.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/grub2.spec b/grub2.spec index c63f940a..0730550e 100644 --- a/grub2.spec +++ b/grub2.spec @@ -17,7 +17,7 @@ Name: grub2 Epoch: 1 Version: 2.12 -Release: 45%{?dist} +Release: 46%{?dist} Summary: Bootloader with support for Linux, Multiboot and more License: GPL-3.0-or-later URL: http://www.gnu.org/software/grub/ @@ -609,6 +609,9 @@ fi %endif %changelog +* Tue Nov 4 2025 Tom Callaway - 2.12-46 +- rebuild for new fuse3 + * Wed Oct 15 2025 Leo Sandoval - 2.12-45 - Include upstream blsuki related patches From a4ec822e7e93aa93bbcfda5719036b7467f3e2a6 Mon Sep 17 00:00:00 2001 From: Leo Sandoval Date: Thu, 13 Nov 2025 12:06:23 -0600 Subject: [PATCH 08/11] blsuki: do not register blscfg command Otherwise the blsuki.c blscfg cmd version replaces the one defined on blscfg.c and the latter is the version we want to use. Signed-off-by: Leo Sandoval --- ...lsuki-do-not-register-blscfg-command.patch | 43 +++++++++++++++++++ grub.patches | 2 +- grub2.spec | 5 ++- 3 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 0390-blsuki-do-not-register-blscfg-command.patch diff --git a/0390-blsuki-do-not-register-blscfg-command.patch b/0390-blsuki-do-not-register-blscfg-command.patch new file mode 100644 index 00000000..3fd4fa59 --- /dev/null +++ b/0390-blsuki-do-not-register-blscfg-command.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Leo Sandoval +Date: Thu, 13 Nov 2025 11:57:52 -0600 +Subject: [PATCH] blsuki: do not register blscfg command + +The blscfg command is already defined on commands/blscfg.c so do not +register it on blsuki.c. Also, the 'uki' command is only intended for +EFI system so remove the other platforms from the module. + +Signed-off-by: Leo Sandoval +--- + grub-core/Makefile.core.def | 2 -- + grub-core/commands/blsuki.c | 4 ---- + 2 files changed, 6 deletions(-) + +diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def +index 885dd436de..a545ee430b 100644 +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -893,8 +893,6 @@ module = { + common = lib/gnulib/filevercmp.c; + enable = powerpc_ieee1275; + enable = efi; +- enable = i386_pc; +- enable = emu; + cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)'; + cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB)'; + }; +diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c +index cb00f936a7..bcd0114d85 100644 +--- a/grub-core/commands/blsuki.c ++++ b/grub-core/commands/blsuki.c +@@ -1475,10 +1475,6 @@ static grub_extcmd_t uki_cmd; + + GRUB_MOD_INIT(blsuki) + { +- bls_cmd = grub_register_extcmd ("blscfg", grub_cmd_blscfg, 0, +- N_("[-p|--path] [-f|--enable-fallback] DIR [-d|--show-default] [-n|--show-non-default] [-e|--entry] FILE"), +- N_("Import Boot Loader Specification snippets."), +- bls_opt); + #ifdef GRUB_MACHINE_EFI + uki_cmd = grub_register_extcmd ("uki", grub_cmd_uki, 0, + N_("[-p|--path] DIR [-f|--enable-fallback] [-d|--show-default] [-n|--show-non-default] [-e|--entry] FILE"), diff --git a/grub.patches b/grub.patches index 98c50a8b..af74d2e3 100644 --- a/grub.patches +++ b/grub.patches @@ -386,4 +386,4 @@ Patch0386: 0386-util-misc.c-Change-offset-type-for-grub_util_write_i.patch Patch0387: 0387-blsuki-Check-for-mounted-boot-in-emu.patch Patch0388: 0388-blsuki-Add-uki-command-to-load-Unified-Kernel-Image-.patch Patch0389: 0389-posix_wrap-Tweaks-in-preparation-for-libtasn1.patch - +Patch0390: 0390-blsuki-do-not-register-blscfg-command.patch diff --git a/grub2.spec b/grub2.spec index 0730550e..bde96ec1 100644 --- a/grub2.spec +++ b/grub2.spec @@ -17,7 +17,7 @@ Name: grub2 Epoch: 1 Version: 2.12 -Release: 46%{?dist} +Release: 47%{?dist} Summary: Bootloader with support for Linux, Multiboot and more License: GPL-3.0-or-later URL: http://www.gnu.org/software/grub/ @@ -609,6 +609,9 @@ fi %endif %changelog +* Thu Nov 13 2025 Leo Sandoval - 2.12-47 +- blsuki: do not register blscfg command + * Tue Nov 4 2025 Tom Callaway - 2.12-46 - rebuild for new fuse3 From 70c5e629500a7364b805759ab649547f627e4ba8 Mon Sep 17 00:00:00 2001 From: Leo Sandoval Date: Mon, 27 Oct 2025 11:55:32 -0600 Subject: [PATCH 09/11] Add support for external environment block on Btrfs Signed-off-by: Leo Sandoval Tested-by: Neal Gompa --- 0391-misc-add-z-length-modifier-support.patch | 81 +++++++ 0392-tests-add-z-modifier-printf-tests.patch | 43 ++++ ...ub-editenv-add-fs_envblk-open-helper.patch | 219 ++++++++++++++++++ ...b-editenv-add-fs_envblk-write-helper.patch | 91 ++++++++ ...v-wire-set_variables-to-optional-fs_.patch | 118 ++++++++++ ...v-wire-unset_variables-to-optional-f.patch | 56 +++++ ...v-wire-list_variables-to-optional-fs.patch | 45 ++++ ...v-add-probe-call-for-external-envblk.patch | 176 ++++++++++++++ ...onment-block-to-reserved-header-area.patch | 93 ++++++++ ...e-grub.cfg-to-use-env_block-when-pre.patch | 82 +++++++ ...date-doc-link-for-bootloader-support.patch | 29 +++ ...Btrfs-env-block-and-special-env-vars.patch | 93 ++++++++ ...ile-read_hook-in-grub_fs_blocklist_r.patch | 73 ++++++ ...eader.in-Prefer-dev-instead-of-root-.patch | 44 ++++ ...he-external-environment-block-for-au.patch | 89 +++++++ ...e-automatic-hidden-menu-and-boot-cou.patch | 36 +++ grub.patches | 16 ++ grub2.spec | 5 +- 18 files changed, 1388 insertions(+), 1 deletion(-) create mode 100644 0391-misc-add-z-length-modifier-support.patch create mode 100644 0392-tests-add-z-modifier-printf-tests.patch create mode 100644 0393-util-grub-editenv-add-fs_envblk-open-helper.patch create mode 100644 0394-util-grub-editenv-add-fs_envblk-write-helper.patch create mode 100644 0395-util-grub-editenv-wire-set_variables-to-optional-fs_.patch create mode 100644 0396-util-grub-editenv-wire-unset_variables-to-optional-f.patch create mode 100644 0397-util-grub-editenv-wire-list_variables-to-optional-fs.patch create mode 100644 0398-util-grub-editenv-add-probe-call-for-external-envblk.patch create mode 100644 0399-btrfs-add-environment-block-to-reserved-header-area.patch create mode 100644 0400-00_header.in-wire-grub.cfg-to-use-env_block-when-pre.patch create mode 100644 0401-btrfs-update-doc-link-for-bootloader-support.patch create mode 100644 0402-docs-add-Btrfs-env-block-and-special-env-vars.patch create mode 100644 0403-kern-fs-Honour-file-read_hook-in-grub_fs_blocklist_r.patch create mode 100644 0404-util-grub.d-00_header.in-Prefer-dev-instead-of-root-.patch create mode 100644 0405-grub.d-Support-the-external-environment-block-for-au.patch create mode 100644 0406-grub-editenv-Make-automatic-hidden-menu-and-boot-cou.patch diff --git a/0391-misc-add-z-length-modifier-support.patch b/0391-misc-add-z-length-modifier-support.patch new file mode 100644 index 00000000..2fdcd77c --- /dev/null +++ b/0391-misc-add-z-length-modifier-support.patch @@ -0,0 +1,81 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Michael Chang via Grub-devel +Date: Fri, 17 Oct 2025 17:01:32 +0800 +Subject: [PATCH] misc: add z length modifier support + +Add support for the 'z' length modifier in the printf code. This allows +printing of size_t and ssize_t values using %zu, %zd and related +formats. The parser maps 'z' to the correct integer width based on +sizeof (size_t). + +Signed-off-by: Michael Chang +Reviewed-by: Neal Gompa +Reviewed-by: Daniel Kiper +--- + grub-core/kern/misc.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c +index c1c77c300c..2568a5489b 100644 +--- a/grub-core/kern/misc.c ++++ b/grub-core/kern/misc.c +@@ -30,6 +30,7 @@ + #if DEBUG_WITH_TIMESTAMPS + #include + #endif ++#include + + union printf_arg + { +@@ -883,6 +884,9 @@ parse_printf_arg_fmt (const char *fmt0, struct printf_args *args, + COMPILE_TIME_ASSERT (sizeof (long) <= sizeof (long long)); + COMPILE_TIME_ASSERT (sizeof (long long) == sizeof (void *) + || sizeof (int) == sizeof (void *)); ++ COMPILE_TIME_ASSERT (sizeof (size_t) == sizeof (unsigned) ++ || sizeof (size_t) == sizeof (unsigned long) ++ || sizeof (size_t) == sizeof (unsigned long long)); + + fmt = fmt0; + while ((c = *fmt++) != 0) +@@ -917,11 +921,17 @@ parse_printf_arg_fmt (const char *fmt0, struct printf_args *args, + fmt++; + + c = *fmt++; ++ if (c == 'z') ++ { ++ c = *fmt++; ++ goto do_count; ++ } + if (c == 'l') + c = *fmt++; + if (c == 'l') + c = *fmt++; + ++ do_count: + switch (c) + { + case 'p': +@@ -1018,6 +1028,14 @@ parse_printf_arg_fmt (const char *fmt0, struct printf_args *args, + continue; + } + ++ if (c == 'z') ++ { ++ c = *fmt++; ++ if (sizeof (size_t) == sizeof (unsigned long)) ++ longfmt = 1; ++ else if (sizeof (size_t) == sizeof (unsigned long long)) ++ longfmt = 2; ++ } + if (c == 'l') + { + c = *fmt++; +@@ -1194,6 +1212,8 @@ grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt0, + } + + c = *fmt++; ++ if (c == 'z') ++ c = *fmt++; + if (c == 'l') + c = *fmt++; + if (c == 'l') diff --git a/0392-tests-add-z-modifier-printf-tests.patch b/0392-tests-add-z-modifier-printf-tests.patch new file mode 100644 index 00000000..ce86dc4a --- /dev/null +++ b/0392-tests-add-z-modifier-printf-tests.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Michael Chang via Grub-devel +Date: Fri, 17 Oct 2025 17:01:33 +0800 +Subject: [PATCH] tests: add z modifier printf tests + +Add unit tests for %zd, %zu and %zx to verify size_t and ssize_t +formatting matches system snprintf. + +Signed-off-by: Michael Chang +Reviewed-by: Neal Gompa +Reviewed-by: Daniel Kiper +--- + tests/printf_unit_test.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/tests/printf_unit_test.c b/tests/printf_unit_test.c +index 098c29fd9c..f2de187b03 100644 +--- a/tests/printf_unit_test.c ++++ b/tests/printf_unit_test.c +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + + #define MSG "printf test failed: %s, %s", real, expected + +@@ -73,6 +74,15 @@ printf_test (void) + grub_snprintf (real, sizeof (real), "%%0%dd ", 1); + snprintf (expected, sizeof (expected), "%%0%dd ", 1); + grub_test_assert (strcmp (real, expected) == 0, MSG); ++ grub_snprintf (real, sizeof (real), "%zd %zd %zd", (ssize_t) -1, (ssize_t) (SIZE_MAX >> 1), (ssize_t) 42); ++ snprintf (expected, sizeof (expected), "%zd %zd %zd", (ssize_t) -1, (ssize_t) (SIZE_MAX >> 1), (ssize_t) 42); ++ grub_test_assert (strcmp (real, expected) == 0, MSG); ++ grub_snprintf (real, sizeof (real), "%zu %zu %zu", (size_t) 0, (size_t) SIZE_MAX, (size_t) 42); ++ snprintf (expected, sizeof (expected), "%zu %zu %zu", (size_t) 0, (size_t) SIZE_MAX, (size_t) 42); ++ grub_test_assert (strcmp (real, expected) == 0, MSG); ++ grub_snprintf (real, sizeof (real), "%zx %zx %zx", (ssize_t) (SIZE_MAX >> 1), (size_t) SIZE_MAX, (size_t) 0xdeadbeefU); ++ snprintf (expected, sizeof (expected), "%zx %zx %zx", (ssize_t) (SIZE_MAX >> 1), (size_t) SIZE_MAX, (size_t) 0xdeadbeefU); ++ grub_test_assert (strcmp (real, expected) == 0, MSG); + } + + GRUB_UNIT_TEST ("printf_unit_test", printf_test); diff --git a/0393-util-grub-editenv-add-fs_envblk-open-helper.patch b/0393-util-grub-editenv-add-fs_envblk-open-helper.patch new file mode 100644 index 00000000..893c4c71 --- /dev/null +++ b/0393-util-grub-editenv-add-fs_envblk-open-helper.patch @@ -0,0 +1,219 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Michael Chang via Grub-devel +Date: Fri, 17 Oct 2025 17:01:34 +0800 +Subject: [PATCH] util/grub-editenv: add fs_envblk open helper + +This patch adds the logic to locate and open an environment block that +is stored in a reserved area on the device. It introduces the function +fs_envblk_open() together with helper routines to read the block pointed +to by the env_block variable, and to create the block on disk when it +does not exist yet. When a block is created, the code records its +location inside the file based envblk by setting env_block in block list +syntax of offset plus size in sectors. + +The env_block variable acts as a link from the file envblk to the raw +disk region so that later runs of grub-editenv can follow it and access +the external block. The helper is exposed through a small ops table +attached to fs_envblk so that later patches can call +fs_envblk->ops->open() without touching core code again. At this stage +variables are still stored in the file envblk and no redirection has +been applied. + +In relation to this, the fs_envblk_spec table defines the file-system +specific layout of the reserved raw blocks used for environment storage. +It is prepared to facilitate integration in grub-editenv, with Btrfs to +be added in the future once its reserved area is defined. + +An fs_envblk_init() helper is added to prepare it for using the ops with +its associated data context if the feature is available. It is not used +yet, but will be used later when a filesystem and its device are probed +to initialize the fs_envblk handle and enable access to the feature. + +Signed-off-by: Michael Chang +Reviewed-by: Neal Gompa +Reviewed-by: Avnish Chouhan +--- + util/grub-editenv.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 170 insertions(+) + +diff --git a/util/grub-editenv.c b/util/grub-editenv.c +index 948eec8a11..a544452e78 100644 +--- a/util/grub-editenv.c ++++ b/util/grub-editenv.c +@@ -123,6 +123,176 @@ block, use `rm %s'."), + NULL, help_filter, NULL + }; + ++struct fs_envblk_spec { ++ const char *fs_name; ++ off_t offset; ++ size_t size; ++}; ++typedef struct fs_envblk_spec fs_envblk_spec_t; ++ ++static grub_envblk_t fs_envblk_open (grub_envblk_t envblk); ++ ++struct fs_envblk_ops { ++ grub_envblk_t (*open) (grub_envblk_t); ++}; ++typedef struct fs_envblk_ops fs_envblk_ops_t; ++ ++struct fs_envblk { ++ fs_envblk_spec_t *spec; ++ fs_envblk_ops_t *ops; ++ const char *dev; ++}; ++typedef struct fs_envblk *fs_envblk_t; ++ ++static fs_envblk_ops_t fs_envblk_ops = { ++ .open = fs_envblk_open ++}; ++ ++/* ++ * fs_envblk_spec describes the file-system specific layout of reserved raw ++ * blocks used as environment blocks. ++ */ ++static fs_envblk_spec_t fs_envblk_spec[] = { ++ { NULL, 0, 0 } ++}; ++ ++static fs_envblk_t fs_envblk = NULL; ++ ++static void __attribute__ ((unused)) ++fs_envblk_init (const char *fs_name, const char *dev) ++{ ++ fs_envblk_spec_t *p; ++ ++ if (fs_name == NULL || dev == NULL) ++ return; ++ ++ for (p = fs_envblk_spec; p->fs_name != NULL; p++) ++ { ++ if (strcmp (fs_name, p->fs_name) == 0) ++ { ++ if (fs_envblk == NULL) ++ fs_envblk = xmalloc (sizeof (*fs_envblk)); ++ fs_envblk->spec = p; ++ fs_envblk->dev = xstrdup (dev); ++ fs_envblk->ops = &fs_envblk_ops; ++ break; ++ } ++ } ++} ++ ++static int ++read_env_block_var (const char *varname, const char *value, void *hook_data) ++{ ++ grub_envblk_t *p_envblk = (grub_envblk_t *) hook_data; ++ off_t off; ++ size_t sz; ++ char *p, *buf; ++ FILE *fp; ++ ++ if (p_envblk == NULL || fs_envblk == NULL) ++ return 1; ++ ++ if (strcmp (varname, "env_block") != 0) ++ return 0; ++ ++ off = strtol (value, &p, 10); ++ if (*p == '+') ++ sz = strtol (p + 1, &p, 10); ++ else ++ return 0; ++ ++ if (*p != '\0' || sz == 0) ++ return 0; ++ ++ off <<= GRUB_DISK_SECTOR_BITS; ++ sz <<= GRUB_DISK_SECTOR_BITS; ++ ++ fp = grub_util_fopen (fs_envblk->dev, "rb"); ++ if (fp == NULL) ++ grub_util_error (_("cannot open `%s': %s"), fs_envblk->dev, strerror (errno)); ++ ++ if (fseek (fp, off, SEEK_SET) < 0) ++ grub_util_error (_("cannot seek `%s': %s"), fs_envblk->dev, strerror (errno)); ++ ++ buf = xmalloc (sz); ++ if ((fread (buf, 1, sz, fp)) != sz) ++ grub_util_error (_("cannot read `%s': %s"), fs_envblk->dev, strerror (errno)); ++ ++ fclose (fp); ++ ++ *p_envblk = grub_envblk_open (buf, sz); ++ ++ return 1; ++} ++ ++static void ++create_env_on_block (void) ++{ ++ FILE *fp; ++ char *buf; ++ const char *device; ++ off_t offset; ++ size_t size; ++ ++ if (fs_envblk == NULL) ++ return; ++ ++ device = fs_envblk->dev; ++ offset = fs_envblk->spec->offset; ++ size = fs_envblk->spec->size; ++ ++ fp = grub_util_fopen (device, "r+b"); ++ if (fp == NULL) ++ grub_util_error (_("cannot open `%s': %s"), device, strerror (errno)); ++ ++ buf = xmalloc (size); ++ memcpy (buf, GRUB_ENVBLK_SIGNATURE, sizeof (GRUB_ENVBLK_SIGNATURE) - 1); ++ memset (buf + sizeof (GRUB_ENVBLK_SIGNATURE) - 1, '#', size - sizeof (GRUB_ENVBLK_SIGNATURE) + 1); ++ ++ if (fseek (fp, offset, SEEK_SET) < 0) ++ grub_util_error (_("cannot seek `%s': %s"), device, strerror (errno)); ++ ++ if (fwrite (buf, 1, size, fp) != size) ++ grub_util_error (_("cannot write to `%s': %s"), device, strerror (errno)); ++ ++ grub_util_file_sync (fp); ++ free (buf); ++ fclose (fp); ++} ++ ++static grub_envblk_t ++fs_envblk_open (grub_envblk_t envblk) ++{ ++ grub_envblk_t envblk_on_block = NULL; ++ char *val; ++ off_t offset; ++ size_t size; ++ ++ if (envblk == NULL) ++ return NULL; ++ ++ offset = fs_envblk->spec->offset; ++ size = fs_envblk->spec->size; ++ ++ grub_envblk_iterate (envblk, &envblk_on_block, read_env_block_var); ++ ++ if (envblk_on_block != NULL && grub_envblk_size (envblk_on_block) == size) ++ return envblk_on_block; ++ ++ create_env_on_block (); ++ ++ offset = offset >> GRUB_DISK_SECTOR_BITS; ++ size = (size + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS; ++ ++ val = xasprintf ("%lld+%zu", (long long) offset, size); ++ if (grub_envblk_set (envblk, "env_block", val) == 0) ++ grub_util_error ("%s", _("environment block too small")); ++ grub_envblk_iterate (envblk, &envblk_on_block, read_env_block_var); ++ free (val); ++ ++ return envblk_on_block; ++} ++ + static grub_envblk_t + open_envblk_file (const char *name) + { diff --git a/0394-util-grub-editenv-add-fs_envblk-write-helper.patch b/0394-util-grub-editenv-add-fs_envblk-write-helper.patch new file mode 100644 index 00000000..f94f747e --- /dev/null +++ b/0394-util-grub-editenv-add-fs_envblk-write-helper.patch @@ -0,0 +1,91 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Michael Chang via Grub-devel +Date: Fri, 17 Oct 2025 17:01:35 +0800 +Subject: [PATCH] util/grub-editenv: add fs_envblk write helper + +This patch adds the function fs_envblk_write to update the reserved +environment block on disk. The helper takes an in memory envblk buffer +and writes it back to the device at the location defined by the +fs_envblk specification. It performs size checks and uses file sync to +ensure that the updated data is flushed. + +The helper is also added into the fs_envblk ops table, together with the +open helper from the previous patch. With this change the basic input +and output path for an external environment block is complete. The +choice of which variables should be written externally will be handled +by later patches. + +Signed-off-by: Michael Chang +Reviewed-by: Neal Gompa +Reviewed-by: Avnish Chouhan +Reviewed-by: Daniel Kiper +--- + util/grub-editenv.c | 38 +++++++++++++++++++++++++++++++++++++- + 1 file changed, 37 insertions(+), 1 deletion(-) + +diff --git a/util/grub-editenv.c b/util/grub-editenv.c +index a544452e78..f4b69bb19f 100644 +--- a/util/grub-editenv.c ++++ b/util/grub-editenv.c +@@ -131,9 +131,11 @@ struct fs_envblk_spec { + typedef struct fs_envblk_spec fs_envblk_spec_t; + + static grub_envblk_t fs_envblk_open (grub_envblk_t envblk); ++static void fs_envblk_write (grub_envblk_t envblk); + + struct fs_envblk_ops { + grub_envblk_t (*open) (grub_envblk_t); ++ void (*write) (grub_envblk_t); + }; + typedef struct fs_envblk_ops fs_envblk_ops_t; + +@@ -145,7 +147,8 @@ struct fs_envblk { + typedef struct fs_envblk *fs_envblk_t; + + static fs_envblk_ops_t fs_envblk_ops = { +- .open = fs_envblk_open ++ .open = fs_envblk_open, ++ .write = fs_envblk_write + }; + + /* +@@ -381,6 +384,39 @@ write_envblk (const char *name, grub_envblk_t envblk) + fclose (fp); + } + ++static void ++fs_envblk_write (grub_envblk_t envblk) ++{ ++ FILE *fp; ++ const char *device; ++ off_t offset; ++ size_t size; ++ ++ if (envblk == NULL) ++ return; ++ ++ device = fs_envblk->dev; ++ offset = fs_envblk->spec->offset; ++ size = fs_envblk->spec->size; ++ ++ if (grub_envblk_size (envblk) > size) ++ grub_util_error ("%s", _("environment block too small")); ++ ++ fp = grub_util_fopen (device, "r+b"); ++ ++ if (fp == NULL) ++ grub_util_error (_("cannot open `%s': %s"), device, strerror (errno)); ++ ++ if (fseek (fp, offset, SEEK_SET) < 0) ++ grub_util_error (_("cannot seek `%s': %s"), device, strerror (errno)); ++ ++ if (fwrite (grub_envblk_buffer (envblk), 1, grub_envblk_size (envblk), fp) != grub_envblk_size (envblk)) ++ grub_util_error (_("cannot write to `%s': %s"), device, strerror (errno)); ++ ++ grub_util_file_sync (fp); ++ fclose (fp); ++} ++ + static void + set_variables (const char *name, int argc, char *argv[]) + { diff --git a/0395-util-grub-editenv-wire-set_variables-to-optional-fs_.patch b/0395-util-grub-editenv-wire-set_variables-to-optional-fs_.patch new file mode 100644 index 00000000..bf9820a3 --- /dev/null +++ b/0395-util-grub-editenv-wire-set_variables-to-optional-fs_.patch @@ -0,0 +1,118 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Michael Chang via Grub-devel +Date: Fri, 17 Oct 2025 17:01:36 +0800 +Subject: [PATCH] util/grub-editenv: wire set_variables to optional fs_envblk + +This patch changes set_variables() so that it can use an external +environment block when one is present. The variable next_entry is +written into the external block, env_block is treated as read only, and +all other variables are written into the normal file based envblk. + +A cleanup step is added to handle cases where GRUB at runtime writes +variables into the external block because file based updates are not +safe on a copy on write filesystem such as Btrfs. For example, the +savedefault command can update saved_entry, and on Btrfs GRUB will place +that update in the external block instead of the file envblk. If an +older copy remains in the external block, it would override the newer +value from the file envblk when GRUB first loads the file and then +applies the external block on top of it. To avoid this, whenever a +variable is updated in the file envblk, any same named key in the +external block is deleted. + +Signed-off-by: Michael Chang +Reviewed-by: Neal Gompa +Reviewed-by: Avnish Chouhan +Reviewed-by: Daniel Kiper +--- + util/grub-editenv.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 56 insertions(+), 2 deletions(-) + +diff --git a/util/grub-editenv.c b/util/grub-editenv.c +index f4b69bb19f..be844bf902 100644 +--- a/util/grub-editenv.c ++++ b/util/grub-editenv.c +@@ -417,12 +417,35 @@ fs_envblk_write (grub_envblk_t envblk) + fclose (fp); + } + ++struct var_lookup_ctx { ++ const char *varname; ++ bool found; ++}; ++typedef struct var_lookup_ctx var_lookup_ctx_t; ++ ++static int ++var_lookup_iter (const char *varname, const char *value __attribute__ ((unused)), void *hook_data) ++{ ++ var_lookup_ctx_t *ctx = (var_lookup_ctx_t *) hook_data; ++ ++ if (grub_strcmp (ctx->varname, varname) == 0) ++ { ++ ctx->found = true; ++ return 1; ++ } ++ return 0; ++} ++ + static void + set_variables (const char *name, int argc, char *argv[]) + { + grub_envblk_t envblk; ++ grub_envblk_t envblk_on_block = NULL; + + envblk = open_envblk_file (name); ++ if (fs_envblk != NULL) ++ envblk_on_block = fs_envblk->ops->open (envblk); ++ + while (argc) + { + char *p; +@@ -433,15 +456,46 @@ set_variables (const char *name, int argc, char *argv[]) + + *(p++) = 0; + +- if (! grub_envblk_set (envblk, argv[0], p)) +- grub_util_error ("%s", _("environment block too small")); ++ if ((strcmp (argv[0], "next_entry") == 0) && envblk_on_block != NULL) ++ { ++ if (grub_envblk_set (envblk_on_block, argv[0], p) == 0) ++ grub_util_error ("%s", _("environment block too small")); ++ goto next; ++ } + ++ if (strcmp (argv[0], "env_block") == 0) ++ { ++ grub_util_warn (_("can't set env_block as it's read-only")); ++ goto next; ++ } ++ ++ if (grub_envblk_set (envblk, argv[0], p) == 0) ++ grub_util_error ("%s", _("environment block too small")); ++ ++ if (envblk_on_block != NULL) ++ { ++ var_lookup_ctx_t ctx = { ++ .varname = argv[0], ++ .found = false ++ }; ++ ++ grub_envblk_iterate (envblk_on_block, &ctx, var_lookup_iter); ++ if (ctx.found == true) ++ grub_envblk_delete (envblk_on_block, argv[0]); ++ } ++ next: + argc--; + argv++; + } + + write_envblk (name, envblk); + grub_envblk_close (envblk); ++ ++ if (envblk_on_block != NULL) ++ { ++ fs_envblk->ops->write (envblk_on_block); ++ grub_envblk_close (envblk_on_block); ++ } + } + + static void diff --git a/0396-util-grub-editenv-wire-unset_variables-to-optional-f.patch b/0396-util-grub-editenv-wire-unset_variables-to-optional-f.patch new file mode 100644 index 00000000..4b517b39 --- /dev/null +++ b/0396-util-grub-editenv-wire-unset_variables-to-optional-f.patch @@ -0,0 +1,56 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Michael Chang via Grub-devel +Date: Fri, 17 Oct 2025 17:01:37 +0800 +Subject: [PATCH] util/grub-editenv: wire unset_variables to optional fs_envblk + +This patch updates unset_variables() so that removals are also applied +to the external environment block when it is present. The code opens the +external block, deletes the same named keys there, and then writes the +external block back using fs_envblk_write(). The file based envblk is +still updated and written as before. + +Signed-off-by: Michael Chang +Reviewed-by: Neal Gompa +Reviewed-by: Avnish Chouhan +Reviewed-by: Daniel Kiper +--- + util/grub-editenv.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/util/grub-editenv.c b/util/grub-editenv.c +index be844bf902..7ff1da9b40 100644 +--- a/util/grub-editenv.c ++++ b/util/grub-editenv.c +@@ -502,18 +502,32 @@ static void + unset_variables (const char *name, int argc, char *argv[]) + { + grub_envblk_t envblk; ++ grub_envblk_t envblk_on_block = NULL; + + envblk = open_envblk_file (name); ++ ++ if (fs_envblk != NULL) ++ envblk_on_block = fs_envblk->ops->open (envblk); ++ + while (argc) + { + grub_envblk_delete (envblk, argv[0]); + ++ if (envblk_on_block != NULL) ++ grub_envblk_delete (envblk_on_block, argv[0]); ++ + argc--; + argv++; + } + + write_envblk (name, envblk); + grub_envblk_close (envblk); ++ ++ if (envblk_on_block != NULL) ++ { ++ fs_envblk->ops->write (envblk_on_block); ++ grub_envblk_close (envblk_on_block); ++ } + } + + struct get_int_value_params { diff --git a/0397-util-grub-editenv-wire-list_variables-to-optional-fs.patch b/0397-util-grub-editenv-wire-list_variables-to-optional-fs.patch new file mode 100644 index 00000000..5687886d --- /dev/null +++ b/0397-util-grub-editenv-wire-list_variables-to-optional-fs.patch @@ -0,0 +1,45 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Michael Chang via Grub-devel +Date: Fri, 17 Oct 2025 17:01:38 +0800 +Subject: [PATCH] util/grub-editenv: wire list_variables to optional fs_envblk + +This patch updates list_variables() so that it also prints entries from +the external environment block when one is present. The function first +lists all variables from the file based envblk, then iterates over the +external envblk and prints those as well. + +The output format remains the same as before. The change makes it +possible to inspect variables regardless of whether they are stored in +the file envblk or in the reserved block. + +Signed-off-by: Michael Chang +Reviewed-by: Neal Gompa +Reviewed-by: Avnish Chouhan +Reviewed-by: Sudhakar Kuppusamy +Reviewed-by: Daniel Kiper +--- + util/grub-editenv.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/util/grub-editenv.c b/util/grub-editenv.c +index 7ff1da9b40..170d8f139a 100644 +--- a/util/grub-editenv.c ++++ b/util/grub-editenv.c +@@ -358,10 +358,17 @@ static void + list_variables (const char *name) + { + grub_envblk_t envblk; ++ grub_envblk_t envblk_on_block = NULL; + + envblk = open_envblk_file (name); ++ grub_envblk_iterate (envblk, &envblk_on_block, read_env_block_var); + grub_envblk_iterate (envblk, NULL, print_var); + grub_envblk_close (envblk); ++ if (envblk_on_block != NULL) ++ { ++ grub_envblk_iterate (envblk_on_block, NULL, print_var); ++ grub_envblk_close (envblk_on_block); ++ } + } + + static void diff --git a/0398-util-grub-editenv-add-probe-call-for-external-envblk.patch b/0398-util-grub-editenv-add-probe-call-for-external-envblk.patch new file mode 100644 index 00000000..9d583168 --- /dev/null +++ b/0398-util-grub-editenv-add-probe-call-for-external-envblk.patch @@ -0,0 +1,176 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Michael Chang via Grub-devel +Date: Fri, 17 Oct 2025 17:01:39 +0800 +Subject: [PATCH] util/grub-editenv: add probe call for external envblk + +This patch adds the probe_fs_envblk() function to identify the root +filesystem and invoke fs_envblk_init() with the probed filesystem type +and device. This checks if the feature is available and initializes the +handle, fs_envblk, to access the external environment block. It avoids +configurations with diskfilter or cryptodisk where filesystem blocks may +be remapped or encrypted. + +The probe is only invoked when grub-editenv is working on the default +environment file path. This restriction ensures that probing and +possible raw device access are not triggered for arbitrary user supplied +paths, but only for the standard grubenv file. In that case the code +checks if the filename equals DEFAULT_ENVBLK_PATH and then calls +probe_fs_envblk with fs_envblk_spec. The result is stored in the global +fs_envblk handle. At this stage the external environment block is only +detected and recorded, and the behavior of grub-editenv is unchanged. + +Signed-off-by: Michael Chang +Reviewed-by: Neal Gompa +Reviewed-by: Avnish Chouhan +--- + util/grub-editenv.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 113 insertions(+), 2 deletions(-) + +diff --git a/util/grub-editenv.c b/util/grub-editenv.c +index 170d8f139a..201dc7ccd4 100644 +--- a/util/grub-editenv.c ++++ b/util/grub-editenv.c +@@ -23,8 +23,11 @@ + #include + #include + #include +-#include ++#include + #include ++#include ++#include ++#include + + #include + #include +@@ -161,7 +164,7 @@ static fs_envblk_spec_t fs_envblk_spec[] = { + + static fs_envblk_t fs_envblk = NULL; + +-static void __attribute__ ((unused)) ++static void + fs_envblk_init (const char *fs_name, const char *dev) + { + fs_envblk_spec_t *p; +@@ -582,6 +585,111 @@ incr_variables (const char *name, int argc, char *argv[]) + grub_envblk_close (envblk); + } + ++static bool ++is_abstraction (grub_device_t dev) ++{ ++ if (dev == NULL || dev->disk == NULL) ++ return false; ++ ++ if (dev->disk->dev->id == GRUB_DISK_DEVICE_DISKFILTER_ID || ++ dev->disk->dev->id == GRUB_DISK_DEVICE_CRYPTODISK_ID) ++ return true; ++ ++ return false; ++} ++ ++static void ++probe_fs_envblk (fs_envblk_spec_t *spec) ++{ ++ char **grub_devices = NULL; ++ char **curdev, **curdrive; ++ size_t ndev = 0; ++ char **grub_drives = NULL; ++ grub_device_t grub_dev = NULL; ++ grub_fs_t grub_fs = NULL; ++ bool have_abstraction = false; ++ ++ grub_util_biosdisk_init (DEFAULT_DEVICE_MAP); ++ grub_init_all (); ++ grub_gcry_init_all (); ++ ++ grub_lvm_fini (); ++ grub_mdraid09_fini (); ++ grub_mdraid1x_fini (); ++ grub_diskfilter_fini (); ++ grub_diskfilter_init (); ++ grub_mdraid09_init (); ++ grub_mdraid1x_init (); ++ grub_lvm_init (); ++ ++ grub_devices = grub_guess_root_devices (DEFAULT_DIRECTORY); ++ ++ if (grub_devices == NULL || grub_devices[0] == NULL) ++ { ++ grub_util_warn (_("cannot find a device for %s (is /dev mounted?)"), DEFAULT_DIRECTORY); ++ goto cleanup; ++ } ++ ++ for (curdev = grub_devices; *curdev != NULL; curdev++, ndev++) ++ grub_util_pull_device (*curdev); ++ ++ grub_drives = xcalloc ((ndev + 1), sizeof (grub_drives[0])); ++ ++ for (curdev = grub_devices, curdrive = grub_drives; *curdev != NULL; curdev++, ++ curdrive++) ++ { ++ *curdrive = grub_util_get_grub_dev (*curdev); ++ if (*curdrive == NULL) ++ { ++ grub_util_warn (_("cannot find a GRUB drive for %s. Check your device.map"), ++ *curdev); ++ goto cleanup; ++ } ++ } ++ *curdrive = NULL; ++ ++ grub_dev = grub_device_open (grub_drives[0]); ++ if (grub_dev == NULL) ++ { ++ grub_util_warn (_("cannot open device %s: %s"), grub_drives[0], grub_errmsg); ++ grub_errno = GRUB_ERR_NONE; ++ goto cleanup; ++ } ++ ++ grub_fs = grub_fs_probe (grub_dev); ++ if (grub_fs == NULL) ++ { ++ grub_util_warn (_("cannot probe fs for %s: %s"), grub_drives[0], grub_errmsg); ++ grub_errno = GRUB_ERR_NONE; ++ goto cleanup; ++ } ++ ++ have_abstraction = is_abstraction (grub_dev); ++ for (curdrive = grub_drives + 1; *curdrive != NULL && have_abstraction == false; curdrive++) ++ { ++ grub_device_t dev = grub_device_open (*curdrive); ++ ++ if (dev == NULL) ++ continue; ++ have_abstraction = is_abstraction (dev); ++ grub_device_close (dev); ++ } ++ ++ if (have_abstraction == false) ++ fs_envblk_init (grub_fs->name, grub_devices[0]); ++ ++ cleanup: ++ if (grub_devices != NULL) ++ for (curdev = grub_devices; *curdev != NULL; curdev++) ++ free (*curdev); ++ free (grub_devices); ++ free (grub_drives); ++ grub_device_close (grub_dev); ++ grub_gcry_fini_all (); ++ grub_fini_all (); ++ grub_util_biosdisk_fini (); ++} ++ + int + main (int argc, char *argv[]) + { +@@ -613,6 +721,9 @@ main (int argc, char *argv[]) + command = argv[curindex++]; + } + ++ if (strcmp (filename, DEFAULT_ENVBLK_PATH) == 0) ++ probe_fs_envblk (fs_envblk_spec); ++ + if (strcmp (command, "create") == 0) + grub_util_create_envblk_file (filename); + else if (strcmp (command, "list") == 0) diff --git a/0399-btrfs-add-environment-block-to-reserved-header-area.patch b/0399-btrfs-add-environment-block-to-reserved-header-area.patch new file mode 100644 index 00000000..dd877089 --- /dev/null +++ b/0399-btrfs-add-environment-block-to-reserved-header-area.patch @@ -0,0 +1,93 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Michael Chang via Grub-devel +Date: Fri, 17 Oct 2025 17:01:40 +0800 +Subject: [PATCH] btrfs: add environment block to reserved header area + +This patch reserves space for the GRUB environment block inside the +Btrfs header. The block is placed at an offset of GRUB_ENV_BTRFS_OFFSET +, 256 KiB from the start of the device, and occupies one sector. To +protect the space, overflow guard sectors are placed before and after +the reserved block. + +The Btrfs header already defines regions for bootloader use. By adding +this entry, GRUB gains a fixed and safe location to store the +environment block without conflicting with other structures in the +header. + +Add Btrfs and its reserved area information to the fs_envblk_spec table. +With the groundworks done in previous patches, the function is now +complete and working in grub-editenv. + +Signed-off-by: Michael Chang +Reviewed-by: Neal Gompa +--- + grub-core/fs/btrfs.c | 10 +++++++++- + include/grub/fs.h | 2 ++ + util/grub-editenv.c | 9 ++++++++- + 3 files changed, 19 insertions(+), 2 deletions(-) + +diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c +index 3228e17886..261c413e9f 100644 +--- a/grub-core/fs/btrfs.c ++++ b/grub-core/fs/btrfs.c +@@ -2650,11 +2650,16 @@ struct embed_region { + * https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs(5)#BOOTLOADER_SUPPORT + * The first 1 MiB on each device is unused with the exception of primary + * superblock that is on the offset 64 KiB and spans 4 KiB. ++ * ++ * Note: If this table is modified, also update ++ * util/grub-editenv.c::fs_envblk_spec, which describes the file-system ++ * specific layout of reserved raw blocks used as environment blocks so that ++ * both stay consistent. + */ + + static const struct { + struct embed_region available; +- struct embed_region used[6]; ++ struct embed_region used[9]; + } btrfs_head = { + .available = {0, GRUB_DISK_KiB_TO_SECTORS (1024)}, /* The first 1 MiB. */ + .used = { +@@ -2662,6 +2667,9 @@ static const struct { + {GRUB_DISK_KiB_TO_SECTORS (64) - 1, 1}, /* Overflow guard. */ + {GRUB_DISK_KiB_TO_SECTORS (64), GRUB_DISK_KiB_TO_SECTORS (4)}, /* 4 KiB superblock. */ + {GRUB_DISK_KiB_TO_SECTORS (68), 1}, /* Overflow guard. */ ++ {(GRUB_ENV_BTRFS_OFFSET >> GRUB_DISK_SECTOR_BITS) - 1, 1}, /* Overflow guard. */ ++ {(GRUB_ENV_BTRFS_OFFSET >> GRUB_DISK_SECTOR_BITS), 1}, /* Environment Block. */ ++ {(GRUB_ENV_BTRFS_OFFSET >> GRUB_DISK_SECTOR_BITS) + 1, 1}, /* Overflow guard. */ + {GRUB_DISK_KiB_TO_SECTORS (1024) - 1, 1}, /* Overflow guard. */ + {0, 0} /* Array terminator. */ + } +diff --git a/include/grub/fs.h b/include/grub/fs.h +index df4c93b16f..89e4d2b9bc 100644 +--- a/include/grub/fs.h ++++ b/include/grub/fs.h +@@ -132,4 +132,6 @@ grub_fs_unregister (grub_fs_t fs) + + grub_fs_t EXPORT_FUNC(grub_fs_probe) (grub_device_t device); + ++#define GRUB_ENV_BTRFS_OFFSET (256 * 1024) ++ + #endif /* ! GRUB_FS_HEADER */ +diff --git a/util/grub-editenv.c b/util/grub-editenv.c +index 201dc7ccd4..3c3f9d09f3 100644 +--- a/util/grub-editenv.c ++++ b/util/grub-editenv.c +@@ -156,9 +156,16 @@ static fs_envblk_ops_t fs_envblk_ops = { + + /* + * fs_envblk_spec describes the file-system specific layout of reserved raw +- * blocks used as environment blocks. ++ * blocks used as environment blocks. At present only Btrfs is supported. Other ++ * file-systems may be added if they provide a similar facility and avoid the ++ * limitation of writing to COW. ++ * ++ * Note: If this table is modified, also update ++ * grub-core/fs/btrfs.c::btrfs_head, which defines the layout in the Btrfs ++ * header and exports GRUB_ENV_BTRFS_OFFSET, so that both stay consistent. + */ + static fs_envblk_spec_t fs_envblk_spec[] = { ++ { "btrfs", GRUB_ENV_BTRFS_OFFSET, GRUB_DISK_SECTOR_SIZE }, + { NULL, 0, 0 } + }; + diff --git a/0400-00_header.in-wire-grub.cfg-to-use-env_block-when-pre.patch b/0400-00_header.in-wire-grub.cfg-to-use-env_block-when-pre.patch new file mode 100644 index 00000000..a807bb91 --- /dev/null +++ b/0400-00_header.in-wire-grub.cfg-to-use-env_block-when-pre.patch @@ -0,0 +1,82 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Michael Chang via Grub-devel +Date: Fri, 17 Oct 2025 17:01:41 +0800 +Subject: [PATCH] 00_header.in: wire grub.cfg to use env_block when present + +This patch extends the generated grub.cfg so that it can use the +external environment block when the variable env_block is defined. +During boot, if env_block is set, grub.cfg builds a device path for it, +exports the variable, and then loads its contents in addition to the +normal grubenv file. + +When GRUB writes variables such as next_entry or saved_entry, the save +commands are changed to write into env_block if it is set, and to fall +back to the grubenv file otherwise. In this way the external environment +block is used automatically, and existing commands like savedefault or +save_env do not need to change. + +Signed-off-by: Michael Chang +Reviewed-by: Neal Gompa +Reviewed-by: Daniel Kiper +--- + util/grub.d/00_header.in | 26 +++++++++++++++++++++++--- + 1 file changed, 23 insertions(+), 3 deletions(-) + +diff --git a/util/grub.d/00_header.in b/util/grub.d/00_header.in +index 3e1b77265d..a5d43929b8 100644 +--- a/util/grub.d/00_header.in ++++ b/util/grub.d/00_header.in +@@ -58,6 +58,13 @@ if [ -f \${config_directory}/grubenv ]; then + elif [ -s \$prefix/grubenv ]; then + load_env + fi ++ ++if [ "\${env_block}" ] ; then ++ set env_block="(\${root})\${env_block}" ++ export env_block ++ load_env -f "\${env_block}" ++fi ++ + EOF + if [ "x$GRUB_BUTTON_CMOS_ADDRESS" != "x" ]; then + cat < +Date: Fri, 17 Oct 2025 17:01:43 +0800 +Subject: [PATCH] btrfs: update doc link for bootloader support + +The old wiki link is obsolete and no longer updated, change it to the +current documentation. + +Signed-off-by: Michael Chang +Reviewed-by: Neal Gompa +Reviewed-by: Daniel Kiper +--- + grub-core/fs/btrfs.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c +index 261c413e9f..2222c57abd 100644 +--- a/grub-core/fs/btrfs.c ++++ b/grub-core/fs/btrfs.c +@@ -2647,7 +2647,8 @@ struct embed_region { + }; + + /* +- * https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs(5)#BOOTLOADER_SUPPORT ++ * https://btrfs.readthedocs.io/en/latest/btrfs-man5.html#man-btrfs5-bootloader-support ++ * or invoke "man 5 btrfs" and visit the "bootloader support" subsection. + * The first 1 MiB on each device is unused with the exception of primary + * superblock that is on the offset 64 KiB and spans 4 KiB. + * diff --git a/0402-docs-add-Btrfs-env-block-and-special-env-vars.patch b/0402-docs-add-Btrfs-env-block-and-special-env-vars.patch new file mode 100644 index 00000000..11cf789d --- /dev/null +++ b/0402-docs-add-Btrfs-env-block-and-special-env-vars.patch @@ -0,0 +1,93 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Leo Sandoval +Date: Mon, 27 Oct 2025 11:18:45 -0600 +Subject: [PATCH] docs: add Btrfs env block and special env vars + +Update grub.texi to describe the external environment block in the +reserved area of Btrfs header used for grub-reboot and savedefault, +and add a section documenting the saved_entry, next_entry, and +env_block variables. + +Signed-off-by: Michael Chang +Reviewed-by: Neal Gompa +Reviewed-by: Daniel Kiper +--- + docs/grub.texi | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 55 insertions(+) + +diff --git a/docs/grub.texi b/docs/grub.texi +index 531e847f32..d669972337 100644 +--- a/docs/grub.texi ++++ b/docs/grub.texi +@@ -3265,6 +3265,7 @@ GRUB. Others may be used freely in GRUB configuration files. + @menu + * Special environment variables:: + * Environment block:: ++* Special environment block variables:: + @end menu + + +@@ -3881,9 +3882,63 @@ For safety reasons, this storage is only available when installed on a plain + disk (no LVM or RAID), using a non-checksumming filesystem (no ZFS), and + using BIOS or EFI functions (no ATA, USB or IEEE1275). + ++On Btrfs filesystems, a reserved area in the filesystem header may be used to ++store the environment block. This static block avoids the problems of updating ++a normal file on a copy-on-write filesystem, where writing raw block is not ++stable and requires metadata update. The reserved area provides a fixed ++location that GRUB can update directly, allowing commands such as ++@command{grub-reboot} and @samp{GRUB_SAVEDEFAULT} to function correctly on ++Btrfs volumes. ++ + @command{grub2-mkconfig} uses this facility to implement + @samp{GRUB_SAVEDEFAULT} (@pxref{Simple configuration}). + ++@node Special environment block variables ++@section Special environment block variables ++ ++These special variables are usually written to the environment block ++(@pxref{Environment block}) to customize the behavior of @file{grub.cfg} ++generated by @command{grub-mkconfig}. ++ ++@menu ++* saved_entry:: ++* next_entry:: ++* env_block:: ++@end menu ++ ++@node saved_entry ++@subsection saved_entry ++ ++The @var{saved_entry} variable sets the default boot entry in @file{grub.cfg} ++created by @command{grub-mkconfig}. It can be set with ++@command{grub-set-default} to choose a default entry, or at runtime with the ++@code{savedefault} function in grub.cfg to save the current entry as the new ++default. This may require write access by GRUB. ++ ++@node next_entry ++@subsection next_entry ++ ++The @var{next_entry} variable sets the boot entry for the next boot only. After ++it is used, GRUB clears the value so it is not reused. This requires write ++access to the environment block (@pxref{Environment block}) at runtime. The ++@command{grub-reboot} command is usually used instead of changing this variable ++directly. ++ ++@node env_block ++@subsection env_block ++ ++If the filesystem is Btrfs and the disk is not an abstracted device such as ++LVM, RAID, or encryption, the reserved space in the Btrfs header can be used as ++the environment block (@pxref{Environment block}). This provides a fixed raw ++block that GRUB can reliably write to. The @var{env_block} records this ++location in GRUB blocklist syntax (@pxref{Block list syntax}) so that ++@command{grub-editenv} and @file{grub.cfg} know how to access and use the ++external raw block. ++ ++This variable is initialized when @file{grubenv} is first created by ++@command{grub-editenv} and is treated as read-only to avoid being overwritten ++with an unpredictable value. ++ + @node Modules + @chapter Modules + diff --git a/0403-kern-fs-Honour-file-read_hook-in-grub_fs_blocklist_r.patch b/0403-kern-fs-Honour-file-read_hook-in-grub_fs_blocklist_r.patch new file mode 100644 index 00000000..6aa5d83e --- /dev/null +++ b/0403-kern-fs-Honour-file-read_hook-in-grub_fs_blocklist_r.patch @@ -0,0 +1,73 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Rasmus Villemoes +Date: Thu, 29 Aug 2024 13:01:02 +0200 +Subject: [PATCH] kern/fs: Honour file->read_hook() in grub_fs_blocklist_read() + +Unlike files accessed via a normal file system, the file->read_hook() is +not honoured when using blocklist notation. + +This means that when trying to use a dedicated, 1 KiB, raw partition +for the environment block and hence does something like + + save_env --file=(hd0,gpt9)0+2 X Y Z + +this fails with "sparse file not allowed", which is rather unexpected, +as I've explicitly said exactly which blocks should be used. Adding +a little debugging reveals that grub_file_size(file) is 1024 as expected, +but total_length is 0, simply because the callback was never invoked, so +blocklists is an empty list. + +Fix that by honouring the ->read_hook() set by the caller, also when +a "file" is specified with blocklist notation. + +Signed-off-by: Rasmus Villemoes +Reviewed-by: Vladimir Serbinenko +Reviewed-by: Daniel Kiper +--- + grub-core/kern/fs.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/grub-core/kern/fs.c b/grub-core/kern/fs.c +index f25bace620..9f6f4ef9da 100644 +--- a/grub-core/kern/fs.c ++++ b/grub-core/kern/fs.c +@@ -216,12 +216,15 @@ grub_fs_blocklist_read (grub_file_t file, char *buf, grub_size_t len) + grub_disk_addr_t sector; + grub_off_t offset; + grub_ssize_t ret = 0; ++ grub_disk_t disk = file->device->disk; + + if (len > file->size - file->offset) + len = file->size - file->offset; + + sector = (file->offset >> GRUB_DISK_SECTOR_BITS); + offset = (file->offset & (GRUB_DISK_SECTOR_SIZE - 1)); ++ disk->read_hook = file->read_hook; ++ disk->read_hook_data = file->read_hook_data; + for (p = file->data; p->length && len > 0; p++) + { + if (sector < p->length) +@@ -233,9 +236,12 @@ grub_fs_blocklist_read (grub_file_t file, char *buf, grub_size_t len) + >> GRUB_DISK_SECTOR_BITS) > p->length - sector) + size = ((p->length - sector) << GRUB_DISK_SECTOR_BITS) - offset; + +- if (grub_disk_read (file->device->disk, p->offset + sector, offset, ++ if (grub_disk_read (disk, p->offset + sector, offset, + size, buf) != GRUB_ERR_NONE) +- return -1; ++ { ++ ret = -1; ++ break; ++ } + + ret += size; + len -= size; +@@ -245,6 +251,8 @@ grub_fs_blocklist_read (grub_file_t file, char *buf, grub_size_t len) + else + sector -= p->length; + } ++ disk->read_hook = NULL; ++ disk->read_hook_data = NULL; + + return ret; + } diff --git a/0404-util-grub.d-00_header.in-Prefer-dev-instead-of-root-.patch b/0404-util-grub.d-00_header.in-Prefer-dev-instead-of-root-.patch new file mode 100644 index 00000000..efe9389e --- /dev/null +++ b/0404-util-grub.d-00_header.in-Prefer-dev-instead-of-root-.patch @@ -0,0 +1,44 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Neal Gompa +Date: Wed, 26 Nov 2025 08:20:26 -0500 +Subject: [PATCH] util/grub.d/00_header.in: Prefer $dev instead of $root for + $env_block + +In UEFI environments where GRUB loads a stub grub.cfg on the EFI +System Partition (ESP) that points to the real one on the operating +system volume, the $root value is set to the ESP instead of the +operating system volume. + +This is particularly problematic for setups where the operating +system boot data is on a Btrfs volume and the external environment +block in the Btrfs bootloader space is used for grubenv data. + +Using $dev in its place resolves this problem, since the stub +grub.cfg populates this variable. Use it if it exists, and +fall back to $root accordingly. + +This is unlikely to be upstreamable, as it depends on the method +Fedora Linux and derivatives use to load the GRUB configuration. + +Signed-off-by: Neal Gompa +--- + util/grub.d/00_header.in | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/util/grub.d/00_header.in b/util/grub.d/00_header.in +index a5d43929b8..01f1f4144e 100644 +--- a/util/grub.d/00_header.in ++++ b/util/grub.d/00_header.in +@@ -60,7 +60,11 @@ elif [ -s \$prefix/grubenv ]; then + fi + + if [ "\${env_block}" ] ; then +- set env_block="(\${root})\${env_block}" ++ if [ "\${dev}" ]; then ++ set env_block="(\${dev})\${env_block}" ++ else ++ set env_block="(\${root})\${env_block}" ++ fi + export env_block + load_env -f "\${env_block}" + fi diff --git a/0405-grub.d-Support-the-external-environment-block-for-au.patch b/0405-grub.d-Support-the-external-environment-block-for-au.patch new file mode 100644 index 00000000..c75926a0 --- /dev/null +++ b/0405-grub.d-Support-the-external-environment-block-for-au.patch @@ -0,0 +1,89 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Neal Gompa +Date: Wed, 26 Nov 2025 08:37:57 -0500 +Subject: [PATCH] grub.d: Support the external environment block for automatic + hidden menu + +This allows the automatic hidden menu (and boot counting) mechanism +to use an external environment block if it exists and is configured +to be used. + +This is particularly important for supporting this mechanism on +systems with the operating system boot data on a Btrfs volume. + +This is unlikely to be upstreamable, as it depends on Fedora's GRUB +hidden menu and boot counting patch sets. + +Signed-off-by: Neal Gompa +--- + util/grub.d/08_fallback_counting.in | 6 +++++- + util/grub.d/10_reset_boot_success.in | 6 +++++- + util/grub.d/12_menu_auto_hide.in | 6 +++++- + util/grub.d/14_menu_show_once.in | 6 +++++- + 4 files changed, 20 insertions(+), 4 deletions(-) + +diff --git a/util/grub.d/08_fallback_counting.in b/util/grub.d/08_fallback_counting.in +index 2e2c3ff7d3..272cdb6533 100644 +--- a/util/grub.d/08_fallback_counting.in ++++ b/util/grub.d/08_fallback_counting.in +@@ -19,6 +19,10 @@ if [ -n "\${boot_counter}" -a "\${boot_success}" = "0" ]; then + else + decrement boot_counter + fi +- save_env boot_counter ++ if [ "\${env_block}" ]; then ++ save_env -f "\${env_block}" boot_counter ++ else ++ save_env boot_counter ++ fi + fi + EOF +diff --git a/util/grub.d/10_reset_boot_success.in b/util/grub.d/10_reset_boot_success.in +index e73f4137b3..ca04f9b699 100644 +--- a/util/grub.d/10_reset_boot_success.in ++++ b/util/grub.d/10_reset_boot_success.in +@@ -21,5 +21,9 @@ elif [ "\${boot_indeterminate}" = "1" ]; then + fi + # Reset boot_success for current boot + set boot_success=0 +-save_env boot_success boot_indeterminate ++if [ "\${env_block}" ]; then ++ save_env -f "\${env_block}" boot_success boot_indeterminate ++else ++ save_env boot_success boot_indeterminate ++fi + EOF +diff --git a/util/grub.d/12_menu_auto_hide.in b/util/grub.d/12_menu_auto_hide.in +index 6a7c0fa0d4..c2c207232e 100644 +--- a/util/grub.d/12_menu_auto_hide.in ++++ b/util/grub.d/12_menu_auto_hide.in +@@ -16,7 +16,11 @@ cat << EOF + if [ x\$feature_timeout_style = xy ] ; then + if [ "\${menu_show_once}" ]; then + unset menu_show_once +- save_env menu_show_once ++ if [ "\${env_block}" ]; then ++ save_env -f "\${env_block}" menu_show_once ++ else ++ save_env menu_show_once ++ fi + set timeout_style=menu + set timeout=60 + elif [ "\${menu_auto_hide}" -a "\${menu_hide_ok}" = "1" ]; then +diff --git a/util/grub.d/14_menu_show_once.in b/util/grub.d/14_menu_show_once.in +index 1cd7f36142..41b37b5ec7 100755 +--- a/util/grub.d/14_menu_show_once.in ++++ b/util/grub.d/14_menu_show_once.in +@@ -7,7 +7,11 @@ if [ x\$feature_timeout_style = xy ]; then + set timeout_style=menu + set timeout="\${menu_show_once_timeout}" + unset menu_show_once_timeout +- save_env menu_show_once_timeout ++ if [ "\${env_block}" ]; then ++ save_env -f "\${env_block}" menu_show_once_timeout ++ else ++ save_env menu_show_once_timeout ++ fi + fi + fi + EOF diff --git a/0406-grub-editenv-Make-automatic-hidden-menu-and-boot-cou.patch b/0406-grub-editenv-Make-automatic-hidden-menu-and-boot-cou.patch new file mode 100644 index 00000000..92b053bf --- /dev/null +++ b/0406-grub-editenv-Make-automatic-hidden-menu-and-boot-cou.patch @@ -0,0 +1,36 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Neal Gompa +Date: Wed, 26 Nov 2025 08:48:10 -0500 +Subject: [PATCH] grub-editenv: Make automatic hidden menu and boot counting + use envblk when available + +This ensures the automatic hidden menu and boot counting settings +are set in the right place when an external environment block is used. + +This is unlikely to be upstreamable, as it depends on Fedora's GRUB +hidden menu and boot counting patch sets. + +Signed-off-by: Neal Gompa +--- + util/grub-editenv.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/util/grub-editenv.c b/util/grub-editenv.c +index 3c3f9d09f3..48611e4e17 100644 +--- a/util/grub-editenv.c ++++ b/util/grub-editenv.c +@@ -473,7 +473,13 @@ set_variables (const char *name, int argc, char *argv[]) + + *(p++) = 0; + +- if ((strcmp (argv[0], "next_entry") == 0) && envblk_on_block != NULL) ++ if (((strcmp (argv[0], "next_entry") == 0) || ++ (strcmp (argv[0], "boot_counter") == 0) || ++ (strcmp (argv[0], "boot_indeterminate") == 0) || ++ (strcmp (argv[0], "boot_success") == 0) || ++ (strcmp (argv[0], "menu_show_once") == 0) || ++ (strcmp (argv[0], "menu_show_once_timeout") == 0)) ++ && envblk_on_block != NULL) + { + if (grub_envblk_set (envblk_on_block, argv[0], p) == 0) + grub_util_error ("%s", _("environment block too small")); diff --git a/grub.patches b/grub.patches index af74d2e3..8598a2de 100644 --- a/grub.patches +++ b/grub.patches @@ -387,3 +387,19 @@ Patch0387: 0387-blsuki-Check-for-mounted-boot-in-emu.patch Patch0388: 0388-blsuki-Add-uki-command-to-load-Unified-Kernel-Image-.patch Patch0389: 0389-posix_wrap-Tweaks-in-preparation-for-libtasn1.patch Patch0390: 0390-blsuki-do-not-register-blscfg-command.patch +Patch0391: 0391-misc-add-z-length-modifier-support.patch +Patch0392: 0392-tests-add-z-modifier-printf-tests.patch +Patch0393: 0393-util-grub-editenv-add-fs_envblk-open-helper.patch +Patch0394: 0394-util-grub-editenv-add-fs_envblk-write-helper.patch +Patch0395: 0395-util-grub-editenv-wire-set_variables-to-optional-fs_.patch +Patch0396: 0396-util-grub-editenv-wire-unset_variables-to-optional-f.patch +Patch0397: 0397-util-grub-editenv-wire-list_variables-to-optional-fs.patch +Patch0398: 0398-util-grub-editenv-add-probe-call-for-external-envblk.patch +Patch0399: 0399-btrfs-add-environment-block-to-reserved-header-area.patch +Patch0400: 0400-00_header.in-wire-grub.cfg-to-use-env_block-when-pre.patch +Patch0401: 0401-btrfs-update-doc-link-for-bootloader-support.patch +Patch0402: 0402-docs-add-Btrfs-env-block-and-special-env-vars.patch +Patch0403: 0403-kern-fs-Honour-file-read_hook-in-grub_fs_blocklist_r.patch +Patch0404: 0404-util-grub.d-00_header.in-Prefer-dev-instead-of-root-.patch +Patch0405: 0405-grub.d-Support-the-external-environment-block-for-au.patch +Patch0406: 0406-grub-editenv-Make-automatic-hidden-menu-and-boot-cou.patch diff --git a/grub2.spec b/grub2.spec index bde96ec1..3faf555e 100644 --- a/grub2.spec +++ b/grub2.spec @@ -17,7 +17,7 @@ Name: grub2 Epoch: 1 Version: 2.12 -Release: 47%{?dist} +Release: 48%{?dist} Summary: Bootloader with support for Linux, Multiboot and more License: GPL-3.0-or-later URL: http://www.gnu.org/software/grub/ @@ -609,6 +609,9 @@ fi %endif %changelog +* Fri Nov 21 2025 Leo Sandoval - 2.12-48 +- Add support for external environment block on Btrfs + * Thu Nov 13 2025 Leo Sandoval - 2.12-47 - blsuki: do not register blscfg command From 16cb5b303d06825e8b36b5ca5e195e811d047bd1 Mon Sep 17 00:00:00 2001 From: Leo Sandoval Date: Fri, 28 Nov 2025 11:37:28 -0600 Subject: [PATCH 10/11] Increase EFI max allocation to max usable address Resolves: #2263643 Signed-off-by: Nicolas Frayer Signed-off-by: Leo Sandoval --- ...max-allocation-to-max-usable-address.patch | 23 +++++++++++++++++++ grub.patches | 1 + grub2.spec | 6 ++++- 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 0407-Increase-EFI-max-allocation-to-max-usable-address.patch diff --git a/0407-Increase-EFI-max-allocation-to-max-usable-address.patch b/0407-Increase-EFI-max-allocation-to-max-usable-address.patch new file mode 100644 index 00000000..a1297e44 --- /dev/null +++ b/0407-Increase-EFI-max-allocation-to-max-usable-address.patch @@ -0,0 +1,23 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Leo Sandoval +Date: Fri, 28 Nov 2025 11:30:19 -0600 +Subject: [PATCH] Increase EFI max allocation to max usable address + +Signed-off-by: Leo Sandoval +--- + include/grub/x86_64/efi/memory.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/grub/x86_64/efi/memory.h b/include/grub/x86_64/efi/memory.h +index e81cfb3221..547e3f82f8 100644 +--- a/include/grub/x86_64/efi/memory.h ++++ b/include/grub/x86_64/efi/memory.h +@@ -3,7 +3,7 @@ + + #if defined (__code_model_large__) + #define GRUB_EFI_MAX_USABLE_ADDRESS __UINTPTR_MAX__ +-#define GRUB_EFI_MAX_ALLOCATION_ADDRESS 0x7fffffff ++#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS + #else + #define GRUB_EFI_MAX_USABLE_ADDRESS 0x7fffffff + #define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS diff --git a/grub.patches b/grub.patches index 8598a2de..67c06e16 100644 --- a/grub.patches +++ b/grub.patches @@ -403,3 +403,4 @@ Patch0403: 0403-kern-fs-Honour-file-read_hook-in-grub_fs_blocklist_r.patch Patch0404: 0404-util-grub.d-00_header.in-Prefer-dev-instead-of-root-.patch Patch0405: 0405-grub.d-Support-the-external-environment-block-for-au.patch Patch0406: 0406-grub-editenv-Make-automatic-hidden-menu-and-boot-cou.patch +Patch0407: 0407-Increase-EFI-max-allocation-to-max-usable-address.patch diff --git a/grub2.spec b/grub2.spec index 3faf555e..552df8d3 100644 --- a/grub2.spec +++ b/grub2.spec @@ -17,7 +17,7 @@ Name: grub2 Epoch: 1 Version: 2.12 -Release: 48%{?dist} +Release: 49%{?dist} Summary: Bootloader with support for Linux, Multiboot and more License: GPL-3.0-or-later URL: http://www.gnu.org/software/grub/ @@ -609,6 +609,10 @@ fi %endif %changelog +* Thu Nov 27 2025 Leo Sandoval - 2.12-49 +- Increase EFI max allocation to max usable address +- Resolves: #2263643 + * Fri Nov 21 2025 Leo Sandoval - 2.12-48 - Add support for external environment block on Btrfs From 85de859da1331046d29ffda9f1aac33d93da8457 Mon Sep 17 00:00:00 2001 From: Joel Capitao Date: Wed, 10 Dec 2025 12:02:40 +0100 Subject: [PATCH 11/11] Do not include EFI dir in common subpackage This fixes [1] which is part of [2]. We cannot ship /usr/lib/efi/* in -common subpackage as bootupd does not expect this directory for ppc64le arch. We are moving the creation and configuration of the directory into specific EFI macros only. [1] e02b6e88c71642ecc063ae7aefbd09b9be80d50e [2] https://fedoraproject.org/wiki/Changes/BootLoaderUpdatesPhase1 Signed-off-by: Joel Capitao --- grub.macros | 4 +++- grub2.spec | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/grub.macros b/grub.macros index f6455473..dd06d94c 100644 --- a/grub.macros +++ b/grub.macros @@ -708,6 +708,7 @@ find . '(' -iname gdb_grub \\\ ')' \\\ -exec cp {} $RPM_BUILD_ROOT/usr/lib/grub/%{grubaltefiarch}/ \\\; \ find $RPM_BUILD_ROOT -type f -iname "*.mod*" -exec chmod a-x {} '\;' \ +install -d -m 0700 ${RPM_BUILD_ROOT}%{grub_efi_dir}/ \ install -m 700 %{2} $RPM_BUILD_ROOT%{grub_efi_dir}/%{2} \ install -m 700 %{3} $RPM_BUILD_ROOT%{grub_efi_dir}/%{3} \ %{expand:%%do_install_protected_file grub2-%{alt_package_arch}} \ @@ -726,6 +727,7 @@ fi \ find $RPM_BUILD_ROOT -iname "*.module" -exec chmod a-x {} '\;' \ ln -s ../boot/grub2/grub.cfg \\\ $RPM_BUILD_ROOT%{_sysconfdir}/grub2-efi.cfg \ +install -d -m 0700 ${RPM_BUILD_ROOT}%{grub_efi_dir}/ \ install -m 700 %{2} $RPM_BUILD_ROOT%{grub_efi_dir}/%{2} \ install -m 700 %{3} $RPM_BUILD_ROOT%{grub_efi_dir}/%{3} \ %ifarch %{arm} \ @@ -830,7 +832,6 @@ cp docs/grub.info $RPM_BUILD_ROOT%{_infodir}/grub2.info \ cp docs/grub-dev.info \\\ $RPM_BUILD_ROOT%{_infodir}/grub2-dev.info \ install -d -m 0700 ${RPM_BUILD_ROOT}%{efi_esp_dir}/ \ -install -d -m 0700 ${RPM_BUILD_ROOT}%{grub_efi_dir}/ \ install -d -m 0700 ${RPM_BUILD_ROOT}/boot/grub2/ \ install -d -m 0700 ${RPM_BUILD_ROOT}/boot/loader/entries \ install -d -m 0700 ${RPM_BUILD_ROOT}/boot/grub2/themes/system \ @@ -879,6 +880,7 @@ ln -s ../boot/grub2/grub.cfg \\\ %config(noreplace) %{_sysconfdir}/grub2.cfg \ %config(noreplace) %{_sysconfdir}/grub2-efi.cfg \ %ghost %attr(0700,root,root) %{efi_esp_dir}/%{2} \ +%dir %attr(0700,root,root) %{grub_efi_dir} \ %attr(0700,root,root) %{grub_efi_dir}/%{2} \ %ifarch %{arm} \ %attr(0700,root,root) %verify(not mtime) %{efi_esp_boot}/BOOTARM.EFI \ diff --git a/grub2.spec b/grub2.spec index 552df8d3..8f73768c 100644 --- a/grub2.spec +++ b/grub2.spec @@ -17,7 +17,7 @@ Name: grub2 Epoch: 1 Version: 2.12 -Release: 49%{?dist} +Release: 50%{?dist} Summary: Bootloader with support for Linux, Multiboot and more License: GPL-3.0-or-later URL: http://www.gnu.org/software/grub/ @@ -446,7 +446,6 @@ fi %dir /boot/grub2/themes/system %attr(0700,root,root) %dir /boot/grub2 %exclude /boot/grub2/* -%dir %attr(0700,root,root) %{grub_efi_dir} %exclude %{grub_efi_dir}/* %ghost %config(noreplace) %verify(not size mode md5 mtime) /boot/grub2/grubenv %license COPYING @@ -609,6 +608,10 @@ fi %endif %changelog +* Wed Dec 10 2025 Joel Capitao - 2.12-50 +- Do not include EFI dir in common subpackage +- Resolves: rhbz#2420430 + * Thu Nov 27 2025 Leo Sandoval - 2.12-49 - Increase EFI max allocation to max usable address - Resolves: #2263643