* [PATCH v2 1/4] efi: Load env block from GRUB_ENV EFI variable in early init
2024-09-23 6:04 [PATCH v2 0/4] EFI envblk Glenn Washburn
@ 2024-09-23 6:04 ` Glenn Washburn
2024-09-23 6:04 ` [PATCH v2 2/4] efi: Add efi-export-env and efi-load-env commands Glenn Washburn
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Glenn Washburn @ 2024-09-23 6:04 UTC (permalink / raw)
To: The development of GNU GRUB, Daniel Kiper; +Cc: Peter Jones, Glenn Washburn
From: Peter Jones <pjones@redhat.com>
This allows setting GRUB variables before any user interaction or GRUB
script can run. It is primarily intended to be used to turn on early
debugging.
Signed-off-by: Peter Jones <pjones@redhat.com>
Replace grub_efi_guid_t -> grub_guid_t
Signed-off-by: Glenn Washburn <development@efficientek.com>
---
grub-core/Makefile.core.def | 1 +
grub-core/kern/efi/init.c | 31 +++++++++++++++++++++++++++++++
include/grub/efi/efi.h | 5 +++++
3 files changed, 37 insertions(+)
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 063ef5dd7801..5699736dad20 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -219,6 +219,7 @@ kernel = {
efi = kern/efi/acpi.c;
efi = kern/efi/sb.c;
efi = kern/lockdown.c;
+ efi = lib/envblk.c;
i386_coreboot = kern/i386/pc/acpi.c;
i386_multiboot = kern/i386/pc/acpi.c;
i386_coreboot = kern/acpi.c;
diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c
index 1637077e1e96..2da6036129a1 100644
--- a/grub-core/kern/efi/init.c
+++ b/grub-core/kern/efi/init.c
@@ -28,8 +28,11 @@
#include <grub/env.h>
#include <grub/mm.h>
#include <grub/kernel.h>
+
#include <grub/stack_protector.h>
+#include <grub/lib/envblk.h>
+
#ifdef GRUB_STACK_PROTECTOR
static grub_efi_char16_t stack_chk_fail_msg[] =
@@ -94,6 +97,31 @@ grub_stack_protector_init (void)
grub_addr_t grub_modbase;
+/* Helper for grub_efi_env_init */
+static int
+set_var (const char *name, const char *value,
+ void *whitelist __attribute__((__unused__)))
+{
+ grub_env_set (name, value);
+ return 0;
+}
+
+static void
+grub_efi_env_init (void)
+{
+ grub_guid_t efi_grub_guid = GRUB_EFI_GRUB_VARIABLE_GUID;
+ struct grub_envblk envblk_s = { NULL, 0 };
+ grub_envblk_t envblk = &envblk_s;
+
+ grub_efi_get_variable ("GRUB_ENV", &efi_grub_guid, &envblk_s.size,
+ (void **) &envblk_s.buf);
+ if (envblk_s.buf == NULL || envblk_s.size < 1)
+ return;
+
+ grub_envblk_iterate (envblk, NULL, set_var);
+ grub_free (envblk_s.buf);
+}
+
void
grub_efi_init (void)
{
@@ -105,6 +133,9 @@ grub_efi_init (void)
/* Initialize the memory management system. */
grub_efi_mm_init ();
+ /* Populate environment with variables from EFI envblk, if it exists. */
+ grub_efi_env_init ();
+
/*
* Lockdown the GRUB and register the shim_lock verifier
* if the UEFI Secure Boot is enabled.
diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
index a5cd99e5afa9..3edf44dbda49 100644
--- a/include/grub/efi/efi.h
+++ b/include/grub/efi/efi.h
@@ -25,6 +25,11 @@
#include <grub/efi/api.h>
#include <grub/efi/pe32.h>
+#define GRUB_EFI_GRUB_VARIABLE_GUID \
+ { 0x91376aff, 0xcba6, 0x42be, \
+ { 0x94, 0x9d, 0x06, 0xfd, 0xe8, 0x11, 0x28, 0xe8 } \
+ }
+
#define GRUB_LINUX_ARM_MAGIC_SIGNATURE 0x016f2818
struct linux_arch_kernel_header {
--
2.34.1
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH v2 2/4] efi: Add efi-export-env and efi-load-env commands
2024-09-23 6:04 [PATCH v2 0/4] EFI envblk Glenn Washburn
2024-09-23 6:04 ` [PATCH v2 1/4] efi: Load env block from GRUB_ENV EFI variable in early init Glenn Washburn
@ 2024-09-23 6:04 ` Glenn Washburn
2024-09-23 6:04 ` [PATCH v2 3/4] tests: Add efienv_test for testing efi-export-env and efi-load-env Glenn Washburn
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Glenn Washburn @ 2024-09-23 6:04 UTC (permalink / raw)
To: The development of GNU GRUB, Daniel Kiper; +Cc: Peter Jones, Glenn Washburn
From: Peter Jones <pjones@redhat.com>
This adds "efi-export-env VARIABLES" and "efi-load-env", which manipulate
the environment block stored in the EFI variable
GRUB_ENV-91376aff-cba6-42be-949d-06fde81128e8.
Signed-off-by: Peter Jones <pjones@redhat.com>
Fix rebase, cherry-pick issues, grub_efi_guid_t -> grub_guid_t,
module name bug, missing return bug, memory leaks, formatting
Add support for specifying multiple variables at once for efi-export-env.
Signed-off-by: Glenn Washburn <development@efficientek.com>
---
docs/grub.texi | 24 +++++
grub-core/Makefile.core.def | 6 ++
grub-core/commands/efi/env.c | 182 +++++++++++++++++++++++++++++++++++
grub-core/kern/efi/efi.c | 3 +
grub-core/lib/envblk.c | 43 +++++++++
include/grub/lib/envblk.h | 3 +
6 files changed, 261 insertions(+)
create mode 100644 grub-core/commands/efi/env.c
diff --git a/docs/grub.texi b/docs/grub.texi
index 2ea6c56d100e..4d53777dce14 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -6391,6 +6391,8 @@ you forget a command, you can run the command @command{help}
* distrust:: Remove a pubkey from trusted keys
* drivemap:: Map a drive to another
* echo:: Display a line of text
+* efi-export-env:: Export environment variable to UEFI
+* efi-load-env:: Load the grub environment from UEFI
* efitextmode:: Set/Get text output mode resolution
* eval:: Evaluate agruments as GRUB commands
* export:: Export an environment variable
@@ -6860,6 +6862,28 @@ character will print that character.
@end deffn
+@node efi-export-env
+@subsection efi-export-env
+
+@deffn Command efi-export-env varname [varname @dots{}]
+Export the given GRUB variables to the GRUB_ENV UEFI variable as a GRUB
+environment block. The contents of an existing environment block in GRUB_ENV
+will be merged. So variables existing in GRUB_ENV but not given as arguements
+will be unaffected by this command.
+@end deffn
+
+
+@node efi-load-env
+@subsection efi-load-env
+
+@deffn Command efi-load-env
+Load the grub environment from the GRUB_ENV UEFI variable. This will overwrite
+existing variables if they exist in the environment block present in the UEFI
+variable. Other existing GRUB variables will be unchanged. This can allow for
+the persistence of state across boots.
+@end deffn
+
+
@node efitextmode
@subsection efitextmode
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 5699736dad20..640f9176d306 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -829,6 +829,12 @@ module = {
enable = efi;
};
+module = {
+ name = efienv;
+ common = commands/efi/env.c;
+ enable = efi;
+};
+
module = {
name = efifwsetup;
efi = commands/efi/efifwsetup.c;
diff --git a/grub-core/commands/efi/env.c b/grub-core/commands/efi/env.c
new file mode 100644
index 000000000000..b5775bcbd4bc
--- /dev/null
+++ b/grub-core/commands/efi/env.c
@@ -0,0 +1,182 @@
+/* env.c - commands to load/export environment block to EFI variable */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2023 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 <http://www.gnu.org/licenses/>.
+ */
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/env.h>
+#include <grub/lib/envblk.h>
+#include <grub/command.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static const grub_guid_t grub_env_guid = GRUB_EFI_GRUB_VARIABLE_GUID;
+
+static grub_err_t
+grub_efi_export_env (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ const char *value;
+ char *old_value;
+ struct grub_envblk envblk_s = { NULL, 0 };
+ grub_envblk_t envblk = &envblk_s;
+ grub_err_t err = GRUB_ERR_NONE;
+ int i, changed = 1;
+ grub_efi_status_t status;
+
+ grub_dprintf ("efienv", "argc:%d\n", argc);
+ for (i = 0; i < argc; i++)
+ grub_dprintf ("efienv", "argv[%d]: %s\n", i, argv[i]);
+
+ if (argc < 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("variable names expected"));
+
+ status = grub_efi_get_variable ("GRUB_ENV", &grub_env_guid, &envblk_s.size,
+ (void **) &envblk_s.buf);
+ if (envblk_s.buf == NULL || envblk_s.size < 1 || status != GRUB_EFI_SUCCESS)
+ {
+ char *buf = grub_malloc (DEFAULT_ENVBLK_SIZE + 1);
+ if (!buf)
+ return grub_errno;
+
+ grub_memcpy (buf, GRUB_ENVBLK_SIGNATURE, sizeof (GRUB_ENVBLK_SIGNATURE) - 1);
+ grub_memset (buf + sizeof (GRUB_ENVBLK_SIGNATURE) - 1, '#',
+ DEFAULT_ENVBLK_SIZE - sizeof (GRUB_ENVBLK_SIGNATURE) + 1);
+ buf[DEFAULT_ENVBLK_SIZE] = '\0';
+
+ envblk_s.buf = buf;
+ envblk_s.size = DEFAULT_ENVBLK_SIZE;
+ }
+ else
+ {
+ char *buf = grub_realloc (envblk_s.buf, envblk_s.size + 1);
+ if (buf == NULL)
+ {
+ err = grub_errno;
+ goto cleanup;
+ }
+
+ envblk_s.buf = buf;
+ envblk_s.buf[envblk_s.size] = '\0';
+ }
+
+ for (i = 0; i < argc; i++)
+ {
+ err = grub_envblk_get (envblk, argv[i], &old_value);
+ if (err != GRUB_ERR_NONE)
+ {
+ grub_dprintf ("efienv", "grub_envblk_get returned %d\n", err);
+ goto cleanup;
+ }
+
+ value = grub_env_get (argv[i]);
+ if ((value == NULL && old_value == NULL) ||
+ (value && old_value && !grub_strcmp (old_value, value)))
+ changed = 0;
+
+ if (old_value != NULL)
+ grub_free (old_value);
+
+ if (changed == 0)
+ {
+ grub_dprintf ("efienv", "No changes necessary for \"%s\"\n", argv[i]);
+ continue;
+ }
+
+ if (value != NULL)
+ {
+ grub_dprintf ("efienv", "setting \"%s\" to \"%s\"\n", argv[i], value);
+ grub_envblk_set (envblk, argv[i], value);
+ }
+ else
+ {
+ grub_dprintf ("efienv", "deleting \"%s\" from envblk\n", argv[i]);
+ grub_envblk_delete (envblk, argv[0]);
+ }
+
+ grub_dprintf ("efienv", "envblk is %" PRIuGRUB_SIZE " bytes:\n\"%s\"\n",
+ envblk_s.size, envblk_s.buf);
+ }
+
+ grub_dprintf ("efienv", "removing GRUB_ENV\n");
+ status = grub_efi_set_variable ("GRUB_ENV", &grub_env_guid, NULL, 0);
+ if (status != GRUB_EFI_SUCCESS)
+ grub_dprintf ("efienv", "removal returned 0x%" PRIxGRUB_EFI_UINTN_T "\n", status);
+
+ grub_dprintf ("efienv", "setting GRUB_ENV\n");
+ status = grub_efi_set_variable ("GRUB_ENV", &grub_env_guid,
+ envblk_s.buf, envblk_s.size);
+ if (status != GRUB_EFI_SUCCESS)
+ grub_dprintf ("efienv", "setting GRUB_ENV returned %" PRIxGRUB_EFI_UINTN_T "\n", status);
+
+ cleanup:
+ grub_free (envblk_s.buf);
+
+ return err;
+}
+
+static int
+set_var (const char *name, const char *value,
+ void *whitelist __attribute__((__unused__)))
+{
+ grub_env_set (name, value);
+ return 0;
+}
+
+static grub_err_t
+grub_efi_load_env (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[] __attribute__((__unused__)))
+{
+ struct grub_envblk envblk_s = { NULL, 0 };
+ grub_envblk_t envblk = &envblk_s;
+
+ if (argc > 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unexpected argument"));
+
+ grub_efi_get_variable ("GRUB_ENV", &grub_env_guid, &envblk_s.size,
+ (void **) &envblk_s.buf);
+ if (envblk_s.buf == NULL || envblk_s.size < 1)
+ return 0;
+
+ grub_envblk_iterate (envblk, NULL, set_var);
+ grub_free (envblk_s.buf);
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_command_t export_cmd, loadenv_cmd;
+
+GRUB_MOD_INIT(efienv)
+{
+ export_cmd = grub_register_command ("efi-export-env", grub_efi_export_env,
+ N_("VARIABLE_NAME [VARIABLE_NAME ...]"),
+ N_("Export environment variable to UEFI."));
+ loadenv_cmd = grub_register_command ("efi-load-env", grub_efi_load_env,
+ NULL, N_("Load the grub environment from UEFI."));
+}
+
+GRUB_MOD_FINI(efienv)
+{
+ grub_unregister_command (export_cmd);
+ grub_unregister_command (loadenv_cmd);
+}
diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c
index b93ae3aba12d..38e508ff6c28 100644
--- a/grub-core/kern/efi/efi.c
+++ b/grub-core/kern/efi/efi.c
@@ -222,6 +222,9 @@ grub_efi_set_variable_with_attributes (const char *var, const grub_guid_t *guid,
if (status == GRUB_EFI_SUCCESS)
return GRUB_ERR_NONE;
+ if (status == GRUB_EFI_NOT_FOUND && datasize == 0)
+ return GRUB_ERR_NONE;
+
return grub_error (GRUB_ERR_IO, "could not set EFI variable `%s'", var);
}
diff --git a/grub-core/lib/envblk.c b/grub-core/lib/envblk.c
index 2e4e78b132d4..ed7eb258e1c7 100644
--- a/grub-core/lib/envblk.c
+++ b/grub-core/lib/envblk.c
@@ -223,6 +223,49 @@ grub_envblk_delete (grub_envblk_t envblk, const char *name)
}
}
+struct get_var_state {
+ const char * const name;
+ char * value;
+ int found;
+};
+
+static int
+get_var (const char * const name, const char * const value, void *statep)
+{
+ struct get_var_state *state = (struct get_var_state *) statep;
+
+ if (!grub_strcmp (state->name, name))
+ {
+ state->found = 1;
+ state->value = grub_strdup (value);
+ if (state->value == NULL)
+ grub_errno = grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+
+ return 1;
+ }
+
+ return 0;
+}
+
+grub_err_t
+grub_envblk_get (grub_envblk_t envblk, const char * const name, char ** const value)
+{
+ struct get_var_state state = {
+ .name = name,
+ .value = NULL,
+ .found = 0,
+ };
+
+ grub_envblk_iterate (envblk, (void *) &state, get_var);
+
+ *value = state.value;
+
+ if (state.found && state.value == NULL)
+ return grub_errno;
+
+ return GRUB_ERR_NONE;
+}
+
void
grub_envblk_iterate (grub_envblk_t envblk,
void *hook_data,
diff --git a/include/grub/lib/envblk.h b/include/grub/lib/envblk.h
index c3e655921709..ab969af24612 100644
--- a/include/grub/lib/envblk.h
+++ b/include/grub/lib/envblk.h
@@ -22,6 +22,8 @@
#define GRUB_ENVBLK_SIGNATURE "# GRUB Environment Block\n"
#define GRUB_ENVBLK_DEFCFG "grubenv"
+#define DEFAULT_ENVBLK_SIZE 1024
+
#ifndef ASM_FILE
struct grub_envblk
@@ -33,6 +35,7 @@ typedef struct grub_envblk *grub_envblk_t;
grub_envblk_t grub_envblk_open (char *buf, grub_size_t size);
int grub_envblk_set (grub_envblk_t envblk, const char *name, const char *value);
+grub_err_t grub_envblk_get (grub_envblk_t envblk, const char * const name, char ** const value);
void grub_envblk_delete (grub_envblk_t envblk, const char *name);
void grub_envblk_iterate (grub_envblk_t envblk,
void *hook_data,
--
2.34.1
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply related [flat|nested] 6+ messages in thread