Blame SOURCES/0216-arm64-Fix-EFI-loader-kernel-image-allocation.patch

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