0dc71c
From e396fd48c78901459f39926fe28c9fbc38ffdddb Mon Sep 17 00:00:00 2001
0dc71c
From: Peter Jones <pjones@redhat.com>
0dc71c
Date: Thu, 18 Sep 2014 11:26:14 -0400
0dc71c
Subject: [PATCH] Load arm with SB enabled.
0dc71c
0dc71c
Make sure we actually try to validate secure boot on this platform (even
0dc71c
though we're not shipping it enabled by default.)
0dc71c
0dc71c
This means giving the kernel grub's loaded image as the vehicle for the
0dc71c
kernel command line, because we can't call systab->bs->LoadImage() if SB
0dc71c
is enabled.
0dc71c
---
0dc71c
 grub-core/Makefile.core.def       |   2 +
0dc71c
 grub-core/loader/arm64/linux.c    | 108 ++++++++++++++++++++------------------
0dc71c
 grub-core/loader/efi/linux.c      |  65 +++++++++++++++++++++++
0dc71c
 grub-core/loader/i386/efi/linux.c |  39 ++------------
0dc71c
 include/grub/arm64/linux.h        |   8 +++
0dc71c
 include/grub/efi/linux.h          |  31 +++++++++++
0dc71c
 6 files changed, 166 insertions(+), 87 deletions(-)
0dc71c
 create mode 100644 grub-core/loader/efi/linux.c
0dc71c
 create mode 100644 include/grub/efi/linux.h
0dc71c
0dc71c
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
0dc71c
index 9ff9ae5..9378c73 100644
0dc71c
--- a/grub-core/Makefile.core.def
0dc71c
+++ b/grub-core/Makefile.core.def
0dc71c
@@ -1682,6 +1682,7 @@ module = {
0dc71c
   ia64_efi = loader/ia64/efi/linux.c;
0dc71c
   arm = loader/arm/linux.c;
0dc71c
   arm64 = loader/arm64/linux.c;
0dc71c
+  arm64 = loader/efi/linux.c;
0dc71c
   fdt = lib/fdt.c;
0dc71c
   common = loader/linux.c;
0dc71c
   common = lib/cmdline.c;
0dc71c
@@ -1718,6 +1719,7 @@ module = {
0dc71c
   name = linuxefi;
0dc71c
   efi = loader/i386/efi/linux.c;
0dc71c
   efi = lib/cmdline.c;
0dc71c
+  efi = loader/efi/linux.c;
0dc71c
   enable = i386_efi;
0dc71c
   enable = x86_64_efi;
0dc71c
 };
0dc71c
diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c
0dc71c
index 0dc144e..bdd9c9b 100644
0dc71c
--- a/grub-core/loader/arm64/linux.c
0dc71c
+++ b/grub-core/loader/arm64/linux.c
0dc71c
@@ -27,6 +27,7 @@
0dc71c
 #include <grub/types.h>
0dc71c
 #include <grub/cpu/linux.h>
0dc71c
 #include <grub/efi/efi.h>
0dc71c
+#include <grub/efi/linux.h>
0dc71c
 #include <grub/efi/pe32.h>
0dc71c
 #include <grub/i18n.h>
0dc71c
 #include <grub/lib/cmdline.h>
0dc71c
@@ -44,6 +45,7 @@ static int loaded;
0dc71c
 
0dc71c
 static void *kernel_addr;
0dc71c
 static grub_uint64_t kernel_size;
0dc71c
+static grub_uint32_t handover_offset;
0dc71c
 
0dc71c
 static char *linux_args;
0dc71c
 static grub_uint32_t cmdline_size;
0dc71c
@@ -135,7 +137,9 @@ finalize_params (void)
0dc71c
 {
0dc71c
   grub_efi_boot_services_t *b;
0dc71c
   grub_efi_status_t status;
0dc71c
+  grub_efi_loaded_image_t *loaded_image = NULL;
0dc71c
   int node, retval;
0dc71c
+  int len;
0dc71c
 
0dc71c
   get_fdt ();
0dc71c
   if (!fdt)
0dc71c
@@ -172,6 +176,23 @@ finalize_params (void)
0dc71c
   grub_dprintf ("linux", "Installed/updated FDT configuration table @ %p\n",
0dc71c
 		fdt);
0dc71c
 
0dc71c
+  /* Convert command line to UCS-2 */
0dc71c
+  loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle);
0dc71c
+  if (!loaded_image)
0dc71c
+    goto failure;
0dc71c
+
0dc71c
+  loaded_image->load_options_size = len =
0dc71c
+    (grub_strlen (linux_args) + 1) * sizeof (grub_efi_char16_t);
0dc71c
+  loaded_image->load_options =
0dc71c
+    grub_efi_allocate_pages (0,
0dc71c
+			     BYTES_TO_PAGES (loaded_image->load_options_size));
0dc71c
+  if (!loaded_image->load_options)
0dc71c
+    return grub_error(GRUB_ERR_BAD_OS, "failed to create kernel parameters");
0dc71c
+
0dc71c
+  loaded_image->load_options_size =
0dc71c
+    2 * grub_utf8_to_utf16 (loaded_image->load_options, len,
0dc71c
+			    (grub_uint8_t *) linux_args, len, NULL);
0dc71c
+
0dc71c
   return GRUB_ERR_NONE;
0dc71c
 
0dc71c
 failure:
0dc71c
@@ -181,6 +202,23 @@ failure:
0dc71c
   return grub_error(GRUB_ERR_BAD_OS, "failed to install/update FDT");
0dc71c
 }
0dc71c
 
0dc71c
+static void
0dc71c
+free_params (void)
0dc71c
+{
0dc71c
+  grub_efi_loaded_image_t *loaded_image = NULL;
0dc71c
+
0dc71c
+  loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle);
0dc71c
+  if (loaded_image)
0dc71c
+    {
0dc71c
+      if (loaded_image->load_options)
0dc71c
+	grub_efi_free_pages ((grub_efi_physical_address_t)
0dc71c
+			      loaded_image->load_options,
0dc71c
+			     BYTES_TO_PAGES (loaded_image->load_options_size));
0dc71c
+      loaded_image->load_options = NULL;
0dc71c
+      loaded_image->load_options_size = 0;
0dc71c
+    }
0dc71c
+}
0dc71c
+
0dc71c
 static grub_err_t
