File 0005-bls_bumpcounter-Add-command-to-bump-boot-counter-for.patch of Package grub2
From be368dac082a780f75a8ad243d7f19fad37bed9d Mon Sep 17 00:00:00 2001
From: Danilo Spinella <danilo.spinella@suse.com>
Date: Thu, 12 Mar 2026 10:51:06 +0100
Subject: [PATCH 5/7] bls_bumpcounter: Add command to bump boot counter for BLS
entries
---
grub-core/Makefile.core.def | 6 +
grub-core/commands/bls_bumpcounter.c | 275 +++++++++++++++++++++++++++
grub-core/normal/menu.c | 23 +++
include/grub/efi/filesystem.h | 155 +++++++++++++++
4 files changed, 459 insertions(+)
create mode 100644 grub-core/commands/bls_bumpcounter.c
create mode 100644 include/grub/efi/filesystem.h
Index: grub-2.14/grub-core/Makefile.core.def
===================================================================
--- grub-2.14.orig/grub-core/Makefile.core.def
+++ grub-2.14/grub-core/Makefile.core.def
@@ -868,6 +868,12 @@ module = {
cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB)';
};
+ module = {
+ name = bls_bumpcounter;
+ efi = commands/bls_bumpcounter.c;
+ enable = efi;
+};
+
module = {
name = boot;
common = commands/boot.c;
Index: grub-2.14/grub-core/commands/bls_bumpcounter.c
===================================================================
--- /dev/null
+++ grub-2.14/grub-core/commands/bls_bumpcounter.c
@@ -0,0 +1,275 @@
+/* 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/err.h"
+#include <grub/charset.h>
+#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\\"
+
+static const grub_guid_t grub_simple_file_system_guid =
+ GRUB_EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
+static const grub_guid_t bli_vendor_guid =
+ GRUB_EFI_VENDOR_BOOT_LOADER_INTERFACE_GUID;
+
+/* Replace linux path delimiter (/) with EFI compatible (\) */
+static void
+use_efi_delimiter_for_path (char* s)
+{
+ char* tmp = grub_strchr(s, '/');
+ while (tmp != NULL)
+ {
+ *tmp++ = '\\';
+ tmp = grub_strchr(tmp, '/');
+ }
+}
+
+/* Calculate the new filename with the bumped counter */
+static char*
+bump_counter (char* entry)
+{
+ char *tmp = grub_strrchr (entry, '+');
+ int tries = -1, tries_left;
+ *tmp = '\0';
+ 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 {
+ return NULL;
+ }
+
+ if (tries == -1)
+ {
+ /* This is the first try, rename accordingly */
+ return grub_xasprintf ("%s+%d-1.conf", entry, tries_left);
+ } else {
+ return grub_xasprintf ("%s+%d-%d.conf", entry, tries_left, tries);
+ }
+}
+
+static grub_err_t
+grub_cmd_bls_bumpcounter (grub_extcmd_context_t ctxt __attribute__ ((unused)),
+ int argc, char **args)
+{
+ grub_efi_loaded_image_t *image;
+ grub_efi_file_info_t *file_info = NULL;
+ grub_efi_file_protocol_t *handle, *root;
+ grub_efi_simple_file_system_protocol_t *volume;
+ grub_efi_boot_services_t *bs;
+ grub_guid_t grub_efi_file_info_guid = GRUB_EFI_FILE_INFO_ID;
+ grub_efi_status_t err;
+ char *entry, *new_path = NULL, *old_path;
+ const char *blsdir;
+ grub_efi_uint64_t size;
+ grub_efi_char16_t *old_path16;
+
+ if (argc != 1) {
+ grub_dprintf ("bls_bumpcounter", "only one argument should be passed\n");
+ return GRUB_ERR_BAD_ARGUMENT;
+ }
+ entry = args[0];
+
+ grub_efi_set_variable_to_string ("LoaderEntrySelected",
+ &bli_vendor_guid, entry,
+ GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS
+ | GRUB_EFI_VARIABLE_RUNTIME_ACCESS);
+
+ /* Look for the start of the boot count.
+ If no '+' symbol has been found, the boot counting isn't enabled for
+ the selected entry */
+ if (grub_strrchr (entry, '+') == NULL)
+ {
+ grub_dprintf ("bls_bumpcounter",
+ "boot counting is not in effect for %s\n", entry);
+ return GRUB_ERR_NONE;
+ }
+
+ image = grub_efi_get_loaded_image (grub_efi_image_handle);
+
+ if (!image)
+ {
+ grub_dprintf ("bls_bumpcounter", "grub_efi_get_loaded_image failed\n");
+ return GRUB_ERR_BUG;
+ }
+
+ 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;
+ }
+
+ blsdir = grub_env_get ("blsdir");
+ if (blsdir)
+ {
+ use_efi_delimiter_for_path ((char*) blsdir);
+ } else {
+ blsdir = GRUB_BLS_CONFIG_PATH;
+ }
+
+ old_path = grub_xasprintf ("%s%s.conf", blsdir, entry);
+ grub_utf8_to_utf16_alloc (old_path, &old_path16, NULL);
+
+ err = root->Open (root, &handle, old_path16,
+ GRUB_EFI_FILE_MODE_READ|GRUB_EFI_FILE_MODE_WRITE, 0);
+ grub_free (old_path16);
+ if (err != GRUB_EFI_SUCCESS)
+ {
+ grub_dprintf("bls_bumpcounter", "Cannot open the entry %s: %lu\n",
+ old_path, (unsigned long)err);
+ grub_free (old_path);
+ return GRUB_ERR_BAD_DEVICE;
+ }
+ grub_free (old_path);
+
+ /* Get the file_info by allocating a fixed size that should be big enough for
+ most cases, and, if it fails, allocate a new one with the exact size
+ returned from GetInfo.
+ Reference: systemd:src/boot/efi/util.c =*/
+ 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;
+ }
+ err = handle->GetInfo (handle, &grub_efi_file_info_guid, &size, file_info);
+ if (err == GRUB_EFI_BUFFER_TOO_SMALL)
+ {
+ grub_free(file_info);
+ /* When we bump the counter, it might happen that we increase the filename
+ length. Add the space for 2 more characters. (i.e. adding the boot done
+ part "-1") */
+ size += 2;
+ 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", err);
+ goto finish;
+ }
+
+ new_path = bump_counter (entry);
+ if (!new_path)
+ goto finish;
+
+ grub_dprintf ("bls_bumpcounter", "renaming entry to %s\n", new_path);
+ grub_utf8_to_utf16 (file_info->FileName,
+ size - offsetof (grub_efi_file_info_t, FileName),
+ (grub_uint8_t*) new_path, grub_strlen (new_path), NULL);
+
+ 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", &bli_vendor_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_BUG;
+}
+
+
+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_bls_bumpcounter,
+ 0,
+ NULL,
+ N_("Bump the boot entry counting."),
+ NULL);
+}
+
+GRUB_MOD_FINI(blsbumpcounter)
+{
+ grub_unregister_extcmd (cmd);
+}
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
@@ -231,6 +231,10 @@ grub_menu_execute_entry(grub_menu_entry_
char *optr, *buf, *oldchosen = NULL, *olddefault = NULL;
const char *ptr, *chosen, *def;
grub_size_t sz = 0;
+#ifdef GRUB_MACHINE_EFI
+ char *args[] = { NULL };
+ char *filename;
+#endif
if (entry->restricted)
{
@@ -324,6 +328,26 @@ grub_menu_execute_entry(grub_menu_entry_
grub_env_set ("default", ptr + 1);
else
grub_env_unset ("default");
+
+#ifdef GRUB_MACHINE_EFI
+ /* Check if this entry has been generated by blscfg */
+ if (entry->blsuki && entry->blsuki->filename)
+ {
+ filename = grub_strdup (entry->blsuki->filename);
+
+ if (filename == NULL)
+ grub_print_error ();
+ else
+ {
+ *args = filename;
+
+ /* Bump the boot counter of the BLS entry */
+ grub_command_execute ("bls_bumpcounter", 1, args);
+ grub_free (filename);
+ }
+ }
+#endif
+
#ifdef GRUB_MACHINE_IEEE1275
char *cas_entry_id = NULL;
char *cas_entry_source;
Index: grub-2.14/include/grub/efi/filesystem.h
===================================================================
--- /dev/null
+++ grub-2.14/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