11b49b8
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
11b49b8
From: Peter Jones <pjones@redhat.com>
11b49b8
Date: Wed, 16 Jan 2019 13:21:46 -0500
11b49b8
Subject: [PATCH] Add efi-export-env and efi-load-env commands
11b49b8
11b49b8
This adds "efi-export-env VARIABLE" and "efi-load-env", which manipulate the
11b49b8
environment block stored in the EFI variable
11b49b8
GRUB_ENV-91376aff-cba6-42be-949d-06fde81128e8.
11b49b8
11b49b8
Signed-off-by: Peter Jones <pjones@redhat.com>
11b49b8
---
11b49b8
 grub-core/Makefile.core.def  |   6 ++
11b49b8
 grub-core/commands/efi/env.c | 168 +++++++++++++++++++++++++++++++++++++++++++
11b49b8
 grub-core/kern/efi/efi.c     |   3 +
11b49b8
 grub-core/kern/efi/init.c    |   5 --
11b49b8
 grub-core/lib/envblk.c       |  43 +++++++++++
11b49b8
 util/editenv.c               |   2 -
11b49b8
 util/grub-set-bootflag.c     |   1 +
11b49b8
 include/grub/efi/efi.h       |   5 ++
11b49b8
 include/grub/lib/envblk.h    |   3 +
11b49b8
 9 files changed, 229 insertions(+), 7 deletions(-)
11b49b8
 create mode 100644 grub-core/commands/efi/env.c
11b49b8
11b49b8
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
11b49b8
index 826e27af900..017080bd599 100644
11b49b8
--- a/grub-core/Makefile.core.def
11b49b8
+++ b/grub-core/Makefile.core.def
11b49b8
@@ -776,6 +776,12 @@ module = {
11b49b8
   enable = efi;
11b49b8
 };
11b49b8
 