0dc71c
 grub_cmd_devicetree (grub_command_t cmd __attribute__ ((unused)),
0dc71c
 		     int argc, char *argv[])
0dc71c
@@ -199,6 +237,10 @@ grub_cmd_devicetree (grub_command_t cmd __attribute__ ((unused)),
0dc71c
   if (argc != 1)
0dc71c
     return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
0dc71c
 
0dc71c
+  if (grub_efi_secure_boot ())
0dc71c
+    return grub_error (GRUB_ERR_INVALID_COMMAND,
0dc71c
+		       N_("Not loading devicetree - Secure Boot is enabled"));
0dc71c
+
0dc71c
   if (loaded_fdt)
0dc71c
     grub_free (loaded_fdt);
0dc71c
   loaded_fdt = NULL;
0dc71c
@@ -243,65 +285,20 @@ out:
0dc71c
 static grub_err_t
0dc71c
 grub_linux_boot (void)
0dc71c
 {
0dc71c
-  grub_efi_memory_mapped_device_path_t *mempath;
0dc71c
-  grub_efi_handle_t image_handle;
0dc71c
-  grub_efi_boot_services_t *b;
0dc71c
-  grub_efi_status_t status;
0dc71c
   grub_err_t retval;
0dc71c
-  grub_efi_loaded_image_t *loaded_image;
0dc71c
-  int len;
0dc71c
 
0dc71c
   retval = finalize_params();
0dc71c
   if (retval != GRUB_ERR_NONE)
0dc71c
     return retval;
0dc71c
 
0dc71c
-  mempath = grub_malloc (2 * sizeof (grub_efi_memory_mapped_device_path_t));
0dc71c
-  if (!mempath)
0dc71c
-    return grub_errno;
0dc71c
-
0dc71c
-  mempath[0].header.type = GRUB_EFI_HARDWARE_DEVICE_PATH_TYPE;
0dc71c
-  mempath[0].header.subtype = GRUB_EFI_MEMORY_MAPPED_DEVICE_PATH_SUBTYPE;
0dc71c
-  mempath[0].header.length = grub_cpu_to_le16_compile_time (sizeof (*mempath));
0dc71c
-  mempath[0].memory_type = GRUB_EFI_LOADER_DATA;
0dc71c
-  mempath[0].start_address = (grub_addr_t) kernel_addr;
0dc71c
-  mempath[0].end_address = (grub_addr_t) kernel_addr + kernel_size;
0dc71c
-
0dc71c
-  mempath[1].header.type = GRUB_EFI_END_DEVICE_PATH_TYPE;
0dc71c
-  mempath[1].header.subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
0dc71c
-  mempath[1].header.length = sizeof (grub_efi_device_path_t);
0dc71c
-
0dc71c
-  b = grub_efi_system_table->boot_services;
0dc71c
-  status = b->load_image (0, grub_efi_image_handle,
0dc71c
-			  (grub_efi_device_path_t *) mempath,
0dc71c
-                          kernel_addr, kernel_size, &image_handle);
0dc71c
-  if (status != GRUB_EFI_SUCCESS)
0dc71c
-    return grub_error (GRUB_ERR_BAD_OS, "cannot load image");
0dc71c
-
0dc71c
   grub_dprintf ("linux", "linux command line: '%s'\n", linux_args);
0dc71c
 
0dc71c
-  /* Convert command line to UCS-2 */
0dc71c
-  loaded_image = grub_efi_get_loaded_image (image_handle);
0dc71c
-  loaded_image->load_options_size = len =
0dc71c
-    (grub_strlen (linux_args) + 1) * sizeof (grub_efi_char16_t);
0dc71c
-  loaded_image->load_options =
0dc71c
-    grub_efi_allocate_pages (0,
0dc71c
-			     BYTES_TO_PAGES (loaded_image->load_options_size));
0dc71c
-  if (!loaded_image->load_options)
0dc71c
-    return grub_errno;
0dc71c
+  retval = grub_efi_linux_boot ((char *)kernel_addr, handover_offset,
0dc71c
+				kernel_addr);
0dc71c
 
0dc71c
-  loaded_image->load_options_size =
0dc71c
-    2 * grub_utf8_to_utf16 (loaded_image->load_options, len,
0dc71c
-			    (grub_uint8_t *) linux_args, len, NULL);
0dc71c
-
0dc71c
-  grub_dprintf("linux", "starting image %p\n", image_handle);
0dc71c
-  status = b->start_image (image_handle, 0, NULL);
0dc71c
-
0dc71c
-  /* When successful, not reached */
0dc71c
-  b->unload_image (image_handle);
0dc71c
-  grub_efi_free_pages ((grub_efi_physical_address_t) loaded_image->load_options,
0dc71c
-		       BYTES_TO_PAGES (loaded_image->load_options_size));
0dc71c
-
0dc71c
-  return grub_errno;
0dc71c
+  /* Never reached... */
0dc71c
+  free_params();
0dc71c
+  return retval;
0dc71c
 }
0dc71c
 
0dc71c
 static grub_err_t
0dc71c
@@ -382,6 +379,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
0dc71c
 {
0dc71c
   grub_file_t file = 0;
0dc71c
   struct grub_arm64_linux_kernel_header lh;
0dc71c
+  struct grub_arm64_linux_pe_header *pe;
0dc71c
 
0dc71c
   grub_dl_ref (my_mod);
0dc71c
 
0dc71c
@@ -426,6 +424,15 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
0dc71c
 
0dc71c
   grub_dprintf ("linux", "kernel @ %p\n", kernel_addr);
0dc71c
 
0dc71c
+  if (!grub_linuxefi_secure_validate (kernel_addr, kernel_size))
0dc71c
+    {
0dc71c
+      grub_error (GRUB_ERR_INVALID_COMMAND, N_("%s has invalid signature"), argv[0]);
0dc71c
+      goto fail;
0dc71c
+    }
0dc71c
+
0dc71c
+  pe = (void *)((unsigned long)kernel_addr + lh.hdr_offset);
0dc71c
+  handover_offset = pe->opt.entry_addr;
0dc71c
+
0dc71c
   cmdline_size = grub_loader_cmdline_size (argc, argv) + sizeof (LINUX_IMAGE);
0dc71c
   linux_args = grub_malloc (cmdline_size);
0dc71c
   if (!linux_args)
0dc71c
@@ -464,7 +471,6 @@ fail:
0dc71c
   return grub_errno;
0dc71c
 }
0dc71c
 
0dc71c
-
0dc71c
 static grub_command_t cmd_linux, cmd_initrd, cmd_devicetree;
0dc71c
 
0dc71c
 GRUB_MOD_INIT (linux)
0dc71c
diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c
0dc71c
new file mode 100644
0dc71c
index 0000000..aea378a
0dc71c
--- /dev/null
0dc71c
+++ b/grub-core/loader/efi/linux.c
0dc71c
@@ -0,0 +1,65 @@
0dc71c
+/*
0dc71c
+ *  GRUB  --  GRand Unified Bootloader
0dc71c
+ *  Copyright (C) 2014 Free Software Foundation, Inc.
0dc71c
+ *
0dc71c
+ *  GRUB is free software: you can redistribute it and/or modify
0dc71c
+ *  it under the terms of the GNU General Public License as published by
0dc71c
+ *  the Free Software Foundation, either version 3 of the License, or
0dc71c
+ *  (at your option) any later version.
0dc71c
+ *
0dc71c
+ *  GRUB is distributed in the hope that it will be useful,
0dc71c
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0dc71c
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0dc71c
+ *  GNU General Public License for more details.
0dc71c
+ *
0dc71c
+ *  You should have received a copy of the GNU General Public License
0dc71c
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
0dc71c
+ */
0dc71c
+
0dc71c
+#include <grub/err.h>
0dc71c
+#include <grub/mm.h>
0dc71c
+#include <grub/types.h>
0dc71c
+#include <grub/cpu/linux.h>
0dc71c
+#include <grub/efi/efi.h>
0dc71c
+#include <grub/efi/pe32.h>
0dc71c
+#include <grub/efi/linux.h>
0dc71c
+
0dc71c
+#define SHIM_LOCK_GUID \
0dc71c
+ { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} }
0dc71c
+
0dc71c
+struct grub_efi_shim_lock
0dc71c
+{
0dc71c
+  grub_efi_status_t (*verify) (void *buffer, grub_uint32_t size);
0dc71c
+};
0dc71c
+typedef struct grub_efi_shim_lock grub_efi_shim_lock_t;
0dc71c
+
0dc71c
+grub_efi_boolean_t
0dc71c
+grub_linuxefi_secure_validate (void *data, grub_uint32_t size)
0dc71c
+{
0dc71c
+  grub_efi_guid_t guid = SHIM_LOCK_GUID;
0dc71c
+  grub_efi_shim_lock_t *shim_lock;
0dc71c
+
0dc71c
+  shim_lock = grub_efi_locate_protocol(&guid, NULL);
0dc71c
+
0dc71c
+  if (!shim_lock)
0dc71c
+    return 1;
0dc71c
+
0dc71c
+  if (shim_lock->verify(data, size) == GRUB_EFI_SUCCESS)
0dc71c
+    return 1;
0dc71c
+
0dc71c
+  return 0;
0dc71c
+}
0dc71c
+
0dc71c
+typedef void (*handover_func) (void *, grub_efi_system_table_t *, void *);
0dc71c
+
0dc71c
+grub_err_t
0dc71c
+grub_efi_linux_boot (void *kernel_addr, grub_off_t offset,
0dc71c
+		     void *kernel_params)
0dc71c
+{
0dc71c
+  handover_func hf;
0dc71c
+
0dc71c
+  hf = (handover_func)((char *)kernel_addr + offset);
0dc71c
+  hf (grub_efi_image_handle, grub_efi_system_table, kernel_params);
0dc71c
+
0dc71c
+  return GRUB_ERR_BUG;
0dc71c
+}
0dc71c
diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
0dc71c
index b79e632..e5b7785 100644
0dc71c
--- a/grub-core/loader/i386/efi/linux.c
0dc71c
+++ b/grub-core/loader/i386/efi/linux.c
0dc71c
@@ -26,6 +26,7 @@
0dc71c
 #include <grub/i18n.h>
