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

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