11b49b8
+module = {
11b49b8
+  name = efienv;
11b49b8
+  common = commands/efi/env.c;
11b49b8
+  enable = efi;
11b49b8
+};
11b49b8
+
11b49b8
 module = {
11b49b8
   name = efifwsetup;
11b49b8
   efi = commands/efi/efifwsetup.c;
11b49b8
diff --git a/grub-core/commands/efi/env.c b/grub-core/commands/efi/env.c
11b49b8
new file mode 100644
11b49b8
index 00000000000..a69079786aa
11b49b8
--- /dev/null
11b49b8
+++ b/grub-core/commands/efi/env.c
11b49b8
@@ -0,0 +1,168 @@
11b49b8
+/*
11b49b8
+ *  GRUB  --  GRand Unified Bootloader
11b49b8
+ *  Copyright (C) 2012  Free Software Foundation, Inc.
11b49b8
+ *
11b49b8
+ *  GRUB is free software: you can redistribute it and/or modify
11b49b8
+ *  it under the terms of the GNU General Public License as published by
11b49b8
+ *  the Free Software Foundation, either version 3 of the License, or
11b49b8
+ *  (at your option) any later version.
11b49b8
+ *
11b49b8
+ *  GRUB is distributed in the hope that it will be useful,
11b49b8
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11b49b8
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11b49b8
+ *  GNU General Public License for more details.
11b49b8
+ *
11b49b8
+ *  You should have received a copy of the GNU General Public License
11b49b8
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
11b49b8
+ */
11b49b8
+#include <grub/dl.h>
11b49b8
+#include <grub/mm.h>
11b49b8
+#include <grub/misc.h>
11b49b8
+#include <grub/types.h>
11b49b8
+#include <grub/mm.h>
11b49b8
+#include <grub/misc.h>
11b49b8
+#include <grub/efi/api.h>
11b49b8
+#include <grub/efi/efi.h>
11b49b8
+#include <grub/env.h>
11b49b8
+#include <grub/lib/envblk.h>
11b49b8
+#include <grub/command.h>
11b49b8
+
11b49b8
+GRUB_MOD_LICENSE ("GPLv3+");
11b49b8
+
11b49b8
+static const grub_efi_guid_t grub_env_guid = GRUB_EFI_GRUB_VARIABLE_GUID;
11b49b8
+
11b49b8
+static grub_err_t
11b49b8
+grub_efi_export_env(grub_command_t cmd __attribute__ ((unused)),
11b49b8
+                    int argc, char *argv[])
11b49b8
+{
11b49b8
+  const char *value;
11b49b8
+  char *old_value;
11b49b8
+  struct grub_envblk envblk_s = { NULL, 0 };
11b49b8
+  grub_envblk_t envblk = &envblk_s;
11b49b8
+  grub_err_t err;
11b49b8
+  int changed = 1;
11b49b8
+  grub_efi_status_t status;
11b49b8
+
11b49b8
+  grub_dprintf ("efienv", "argc:%d\n", argc);
11b49b8
+  for (int i = 0; i < argc; i++)
11b49b8
+    grub_dprintf ("efienv", "argv[%d]: %s\n", i, argv[i]);
11b49b8
+
11b49b8
+  if (argc != 1)
11b49b8
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("variable name expected"));
11b49b8
+
11b49b8
+  envblk_s.buf = grub_efi_get_variable ("GRUB_ENV", &grub_env_guid,
11b49b8
+					&envblk_s.size);
11b49b8
+  if (!envblk_s.buf || envblk_s.size < 1)
11b49b8
+    {
11b49b8
+      char *buf = grub_malloc (1025);
11b49b8
+      if (!buf)
11b49b8
+        return grub_errno;
11b49b8
+
11b49b8
+      grub_memcpy (buf, GRUB_ENVBLK_SIGNATURE, sizeof (GRUB_ENVBLK_SIGNATURE) - 1);
11b49b8
+      grub_memset (buf + sizeof (GRUB_ENVBLK_SIGNATURE) - 1, '#',
11b49b8
+	      DEFAULT_ENVBLK_SIZE - sizeof (GRUB_ENVBLK_SIGNATURE) + 1);
11b49b8
+      buf[1024] = '\0';
11b49b8
+
11b49b8
+      envblk_s.buf = buf;
11b49b8
+      envblk_s.size = 1024;
11b49b8
+    }
11b49b8
+  else
11b49b8
+    {
11b49b8
+      char *buf = grub_realloc (envblk_s.buf, envblk_s.size + 1);
11b49b8
+      if (!buf)
11b49b8
+	return grub_errno;
11b49b8
+
11b49b8
+      envblk_s.buf = buf;
11b49b8
+      envblk_s.buf[envblk_s.size] = '\0';
11b49b8
+    }
11b49b8
+
11b49b8
+  err = grub_envblk_get(envblk, argv[0], &old_value);
11b49b8
+  if (err != GRUB_ERR_NONE)
11b49b8
+    {
11b49b8
+      grub_dprintf ("efienv", "grub_envblk_get returned %d\n", err);
11b49b8
+      return err;
11b49b8
+    }
11b49b8
+
11b49b8
+  value = grub_env_get(argv[0]);
11b49b8
+  if ((!value && !old_value) ||
11b49b8
+      (value && old_value && !grub_strcmp(old_value, value)))
11b49b8
+    changed = 0;
11b49b8
+
11b49b8
+  if (old_value)
11b49b8
+    grub_free(old_value);
11b49b8
+
11b49b8
+  if (changed == 0)
11b49b8
+    {
11b49b8
+      grub_dprintf ("efienv", "No changes necessary\n");
11b49b8
+      return 0;
11b49b8
+    }
11b49b8
+
11b49b8
+  if (value)
11b49b8
+    {
11b49b8
+      grub_dprintf ("efienv", "setting \"%s\" to \"%s\"\n", argv[0], value);
11b49b8
+      grub_envblk_set(envblk, argv[0], value);
11b49b8
+    }
11b49b8
+  else
11b49b8
+    {
11b49b8
+      grub_dprintf ("efienv", "deleting \"%s\" from envblk\n", argv[0]);
11b49b8
+      grub_envblk_delete(envblk, argv[0]);
11b49b8
+    }
11b49b8
+
11b49b8
+  grub_dprintf ("efienv", "envblk is %lu bytes:\n\"%s\"\n", envblk_s.size, envblk_s.buf);
11b49b8
+
11b49b8
+  grub_dprintf ("efienv", "removing GRUB_ENV\n");
11b49b8
+  status = grub_efi_set_variable ("GRUB_ENV", &grub_env_guid, NULL, 0);
11b49b8
+  if (status != GRUB_EFI_SUCCESS)
11b49b8
+    grub_dprintf ("efienv", "removal returned %ld\n", status);
11b49b8
+
11b49b8
+  grub_dprintf ("efienv", "setting GRUB_ENV\n");
11b49b8
+  status = grub_efi_set_variable ("GRUB_ENV", &grub_env_guid,
11b49b8
+				  envblk_s.buf, envblk_s.size);
11b49b8
+  if (status != GRUB_EFI_SUCCESS)
11b49b8
+    grub_dprintf ("efienv", "setting GRUB_ENV returned %ld\n", status);
11b49b8
+
11b49b8
+  return 0;
11b49b8
+}
11b49b8
+
11b49b8
+static int
11b49b8
+set_var (const char *name, const char *value,
11b49b8
+	 void *whitelist __attribute__((__unused__)))
11b49b8
+{
11b49b8
+  grub_env_set (name, value);
11b49b8
+  return 0;
11b49b8
+}
11b49b8
+
11b49b8
+static grub_err_t
11b49b8
+grub_efi_load_env(grub_command_t cmd __attribute__ ((unused)),
11b49b8
+                    int argc, char *argv[] __attribute__((__unused__)))
11b49b8
+{
11b49b8
+  struct grub_envblk envblk_s = { NULL, 0 };
11b49b8
+  grub_envblk_t envblk = &envblk_s;
11b49b8
+
11b49b8
+  envblk_s.buf = grub_efi_get_variable ("GRUB_ENV", &grub_env_guid,
11b49b8
+					&envblk_s.size);
11b49b8
+  if (!envblk_s.buf || envblk_s.size < 1)
11b49b8
+    return 0;
11b49b8
+
11b49b8
+  if (argc > 0)
11b49b8
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unexpected argument"));
11b49b8
+
11b49b8
+  grub_envblk_iterate (envblk, NULL, set_var);
11b49b8
+  grub_free (envblk_s.buf);
11b49b8
+}
11b49b8
+
11b49b8
+static grub_command_t export_cmd, loadenv_cmd;
11b49b8
+
11b49b8
+GRUB_MOD_INIT(lsefi)
11b49b8
+{
11b49b8
+  export_cmd = grub_register_command ("efi-export-env", grub_efi_export_env,
11b49b8
+	    N_("VARIABLE_NAME"), N_("Export environment variable to UEFI."));
11b49b8
+  loadenv_cmd = grub_register_command ("efi-load-env", grub_efi_load_env,
11b49b8
+	    NULL, N_("Load the grub environment from UEFI."));
11b49b8
+}
11b49b8
+
11b49b8
+GRUB_MOD_FINI(lsefi)
11b49b8
+{
11b49b8
+  grub_unregister_command (export_cmd);
11b49b8
+  grub_unregister_command (loadenv_cmd);
11b49b8
+}
11b49b8
diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c
11b49b8
index 4d36fe31177..1079ac74a47 100644
11b49b8
--- a/grub-core/kern/efi/efi.c
11b49b8
+++ b/grub-core/kern/efi/efi.c
11b49b8
@@ -224,6 +224,9 @@ grub_efi_set_variable(const char *var, const grub_efi_guid_t *guid,
11b49b8
   if (status == GRUB_EFI_SUCCESS)
11b49b8
     return GRUB_ERR_NONE;
11b49b8
 
11b49b8
+  if (status == GRUB_EFI_NOT_FOUND && datasize == 0)
11b49b8
+    return GRUB_ERR_NONE;
11b49b8
+
11b49b8
   return grub_error (GRUB_ERR_IO, "could not set EFI variable `%s'", var);
11b49b8
 }