0dc71c
 #include <grub/lib/cmdline.h>
0dc71c
 #include <grub/efi/efi.h>
0dc71c
+#include <grub/efi/linux.h>
0dc71c
 
0dc71c
 GRUB_MOD_LICENSE ("GPLv3+");
0dc71c
 
0dc71c
@@ -40,52 +41,18 @@ static char *linux_cmdline;
0dc71c
 
0dc71c
 #define BYTES_TO_PAGES(bytes)   (((bytes) + 0xfff) >> 12)
0dc71c
 
0dc71c
-#define SHIM_LOCK_GUID \
0dc71c
-  { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} }
0dc71c
-
0dc71c
-struct grub_efi_shim_lock
0dc71c
-{
0dc71c
-  grub_efi_status_t (*verify) (void *buffer, grub_uint32_t size);
0dc71c
-};
0dc71c
-typedef struct grub_efi_shim_lock grub_efi_shim_lock_t;
0dc71c
-
0dc71c
-static grub_efi_boolean_t
0dc71c
-grub_linuxefi_secure_validate (void *data, grub_uint32_t size)
0dc71c
-{
0dc71c
-  grub_efi_guid_t guid = SHIM_LOCK_GUID;
0dc71c
-  grub_efi_shim_lock_t *shim_lock;
0dc71c
-
0dc71c
-  shim_lock = grub_efi_locate_protocol(&guid, NULL);
0dc71c
-
0dc71c
-  if (!shim_lock)
0dc71c
-    return 1;
0dc71c
-
0dc71c
-  if (shim_lock->verify(data, size) == GRUB_EFI_SUCCESS)
0dc71c
-    return 1;
0dc71c
-
0dc71c
-  return 0;
0dc71c
-}
0dc71c
-
0dc71c
-typedef void(*handover_func)(void *, grub_efi_system_table_t *, struct linux_kernel_params *);
0dc71c
-
0dc71c
 static grub_err_t
