diff options
Diffstat (limited to '')
-rw-r--r-- | debian/patches/linuxefi.patch | 552 |
1 files changed, 552 insertions, 0 deletions
diff --git a/debian/patches/linuxefi.patch b/debian/patches/linuxefi.patch new file mode 100644 index 0000000..20fe4de --- /dev/null +++ b/debian/patches/linuxefi.patch @@ -0,0 +1,552 @@ +From fc550c31c25dcf9eb58ca1e987f3ce2be8ebac28 Mon Sep 17 00:00:00 2001 +From: Matthew Garrett <mjg@redhat.com> +Date: Mon, 13 Jan 2014 12:13:15 +0000 +Subject: Add "linuxefi" loader which avoids ExitBootServices + +Origin: vendor, http://pkgs.fedoraproject.org/cgit/grub2.git/tree/grub2-linuxefi.patch +Author: Colin Watson <cjwatson@ubuntu.com> +Author: Steve Langasek <steve.langasek@canonical.com> +Author: Linn Crosetto <linn@hpe.com> +Forwarded: no +Last-Update: 2021-09-24 + +Patch-Name: linuxefi.patch +--- + grub-core/Makefile.core.def | 7 + + grub-core/kern/efi/mm.c | 32 +++ + grub-core/loader/i386/efi/linux.c | 383 ++++++++++++++++++++++++++++++ + grub-core/loader/i386/linux.c | 41 ++++ + include/grub/efi/efi.h | 3 + + 5 files changed, 466 insertions(+) + create mode 100644 grub-core/loader/i386/efi/linux.c + +diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def +index 8022e1c0a..e5b3d27f5 100644 +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -1874,6 +1874,13 @@ module = { + enable = x86_64_efi; + }; + ++module = { ++ name = linuxefi; ++ efi = loader/i386/efi/linux.c; ++ enable = i386_efi; ++ enable = x86_64_efi; ++}; ++ + module = { + name = chain; + efi = loader/efi/chainloader.c; +diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c +index 9838fb2f5..f6aef0ef6 100644 +--- a/grub-core/kern/efi/mm.c ++++ b/grub-core/kern/efi/mm.c +@@ -113,6 +113,38 @@ grub_efi_drop_alloc (grub_efi_physical_address_t address, + } + } + ++/* Allocate pages below a specified address */ ++void * ++grub_efi_allocate_pages_max (grub_efi_physical_address_t max, ++ grub_efi_uintn_t pages) ++{ ++ grub_efi_status_t status; ++ grub_efi_boot_services_t *b; ++ grub_efi_physical_address_t address = max; ++ ++ if (max > 0xffffffff) ++ return 0; ++ ++ b = grub_efi_system_table->boot_services; ++ status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_MAX_ADDRESS, GRUB_EFI_LOADER_DATA, pages, &address); ++ ++ if (status != GRUB_EFI_SUCCESS) ++ return 0; ++ ++ if (address == 0) ++ { ++ /* Uggh, the address 0 was allocated... This is too annoying, ++ so reallocate another one. */ ++ address = max; ++ status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_MAX_ADDRESS, GRUB_EFI_LOADER_DATA, pages, &address); ++ grub_efi_free_pages (0, pages); ++ if (status != GRUB_EFI_SUCCESS) ++ return 0; ++ } ++ ++ return (void *) ((grub_addr_t) address); ++} ++ + /* Allocate pages. Return the pointer to the first of allocated pages. */ + void * + grub_efi_allocate_pages_real (grub_efi_physical_address_t address, +diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c +new file mode 100644 +index 000000000..45b68c05a +--- /dev/null ++++ b/grub-core/loader/i386/efi/linux.c +@@ -0,0 +1,383 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2012 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <grub/loader.h> ++#include <grub/file.h> ++#include <grub/err.h> ++#include <grub/misc.h> ++#include <grub/types.h> ++#include <grub/mm.h> ++#include <grub/cpu/linux.h> ++#include <grub/command.h> ++#include <grub/i18n.h> ++#include <grub/lib/cmdline.h> ++#include <grub/linux.h> ++#include <grub/efi/efi.h> ++#include <grub/efi/sb.h> ++ ++GRUB_MOD_LICENSE ("GPLv3+"); ++ ++static grub_dl_t my_mod; ++static int loaded; ++static void *kernel_mem; ++static grub_uint64_t kernel_size; ++static grub_uint8_t *initrd_mem; ++static grub_uint32_t handover_offset; ++struct linux_kernel_params *params; ++static char *linux_cmdline; ++ ++#define BYTES_TO_PAGES(bytes) (((bytes) + 0xfff) >> 12) ++ ++#define SHIM_LOCK_GUID \ ++ { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} } ++ ++struct grub_efi_shim_lock ++{ ++ grub_efi_status_t (*verify) (void *buffer, grub_uint32_t size); ++}; ++typedef struct grub_efi_shim_lock grub_efi_shim_lock_t; ++ ++static grub_efi_boolean_t ++grub_linuxefi_secure_validate (void *data, grub_uint32_t size) ++{ ++ grub_efi_guid_t guid = SHIM_LOCK_GUID; ++ grub_efi_shim_lock_t *shim_lock; ++ grub_efi_status_t status; ++ ++ if (grub_efi_get_secureboot () != GRUB_EFI_SECUREBOOT_MODE_ENABLED) ++ { ++ grub_dprintf ("linuxefi", "secure boot not enabled, not validating"); ++ return 1; ++ } ++ ++ grub_dprintf ("linuxefi", "Locating shim protocol\n"); ++ shim_lock = grub_efi_locate_protocol(&guid, NULL); ++ ++ if (!shim_lock) ++ { ++ grub_dprintf ("linuxefi", "shim not available\n"); ++ return 0; ++ } ++ ++ grub_dprintf ("linuxefi", "Asking shim to verify kernel signature\n"); ++ status = shim_lock->verify(data, size); ++ if (status == GRUB_EFI_SUCCESS) ++ { ++ grub_dprintf ("linuxefi", "Kernel signature verification passed\n"); ++ return 1; ++ } ++ ++ grub_dprintf ("linuxefi", "Kernel signature verification failed (0x%lx)\n", ++ (unsigned long) status); ++ return 0; ++} ++ ++typedef void(*handover_func)(void *, grub_efi_system_table_t *, struct linux_kernel_params *); ++ ++static grub_err_t ++grub_linuxefi_boot (void) ++{ ++ handover_func hf; ++ int offset = 0; ++ ++#ifdef __x86_64__ ++ offset = 512; ++#endif ++ ++ hf = (handover_func)((char *)kernel_mem + handover_offset + offset); ++ ++ asm volatile ("cli"); ++ ++ hf (grub_efi_image_handle, grub_efi_system_table, params); ++ ++ /* Not reached */ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_linuxefi_unload (void) ++{ ++ grub_dl_unref (my_mod); ++ loaded = 0; ++ if (initrd_mem) ++ grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)initrd_mem, BYTES_TO_PAGES(params->ramdisk_size)); ++ if (linux_cmdline) ++ grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)linux_cmdline, BYTES_TO_PAGES(params->cmdline_size + 1)); ++ if (kernel_mem) ++ grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)kernel_mem, BYTES_TO_PAGES(kernel_size)); ++ if (params) ++ grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)params, BYTES_TO_PAGES(16384)); ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), ++ int argc, char *argv[]) ++{ ++ grub_size_t size = 0; ++ struct grub_linux_initrd_context initrd_ctx; ++ ++ if (argc == 0) ++ { ++ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); ++ goto fail; ++ } ++ ++ if (!loaded) ++ { ++ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first")); ++ goto fail; ++ } ++ ++ if (grub_initrd_init (argc, argv, &initrd_ctx)) ++ goto fail; ++ ++ size = grub_get_initrd_size (&initrd_ctx); ++ ++ initrd_mem = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(size)); ++ ++ if (!initrd_mem) ++ { ++ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate initrd")); ++ goto fail; ++ } ++ ++ grub_dprintf ("linuxefi", "initrd_mem = %lx\n", (unsigned long) initrd_mem); ++ ++ params->ramdisk_size = size; ++ params->ramdisk_image = (grub_uint32_t)(grub_addr_t) initrd_mem; ++ ++ if (grub_initrd_load (&initrd_ctx, argv, initrd_mem)) ++ goto fail; ++ ++ params->ramdisk_size = size; ++ ++ fail: ++ grub_initrd_close (&initrd_ctx); ++ ++ if (initrd_mem && grub_errno) ++ grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)initrd_mem, BYTES_TO_PAGES(size)); ++ ++ return grub_errno; ++} ++ ++static grub_err_t ++grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), ++ int argc, char *argv[]) ++{ ++ grub_file_t file = 0; ++ struct linux_i386_kernel_header lh; ++ grub_ssize_t len, start, filelen; ++ void *kernel; ++ ++ grub_dl_ref (my_mod); ++ ++ if (argc == 0) ++ { ++ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); ++ goto fail; ++ } ++ ++ file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL); ++ if (! file) ++ goto fail; ++ ++ filelen = grub_file_size (file); ++ ++ kernel = grub_malloc(filelen); ++ ++ if (!kernel) ++ { ++ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer")); ++ goto fail; ++ } ++ ++ if (grub_file_read (file, kernel, filelen) != filelen) ++ { ++ grub_error (GRUB_ERR_FILE_READ_ERROR, N_("Can't read kernel %s"), argv[0]); ++ goto fail; ++ } ++ ++ if (! grub_linuxefi_secure_validate (kernel, filelen)) ++ { ++ grub_error (GRUB_ERR_ACCESS_DENIED, N_("%s has invalid signature"), argv[0]); ++ grub_free (kernel); ++ goto fail; ++ } ++ ++ grub_file_seek (file, 0); ++ ++ grub_free(kernel); ++ ++ params = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(16384)); ++ ++ if (! params) ++ { ++ grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters"); ++ goto fail; ++ } ++ ++ grub_dprintf ("linuxefi", "params = %lx\n", (unsigned long) params); ++ ++ grub_memset (params, 0, 16384); ++ ++ if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh)) ++ { ++ if (!grub_errno) ++ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), ++ argv[0]); ++ goto fail; ++ } ++ ++ if (lh.boot_flag != grub_cpu_to_le16 (0xaa55)) ++ { ++ grub_error (GRUB_ERR_BAD_OS, N_("invalid magic number")); ++ goto fail; ++ } ++ ++ if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS) ++ { ++ grub_error (GRUB_ERR_BAD_OS, N_("too many setup sectors")); ++ goto fail; ++ } ++ ++ if (lh.version < grub_cpu_to_le16 (0x020b)) ++ { ++ grub_error (GRUB_ERR_BAD_OS, N_("kernel too old")); ++ goto fail; ++ } ++ ++ if (!lh.handover_offset) ++ { ++ grub_error (GRUB_ERR_BAD_OS, N_("kernel doesn't support EFI handover")); ++ goto fail; ++ } ++ ++ linux_cmdline = grub_efi_allocate_pages_max(0x3fffffff, ++ BYTES_TO_PAGES(lh.cmdline_size + 1)); ++ ++ if (!linux_cmdline) ++ { ++ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate cmdline")); ++ goto fail; ++ } ++ ++ grub_dprintf ("linuxefi", "linux_cmdline = %lx\n", ++ (unsigned long) linux_cmdline); ++ ++ grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE)); ++ { ++ grub_err_t err; ++ err = grub_create_loader_cmdline (argc, argv, ++ linux_cmdline ++ + sizeof (LINUX_IMAGE) - 1, ++ lh.cmdline_size ++ - (sizeof (LINUX_IMAGE) - 1), ++ GRUB_VERIFY_KERNEL_CMDLINE); ++ if (err) ++ goto fail; ++ } ++ ++ lh.cmd_line_ptr = (grub_uint32_t)(grub_addr_t)linux_cmdline; ++ ++ handover_offset = lh.handover_offset; ++ ++ start = (lh.setup_sects + 1) * 512; ++ len = grub_file_size(file) - start; ++ ++ kernel_mem = grub_efi_allocate_fixed(lh.pref_address, ++ BYTES_TO_PAGES(lh.init_size)); ++ ++ if (!kernel_mem) ++ kernel_mem = grub_efi_allocate_pages_max(0x3fffffff, ++ BYTES_TO_PAGES(lh.init_size)); ++ ++ if (!kernel_mem) ++ { ++ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate kernel")); ++ goto fail; ++ } ++ grub_errno = GRUB_ERR_NONE; ++ ++ grub_dprintf ("linuxefi", "kernel_mem = %lx\n", (unsigned long) kernel_mem); ++ ++ if (grub_file_seek (file, start) == (grub_off_t) -1) ++ { ++ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), ++ argv[0]); ++ goto fail; ++ } ++ ++ if (grub_file_read (file, kernel_mem, len) != len && !grub_errno) ++ { ++ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), ++ argv[0]); ++ } ++ ++ if (grub_errno == GRUB_ERR_NONE) ++ { ++ grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0); ++ loaded = 1; ++ lh.code32_start = (grub_uint32_t)(grub_addr_t) kernel_mem; ++ } ++ ++ grub_memcpy (params, &lh, 2 * 512); ++ ++ params->type_of_loader = 0x21; ++ ++ fail: ++ ++ if (file) ++ grub_file_close (file); ++ ++ if (grub_errno != GRUB_ERR_NONE) ++ { ++ grub_dl_unref (my_mod); ++ loaded = 0; ++ } ++ ++ if (linux_cmdline && !loaded) ++ grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)linux_cmdline, BYTES_TO_PAGES(lh.cmdline_size + 1)); ++ ++ if (kernel_mem && !loaded) ++ grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)kernel_mem, BYTES_TO_PAGES(kernel_size)); ++ ++ if (params && !loaded) ++ grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)params, BYTES_TO_PAGES(16384)); ++ ++ return grub_errno; ++} ++ ++static grub_command_t cmd_linux, cmd_initrd; ++ ++GRUB_MOD_INIT(linuxefi) ++{ ++ cmd_linux = ++ grub_register_command ("linuxefi", grub_cmd_linux, ++ 0, N_("Load Linux.")); ++ cmd_initrd = ++ grub_register_command ("initrdefi", grub_cmd_initrd, ++ 0, N_("Load initrd.")); ++ my_mod = mod; ++} ++ ++GRUB_MOD_FINI(linuxefi) ++{ ++ grub_unregister_command (cmd_linux); ++ grub_unregister_command (cmd_initrd); ++} +diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c +index 9f74a96b1..be37a1640 100644 +--- a/grub-core/loader/i386/linux.c ++++ b/grub-core/loader/i386/linux.c +@@ -78,6 +78,8 @@ static grub_size_t maximal_cmdline_size; + static struct linux_kernel_params linux_params; + static char *linux_cmdline; + #ifdef GRUB_MACHINE_EFI ++static int using_linuxefi; ++static grub_command_t initrdefi_cmd; + static grub_efi_uintn_t efi_mmap_size; + #else + static const grub_size_t efi_mmap_size = 0; +@@ -659,6 +661,39 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + + grub_dl_ref (my_mod); + ++#ifdef GRUB_MACHINE_EFI ++ using_linuxefi = 0; ++ if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) ++ { ++ /* linuxefi requires a successful signature check and then hand over ++ to the kernel without calling ExitBootServices. */ ++ grub_dl_t mod; ++ grub_command_t linuxefi_cmd; ++ ++ grub_dprintf ("linux", "Secure Boot enabled: trying linuxefi\n"); ++ ++ mod = grub_dl_load ("linuxefi"); ++ if (mod) ++ { ++ grub_dl_ref (mod); ++ linuxefi_cmd = grub_command_find ("linuxefi"); ++ initrdefi_cmd = grub_command_find ("initrdefi"); ++ if (linuxefi_cmd && initrdefi_cmd) ++ { ++ (linuxefi_cmd->func) (linuxefi_cmd, argc, argv); ++ if (grub_errno == GRUB_ERR_NONE) ++ { ++ grub_dprintf ("linux", "Handing off to linuxefi\n"); ++ using_linuxefi = 1; ++ return GRUB_ERR_NONE; ++ } ++ grub_dprintf ("linux", "linuxefi failed (%d)\n", grub_errno); ++ goto fail; ++ } ++ } ++ } ++#endif ++ + if (argc == 0) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); +@@ -1042,6 +1077,12 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), + grub_err_t err; + struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 }; + ++#ifdef GRUB_MACHINE_EFI ++ /* If we're using linuxefi, just forward to initrdefi. */ ++ if (using_linuxefi && initrdefi_cmd) ++ return (initrdefi_cmd->func) (initrdefi_cmd, argc, argv); ++#endif ++ + if (argc == 0) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); +diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h +index 83d958f99..08f6ee00a 100644 +--- a/include/grub/efi/efi.h ++++ b/include/grub/efi/efi.h +@@ -47,6 +47,9 @@ EXPORT_FUNC(grub_efi_allocate_fixed) (grub_efi_physical_address_t address, + grub_efi_uintn_t pages); + void * + EXPORT_FUNC(grub_efi_allocate_any_pages) (grub_efi_uintn_t pages); ++void * ++EXPORT_FUNC(grub_efi_allocate_pages_max) (grub_efi_physical_address_t max, ++ grub_efi_uintn_t pages); + void EXPORT_FUNC(grub_efi_free_pages) (grub_efi_physical_address_t address, + grub_efi_uintn_t pages); + grub_efi_uintn_t EXPORT_FUNC(grub_efi_find_mmap_size) (void); |