a4d61ac
From 7b886580f92bf6b766b042b6ef46cb77a5ba7451 Mon Sep 17 00:00:00 2001
a4d61ac
From: Peter Jones <pjones@redhat.com>
a4d61ac
Date: Fri, 25 May 2012 10:49:06 -0400
a4d61ac
Subject: [PATCH] Add check_completed_boot command on EFI systems.
a4d61ac
a4d61ac
check_completed_boot <guid> [<timeout>]
a4d61ac
a4d61ac
checks for a 1-byte integer in an EFI variable guid:CompletedBoot and sets
a4d61ac
a command-line specified timeout, with a default of 30s, if the variable is
a4d61ac
not equal to 1.  This can be used to enter the grub menus in the event that
a4d61ac
your OS did not correctly boot on the previous boot.  It also unconditionally
a4d61ac
sets the value to 0.
a4d61ac
---
a4d61ac
 grub-core/Makefile.core.def           |    6 ++
a4d61ac
 grub-core/commands/efi/eficompleted.c |  117 +++++++++++++++++++++++++++++++++
a4d61ac
 2 files changed, 123 insertions(+)
a4d61ac
 create mode 100644 grub-core/commands/efi/eficompleted.c
a4d61ac
a4d61ac
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
a4d61ac
index d0c06d5..0a21838 100644
a4d61ac
--- a/grub-core/Makefile.core.def
a4d61ac
+++ b/grub-core/Makefile.core.def
a4d61ac
@@ -582,6 +582,12 @@ module = {
a4d61ac
 };
a4d61ac
 
a4d61ac
 module = {
a4d61ac
+  name = eficompleted;
a4d61ac
+  efi = commands/efi/eficompleted.c;
a4d61ac
+  enable = efi;
a4d61ac
+};
a4d61ac
+
a4d61ac
+module = {
a4d61ac
   name = blocklist;
a4d61ac
   common = commands/blocklist.c;
a4d61ac
 };