0dc71c
 grub_linuxefi_boot (void)
0dc71c
 {
0dc71c
-  handover_func hf;
0dc71c
   int offset = 0;
0dc71c
 
0dc71c
 #ifdef __x86_64__
0dc71c
   offset = 512;
0dc71c
 #endif
0dc71c
-
0dc71c
-  hf = (handover_func)((char *)kernel_mem + handover_offset + offset);
0dc71c
-
0dc71c
   asm volatile ("cli");
0dc71c
 
0dc71c
-  hf (grub_efi_image_handle, grub_efi_system_table, params);
0dc71c
-
0dc71c
-  /* Not reached */
0dc71c
-  return GRUB_ERR_NONE;
0dc71c
+  return grub_efi_linux_boot ((char *)kernel_mem, handover_offset + offset,
0dc71c
+			      params);
0dc71c
 }
0dc71c
 
0dc71c
 static grub_err_t
0dc71c
diff --git a/include/grub/arm64/linux.h b/include/grub/arm64/linux.h
0dc71c
index 864e5dc..2cbd64f 100644
0dc71c
--- a/include/grub/arm64/linux.h
0dc71c
+++ b/include/grub/arm64/linux.h
0dc71c
@@ -20,6 +20,7 @@
0dc71c
 #define GRUB_LINUX_CPU_HEADER 1
