nalika / rpms / grub2

Forked from rpms/grub2 2 years ago
Clone

Blame SOURCES/0185-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>
b35c50
[pjones: arm: check for the PE magic for the compiled arch]
b35c50
Signed-off-by: Peter Jones <pjones@redhat.com>
b35c50
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
8e15ce
---
8e15ce
 grub-core/loader/arm64/linux.c | 100 +++++++++++++++++++++++++++--------------
b35c50
 include/grub/arm/linux.h       |   1 +
b35c50
 include/grub/arm64/linux.h     |   1 +
b35c50
 3 files changed, 68 insertions(+), 34 deletions(-)
8e15ce
8e15ce
diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c
b35c50
index 47f8cf0d84..f18d90bd74 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
+
b35c50
+  if (pe->opt.magic != GRUB_PE32_PEXX_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
 }
b35c50
diff --git a/include/grub/arm/linux.h b/include/grub/arm/linux.h
b35c50
index b582f67f66..966a5074f5 100644
b35c50
--- a/include/grub/arm/linux.h
b35c50
+++ b/include/grub/arm/linux.h
b35c50
@@ -44,6 +44,7 @@ struct grub_arm_linux_pe_header
b35c50
 
b35c50
 #if defined(__arm__)
b35c50
 # define GRUB_LINUX_ARMXX_MAGIC_SIGNATURE GRUB_LINUX_ARM_MAGIC_SIGNATURE
b35c50
+# define GRUB_PE32_PEXX_MAGIC GRUB_PE32_PE32_MAGIC
b35c50
 # define linux_arch_kernel_header linux_arm_kernel_header
b35c50
 # define grub_armxx_linux_pe_header grub_arm_linux_pe_header
b35c50
 #endif
b35c50
diff --git a/include/grub/arm64/linux.h b/include/grub/arm64/linux.h
b35c50
index ea030312df..422bf2bf24 100644
b35c50
--- a/include/grub/arm64/linux.h
b35c50
+++ b/include/grub/arm64/linux.h
b35c50
@@ -48,6 +48,7 @@ struct grub_arm64_linux_pe_header
b35c50
 
b35c50
 #if defined(__aarch64__)
b35c50
 # define GRUB_LINUX_ARMXX_MAGIC_SIGNATURE GRUB_LINUX_ARM64_MAGIC_SIGNATURE
b35c50
+# define GRUB_PE32_PEXX_MAGIC GRUB_PE32_PE64_MAGIC
b35c50
 # define linux_arch_kernel_header linux_arm64_kernel_header
b35c50
 # define grub_armxx_linux_pe_header grub_arm64_linux_pe_header
b35c50
 #endif