a4d61ac
diff --git a/grub-core/commands/efi/eficompleted.c b/grub-core/commands/efi/eficompleted.c
a4d61ac
new file mode 100644
a4d61ac
index 0000000..77a856a
a4d61ac
--- /dev/null
a4d61ac
+++ b/grub-core/commands/efi/eficompleted.c
a4d61ac
@@ -0,0 +1,117 @@
a4d61ac
+/* completed.c - Check if previous boot was successful. */
a4d61ac
+/*
a4d61ac
+ *  GRUB  --  GRand Unified Bootloader
a4d61ac
+ *  Copyright (C) 2012  Free Software Foundation, Inc.
a4d61ac
+ *
a4d61ac
+ *  GRUB is free software: you can redistribute it and/or modify
a4d61ac
+ *  it under the terms of the GNU General Public License as published by
a4d61ac
+ *  the Free Software Foundation, either version 3 of the License, or
a4d61ac
+ *  (at your option) any later version.
a4d61ac
+ *
a4d61ac
+ *  GRUB is distributed in the hope that it will be useful,
a4d61ac
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
a4d61ac
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
a4d61ac
+ *  GNU General Public License for more details.
a4d61ac
+ *
a4d61ac
+ *  You should have received a copy of the GNU General Public License
a4d61ac
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
a4d61ac
+ */
a4d61ac
+#include <grub/types.h>
a4d61ac
+#include <grub/mm.h>
a4d61ac
+#include <grub/misc.h>
a4d61ac
+#include <grub/efi/api.h>
a4d61ac
+#include <grub/efi/efi.h>
a4d61ac
+#include <grub/command.h>
a4d61ac
+
a4d61ac
+GRUB_MOD_LICENSE ("GPLv3+");
a4d61ac
+
a4d61ac
+static grub_err_t
a4d61ac
+grub_efi_parse_guid(char *arg, grub_efi_guid_t *outguid)
a4d61ac
+{
a4d61ac
+  grub_err_t status = GRUB_ERR_NONE;
a4d61ac
+  grub_efi_guid_t guid;
a4d61ac
+  char *s = arg;
a4d61ac
+  grub_uint64_t guidcomp;
a4d61ac
+  int i;
a4d61ac
+
a4d61ac
+  guid.data1 = grub_cpu_to_le32 (grub_strtoul(s, &s, 16));
a4d61ac
+  if (*s != '-')
a4d61ac
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid guid `%s'", arg);
a4d61ac
+  s++;
a4d61ac
+
a4d61ac
+  guid.data2 = grub_cpu_to_le16 (grub_strtoul(s, &s, 16));
a4d61ac
+  if (*s != '-')
a4d61ac
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid guid `%s'", arg);
a4d61ac
+  s++;
a4d61ac
+
a4d61ac
+  guid.data2 = grub_cpu_to_le16 (grub_strtoul(s, &s, 16));
a4d61ac
+  if (*s != '-')
a4d61ac
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid guid `%s'", arg);
a4d61ac
+  s++;
a4d61ac
+
a4d61ac
+  guidcomp = grub_strtoull (s, 0, 16);
a4d61ac
+  for (i = 0; i < 8; i++)
a4d61ac
+    guid.data4[i] = (guidcomp >> (56 - 8 * i)) & 0xff;
a4d61ac
+
a4d61ac
+  grub_memcpy(outguid, &guid, sizeof (*outguid));
a4d61ac
+  return GRUB_ERR_NONE;
a4d61ac
+}
a4d61ac
+
a4d61ac
+static grub_err_t
a4d61ac
+grub_cmd_completed (grub_command_t cmd __attribute__ ((unused)),
a4d61ac
+		  int argc __attribute__ ((unused)),
a4d61ac
+		  char **args __attribute__ ((unused)))
a4d61ac
+{
a4d61ac
+  grub_efi_uint8_t *old_completed_boot;
a4d61ac
+  grub_efi_uint8_t completed_boot = 0;
a4d61ac
+  unsigned long timeout = 30;
a4d61ac
+  grub_efi_guid_t guid;
a4d61ac
+  grub_err_t status;
a4d61ac
+  grub_size_t cb_size;
a4d61ac
+
a4d61ac
+  if (argc < 2)
a4d61ac
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "too few arguments");
a4d61ac
+
a4d61ac
+  if (argc > 3)
a4d61ac
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "too many arguments");
a4d61ac
+
a4d61ac
+  status = grub_efi_parse_guid(args[1], &guid);
a4d61ac
+  if (status != GRUB_ERR_NONE)
a4d61ac
+    return status;
a4d61ac
+
a4d61ac
+  if (argc > 2)
a4d61ac
+    {
a4d61ac
+      char *s = args[2];
a4d61ac
+      timeout = grub_strtoul(s, &s, 0);
a4d61ac
+      if (grub_errno != GRUB_ERR_NONE)
a4d61ac
+	return grub_errno;
a4d61ac
+    }
a4d61ac
+
a4d61ac
+  old_completed_boot = grub_efi_get_variable("CompletedBoot", &guid, &cb_size);
a4d61ac
+  status = grub_efi_set_variable("CompletedBoot", &guid, &completed_boot,
a4d61ac
+				 sizeof (completed_boot));
a4d61ac
+
a4d61ac
+  if (old_completed_boot == NULL)
a4d61ac
+    {
a4d61ac
+      /* We assume this means it's our first boot after installation. */
a4d61ac
+      return GRUB_ERR_NONE;
a4d61ac
+    }
a4d61ac
+
a4d61ac
+  if (cb_size != sizeof(*old_completed_boot) || *old_completed_boot != 1)
a4d61ac
+    grub_env_set("timeout", timeout);
a4d61ac
+
a4d61ac
+  return GRUB_ERR_NONE;
a4d61ac
+}
a4d61ac
+
a4d61ac
+static grub_command_t cmd = NULL;
a4d61ac
+
a4d61ac
+GRUB_MOD_INIT(eficompleted)
a4d61ac
+{
a4d61ac
+  cmd = grub_register_command("check_completed_boot", grub_cmd_completed, "",
a4d61ac
+			      "Check if the last boot completed successfully.");
a4d61ac
+}
a4d61ac
+
a4d61ac
+GRUB_MOD_FINI(eficompleted)
a4d61ac
+{
a4d61ac
+  grub_unregister_command (cmd);
a4d61ac
+}
a4d61ac
-- 
a4d61ac
1.7.10.1
a4d61ac