11b49b8
 
11b49b8
diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c
11b49b8
index e6183a4c44d..d1afa3af11e 100644
11b49b8
--- a/grub-core/kern/efi/init.c
11b49b8
+++ b/grub-core/kern/efi/init.c
11b49b8
@@ -29,11 +29,6 @@
11b49b8
 
11b49b8
 grub_addr_t grub_modbase;
11b49b8
 
11b49b8
-#define GRUB_EFI_GRUB_VARIABLE_GUID \
11b49b8
-  { 0x91376aff, 0xcba6, 0x42be, \
11b49b8
-    { 0x94, 0x9d, 0x06, 0xfd, 0xe8, 0x11, 0x28, 0xe8 } \
11b49b8
-  }
11b49b8
-
11b49b8
 /* Helper for grub_efi_env_init */
11b49b8
 static int
11b49b8
 set_var (const char *name, const char *value,
11b49b8
diff --git a/grub-core/lib/envblk.c b/grub-core/lib/envblk.c
11b49b8
index 230e0e9d9ab..f89d86d4e8d 100644
11b49b8
--- a/grub-core/lib/envblk.c
11b49b8
+++ b/grub-core/lib/envblk.c
11b49b8
@@ -223,6 +223,49 @@ grub_envblk_delete (grub_envblk_t envblk, const char *name)
11b49b8
     }
11b49b8
 }
11b49b8
 
