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