7027325
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
7027325
From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
7027325
Date: Mon, 2 Aug 2021 23:10:01 +1000
7027325
Subject: [PATCH] arm64: Fix EFI loader kernel image allocation
7027325
7027325
We are currently allocating just enough memory for the file size,
7027325
which means that the kernel BSS is in limbo (and not even zeroed).
7027325
7027325
We are also not honoring the alignment specified in the image
7027325
PE header.
7027325
7027325
This makes us use the PE optional header in which the kernel puts the
7027325
actual size it needs, including BSS, and make sure we clear it, and
7027325
honors the specified alignment for the image.
7027325
7027325
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
e602a06
[pjones: arm: check for the PE magic for the compiled arch]
e602a06
Signed-off-by: Peter Jones <pjones@redhat.com>
e602a06
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
7027325
---
7027325
 grub-core/loader/arm64/linux.c | 100 +++++++++++++++++++++++++++--------------
e602a06
 include/grub/arm/linux.h       |   1 +
e602a06
 include/grub/arm64/linux.h     |   1 +
e602a06
 3 files changed, 68 insertions(+), 34 deletions(-)
7027325
7027325
diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c
e622855
index 47f8cf0d84..f18d90bd74 100644
7027325
--- a/grub-core/loader/arm64/linux.c
7027325
+++ b/grub-core/loader/arm64/linux.c
7027325
@@ -41,6 +41,8 @@ GRUB_MOD_LICENSE ("GPLv3+");
7027325
 static grub_dl_t my_mod;
7027325
 static int loaded;
7027325
 
7027325
+static void *kernel_alloc_addr;
7027325
+static grub_uint32_t kernel_alloc_pages;
7027325
 static void *kernel_addr;
7027325
 static grub_uint64_t kernel_size;
7027325
 static grub_uint32_t handover_offset;
7027325
@@ -204,9 +206,8 @@ grub_linux_unload (void)
7027325
 			 GRUB_EFI_BYTES_TO_PAGES (initrd_end - initrd_start));
7027325
   initrd_start = initrd_end = 0;
7027325
   grub_free (linux_args);
7027325
-  if (kernel_addr)
7027325
-    grub_efi_free_pages ((grub_addr_t) kernel_addr,
7027325
-			 GRUB_EFI_BYTES_TO_PAGES (kernel_size));
7027325
+  if (kernel_alloc_addr)
7027325
+    grub_efi_free_pages ((grub_addr_t) kernel_alloc_addr, kernel_alloc_pages);
7027325
   grub_fdt_unload ();
7027325
   return GRUB_ERR_NONE;
7027325
 }
7027325
@@ -311,14 +312,35 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
7027325
   return grub_errno;
7027325
 }
7027325
 
7027325
+static grub_err_t
7027325
+parse_pe_header (void *kernel, grub_uint64_t *total_size,
7027325
+		 grub_uint32_t *entry_offset,
7027325
+		 grub_uint32_t *alignment)
7027325
+{
7027325
+  struct linux_arch_kernel_header *lh = kernel;
7027325
+  struct grub_armxx_linux_pe_header *pe;
7027325
+
7027325
+  pe = (void *)((unsigned long)kernel + lh->hdr_offset);
7027325
+
e602a06
+  if (pe->opt.magic != GRUB_PE32_PEXX_MAGIC)
7027325
+    return grub_error(GRUB_ERR_BAD_OS, "Invalid PE optional header magic");
7027325
+
7027325
+  *total_size   = pe->opt.image_size;
7027325
+  *entry_offset = pe->opt.entry_addr;
7027325
+  *alignment    = pe->opt.section_alignment;
7027325
+
7027325
+  return GRUB_ERR_NONE;
7027325
+}
7027325
+
7027325
 static grub_err_t
7027325
 grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
7027325
 		int argc, char *argv[])
7027325
 {
7027325
   grub_file_t file = 0;
7027325
-  struct linux_arch_kernel_header lh;
7027325
-  struct grub_armxx_linux_pe_header *pe;
7027325
   grub_err_t err;
7027325
+  grub_off_t filelen;
7027325
+  grub_uint32_t align;
7027325
+  void *kernel = NULL;
7027325
   int rc;
7027325
 
7027325
   grub_dl_ref (my_mod);
7027325
@@ -333,40 +355,24 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
7027325
   if (!file)
7027325
     goto fail;
7027325
 
7027325
-  kernel_size = grub_file_size (file);
7027325
-
7027325
-  if (grub_file_read (file, &lh, sizeof (lh)) < (long) sizeof (lh))
7027325
-    return grub_errno;
7027325
-
7027325
-  if (grub_arch_efi_linux_check_image (&lh) != GRUB_ERR_NONE)
7027325
-    goto fail;
7027325
-
7027325
-  grub_loader_unset();
7027325
-
7027325
-  grub_dprintf ("linux", "kernel file size: %lld\n", (long long) kernel_size);
7027325
-  kernel_addr = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (kernel_size));
7027325
-  grub_dprintf ("linux", "kernel numpages: %lld\n",
7027325
-		(long long) GRUB_EFI_BYTES_TO_PAGES (kernel_size));
7027325
-  if (!kernel_addr)
7027325
+  filelen = grub_file_size (file);
7027325
+  kernel = grub_malloc(filelen);
7027325
+  if (!kernel)
7027325
     {
7027325
-      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
7027325
+      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel load buffer"));
7027325
       goto fail;
7027325
     }
7027325
 
