File grub2-bls-boot-counting.patch of Package grub2
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/
Index: grub-2.12/include/grub/efi/filesystem.h
===================================================================
--- /dev/null
+++ grub-2.12/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
Index: grub-2.12/grub-core/commands/blscfg.c
===================================================================
--- grub-2.12.orig/grub-core/commands/blscfg.c
+++ grub-2.12/grub-core/commands/blscfg.c
@@ -791,6 +791,8 @@ static void create_entry (struct bls_ent
int i, index;
bool add_dt_prefix = false;
+ char *bumpcounter = NULL;
+
grub_dprintf("blscfg", "%s got here\n", __func__);
clinux = bls_get_val (entry, "linux", NULL);
if (!clinux)
@@ -949,6 +951,19 @@ static void create_entry (struct bls_ent
grub_free(prefix);
}
+ /* "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");
+
grub_dprintf ("blscfg2", "devicetree %s for id:\"%s\"\n", dt, id);
const char *sdval = grub_env_get("save_default");
@@ -961,7 +976,7 @@ static void create_entry (struct bls_ent
"insmod gzio\n"
"linux %s%s%s%s\n"
#endif
- "%s%s",
+ "%s%s%s",
savedefault ? "savedefault\n" : "",
#ifdef GRUB_MACHINE_EMU
separate_boot ? GRUB_BOOT_DEVICE : "",
@@ -969,7 +984,8 @@ static void create_entry (struct bls_ent
bootdev,
#endif
clinux, options ? " " : "", options ? options : "",
- initrd ? initrd : "", dt ? dt : "");
+ bumpcounter ? bumpcounter : "", initrd ? initrd : "",
+ dt ? dt : "");
grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, src, 0, 0, &index, entry);
grub_dprintf ("blscfg", "Added entry %d id:\"%s\"\n", index, id);
@@ -987,6 +1003,7 @@ finish:
grub_free (argv);
grub_free (src);
grub_free (bootdev);
+ grub_free (bumpcounter);
}
struct find_entry_info {
Index: grub-2.12/grub-core/commands/blsbumpcounter.c
===================================================================
--- /dev/null
+++ grub-2.12/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 UNUSED,
+ int argc UNUSED, char **args 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);
+}
Index: grub-2.12/grub-core/Makefile.core.def
===================================================================
--- grub-2.12.orig/grub-core/Makefile.core.def
+++ grub-2.12/grub-core/Makefile.core.def
@@ -858,6 +858,13 @@ module = {
};
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;
Index: grub-2.12/grub-core/commands/efi/blsbumpcounter.c
===================================================================
--- /dev/null
+++ grub-2.12/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 UNUSED,
+ int argc UNUSED, char **args UNUSED)
+{
+ 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);
+}