0dc71c
 
0dc71c
 #include <grub/efi/efi.h>
0dc71c
+#include <grub/efi/pe32.h>
0dc71c
 
0dc71c
 #define GRUB_ARM64_LINUX_MAGIC 0x644d5241 /* 'ARM\x64' */
0dc71c
 
0dc71c
@@ -38,4 +39,11 @@ struct grub_arm64_linux_kernel_header
0dc71c
   grub_uint32_t hdr_offset;	/* Offset of PE/COFF header */
0dc71c
 };
0dc71c
 
0dc71c
+struct grub_arm64_linux_pe_header
0dc71c
+{
0dc71c
+  grub_uint32_t magic;
0dc71c
+  struct grub_pe32_coff_header coff;
0dc71c
+  struct grub_pe64_optional_header opt;
0dc71c
+};
0dc71c
+
0dc71c
 #endif /* ! GRUB_LINUX_CPU_HEADER */
0dc71c
diff --git a/include/grub/efi/linux.h b/include/grub/efi/linux.h
0dc71c
new file mode 100644
0dc71c
index 0000000..d9ede36
0dc71c
--- /dev/null
0dc71c
+++ b/include/grub/efi/linux.h
0dc71c
@@ -0,0 +1,31 @@
0dc71c
+/*
0dc71c
+ *  GRUB  --  GRand Unified Bootloader
0dc71c
+ *  Copyright (C) 2014  Free Software Foundation, Inc.
0dc71c
+ *
0dc71c
+ *  GRUB is free software: you can redistribute it and/or modify
0dc71c
+ *  it under the terms of the GNU General Public License as published by
0dc71c
+ *  the Free Software Foundation, either version 3 of the License, or
0dc71c
+ *  (at your option) any later version.
0dc71c
+ *
0dc71c
+ *  GRUB is distributed in the hope that it will be useful,
0dc71c
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0dc71c
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0dc71c
+ *  GNU General Public License for more details.
0dc71c
+ *
0dc71c
+ *  You should have received a copy of the GNU General Public License
0dc71c
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
0dc71c
+ */
0dc71c
+#ifndef GRUB_EFI_LINUX_HEADER
0dc71c
+#define GRUB_EFI_LINUX_HEADER	1
0dc71c
+
0dc71c
+#include <grub/efi/api.h>
0dc71c
+#include <grub/err.h>
0dc71c
+#include <grub/symbol.h>
0dc71c
+
0dc71c
+grub_efi_boolean_t
0dc71c
+EXPORT_FUNC(grub_linuxefi_secure_validate) (void *data, grub_uint32_t size);
0dc71c
+grub_err_t
0dc71c
+EXPORT_FUNC(grub_efi_linux_boot) (void *kernel_address, grub_off_t offset,
0dc71c
+				  void *kernel_param);
0dc71c
+
0dc71c
+#endif /* ! GRUB_EFI_LINUX_HEADER */
0dc71c
-- 
0dc71c
1.9.3
0dc71c