Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package grub2 for openSUSE:Factory checked in at 2026-04-22 17:12:53 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/grub2 (Old) and /work/SRC/openSUSE:Factory/.grub2.new.11940 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "grub2" Wed Apr 22 17:12:53 2026 rev:388 rq:1348785 version:2.14 Changes: -------- --- /work/SRC/openSUSE:Factory/grub2/grub2.changes 2026-04-21 12:42:51.717544630 +0200 +++ /work/SRC/openSUSE:Factory/.grub2.new.11940/grub2.changes 2026-04-22 17:12:55.220991292 +0200 @@ -2,68 +1,0 @@ -Mon Apr 20 09:03:24 UTC 2026 - Michael Chang <[email protected]> - -- VUL-0: grub: potentially problematic utf8 conversion in bli patches (bsc#1262129) - * 0001-Fix-problematic-utf8-conversion-in-bli-patches.patch - -------------------------------------------------------------------- -Fri Apr 17 07:45:21 UTC 2026 - Radoslav Kolev <[email protected]> - -- Fix build for glibc 2.43 by taking upstream changes (bsc#1257256) - * 0001-osdep-linux-ofpath-Update-strstr-calls.patch - * 0001-util-probe-Save-strrchr-ret-val-to-const-data-ptr.patch - * 0002-util-resolve-Save-str-r-chr-ret-val-to-const-data-pt.patch - -------------------------------------------------------------------- -Tue Apr 14 12:34:01 UTC 2026 - Danilo Spinella <[email protected]> - -- Fix string to integer conversion for LoaderConfigTimeout - * 0004-bli-Add-support-for-LoaderConfigTimeout-and-LoaderCo.patch - -------------------------------------------------------------------- -Tue Apr 14 00:39:13 UTC 2026 - Michael Chang <[email protected]> - -- grub2.spec: When building the grubbls image, do not hardcode the timeout - value in the early config because it is set by bli.mod when it is loaded -- grub2.spec: Remove hardcoded terminal and theme settings from the early - config as they are now applied at runtime - -------------------------------------------------------------------- -Mon Apr 13 07:12:45 UTC 2026 - Michael Chang <[email protected]> - -- Fix missing install device check in grub2-install on PowerPC which could lead - to bootlist corruption (bsc#1221126) - * 0001-Mandatory-install-device-check-for-PowerPC.patch - -------------------------------------------------------------------- -Tue Apr 7 06:45:13 UTC 2026 - Michael Chang <[email protected]> - -- Fix double free in xen booting if root filesystem is Btrfs (bsc#1259543) - * grub2-btrfs-01-add-ability-to-boot-from-subvolumes.patch - * grub2-btrfs-09-get-default-subvolume.patch - -------------------------------------------------------------------- -Mon Mar 23 16:16:32 UTC 2026 - Danilo Spinella <[email protected]> - -- Rewrite BLI patches: - * 0001-blsuki-Add-support-for-LoaderEntries.patch - * 0002-menu-Allow-default-entry-to-have-.conf-suffix.patch - * 0003-bli-Add-support-for-LoaderEntryDefault-and-LoaderEnt.patch - * 0004-bli-Add-support-for-LoaderConfigTimeout-and-LoaderCo.patch - * 0005-bls_bumpcounter-Add-command-to-bump-boot-counter-for.patch - * 0006-bli-Add-support-for-LoaderFeatures.patch - * 0007-blsuki-Fix-sorting-for-entries-with-boot-counting-en.patch - * 0008-blsuki-append-leftover-LoaderEntries.patch - * 0009-blsuki-conservative-UTF-8-buffer-size.patch -- Remove patches: - * 0001-bls-Accept-.conf-suffix-in-setting-default-entry.patch - * grub2-bls-boot-counting.patch - * grub2-bls-boot-assessment.patch - * grub2-blscfg-set-efivars.patch - * grub2-bls-loader-entry-oneshot.patch - * grub2-blsbumpcounter-menu.patch - * grub2-bls-loader-entry-default.patch - * grub2-bls-loader-entries-boot-counting.patch - * grub2-bls-loader-features.patch - * grub2-bls-loader-config-timeout.patch - * grub2-bls-loader-config-timeout-fix.patch - -------------------------------------------------------------------- Old: ---- 0001-Fix-problematic-utf8-conversion-in-bli-patches.patch 0001-Mandatory-install-device-check-for-PowerPC.patch 0001-blsuki-Add-support-for-LoaderEntries.patch 0001-osdep-linux-ofpath-Update-strstr-calls.patch 0001-util-probe-Save-strrchr-ret-val-to-const-data-ptr.patch 0002-menu-Allow-default-entry-to-have-.conf-suffix.patch 0002-util-resolve-Save-str-r-chr-ret-val-to-const-data-pt.patch 0003-bli-Add-support-for-LoaderEntryDefault-and-LoaderEnt.patch 0004-bli-Add-support-for-LoaderConfigTimeout-and-LoaderCo.patch 0005-bls_bumpcounter-Add-command-to-bump-boot-counter-for.patch 0006-bli-Add-support-for-LoaderFeatures.patch 0007-blsuki-Fix-sorting-for-entries-with-boot-counting-en.patch 0008-blsuki-append-leftover-LoaderEntries.patch 0009-blsuki-conservative-UTF-8-buffer-size.patch New: ---- 0001-bls-Accept-.conf-suffix-in-setting-default-entry.patch grub2-bls-boot-assessment.patch grub2-bls-boot-counting.patch grub2-bls-loader-config-timeout-fix.patch grub2-bls-loader-config-timeout.patch grub2-bls-loader-entries-boot-counting.patch grub2-bls-loader-entry-default.patch grub2-bls-loader-entry-oneshot.patch grub2-bls-loader-features.patch grub2-blsbumpcounter-menu.patch grub2-blscfg-set-efivars.patch ----------(Old B)---------- Old: WARN: 0001-Fix-problematic-utf8-conversion-in-bli-patches.patch not found in changes Old: WARN: 0001-Mandatory-install-device-check-for-PowerPC.patch not found in changes Old: WARN: 0001-blsuki-Add-support-for-LoaderEntries.patch not found in changes Old: WARN: 0001-osdep-linux-ofpath-Update-strstr-calls.patch not found in changes Old: WARN: 0001-util-probe-Save-strrchr-ret-val-to-const-data-ptr.patch not found in changes Old: WARN: 0002-menu-Allow-default-entry-to-have-.conf-suffix.patch not found in changes Old: WARN: 0002-util-resolve-Save-str-r-chr-ret-val-to-const-data-pt.patch not found in changes Old: WARN: 0003-bli-Add-support-for-LoaderEntryDefault-and-LoaderEnt.patch not found in changes Old: WARN: 0004-bli-Add-support-for-LoaderConfigTimeout-and-LoaderCo.patch not found in changes Old: WARN: 0005-bls_bumpcounter-Add-command-to-bump-boot-counter-for.patch not found in changes Old: WARN: 0006-bli-Add-support-for-LoaderFeatures.patch not found in changes Old: WARN: 0007-blsuki-Fix-sorting-for-entries-with-boot-counting-en.patch not found in changes Old: WARN: 0008-blsuki-append-leftover-LoaderEntries.patch not found in changes Old: WARN: 0009-blsuki-conservative-UTF-8-buffer-size.patch not found in changes ----------(Old E)---------- ----------(New B)---------- New:- Fix grub-bls does not rollback via setting new default (bsc#1237198) * 0001-bls-Accept-.conf-suffix-in-setting-default-entry.patch New: * grub2-bls-boot-counting.patch * grub2-bls-boot-assessment.patch * grub2-bls-boot-show-snapshot.patch New: * 0001-blscfg-read-fragments-in-order.patch * grub2-bls-boot-counting.patch * grub2-bls-boot-assessment.patch New:- Fix LoaderConfigTimeout and LoaderConfigTimeoutOneshot (bsc#1259477) * grub2-bls-loader-config-timeout-fix.patch New: * grub2-bls-loader-features.patch * grub2-bls-loader-config-timeout.patch - New patch New: entries with boot counting enabled * grub2-bls-loader-entries-boot-counting.patch * grub2-bls-loader-features.patch New:- Add support for `LoaderEntryDefault` EFI variable * grub2-bls-loader-entry-default.patch New: * 0001-Improve-TPM-key-protection-on-boot-interruptions.patch * grub2-bls-loader-entry-oneshot.patch * grub2-blsbumpcounter-menu.patch New: * grub2-bls-loader-entries-boot-counting.patch * grub2-bls-loader-features.patch * grub2-bls-loader-config-timeout.patch New: * grub2-bls-loader-entry-oneshot.patch * grub2-blsbumpcounter-menu.patch - Patch removed New: * grub2-bls-boot-show-snapshot.patch * grub2-blscfg-set-efivars.patch * 0001-Improve-TPM-key-protection-on-boot-interruptions.patch ----------(New E)---------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ grub2.spec ++++++ --- /var/tmp/diff_new_pack.lBGNPX/_old 2026-04-22 17:13:00.989230071 +0200 +++ /var/tmp/diff_new_pack.lBGNPX/_new 2026-04-22 17:13:00.993230237 +0200 @@ -359,6 +359,7 @@ Patch232: 0001-ieee1275-support-added-for-multiple-nvme-bootpaths.patch Patch236: 0001-kern-main-Fix-cmdpath-in-root-directory.patch Patch237: grub2-s390x-secure-execution-support.patch +Patch259: 0001-bls-Accept-.conf-suffix-in-setting-default-entry.patch Patch263: 0001-autofs-Ignore-zfs-not-found.patch Patch264: 0001-s390x-emu-Pass-through-PAES-cipher-as-AES.patch Patch274: 0001-ofpath-Add-error-check-in-NVMEoF-device-translation.patch @@ -366,10 +367,15 @@ Patch277: 0001-prep_loadenv-Measure-the-environment-block-into-PCR-.patch Patch294: 0001-Fix-PowerPC-CAS-reboot-to-evaluate-menu-context.patch Patch295: 0001-blscfg-read-fragments-in-order.patch +Patch296: grub2-bls-boot-counting.patch +Patch297: grub2-bls-boot-assessment.patch Patch298: grub2-bls-boot-show-snapshot.patch +Patch300: grub2-blscfg-set-efivars.patch Patch309: 0001-Improve-TPM-key-protection-on-boot-interruptions.patch Patch310: 0004-Key-revocation-on-out-of-bound-file-access.patch +Patch311: grub2-bls-loader-entry-oneshot.patch Patch312: 0001-mkconfig-Determine-GRUB_DISTRIBUTOR-from-etc-SUSE-br.patch +Patch313: grub2-blsbumpcounter-menu.patch Patch315: 0001-test-Fix-f-test-on-files-over-network.patch Patch316: 0002-http-Return-HTTP-status-code-in-http_establish.patch Patch317: 0003-docs-Clarify-test-for-files-on-TFTP-and-HTTP.patch @@ -379,7 +385,11 @@ Patch336: 0002-linux-fallback-to-direct-PE-entry-boot-on-arm64.patch Patch337: 0003-efi-chainloader-fallback-to-direct-image-execution.patch Patch338: 0004-efi-chainloader-fix-missing-file_path-in-loaded_imag.patch +Patch342: grub2-bls-loader-entry-default.patch Patch344: grub2-i386-pc-no-pageflipping.patch +Patch401: grub2-bls-loader-entries-boot-counting.patch +Patch402: grub2-bls-loader-features.patch +Patch403: grub2-bls-loader-config-timeout.patch Patch404: 0001-editenv-create-health_check_flag-env-var-on-RW-raw-b.patch Patch405: 0001-00_header-Omit-loading-efi_uga-on-non-x86-EFI-platfo.patch Patch406: 0001-Revert-configure-Print-a-more-helpful-error-if-autoc.patch @@ -388,21 +398,8 @@ Patch409: 0001-bls-Allow-configuration-of-active-console-type.patch Patch410: 0002-grubbls-Add-automatic-fwsetup-menu-entry.patch Patch411: 0001-ieee1275-support-dm-multipath-bootlist.patch +Patch412: grub2-bls-loader-config-timeout-fix.patch Patch413: 0001-mdraid1x-fix-raid_disks-decoding-on-big-endian-syste.patch -Patch414: 0001-blsuki-Add-support-for-LoaderEntries.patch -Patch415: 0002-menu-Allow-default-entry-to-have-.conf-suffix.patch -Patch416: 0003-bli-Add-support-for-LoaderEntryDefault-and-LoaderEnt.patch -Patch417: 0004-bli-Add-support-for-LoaderConfigTimeout-and-LoaderCo.patch -Patch418: 0005-bls_bumpcounter-Add-command-to-bump-boot-counter-for.patch -Patch419: 0006-bli-Add-support-for-LoaderFeatures.patch -Patch420: 0007-blsuki-Fix-sorting-for-entries-with-boot-counting-en.patch -Patch421: 0008-blsuki-append-leftover-LoaderEntries.patch -Patch422: 0009-blsuki-conservative-UTF-8-buffer-size.patch -Patch423: 0001-Mandatory-install-device-check-for-PowerPC.patch -Patch424: 0001-osdep-linux-ofpath-Update-strstr-calls.patch -Patch425: 0001-util-probe-Save-strrchr-ret-val-to-const-data-ptr.patch -Patch426: 0002-util-resolve-Save-str-r-chr-ret-val-to-const-data-pt.patch -Patch427: 0001-Fix-problematic-utf8-conversion-in-bli-patches.patch %if 0%{?suse_version} < 1600 Requires: gettext-runtime @@ -736,7 +733,7 @@ %define _configure ../configure # We don't want to let rpm override *FLAGS with default a.k.a bogus values. -CFLAGS="-fno-strict-aliasing -fno-inline-functions-called-once" +CFLAGS="-fno-strict-aliasing -fno-inline-functions-called-once -Wno-error=discarded-qualifiers" CXXFLAGS=" " FFLAGS=" " export CFLAGS CXXFLAGS FFLAGS @@ -787,7 +784,7 @@ PXE_MODULES="tftp http" CRYPTO_MODULES="luks luks2 gcry_rijndael gcry_sha1 gcry_sha256 gcry_sha512 crypttab" %ifarch %{efi} -CD_MODULES="${CD_MODULES} chain efifwsetup efinet read tpm tss2 tpm2_key_protector memdisk tar squash4 xzio blsuki bls_bumpcounter" +CD_MODULES="${CD_MODULES} chain efifwsetup efinet read tpm tss2 tpm2_key_protector memdisk tar squash4 xzio blsuki blsbumpcounter" PXE_MODULES="${PXE_MODULES} efinet" %else CD_MODULES="${CD_MODULES} net ofnet" @@ -861,10 +858,23 @@ regexp --set 1:root '\((.*)\)' "$cmdpath" +set timeout=8 set gfxmode=auto set gfxpayload=keep set enable_blscfg=1 +terminal_input console +terminal_output console +terminal_output --append gfxterm + +loadfont (memdisk)/boot/grub/themes/DejaVuSans-Bold14.pf2 +loadfont (memdisk)/boot/grub/themes/DejaVuSans10.pf2 +loadfont (memdisk)/boot/grub/themes/DejaVuSans12.pf2 +loadfont (memdisk)/boot/grub/themes/ascii.pf2 + +set theme=(memdisk)/boot/grub/themes/theme.txt +export theme + EOF %if 0%{?suse_version} > 1500 @@ -880,7 +890,7 @@ %{?sbat_generation:--sbat sbat.csv} \ -d grub-core \ all_video boot font gfxmenu gfxterm gzio halt jpeg minicmd normal part_gpt png reboot video \ - fat tpm tss2 tpm2_key_protector memdisk tar squash4 xzio blsuki bls_bumpcounter linux bli regexp loadenv test echo true sleep efifwsetup + fat tpm tss2 tpm2_key_protector memdisk tar squash4 xzio blsuki blsbumpcounter linux bli regexp loadenv test echo true sleep efifwsetup %endif %ifarch x86_64 aarch64 ++++++ 0001-bls-Accept-.conf-suffix-in-setting-default-entry.patch ++++++ >From e873743f4ed7841542dd7dc11a183cb136670382 Mon Sep 17 00:00:00 2001 From: Michael Chang <[email protected]> Date: Wed, 19 Feb 2025 14:52:52 +0800 Subject: [PATCH] bls: Accept .conf suffix in setting default entry Signed-off-by: Michael Chang <[email protected]> --- grub-core/normal/menu.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c index b11b28e0d9..dfdf0c7268 100644 --- a/grub-core/normal/menu.c +++ b/grub-core/normal/menu.c @@ -557,6 +557,26 @@ get_entry_number (grub_menu_t menu, const char *name) entry = i; break; } + + if (e->bls) + { + char *v, *ext; + + if ((v = grub_strdup (val)) && + (ext = grub_strrchr (v, '.')) && + grub_strcmp (ext, ".conf") == 0) + { + *ext = '\0'; + if (menuentry_eq (e->id, v)) + { + entry = i; + grub_free (v); + break; + } + } + grub_free (v); + } + e = e->next; /* Skip hidden entries */ -- 2.48.1 ++++++ grub2-bls-boot-assessment.patch ++++++ Implement Automatic Boot Assessment for grub2-bls. https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT/ The entries are ordered first by the boot tries left, keeping the one without available tries (e.g. <entry>+0-3.conf) after the one without a boot counter or with a positive boot counter. After removing the boot counter from the release string, keep the ordering as it worked previously. --- a/grub-core/commands/blsuki.c +++ b/grub-core/commands/blsuki.c @@ -255,6 +255,39 @@ return ret; } +static long +tries_left (const char *filename) +{ + char *tries_left_str; + long ret = -1; + char *str = grub_strdup (filename); + + if (str == NULL) + return -1; + + /* Search for the start of the tries left, as per boot assessment */ + tries_left_str = grub_strrchr (str, '+'); + + if (tries_left_str != NULL) + { + const char *end; + long tries; + + ++tries_left_str; + tries = grub_strtol (tries_left_str, (const char **) &end, 10); + + if (grub_errno == GRUB_ERR_NONE) + { + if (*end == '-' || *end == '.') + ret = tries; + } + else + grub_errno = GRUB_ERR_NONE; + } + + grub_free (str); + return ret; +} /* * Add a new grub_blsuki_entry_t struct to the entries list and sort it's * position on the list. @@ -274,10 +307,19 @@ FOR_BLSUKI_ENTRIES (e) { + long t1, t2; + rc = filevercmp (entry->filename, e->filename); if (rc == 0) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("duplicate file: `%s'"), entry->filename); + t1 = tries_left (entry->filename); + t2 = tries_left (e->filename); + if (t2 == 0 && t1 != 0) + rc = 1; + else if (t2 != 0 && t1 == 0) + rc = -1; + if (rc > 0) { grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename); ++++++ grub2-bls-boot-counting.patch ++++++ Add a new bls_bumpcunter grub command that implement boot counting for bls entries. Boot counting, explained in systemd Automatic Boot Assessment, keep track of the avaiable tries for each entry and the number of attempted boot. The bls_bumpcunter command parse the entry id, check if there is a boot count enabled and update its value accordingly. Then, EFI_SIMPLE_FILE_SYSTEM_PROTOCOL is used to rename the entry on the EFI partition. https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT/ --- /dev/null +++ b/include/grub/efi/filesystem.h @@ -0,0 +1,155 @@ +/* + * GRUB -- GRand Unified Bootloader + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef GRUB_EFI_TPM_HEADER +#define GRUB_EFI_TPM_HEADER 1 + +#include <grub/efi/api.h> +#include <grub/env.h> + +struct grub_efi_file_io_token { + grub_efi_event_t Event; + grub_efi_status_t Status; + grub_efi_uint64_t BufferSize; + void *Buffer; +}; + +typedef struct grub_efi_file_io_token grub_efi_file_io_token_t; + + +struct grub_efi_file_protocol +{ + grub_efi_uint64_t Revision; + + grub_efi_status_t + (__grub_efi_api *Open) (struct grub_efi_file_protocol *this, + struct grub_efi_file_protocol **new_handle, + grub_efi_char16_t *filename, + grub_efi_uint64_t open_mode, + grub_efi_uint64_t attributes); + + grub_efi_status_t + (__grub_efi_api *Close) (struct grub_efi_file_protocol *this); + + grub_efi_status_t + (__grub_efi_api *Delete) (struct grub_efi_file_protocol *this); + + grub_efi_status_t + (__grub_efi_api *Read) (struct grub_efi_file_protocol *this, + grub_efi_uint64_t *buffer_size, + void *buffer); + + grub_efi_status_t + (__grub_efi_api *Write) (struct grub_efi_file_protocol *this, + grub_efi_uint64_t *buffer_size, + void *buffer); + + grub_efi_status_t + (__grub_efi_api *GetPosition) (struct grub_efi_file_protocol *this, + grub_efi_uint64_t *position); + + grub_efi_status_t + (__grub_efi_api *SetPosition) (struct grub_efi_file_protocol *this, + grub_efi_uint64_t position); + + grub_efi_status_t + (__grub_efi_api *GetInfo) (struct grub_efi_file_protocol *this, + grub_guid_t *information_type, + grub_efi_uint64_t *buffer_size, + void *buffer); + + grub_efi_status_t + (__grub_efi_api *SetInfo) (struct grub_efi_file_protocol *this, + grub_guid_t *information_type, + grub_efi_uint64_t buffer_size, + void *buffer); + + grub_efi_status_t + (__grub_efi_api *Flush) (struct grub_efi_file_protocol *this); + + grub_efi_status_t + (__grub_efi_api *OpenEx) (struct grub_efi_file_protocol *this, + struct grub_efi_file_protocol **new_handle, + grub_efi_char16_t *filename, + grub_efi_uint64_t open_mode, + grub_efi_uint64_t attributes, + grub_efi_file_io_token_t *token); + + grub_efi_status_t + (__grub_efi_api *ReadEx) (struct grub_efi_file_protocol *this, + grub_efi_file_io_token_t *token); + + grub_efi_status_t + (__grub_efi_api *WriteEx) (struct grub_efi_file_protocol *this, + grub_efi_file_io_token_t *token); + + grub_efi_status_t + (__grub_efi_api *FlushEx) (struct grub_efi_file_protocol *this, + grub_efi_file_io_token_t *token); +}; + +typedef struct grub_efi_file_protocol grub_efi_file_protocol_t; + +/******************************************************* + Open Modes + ******************************************************/ +#define GRUB_EFI_FILE_MODE_READ 0x0000000000000001 +#define GRUB_EFI_FILE_MODE_WRITE 0x0000000000000002 +#define GRUB_EFI_FILE_MODE_CREATE 0x8000000000000000 + +/******************************************************* + File Attributes + ******************************************************/ +#define GRUB_EFI_FILE_READ_ONLY 0x0000000000000001 +#define GRUB_EFI_FILE_HIDDEN 0x0000000000000002 +#define GRUB_EFI_FILE_SYSTEM 0x0000000000000004 +#define GRUB_EFI_FILE_RESERVED 0x0000000000000008 +#define GRUB_EFI_FILE_DIRECTORY 0x0000000000000010 +#define GRUB_EFI_FILE_ARCHIVE 0x0000000000000020 +#define GRUB_EFI_FILE_VALID_ATTR 0x0000000000000037 + +struct grub_efi_file_info { + grub_efi_uint64_t Size; + grub_efi_uint64_t FileSize; + grub_efi_uint64_t PhysicalSize; + grub_efi_time_t CreateTime; + grub_efi_time_t LastAccessTime; + grub_efi_time_t ModificationTime; + grub_efi_uint64_t Attribute; + grub_efi_char16_t FileName[]; +}; + +typedef struct grub_efi_file_info grub_efi_file_info_t; + +#define GRUB_EFI_FILE_INFO_ID \ + {0x09576e92,0x6d3f,0x11d2, \ + {0x8e,0x39,0x00,0xa0,0xc9,0x69,0x72,0x3b} \ + } + +struct grub_efi_simple_file_system_protocol +{ + grub_efi_uint64_t Revision; + + grub_efi_status_t + (__grub_efi_api *OpenVolume) (struct grub_efi_simple_file_system_protocol *this, + struct grub_efi_file_protocol **root); +}; + +typedef struct grub_efi_simple_file_system_protocol grub_efi_simple_file_system_protocol_t; + + +#endif --- /dev/null +++ b/grub-core/commands/blsbumpcounter.c @@ -0,0 +1,54 @@ +/*-*- Mode: C; c-basic-offset: 2; indent-tabs-mode: t -*-*/ + +/* blsbumpcounter.c - implementation of boot counting for the Automatic Boot Assessment */ + +/* + * GRUB -- GRand Unified Bootloader + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/extcmd.h> +#include <grub/dl.h> + +#include <stddef.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + + +static grub_err_t +grub_cmd_bumpcounters (grub_extcmd_context_t ctxt __attribute__ ((unused)), + int argc __attribute__ ((unused)), char **args __attribute__ ((unused))) +{ + /* placeholder, as blsbumpcounter only work on EFI platforms */ + return GRUB_ERR_NONE; +} + + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT(blsbumpcounter) +{ + cmd = grub_register_extcmd ("bls_bumpcounter", + grub_cmd_bumpcounters, + 0, + NULL, + N_("Bump the boot entry counting (only works on EFI)."), + NULL); +} + +GRUB_MOD_FINI(blsbumpcounter) +{ + grub_unregister_extcmd (cmd); +} --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -869,6 +869,13 @@ }; module = { + name = blsbumpcounter; + common = commands/blsbumpcounter.c; + efi = commands/efi/blsbumpcounter.c; +}; + + +module = { name = boot; common = commands/boot.c; i386_pc = lib/i386/pc/biosnum.c; --- /dev/null +++ b/grub-core/commands/efi/blsbumpcounter.c @@ -0,0 +1,252 @@ +/*-*- Mode: C; c-basic-offset: 2; indent-tabs-mode: t -*-*/ + +/* bls.c - implementation of the boot loader spec */ + +/* + * GRUB -- GRand Unified Bootloader + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/extcmd.h> +#include <grub/fs.h> +#include <grub/env.h> +#include <grub/lib/envblk.h> +#include <grub/efi/api.h> +#include <grub/efi/efi.h> +#include <grub/efi/filesystem.h> + +#include <stddef.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define GRUB_BLS_CONFIG_PATH "\\loader\\entries\\" + +#define GRUB_EFI_LOADER_GUID \ + { 0x4a67b082, 0x0a4c, 0x41cf, { 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f } } + +static grub_guid_t grub_simple_file_system_guid = GRUB_EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID; +static grub_guid_t grub_efi_loader_guid = GRUB_EFI_LOADER_GUID; + +static grub_err_t +grub_cmd_bumpcounters (grub_extcmd_context_t ctxt __attribute__ ((unused)), + int argc, char **args) +{ + grub_efi_file_info_t *file_info = NULL; + grub_efi_file_protocol_t *handle = NULL; + grub_efi_file_protocol_t *root = NULL; + grub_efi_simple_file_system_protocol_t *volume = NULL; + char* id = NULL; + + grub_dprintf("bls_bumpcounter", "starting bumpcounter\n"); + /* There should be exactly two arguments, the entry that is getting booted and the disk where + it can be found */ + if (argc != 1) { + grub_dprintf("bls_bumpcounter", "one argument should be passed\n"); + return GRUB_ERR_BAD_ARGUMENT; + } + id = args[0]; + + /* Look for the start of the count + If no '+' symbol has been found, the boot counting isn't enabled for the selected entry */ + if (grub_strrchr(id, '+') == NULL) { + grub_dprintf("bls_bumpcounter", "boot counting is not in effect for id %s\n", id); + return GRUB_ERR_NONE; + } + + grub_efi_loaded_image_t *image = NULL; + grub_dprintf("bls_bumpcounter", "Using loaded EFI image device\n"); + image = grub_efi_get_loaded_image (grub_efi_image_handle); + + if (!image) { + grub_dprintf("bls_bumpcounter", "grub_efi_get_loaded_image failed\n"); + return 0; + } + + grub_efi_status_t err; + grub_efi_boot_services_t *bs; + bs = grub_efi_system_table->boot_services; + err = bs->handle_protocol (image->device_handle, + (void *) &grub_simple_file_system_guid, (void *) &volume); + if (err != GRUB_EFI_SUCCESS) { + grub_dprintf("bls_bumpcounter", "Cannot get handle to EFI_SIMPLE_FILE_SYSTEM_PROTOCOL: %lu\n", (unsigned long)err); + return GRUB_ERR_BAD_DEVICE; + } + volume->OpenVolume(volume, &root); + if (err != GRUB_EFI_SUCCESS) { + grub_dprintf("bls_bumpcounter", "Cannot open the volume: %lu\n", (unsigned long)err); + return GRUB_ERR_BAD_DEVICE; + } + + char *blsdir = (char *)grub_env_get ("blsdir"); + char *tmp = NULL; + if (blsdir) { + tmp = blsdir; + while (*tmp) { + if (*tmp == '/') { + /* Replace linux path delimiter (/) with EFI compatible (\) */ + *tmp = '\\'; + } + tmp++; + } + } else { + blsdir = (char *)GRUB_BLS_CONFIG_PATH; + } + + unsigned long int len = grub_strlen(blsdir) + grub_strlen(id) + sizeof(".conf") + 1; + grub_efi_char16_t* old_path = grub_malloc(len * sizeof(grub_efi_char16_t)); + if (!old_path) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto finish; + } + grub_efi_char16_t* tmp_path = old_path; + tmp = blsdir; + while (*tmp) { + *tmp_path++ = (grub_efi_char16_t)*tmp++; + } + tmp = id; + while (*tmp) { + *tmp_path++ = (grub_efi_char16_t)*tmp++; + } + static const char* ext = ".conf"; + tmp = (char *)ext; + while (*tmp) { + *tmp_path++ = (grub_efi_char16_t)*tmp++; + } + *tmp_path = (grub_efi_char16_t)'\0'; + + err = root->Open(root, &handle, old_path, GRUB_EFI_FILE_MODE_READ|GRUB_EFI_FILE_MODE_WRITE, 0); + grub_free(old_path); + if (err != GRUB_EFI_SUCCESS) { + grub_dprintf("bls_bumpcounter", "Cannot open the entry %s%s.conf : %lu\n", blsdir, id, (unsigned long)err); + goto finish; + } + + /* Just like get_file_info works in systemd:src/boot/efi/util.c, get the file_info */ + grub_efi_uint64_t size = offsetof(grub_efi_file_info_t, FileName) + 256U * sizeof(grub_efi_char16_t); + file_info = grub_malloc(size); + if (!file_info) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto finish; + } + grub_guid_t grub_efi_file_info_guid = GRUB_EFI_FILE_INFO_ID; + err = handle->GetInfo(handle, &grub_efi_file_info_guid, &size, file_info); + if (err == GRUB_EFI_BUFFER_TOO_SMALL) { + grub_free(file_info); + file_info = grub_malloc(size); + if (!file_info) { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto finish; + } + err = handle->GetInfo(handle, &grub_efi_file_info_guid, &size, file_info); + } + + if (err != GRUB_EFI_SUCCESS) { + grub_dprintf("bls_bumpcounter", "Cannot get the file_info of the entry: %lu\n", (unsigned long)err); + goto finish; + } + + /* Calculate the new filename with the bumped counter + Look for the start of the count */ + tmp = grub_strrchr(id, '+'); + *tmp = '\0'; + int tries = -1; + int tries_left = grub_strtol(++tmp, (const char**) &tmp, 10); + /* The parsing succeeded */ + if (tmp != NULL) { + if (tries_left > 0) { + tries_left--; + } + if (*tmp == '-') { + tmp++; + tries = grub_strtol(tmp, (const char**) &tmp, 10); + if (tmp != NULL) { + tries++; + } else { + tries = -1; + } + } + } else { + goto finish; + } + + char *new_path; + if (tries == -1) { + /* This is the first try, rename accordingly */ + new_path = grub_xasprintf ("%s+%d-1.conf", id, tries_left); + } else { + new_path = grub_xasprintf ("%s+%d-%d.conf", id, tries_left, tries); + } + grub_dprintf("bls_bumpcounter", "renaming entry to %s\n", new_path); + + /* Copy the new filename into the file_info struct */ + char* src = new_path; + grub_efi_char16_t *dst = file_info->FileName; + while (*src) { + *dst++ = (grub_efi_char16_t) *src++; + } + *dst = (grub_efi_char16_t) '\0'; + + handle->SetInfo(handle, &grub_efi_file_info_guid, size, file_info); + + if (err != GRUB_EFI_SUCCESS) { + grub_dprintf("bls_bumpcounter", "Cannot rename file: %lu\n", (unsigned long)err); + goto finish; + } + + handle->Flush(handle); + grub_dprintf("bls_bumpcounter", "entry renamed\n"); + handle->Close(handle); + + char* loader_boot_count_path = grub_xasprintf("%s%s", blsdir, new_path); + grub_free(new_path); + grub_efi_set_variable_to_string("LoaderBootCountPath", &grub_efi_loader_guid, loader_boot_count_path, + GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS | + GRUB_EFI_VARIABLE_RUNTIME_ACCESS); + grub_free(loader_boot_count_path); + + if (err != GRUB_EFI_SUCCESS) { + goto finish; + } + + grub_free(file_info); + + return GRUB_ERR_NONE; + +finish: + grub_free(file_info); + + return GRUB_ERR_BAD_DEVICE; +} + + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT(blsbumpcounter) +{ + grub_dprintf("bls_bumpcounter", "%s got here\n", __func__); + cmd = grub_register_extcmd ("bls_bumpcounter", + grub_cmd_bumpcounters, + 0, + NULL, + N_("Bump the boot entry counting."), + NULL); +} + +GRUB_MOD_FINI(blsbumpcounter) +{ + grub_unregister_extcmd (cmd); +} --- a/grub-core/commands/blsuki.c +++ b/grub-core/commands/blsuki.c @@ -1044,6 +1044,8 @@ grub_size_t size; bool blsuki_save_default; + char *bumpcounter = NULL; + linux_path = blsuki_get_val (entry, "linux", NULL); if (linux_path == NULL) { @@ -1089,10 +1091,23 @@ if (grub_errno != GRUB_ERR_NONE) goto finish; + /* "bls_bumpcounter " + id + "\n" */ + int bumpcounter_size = sizeof("bls_bumpcounter ") + grub_strlen(id) + 1; + bumpcounter = grub_malloc(bumpcounter_size); + if (!bumpcounter) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto finish; + } + char *tmp = bumpcounter; + tmp = grub_stpcpy(tmp, "bls_bumpcounter "); + tmp = grub_stpcpy(tmp, id); + tmp = grub_stpcpy(tmp, "\n"); + blsuki_save_default = grub_env_get_bool ("blsuki_save_default", false); - src = grub_xasprintf ("%s%s%s%s", + src = grub_xasprintf ("%s%s%s%s%s", blsuki_save_default ? "savedefault\n" : "", - linux_cmd, initrd_cmd ? initrd_cmd : "", + linux_cmd, bumpcounter ? bumpcounter : "", initrd_cmd ? initrd_cmd : "", dt_cmd ? dt_cmd : ""); grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, src, 0, entry, 0); @@ -1105,6 +1120,7 @@ grub_free (args); grub_free (argv); grub_free (src); + grub_free (bumpcounter); } #ifdef GRUB_MACHINE_EFI ++++++ grub2-bls-loader-config-timeout-fix.patch ++++++ Index: grub-2.14/grub-core/normal/menu.c =================================================================== --- grub-2.14.orig/grub-core/normal/menu.c +++ grub-2.14/grub-core/normal/menu.c @@ -46,6 +46,8 @@ { 0x4a67b082, 0x0a4c, 0x41cf, { 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f } } static grub_guid_t grub_efi_loader_guid = GRUB_EFI_LOADER_GUID; +#define GRUB_EFI_UINT_MAX 4294967295U + #define GRUB_EFI_LOADER_FEATURE_CONFIG_TIMEOUT (1 << 0) #define GRUB_EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT (1 << 1) #define GRUB_EFI_LOADER_FEATURE_ENTRY_DEFAULT (1 << 2) @@ -68,7 +70,8 @@ grub_err_t (*grub_gfxmenu_try_hook) (int enum timeout_style { TIMEOUT_STYLE_MENU, TIMEOUT_STYLE_COUNTDOWN, - TIMEOUT_STYLE_HIDDEN + TIMEOUT_STYLE_HIDDEN, + TIMEOUT_STYLE_DISABLED, }; struct timeout_style_name { @@ -78,6 +81,7 @@ struct timeout_style_name { {"menu", TIMEOUT_STYLE_MENU}, {"countdown", TIMEOUT_STYLE_COUNTDOWN}, {"hidden", TIMEOUT_STYLE_HIDDEN}, + {"disabled", TIMEOUT_STYLE_DISABLED}, {NULL, 0} }; @@ -858,59 +862,60 @@ run_menu (grub_menu_t menu, int nested, #ifdef GRUB_MACHINE_EFI if (val && (val[0] == '1' || val[0] == 'y')) { - int timeout_oneshot = -1; + unsigned long timeout_oneshot; + bool has_timeout_oneshot = false; /* https://systemd.io/BOOT_LOADER_INTERFACE/ */ char* loader_config_timeout_oneshot = get_efivar("LoaderConfigTimeoutOneShot"); - grub_dprintf("bls", "%s\n", loader_config_timeout_oneshot); if (loader_config_timeout_oneshot) { + /* Remove any value */ grub_efi_set_variable_to_string("LoaderConfigTimeoutOneShot", &grub_efi_loader_guid, "", 0); - timeout_oneshot = (int) grub_strtoul (loader_config_timeout_oneshot, 0, 0); + timeout_oneshot = grub_strtoul (loader_config_timeout_oneshot, 0, 0); if (grub_errno != GRUB_ERR_NONE) - { - grub_errno = GRUB_ERR_NONE; - timeout_oneshot = -1; - } + grub_errno = GRUB_ERR_NONE; /* We have a valid value */ - if (timeout_oneshot != -1) + else { if (timeout_oneshot == 0) - { - /* Wait indefinitely. In this case we want to set the timeout value to -1. - In this case unsetting timeout will have the same effect. */ - grub_env_unset ("timeout"); - } else { - grub_menu_set_timeout (timeout_oneshot); - } - grub_env_set("timeout_style", "menu"); + /* Wait indefinitely. */ + grub_env_unset ("timeout"); + else { + grub_menu_set_timeout (timeout_oneshot); + has_timeout_oneshot = true; + } + grub_env_set("timeout_style", "menu"); } grub_free(loader_config_timeout_oneshot); } - /* If there was no LoaderConfigTimeoutOneShot value or it was invalid + /* If there was no LoaderConfigTimeoutOneShot value or it was invalid, read LoaderConfigTimeout */ - if (timeout_oneshot == -1) + if (!has_timeout_oneshot) { char* loader_config_timeout = get_efivar("LoaderConfigTimeout"); if (loader_config_timeout) { if (grub_strcmp(loader_config_timeout, "menu-force") == 0) { - grub_env_unset ("timeout"); + grub_env_unset("timeout"); + grub_env_set("timeout_style", "menu"); } else if (grub_strcmp(loader_config_timeout, "0") == 0 || grub_strcmp(loader_config_timeout, "menu-hidden") == 0) { - grub_menu_set_timeout (1); + grub_menu_set_timeout (3); grub_env_set("timeout_style", "hidden"); } else if (grub_strcmp(loader_config_timeout, "menu-disabled") == 0) { - grub_menu_set_timeout (0); - grub_env_unset ("timeout"); + grub_env_set ("timeout", "0"); + grub_env_set("timeout_style", "disabled"); } else { - int loader_timeout = (int) grub_strtoul (loader_config_timeout, 0, 0); + unsigned long loader_timeout = grub_strtoul (loader_config_timeout, 0, 0); if (grub_errno == GRUB_ERR_NONE) { - grub_menu_set_timeout (loader_timeout); + if (loader_timeout == GRUB_EFI_UINT_MAX) + grub_env_unset("timeout"); + else + grub_menu_set_timeout (loader_timeout); grub_env_set("timeout_style", "menu"); } else { grub_errno = GRUB_ERR_NONE; @@ -986,10 +991,11 @@ run_menu (grub_menu_t menu, int nested, } /* If timeout is 0, drawing is pointless (and ugly). */ - if (timeout == 0) + if (timeout == 0 || timeout_style == TIMEOUT_STYLE_DISABLED) { *auto_boot = 1; - *notify_boot = timeout_style != TIMEOUT_STYLE_HIDDEN; + *notify_boot = timeout_style != TIMEOUT_STYLE_HIDDEN && + timeout_style != TIMEOUT_STYLE_DISABLED; return default_entry; } ++++++ grub2-bls-loader-config-timeout.patch ++++++ Add support for LoaderConfigTimeout and LoaderConfigTimeoutOneShot EFI variables. Index: grub-2.14~rc1/grub-core/normal/menu.c =================================================================== --- grub-2.14~rc1.orig/grub-core/normal/menu.c +++ grub-2.14~rc1/grub-core/normal/menu.c @@ -756,34 +756,38 @@ workaround_snapshot_menu_default_entry ( } #ifdef GRUB_MACHINE_EFI -static int -get_entry_from_efivar(grub_menu_t menu, const char* efivar) +static char* +get_efivar(const char* efivar) { grub_efi_status_t status; - grub_size_t entry_size; - grub_efi_char16_t *entry_efi = NULL; - char *entry_name = NULL; - int entry_index = -1; + grub_size_t size; + grub_efi_char16_t *val_efi = NULL; + char *val = NULL; status = grub_efi_get_variable(efivar, - &grub_efi_loader_guid, - &entry_size, - (void**) &entry_efi); - if (status == GRUB_EFI_SUCCESS) + &grub_efi_loader_guid, + &size, + (void**) &val_efi); + if (status == GRUB_EFI_SUCCESS && size != 0) { - grub_efi_char16_t *src = entry_efi; - int size = 0; - while (*src++) - size++; - if (size != 0) - { - entry_name = grub_malloc (size * sizeof (char)); - grub_utf16_to_utf8 ((grub_uint8_t*) entry_name, - (grub_uint16_t*) entry_efi, size); - entry_index = search_entry (menu, entry_name); - } + val = grub_malloc (size * sizeof (char)); + grub_utf16_to_utf8 ((grub_uint8_t*) val, + (grub_uint16_t*) val_efi, size); } - grub_free(entry_name); - return entry_index; + return val; +} + +static int +get_entry_from_efivar(grub_menu_t menu, const char* efivar) +{ + char* entry_name = get_efivar(efivar); + if (entry_name) + { + int idx = search_entry (menu, entry_name); + grub_free(entry_name); + return idx; + } else { + return -1; + } } #endif @@ -814,10 +818,13 @@ run_menu (grub_menu_t menu, int nested, { static long loader_features = + GRUB_EFI_LOADER_FEATURE_CONFIG_TIMEOUT | + GRUB_EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT | GRUB_EFI_LOADER_FEATURE_ENTRY_DEFAULT | GRUB_EFI_LOADER_FEATURE_ENTRY_ONESHOT | GRUB_EFI_LOADER_FEATURE_BOOT_COUNTING | - GRUB_EFI_LOADER_FEATURE_XBOOTLDR; + GRUB_EFI_LOADER_FEATURE_XBOOTLDR | + GRUB_EFI_LOADER_FEATURE_MENU_DISABLE; grub_efi_set_variable("LoaderFeatures", &grub_efi_loader_guid, &loader_features, sizeof(long)); @@ -845,6 +852,72 @@ run_menu (grub_menu_t menu, int nested, if (default_entry < 0 || default_entry >= menu->size) default_entry = 0; +#ifdef GRUB_MACHINE_EFI + if (val && (val[0] == '1' || val[0] == 'y')) + { + int timeout_oneshot = -1; + /* https://systemd.io/BOOT_LOADER_INTERFACE/ */ + char* loader_config_timeout_oneshot = get_efivar("LoaderConfigTimeoutOneShot"); + grub_dprintf("bls", "%s\n", loader_config_timeout_oneshot); + if (loader_config_timeout_oneshot) + { + grub_efi_set_variable_to_string("LoaderConfigTimeoutOneShot", + &grub_efi_loader_guid, "", 0); + timeout_oneshot = (int) grub_strtoul (loader_config_timeout_oneshot, 0, 0); + if (grub_errno != GRUB_ERR_NONE) + { + grub_errno = GRUB_ERR_NONE; + timeout_oneshot = -1; + } + /* We have a valid value */ + if (timeout_oneshot != -1) + { + if (timeout_oneshot == 0) + { + /* Wait indefinitely. In this case we want to set the timeout value to -1. + In this case unsetting timeout will have the same effect. */ + grub_env_unset ("timeout"); + } else { + grub_menu_set_timeout (timeout_oneshot); + } + grub_env_set("timeout_style", "menu"); + } + grub_free(loader_config_timeout_oneshot); + } + /* If there was no LoaderConfigTimeoutOneShot value or it was invalid + read LoaderConfigTimeout */ + if (timeout_oneshot == -1) + { + char* loader_config_timeout = get_efivar("LoaderConfigTimeout"); + if (loader_config_timeout) + { + if (grub_strcmp(loader_config_timeout, "menu-force") == 0) + { + grub_env_unset ("timeout"); + } else if (grub_strcmp(loader_config_timeout, "0") == 0 || + grub_strcmp(loader_config_timeout, "menu-hidden") == 0) + { + grub_menu_set_timeout (1); + grub_env_set("timeout_style", "hidden"); + } else if (grub_strcmp(loader_config_timeout, "menu-disabled") == 0) + { + grub_menu_set_timeout (0); + grub_env_unset ("timeout"); + } else { + int loader_timeout = (int) grub_strtoul (loader_config_timeout, 0, 0); + if (grub_errno == GRUB_ERR_NONE) + { + grub_menu_set_timeout (loader_timeout); + grub_env_set("timeout_style", "menu"); + } else { + grub_errno = GRUB_ERR_NONE; + } + } + grub_free(loader_config_timeout); + } + } + } +#endif timeout = grub_menu_get_timeout (); if (timeout < 0) /* If there is no timeout, the "countdown" and "hidden" styles result in ++++++ grub2-bls-loader-entries-boot-counting.patch ++++++ Index: grub-2.14~rc1/grub-core/commands/blsuki.c =================================================================== --- grub-2.14~rc1.orig/grub-core/commands/blsuki.c +++ grub-2.14~rc1/grub-core/commands/blsuki.c @@ -1565,7 +1565,10 @@ blsuki_create_entries (bool show_default #ifdef GRUB_MACHINE_EFI grub_size_t len = grub_strlen (entry->filename); - if (len > BLS_EXT_LEN && grub_strcmp (entry->filename + len - BLS_EXT_LEN, ".conf") == 0) + char *boot_counting_start = grub_strrchr(entry->filename, '+'); + if (boot_counting_start != NULL) + size += (boot_counting_start - entry->filename + 1); + else if (len > BLS_EXT_LEN && grub_strcmp (entry->filename + len - BLS_EXT_LEN, ".conf") == 0) size += (len - BLS_EXT_LEN + 1); else size += (len + 1); @@ -1588,7 +1591,10 @@ blsuki_create_entries (bool show_default { grub_size_t len = grub_strlen (entry->filename); - if (len > BLS_EXT_LEN && grub_strcmp (entry->filename + len - BLS_EXT_LEN, ".conf") == 0) + char *boot_counting_start = grub_strrchr(entry->filename, '+'); + if (boot_counting_start != NULL) + len = boot_counting_start - entry->filename; + else if (len > BLS_EXT_LEN && grub_strcmp (entry->filename + len - BLS_EXT_LEN, ".conf") == 0) len -= BLS_EXT_LEN; if (r_size < (len + 1)) ++++++ grub2-bls-loader-entry-default.patch ++++++ Factoring out get_entry_from_efivar helper to reduce code duplication and add support for LoaderEntryDefault. Index: grub-2.12/grub-core/normal/menu.c =================================================================== --- grub-2.12.orig/grub-core/normal/menu.c +++ grub-2.12/grub-core/normal/menu.c @@ -741,6 +741,38 @@ workaround_snapshot_menu_default_entry ( return; } +#ifdef GRUB_MACHINE_EFI +static int +get_entry_from_efivar(grub_menu_t menu, const char* efivar) +{ + grub_efi_status_t status; + grub_size_t entry_size; + grub_efi_char16_t *entry_efi = NULL; + char *entry_name = NULL; + int entry_index = -1; + status = grub_efi_get_variable(efivar, + &grub_efi_loader_guid, + &entry_size, + (void**) &entry_efi); + if (status == GRUB_EFI_SUCCESS) + { + grub_efi_char16_t *src = entry_efi; + int size = 0; + while (*src++) + size++; + if (size != 0) + { + entry_name = grub_malloc (size * sizeof (char)); + grub_utf16_to_utf8 ((grub_uint8_t*) entry_name, + (grub_uint16_t*) entry_efi, size); + entry_index = search_entry (menu, entry_name); + } + } + grub_free(entry_name); + return entry_index; +} +#endif + #define GRUB_MENU_PAGE_SIZE 10 /* Show the menu and handle menu entry selection. Returns the menu entry @@ -766,36 +798,19 @@ run_menu (grub_menu_t menu, int nested, const char* val = grub_env_get ("enable_blscfg"); if (val && (val[0] == '1' || val[0] == 'y')) { - grub_efi_status_t status; - int oneshot_entry_index; - grub_efi_char16_t *oneshot_entry_efi = NULL; - char *oneshot_entry = NULL; - grub_size_t oneshot_entry_size; - status = grub_efi_get_variable("LoaderEntryOneShot", - &grub_efi_loader_guid, - &oneshot_entry_size, - (void**) &oneshot_entry_efi); - if (status == GRUB_EFI_SUCCESS) + int oneshot_entry, default_entry_efi; + oneshot_entry = get_entry_from_efivar(menu, "LoaderEntryOneShot"); + if (oneshot_entry != -1) { - grub_efi_char16_t *src = oneshot_entry_efi; - int size = 0; - while (*src++) - size++; - if (size == 0) + default_entry = oneshot_entry; + grub_efi_set_variable_to_string("LoaderEntryOneShot", + &grub_efi_loader_guid, "", 0); + } else { + default_entry_efi = get_entry_from_efivar(menu, "LoaderEntryDefault"); + if (default_entry_efi != -1) { - oneshot_entry = grub_malloc (size * sizeof (char)); - grub_utf16_to_utf8 ((grub_uint8_t*) oneshot_entry, - (grub_uint16_t*) oneshot_entry_efi, size); - oneshot_entry_index = search_entry (menu, oneshot_entry); - if (oneshot_entry_index != -1) - { - default_entry = oneshot_entry_index; - grub_efi_set_variable_to_string("LoaderEntryOneShot", - &grub_efi_loader_guid, "", 0); - } - grub_free(oneshot_entry); + default_entry = default_entry_efi; } - grub_free(oneshot_entry_efi); } } #endif ++++++ grub2-bls-loader-entry-oneshot.patch ++++++ Add support for LoaderEntryOneshot EFI variable --- a/grub-core/normal/menu.c +++ b/grub-core/normal/menu.c @@ -37,6 +37,16 @@ #ifdef GRUB_MACHINE_IEEE1275 #include <grub/ieee1275/ieee1275.h> #endif +#ifdef GRUB_MACHINE_EFI +#include <grub/charset.h> +#include <grub/efi/api.h> +#include <grub/efi/efi.h> + +#define GRUB_EFI_LOADER_GUID \ + { 0x4a67b082, 0x0a4c, 0x41cf, { 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f } } +static grub_guid_t grub_efi_loader_guid = GRUB_EFI_LOADER_GUID; + +#endif /* Time to delay after displaying an error message about a default/fallback entry failing to boot. */ @@ -563,6 +573,62 @@ } } +/* Search for an entry in the menu by its name */ +static int +search_entry (grub_menu_t menu, const char* name) +{ + /* See if the variable matches the title of a menu entry. */ + grub_menu_entry_t e = menu->entry_list; + int i; + int entry; + + /* Skip hidden entries */ + while (e && e->hidden) + e = e->next; + + grub_errno = GRUB_ERR_NONE; + + for (i = 0; e; i++) + { + if (menuentry_eq (e->title, name) + || menuentry_eq (e->id, name)) + { + entry = i; + break; + } + + if (e->blsuki) + { + char *v, *ext; + + if ((v = grub_strdup (name)) && + (ext = grub_strrchr (v, '.')) && + grub_strcmp (ext, ".conf") == 0) + { + *ext = '\0'; + if (menuentry_eq (e->id, v)) + { + entry = i; + grub_free (v); + break; + } + } + grub_free (v); + } + + e = e->next; + + /* Skip hidden entries */ + while (e && e->hidden) + e = e->next; + } + + if (! e) + entry = -1; + + return entry; +} + /* Get the entry number from the variable NAME. */ static int @@ -581,53 +647,7 @@ if (grub_errno == GRUB_ERR_BAD_NUMBER) { - /* See if the variable matches the title of a menu entry. */ - grub_menu_entry_t e = menu->entry_list; - int i; - - /* Skip hidden entries */ - while (e && e->hidden) - e = e->next; - - grub_errno = GRUB_ERR_NONE; - - for (i = 0; e; i++) - { - if (menuentry_eq (e->title, val) - || menuentry_eq (e->id, val)) - { - entry = i; - break; - } - - if (e->bls) - { - char *v, *ext; - - if ((v = grub_strdup (val)) && - (ext = grub_strrchr (v, '.')) && - grub_strcmp (ext, ".conf") == 0) - { - *ext = '\0'; - if (menuentry_eq (e->id, v)) - { - entry = i; - grub_free (v); - break; - } - } - grub_free (v); - } - - e = e->next; - - /* Skip hidden entries */ - while (e && e->hidden) - e = e->next; - } - - if (! e) - entry = -1; + entry = search_entry (menu, val); } if (grub_errno != GRUB_ERR_NONE) @@ -725,6 +745,45 @@ default_entry = get_entry_number (menu, "default"); +#ifdef GRUB_MACHINE_EFI + /* verify we are using blscfg */ + const char* val = grub_env_get ("enable_blscfg"); + if (val && (val[0] == '1' || val[0] == 'y')) + { + grub_efi_status_t status; + int oneshot_entry_index; + grub_efi_char16_t *oneshot_entry_efi = NULL; + char *oneshot_entry = NULL; + grub_size_t oneshot_entry_size; + status = grub_efi_get_variable("LoaderEntryOneShot", + &grub_efi_loader_guid, + &oneshot_entry_size, + (void**) &oneshot_entry_efi); + if (status == GRUB_EFI_SUCCESS) + { + grub_efi_char16_t *src = oneshot_entry_efi; + int size = 0; + while (*src++) + size++; + if (size == 0) + { + oneshot_entry = grub_malloc (size * sizeof (char)); + grub_utf16_to_utf8 ((grub_uint8_t*) oneshot_entry, + (grub_uint16_t*) oneshot_entry_efi, size); + oneshot_entry_index = search_entry (menu, oneshot_entry); + if (oneshot_entry_index != -1) + { + default_entry = oneshot_entry_index; + grub_efi_set_variable_to_string("LoaderEntryOneShot", + &grub_efi_loader_guid, "", 0); + } + grub_free(oneshot_entry); + } + grub_free(oneshot_entry_efi); + } + } +#endif + workaround_snapshot_menu_default_entry (menu, "default", &default_entry); /* If DEFAULT_ENTRY is not within the menu entries, fall back to ++++++ grub2-bls-loader-features.patch ++++++ Add support for LoaderFeatures EFI variable. Index: grub-2.14~rc1/grub-core/normal/menu.c =================================================================== --- grub-2.14~rc1.orig/grub-core/normal/menu.c +++ grub-2.14~rc1/grub-core/normal/menu.c @@ -46,6 +46,14 @@ { 0x4a67b082, 0x0a4c, 0x41cf, { 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f } } static grub_guid_t grub_efi_loader_guid = GRUB_EFI_LOADER_GUID; +#define GRUB_EFI_LOADER_FEATURE_CONFIG_TIMEOUT (1 << 0) +#define GRUB_EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT (1 << 1) +#define GRUB_EFI_LOADER_FEATURE_ENTRY_DEFAULT (1 << 2) +#define GRUB_EFI_LOADER_FEATURE_ENTRY_ONESHOT (1 << 3) +#define GRUB_EFI_LOADER_FEATURE_BOOT_COUNTING (1 << 4) +#define GRUB_EFI_LOADER_FEATURE_XBOOTLDR (1 << 5) +#define GRUB_EFI_LOADER_FEATURE_MENU_DISABLE (1 << 13) + #endif /* Time to delay after displaying an error message about a default/fallback @@ -805,6 +813,15 @@ run_menu (grub_menu_t menu, int nested, const char* val = grub_env_get ("enable_blscfg"); if (val && (val[0] == '1' || val[0] == 'y')) { + + static long loader_features = + GRUB_EFI_LOADER_FEATURE_ENTRY_DEFAULT | + GRUB_EFI_LOADER_FEATURE_ENTRY_ONESHOT | + GRUB_EFI_LOADER_FEATURE_BOOT_COUNTING | + GRUB_EFI_LOADER_FEATURE_XBOOTLDR; + + grub_efi_set_variable("LoaderFeatures", &grub_efi_loader_guid, &loader_features, sizeof(long)); + int oneshot_entry, default_entry_efi; oneshot_entry = get_entry_from_efivar(menu, "LoaderEntryOneShot"); if (oneshot_entry != -1) ++++++ grub2-blsbumpcounter-menu.patch ++++++ Call the command bls_bumpcounter when an entry is executed. Adding the bls_bumpcounter command in the list of commands generated by blscfg breaks FDE, as each command is logged in the PCR. Do not unset `enable_blscfg` grub env var, otherwise bls_bumpcounter would not be called. --- a/grub-core/normal/menu.c +++ b/grub-core/normal/menu.c @@ -332,6 +332,29 @@ grub_env_set ("default", ptr + 1); else grub_env_unset ("default"); + +#ifdef GRUB_MACHINE_EFI + const char* val = grub_env_get ("enable_blscfg"); + if (val && (val[0] == '1' || val[0] == 'y') && entry->blsuki != NULL) + { + char* id = grub_strdup (entry->blsuki->filename); + + if (id == NULL) + grub_print_error (); + else + { + char* args[] = { id }; + grub_size_t id_len = grub_strlen (id); + + if (id_len >= 4 && grub_strcmp (id + id_len - 4, ".conf") == 0) + id[id_len - 4] = '\0'; + grub_command_execute ("bls_bumpcounter", 1, args); + grub_free (id); + } + } + grub_env_unset ("enable_blscfg"); +#endif + #ifdef GRUB_MACHINE_IEEE1275 char *cas_entry_id = NULL; char *cas_entry_source; --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -356,7 +356,6 @@ /* Ignore any error. */ grub_errno = GRUB_ERR_NONE; /* unset to let configfile and source commands continue to work */ - grub_env_unset ("enable_blscfg"); goto check_batch; } #endif --- a/grub-core/commands/blsuki.c +++ b/grub-core/commands/blsuki.c @@ -1094,8 +1094,6 @@ grub_size_t size; bool blsuki_save_default; - char *bumpcounter = NULL; - linux_path = blsuki_get_val (entry, "linux", NULL); if (linux_path == NULL) { @@ -1156,23 +1154,10 @@ if (grub_errno != GRUB_ERR_NONE) goto finish; - /* "bls_bumpcounter " + id + "\n" */ - int bumpcounter_size = sizeof("bls_bumpcounter ") + grub_strlen(id) + 1; - bumpcounter = grub_malloc(bumpcounter_size); - if (!bumpcounter) - { - grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); - goto finish; - } - char *tmp = bumpcounter; - tmp = grub_stpcpy(tmp, "bls_bumpcounter "); - tmp = grub_stpcpy(tmp, id); - tmp = grub_stpcpy(tmp, "\n"); - blsuki_save_default = grub_env_get_bool ("blsuki_save_default", false); - src = grub_xasprintf ("%s%s%s%s%s", + src = grub_xasprintf ("%s%s%s%s", blsuki_save_default ? "savedefault\n" : "", - linux_cmd, bumpcounter ? bumpcounter : "", initrd_cmd ? initrd_cmd : "", + 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, 0); @@ -1186,7 +1171,6 @@ grub_free (args); grub_free (argv); grub_free (src); - grub_free (bumpcounter); } #ifdef GRUB_MACHINE_EFI ++++++ grub2-blscfg-set-efivars.patch ++++++ Set the EFI variables LoaderEntries and LoaderEntrySelected to follow systemd-boot implementation and make bootctl work. --- a/grub-core/commands/efi/blsbumpcounter.c +++ b/grub-core/commands/efi/blsbumpcounter.c @@ -60,8 +60,14 @@ /* Look for the start of the count If no '+' symbol has been found, the boot counting isn't enabled for the selected entry */ + char* new_path = grub_xasprintf ("%s.conf", id); + grub_efi_set_variable_to_string("LoaderEntrySelected", &grub_efi_loader_guid, new_path, + GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS | + GRUB_EFI_VARIABLE_RUNTIME_ACCESS); + grub_free(new_path); if (grub_strrchr(id, '+') == NULL) { grub_dprintf("bls_bumpcounter", "boot counting is not in effect for id %s\n", id); + return GRUB_ERR_NONE; } @@ -183,7 +189,6 @@ goto finish; } - char *new_path; if (tries == -1) { /* This is the first try, rename accordingly */ new_path = grub_xasprintf ("%s+%d-1.conf", id, tries_left); --- a/grub-core/commands/blsuki.c +++ b/grub-core/commands/blsuki.c @@ -45,6 +45,14 @@ #define GRUB_BOOT_DEVICE "" #endif +#ifdef GRUB_MACHINE_EFI +#include <grub/efi/efi.h> +#define GRUB_EFI_LOADER_GUID \ + { 0x4a67b082, 0x0a4c, 0x41cf, { 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f } } + +static grub_guid_t grub_efi_loader_guid = GRUB_EFI_LOADER_GUID; +#endif + GRUB_MOD_LICENSE ("GPLv3+"); #define GRUB_BLS_CONFIG_PATH "/loader/entries/" @@ -1537,6 +1545,12 @@ const char *def_entry = NULL; grub_blsuki_entry_t *entry = NULL; int idx = 0; +#ifdef GRUB_MACHINE_EFI + grub_size_t size = 0, r_size; + grub_efi_char16_t *efi_entries = NULL; + grub_efi_char16_t *p = NULL; + char* tmp = NULL; +#endif def_entry = grub_env_get ("default"); @@ -1558,11 +1572,58 @@ uki_create_entry (entry); #endif entry->visible = true; +#ifdef GRUB_MACHINE_EFI + grub_size_t len = grub_strlen (entry->filename); + + if (len > BLS_EXT_LEN && grub_strcmp (entry->filename + len - BLS_EXT_LEN, ".conf") == 0) + size += (len - BLS_EXT_LEN + 1); + else + size += (len + 1); +#endif } idx++; } +#ifdef GRUB_MACHINE_EFI + efi_entries = grub_malloc (size * sizeof (grub_efi_char16_t)); + if (efi_entries == NULL) + return grub_errno; + + p = efi_entries; + r_size = size; + FOR_BLSUKI_ENTRIES (entry) + { + if (entry->visible) + { + grub_size_t len = grub_strlen (entry->filename); + + if (len > BLS_EXT_LEN && grub_strcmp (entry->filename + len - BLS_EXT_LEN, ".conf") == 0) + len -= BLS_EXT_LEN; + + if (r_size < (len + 1)) + { + grub_dprintf ("blsuki", "LoaderEntries buffer too small\n"); + break; + } + + r_size -= (len + 1); + tmp = entry->filename; + while (len) + { + *p++ = (grub_efi_char16_t) *tmp++; + len--; + } + *p++ = (grub_efi_char16_t) '\0'; + } + } + grub_efi_set_variable_with_attributes ("LoaderEntries", &grub_efi_loader_guid, + efi_entries, (size - r_size) * sizeof (grub_efi_char16_t), + GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS | + GRUB_EFI_VARIABLE_RUNTIME_ACCESS); + grub_free (efi_entries); +#endif + return GRUB_ERR_NONE; } ++++++ grub2-btrfs-01-add-ability-to-boot-from-subvolumes.patch ++++++ --- /var/tmp/diff_new_pack.lBGNPX/_old 2026-04-22 17:13:01.905267991 +0200 +++ /var/tmp/diff_new_pack.lBGNPX/_new 2026-04-22 17:13:01.913268322 +0200 @@ -25,9 +25,6 @@ * Fix gcc-12 error: the comparison will always evaluate as 'true' for the address of 'label' will never be NULL [-Werror=address] -v3: - * Check find_pathname() in grub_cmd_btrfs_list_subvols() and return an error - on failure --- grub-core/fs/btrfs.c | 561 +++++++++++++++++++++++++++++++++++++++++++++++++-- @@ -375,7 +372,7 @@ grub_btrfs_unmount (data); -@@ -2431,6 +2638,253 @@ +@@ -2431,6 +2638,248 @@ } #endif @@ -581,12 +578,7 @@ + } + buf[elemsize] = 0; + -+ err = find_pathname(data, ref->dirid, fs_root, ref->name, &p); -+ if (err) -+ { -+ r = -err; -+ break; -+ } ++ find_pathname(data, ref->dirid, fs_root, ref->name, &p); + + if (print) + { @@ -629,7 +621,7 @@ static struct grub_fs grub_btrfs_fs = { .name = "btrfs", .fs_dir = grub_btrfs_dir, -@@ -2446,13 +2900,89 @@ +@@ -2446,13 +2895,89 @@ #endif }; ++++++ grub2-btrfs-09-get-default-subvolume.patch ++++++ --- /var/tmp/diff_new_pack.lBGNPX/_old 2026-04-22 17:13:01.965270475 +0200 +++ /var/tmp/diff_new_pack.lBGNPX/_new 2026-04-22 17:13:01.973270806 +0200 @@ -3,15 +3,9 @@ * Use overflow checking primitives where the arithmetic expression for buffer allocations may include unvalidated data -v2: - * Check find_pathname() in grub_btrfs_get_parent_subvol_path() and return - an error on failure - * Use get_fs_root() with offset -1 in grub_btrfs_get_parent_subvol_path() so - lower_bound() can find the highest-offset ROOT_ITEM for the subvolume id - --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c -@@ -3146,6 +3146,260 @@ +@@ -3104,6 +3104,254 @@ return 0; } @@ -76,7 +70,7 @@ + ref = (struct grub_btrfs_root_ref *)buf; + + err = get_fs_root(data, data->sblock.root_tree, grub_le_to_cpu64 (key_out.offset), -+ -1, &fs_root); ++ 0, &fs_root); + if (err) + { + grub_free(buf); @@ -84,13 +78,7 @@ + return err; + } + -+ err = find_pathname(data, grub_le_to_cpu64 (ref->dirid), fs_root, ref->name, &parent_path); -+ if (err) -+ { -+ grub_free(buf); -+ free_iterator(&desc); -+ return err; -+ } ++ find_pathname(data, grub_le_to_cpu64 (ref->dirid), fs_root, ref->name, &parent_path); + + if (child_path) + { @@ -272,7 +260,7 @@ static struct grub_fs grub_btrfs_fs = { .name = "btrfs", .fs_dir = grub_btrfs_dir, -@@ -3164,6 +3418,7 @@ +@@ -3122,6 +3370,7 @@ static grub_command_t cmd_info; static grub_command_t cmd_mount_subvol; static grub_extcmd_t cmd_list_subvols; @@ -280,7 +268,7 @@ static char * subvolid_set_env (struct grub_env_var *var __attribute__ ((unused)), -@@ -3235,6 +3490,11 @@ +@@ -3192,6 +3441,11 @@ "[-p|-n] [-o var] DEVICE", "Print list of BtrFS subvolumes on " "DEVICE.", options);