11b49b8
+struct get_var_state {
11b49b8
+  const char * const name;
11b49b8
+  char * value;
11b49b8
+  int found;
11b49b8
+};
11b49b8
+
11b49b8
+static int
11b49b8
+get_var (const char * const name, const char * const value, void *statep)
11b49b8
+{
11b49b8
+  struct get_var_state *state = (struct get_var_state *)statep;
11b49b8
+
11b49b8
+  if (!grub_strcmp(state->name, name))
11b49b8
+    {
11b49b8
+      state->found = 1;
11b49b8
+      state->value = grub_strdup(value);
11b49b8
+      if (!state->value)
11b49b8
+	grub_errno = grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
11b49b8
+
11b49b8
+      return 1;
11b49b8
+    }
11b49b8
+
11b49b8
+  return 0;
11b49b8
+}
11b49b8
+
11b49b8
+grub_err_t
11b49b8
+grub_envblk_get (grub_envblk_t envblk, const char * const name, char ** const value)
11b49b8
+{
11b49b8
+  struct get_var_state state = {
11b49b8
+      .name = name,
11b49b8
+      .value = NULL,
11b49b8
+      .found = 0,
11b49b8
+  };
11b49b8
+
11b49b8
+  grub_envblk_iterate(envblk, (void *)&state, get_var);
11b49b8
+
11b49b8
+  *value = state.value;
11b49b8
+
11b49b8
+  if (state.found && !state.value)
11b49b8
+    return grub_errno;
11b49b8
+
11b49b8
+  return GRUB_ERR_NONE;
11b49b8
+}
11b49b8
+
11b49b8
 void
11b49b8
 grub_envblk_iterate (grub_envblk_t envblk,
11b49b8
                      void *hook_data,
11b49b8
diff --git a/util/editenv.c b/util/editenv.c
11b49b8
index 41bc7cb1c9a..844a14d90da 100644
11b49b8
--- a/util/editenv.c
11b49b8
+++ b/util/editenv.c
11b49b8
@@ -30,8 +30,6 @@
11b49b8
 #include <string.h>
11b49b8
 #include <libgen.h>
11b49b8
 
11b49b8
-#define DEFAULT_ENVBLK_SIZE	1024
11b49b8
-
11b49b8
 void
11b49b8
 grub_util_create_envblk_file (const char *name)
11b49b8
 {
11b49b8
diff --git a/util/grub-set-bootflag.c b/util/grub-set-bootflag.c
11b49b8
index f8dc310909a..20062fe802b 100644
11b49b8
--- a/util/grub-set-bootflag.c
11b49b8
+++ b/util/grub-set-bootflag.c
11b49b8
@@ -25,6 +25,7 @@
11b49b8
 
11b49b8
 #include <config-util.h>     /* For *_DIR_NAME defines */
11b49b8
 #include <grub/types.h>
11b49b8
+#include <grub/err.h>
11b49b8
 #include <grub/lib/envblk.h> /* For GRUB_ENVBLK_DEFCFG define */
11b49b8
 #include <stdio.h>
11b49b8
 #include <string.h>
11b49b8
diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
11b49b8
index 570a69361a5..2778b95df17 100644
11b49b8
--- a/include/grub/efi/efi.h
11b49b8
+++ b/include/grub/efi/efi.h
11b49b8
@@ -24,6 +24,11 @@
11b49b8
 #include <grub/dl.h>
11b49b8
 #include <grub/efi/api.h>
11b49b8
 
11b49b8
+#define GRUB_EFI_GRUB_VARIABLE_GUID \
11b49b8
+  { 0x91376aff, 0xcba6, 0x42be, \
11b49b8
+    { 0x94, 0x9d, 0x06, 0xfd, 0xe8, 0x11, 0x28, 0xe8 } \
11b49b8
+  }
11b49b8
+
11b49b8
 /* Variables.  */
11b49b8
 extern grub_efi_system_table_t *EXPORT_VAR(grub_efi_system_table);
11b49b8
 extern grub_efi_handle_t EXPORT_VAR(grub_efi_image_handle);
11b49b8
diff --git a/include/grub/lib/envblk.h b/include/grub/lib/envblk.h
11b49b8
index c3e65592170..ab969af2461 100644
11b49b8
--- a/include/grub/lib/envblk.h
11b49b8
+++ b/include/grub/lib/envblk.h
11b49b8
@@ -22,6 +22,8 @@
11b49b8
 #define GRUB_ENVBLK_SIGNATURE	"# GRUB Environment Block\n"
11b49b8
 #define GRUB_ENVBLK_DEFCFG	"grubenv"
11b49b8
 
11b49b8
+#define DEFAULT_ENVBLK_SIZE	1024
11b49b8
+
11b49b8
 #ifndef ASM_FILE
11b49b8
 
11b49b8
 struct grub_envblk
11b49b8
@@ -33,6 +35,7 @@ typedef struct grub_envblk *grub_envblk_t;
11b49b8
 
11b49b8
 grub_envblk_t grub_envblk_open (char *buf, grub_size_t size);
11b49b8
 int grub_envblk_set (grub_envblk_t envblk, const char *name, const char *value);
11b49b8
+grub_err_t grub_envblk_get (grub_envblk_t envblk, const char * const name, char ** const value);
11b49b8
 void grub_envblk_delete (grub_envblk_t envblk, const char *name);
11b49b8
 void grub_envblk_iterate (grub_envblk_t envblk,
11b49b8
                           void *hook_data,