7027325
-  grub_file_seek (file, 0);
7027325
-  if (grub_file_read (file, kernel_addr, kernel_size)
7027325
-      < (grub_int64_t) kernel_size)
7027325
+  if (grub_file_read (file, kernel, filelen) < (grub_ssize_t)filelen)
7027325
     {
7027325
-      if (!grub_errno)
7027325
-	grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), argv[0]);
7027325
+      grub_error (GRUB_ERR_FILE_READ_ERROR, N_("Can't read kernel %s"),
7027325
+		  argv[0]);
7027325
       goto fail;
7027325
     }
7027325
 
7027325
-  grub_dprintf ("linux", "kernel @ %p\n", kernel_addr);
7027325
-
7027325
   if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
7027325
     {
7027325
-      rc = grub_linuxefi_secure_validate (kernel_addr, kernel_size);
7027325
+      rc = grub_linuxefi_secure_validate (kernel, filelen);
7027325
       if (rc <= 0)
7027325
 	{
7027325
 	  grub_error (GRUB_ERR_INVALID_COMMAND,
7027325
@@ -375,8 +381,32 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
7027325
 	}
7027325
     }
7027325
 
7027325
-  pe = (void *)((unsigned long)kernel_addr + lh.hdr_offset);
7027325
-  handover_offset = pe->opt.entry_addr;
7027325
+  if (grub_arch_efi_linux_check_image (kernel) != GRUB_ERR_NONE)
7027325
+    goto fail;
7027325
+  if (parse_pe_header (kernel, &kernel_size, &handover_offset, &align) != GRUB_ERR_NONE)
7027325
+    goto fail;
7027325
+  grub_dprintf ("linux", "kernel mem size     : %lld\n", (long long) kernel_size);
7027325
+  grub_dprintf ("linux", "kernel entry offset : %d\n", handover_offset);
7027325
+  grub_dprintf ("linux", "kernel alignment    : 0x%x\n", align);
7027325
+
7027325
+  grub_loader_unset();
7027325
+
7027325
+  kernel_alloc_pages = GRUB_EFI_BYTES_TO_PAGES (kernel_size + align - 1);
7027325
+  kernel_alloc_addr = grub_efi_allocate_any_pages (kernel_alloc_pages);
7027325
+  grub_dprintf ("linux", "kernel numpages: %d\n", kernel_alloc_pages);
7027325
+  if (!kernel_alloc_addr)
7027325
+    {
7027325
+      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
7027325
+      goto fail;
7027325
+    }
7027325
+  kernel_addr = (void *)ALIGN_UP((grub_uint64_t)kernel_alloc_addr, align);
7027325
+
7027325
+  grub_dprintf ("linux", "kernel @ %p\n", kernel_addr);
7027325
+  grub_memcpy (kernel_addr, kernel, grub_min(filelen, kernel_size));
7027325
+  if (kernel_size > filelen)
7027325
+    grub_memset ((char *)kernel_addr + filelen, 0, kernel_size - filelen);
7027325
+  grub_free(kernel);
7027325
+  kernel = NULL;
7027325
 
7027325
   cmdline_size = grub_loader_cmdline_size (argc, argv) + sizeof (LINUX_IMAGE);
7027325
   linux_args = grub_malloc (cmdline_size);
7027325
@@ -400,6 +430,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
7027325
     }
7027325
 
7027325
 fail:
7027325
+  if (kernel)
7027325
+    grub_free (kernel);
7027325
+
7027325
   if (file)
7027325
     grub_file_close (file);
7027325
 
7027325
@@ -412,9 +445,8 @@ fail:
7027325
   if (linux_args && !loaded)
7027325
     grub_free (linux_args);
7027325
 
7027325
-  if (kernel_addr && !loaded)
7027325
-    grub_efi_free_pages ((grub_addr_t) kernel_addr,
7027325
-			 GRUB_EFI_BYTES_TO_PAGES (kernel_size));
7027325
+  if (kernel_alloc_addr && !loaded)
7027325
+    grub_efi_free_pages ((grub_addr_t) kernel_alloc_addr, kernel_alloc_pages);
7027325
 
7027325
   return grub_errno;
7027325
 }
e602a06
diff --git a/include/grub/arm/linux.h b/include/grub/arm/linux.h
e622855
index b582f67f66..966a5074f5 100644
e602a06
--- a/include/grub/arm/linux.h
e602a06
+++ b/include/grub/arm/linux.h
e602a06
@@ -44,6 +44,7 @@ struct grub_arm_linux_pe_header
e602a06
 
e602a06
 #if defined(__arm__)
e602a06
 # define GRUB_LINUX_ARMXX_MAGIC_SIGNATURE GRUB_LINUX_ARM_MAGIC_SIGNATURE
e602a06
+# define GRUB_PE32_PEXX_MAGIC GRUB_PE32_PE32_MAGIC
e602a06
 # define linux_arch_kernel_header linux_arm_kernel_header
e602a06
 # define grub_armxx_linux_pe_header grub_arm_linux_pe_header
e602a06
 #endif
e602a06
diff --git a/include/grub/arm64/linux.h b/include/grub/arm64/linux.h
e622855
index ea030312df..422bf2bf24 100644
e602a06
--- a/include/grub/arm64/linux.h
e602a06
+++ b/include/grub/arm64/linux.h
e602a06
@@ -48,6 +48,7 @@ struct grub_arm64_linux_pe_header
e602a06
 
e602a06
 #if defined(__aarch64__)
e602a06
 # define GRUB_LINUX_ARMXX_MAGIC_SIGNATURE GRUB_LINUX_ARM64_MAGIC_SIGNATURE
e602a06
+# define GRUB_PE32_PEXX_MAGIC GRUB_PE32_PE64_MAGIC
e602a06
 # define linux_arch_kernel_header linux_arm64_kernel_header
e602a06
 # define grub_armxx_linux_pe_header grub_arm64_linux_pe_header
e602a06
 #endif