[PATCH] Add check_completed_boot command on EFI systems.

Peter Jones pjones at redhat.com
Fri May 25 15:35:39 UTC 2012


check_completed_boot <guid> [<timeout>]

checks for a 1-byte integer in an EFI variable guid:CompletedBoot and sets
a command-line specified timeout, with a default of 30s, if the variable is
not equal to 1.  This can be used to enter the grub menus in the event that
your OS did not correctly boot on the previous boot.  It also unconditionally
sets the value to 0.
---
 grub-core/Makefile.core.def           |    6 ++
 grub-core/commands/efi/eficompleted.c |  117 +++++++++++++++++++++++++++++++++
 2 files changed, 123 insertions(+)
 create mode 100644 grub-core/commands/efi/eficompleted.c

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index d0c06d5..0a21838 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -582,6 +582,12 @@ module = {
 };
 
 module = {
+  name = eficompleted;
+  efi = commands/efi/eficompleted.c;
+  enable = efi;
+};
+
+module = {
   name = blocklist;
   common = commands/blocklist.c;
 };
diff --git a/grub-core/commands/efi/eficompleted.c b/grub-core/commands/efi/eficompleted.c
new file mode 100644
index 0000000..77a856a
--- /dev/null
+++ b/grub-core/commands/efi/eficompleted.c
@@ -0,0 +1,117 @@
+/* completed.c - Check if previous boot was successful. */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2012  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/types.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/command.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_err_t
+grub_efi_parse_guid(char *arg, grub_efi_guid_t *outguid)
+{
+  grub_err_t status = GRUB_ERR_NONE;
+  grub_efi_guid_t guid;
+  char *s = arg;
+  grub_uint64_t guidcomp;
+  int i;
+
+  guid.data1 = grub_cpu_to_le32 (grub_strtoul(s, &s, 16));
+  if (*s != '-')
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid guid `%s'", arg);
+  s++;
+
+  guid.data2 = grub_cpu_to_le16 (grub_strtoul(s, &s, 16));
+  if (*s != '-')
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid guid `%s'", arg);
+  s++;
+
+  guid.data2 = grub_cpu_to_le16 (grub_strtoul(s, &s, 16));
+  if (*s != '-')
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid guid `%s'", arg);
+  s++;
+
+  guidcomp = grub_strtoull (s, 0, 16);
+  for (i = 0; i < 8; i++)
+    guid.data4[i] = (guidcomp >> (56 - 8 * i)) & 0xff;
+
+  grub_memcpy(outguid, &guid, sizeof (*outguid));
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_completed (grub_command_t cmd __attribute__ ((unused)),
+		  int argc __attribute__ ((unused)),
+		  char **args __attribute__ ((unused)))
+{
+  grub_efi_uint8_t *old_completed_boot;
+  grub_efi_uint8_t completed_boot = 0;
+  unsigned long timeout = 30;
+  grub_efi_guid_t guid;
+  grub_err_t status;
+  grub_size_t cb_size;
+
+  if (argc < 2)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "too few arguments");
+
+  if (argc > 3)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "too many arguments");
+
+  status = grub_efi_parse_guid(args[1], &guid);
+  if (status != GRUB_ERR_NONE)
+    return status;
+
+  if (argc > 2)
+    {
+      char *s = args[2];
+      timeout = grub_strtoul(s, &s, 0);
+      if (grub_errno != GRUB_ERR_NONE)
+	return grub_errno;
+    }
+
+  old_completed_boot = grub_efi_get_variable("CompletedBoot", &guid, &cb_size);
+  status = grub_efi_set_variable("CompletedBoot", &guid, &completed_boot,
+				 sizeof (completed_boot));
+
+  if (old_completed_boot == NULL)
+    {
+      /* We assume this means it's our first boot after installation. */
+      return GRUB_ERR_NONE;
+    }
+
+  if (cb_size != sizeof(*old_completed_boot) || *old_completed_boot != 1)
+    grub_env_set("timeout", timeout);
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_command_t cmd = NULL;
+
+GRUB_MOD_INIT(eficompleted)
+{
+  cmd = grub_register_command("check_completed_boot", grub_cmd_completed, "",
+			      "Check if the last boot completed successfully.");
+}
+
+GRUB_MOD_FINI(eficompleted)
+{
+  grub_unregister_command (cmd);
+}
-- 
1.7.10.1



More information about the devel mailing list