597 lines
18 KiB
Diff
597 lines
18 KiB
Diff
|
From 06ff1079788fedac5e3f1f12ed7bbe69228a7ae0 Mon Sep 17 00:00:00 2001
|
||
|
From: Michael Chang <mchang@suse.com>
|
||
|
Date: Tue, 18 Dec 2012 16:54:03 +0800
|
||
|
Subject: [PATCH] Add secureboot support on efi chainloader
|
||
|
|
||
|
References: fate#314485
|
||
|
Patch-Mainline: no
|
||
|
|
||
|
Expand the chainloader to be able to verify the image by means of shim
|
||
|
lock protocol. The PE/COFF image is loaded and relocated by the
|
||
|
chainloader instead of calling LoadImage and StartImage UEFI boot
|
||
|
Service as they require positive verification result from keys enrolled
|
||
|
in KEK or DB. The shim will use MOK in addition to firmware enrolled
|
||
|
keys to verify the image.
|
||
|
|
||
|
The chainloader module could be used to load other UEFI bootloaders,
|
||
|
such as xen.efi, and could be signed by any of MOK, KEK or DB.
|
||
|
|
||
|
v1:
|
||
|
Use grub_efi_get_secureboot to get secure boot status
|
||
|
|
||
|
Signed-off-by: Michael Chang <mchang@suse.com>
|
||
|
---
|
||
|
grub-core/loader/efi/chainloader.c | 538 +++++++++++++++++++++++++++++++++--
|
||
|
1 files changed, 507 insertions(+), 31 deletions(-)
|
||
|
|
||
|
--- a/grub-core/loader/efi/chainloader.c
|
||
|
+++ b/grub-core/loader/efi/chainloader.c
|
||
|
@@ -41,10 +41,24 @@
|
||
|
#include <grub/i386/macho.h>
|
||
|
#endif
|
||
|
|
||
|
+#ifdef __x86_64__
|
||
|
+#define SUPPORT_SECURE_BOOT
|
||
|
+#endif
|
||
|
+
|
||
|
+#ifdef SUPPORT_SECURE_BOOT
|
||
|
+#include <grub/efi/pe32.h>
|
||
|
+#include <grub/efi/sb.h>
|
||
|
+#endif
|
||
|
+
|
||
|
GRUB_MOD_LICENSE ("GPLv3+");
|
||
|
|
||
|
static grub_dl_t my_mod;
|
||
|
|
||
|
+#ifdef SUPPORT_SECURE_BOOT
|
||
|
+static grub_efi_boolean_t debug_secureboot = 0;
|
||
|
+static grub_efi_status_t (__grub_efi_api *entry_point) (grub_efi_handle_t image_handle, grub_efi_system_table_t *system_table);
|
||
|
+#endif
|
||
|
+
|
||
|
static grub_err_t
|
||
|
grub_chainloader_unload (void *context)
|
||
|
{
|
||
|
@@ -209,6 +223,421 @@
|
||
|
return file_path;
|
||
|
}
|
||
|
|
||
|
+#ifdef SUPPORT_SECURE_BOOT
|
||
|
+#define SHIM_LOCK_GUID \
|
||
|
+ { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} }
|
||
|
+
|
||
|
+struct grub_pe32_header_no_msdos_stub
|
||
|
+{
|
||
|
+ char signature[GRUB_PE32_SIGNATURE_SIZE];
|
||
|
+ struct grub_pe32_coff_header coff_header;
|
||
|
+ struct grub_pe64_optional_header optional_header;
|
||
|
+};
|
||
|
+
|
||
|
+struct pe_coff_loader_image_context
|
||
|
+{
|
||
|
+ grub_efi_uint64_t image_address;
|
||
|
+ grub_efi_uint64_t image_size;
|
||
|
+ grub_efi_uint64_t entry_point;
|
||
|
+ grub_efi_uintn_t size_of_headers;
|
||
|
+ grub_efi_uint16_t image_type;
|
||
|
+ grub_efi_uint16_t number_of_sections;
|
||
|
+ struct grub_pe32_section_table *first_section;
|
||
|
+ struct grub_pe32_data_directory *reloc_dir;
|
||
|
+ struct grub_pe32_data_directory *sec_dir;
|
||
|
+ grub_efi_uint64_t number_of_rva_and_sizes;
|
||
|
+ struct grub_pe32_header_no_msdos_stub *pe_hdr;
|
||
|
+};
|
||
|
+
|
||
|
+struct grub_secureboot_chainloader_context
|
||
|
+{
|
||
|
+ grub_efi_physical_address_t address;
|
||
|
+ grub_efi_uintn_t pages;
|
||
|
+ grub_ssize_t fsize;
|
||
|
+ grub_efi_device_path_t *file_path;
|
||
|
+ grub_efi_char16_t *cmdline;
|
||
|
+ grub_ssize_t cmdline_len;
|
||
|
+ grub_efi_handle_t dev_handle;
|
||
|
+};
|
||
|
+
|
||
|
+typedef struct pe_coff_loader_image_context pe_coff_loader_image_context_t;
|
||
|
+
|
||
|
+struct grub_efi_shim_lock
|
||
|
+{
|
||
|
+ grub_efi_status_t (*verify)(void *buffer,
|
||
|
+ grub_efi_uint32_t size);
|
||
|
+ grub_efi_status_t (*hash)(void *data,
|
||
|
+ grub_efi_int32_t datasize,
|
||
|
+ pe_coff_loader_image_context_t *context,
|
||
|
+ grub_efi_uint8_t *sha256hash,
|
||
|
+ grub_efi_uint8_t *sha1hash);
|
||
|
+ grub_efi_status_t (*context)(void *data,
|
||
|
+ grub_efi_uint32_t size,
|
||
|
+ pe_coff_loader_image_context_t *context);
|
||
|
+};
|
||
|
+
|
||
|
+typedef struct grub_efi_shim_lock grub_efi_shim_lock_t;
|
||
|
+
|
||
|
+static grub_efi_boolean_t
|
||
|
+grub_secure_validate (void *data, grub_efi_uint32_t size)
|
||
|
+{
|
||
|
+ grub_guid_t guid = SHIM_LOCK_GUID;
|
||
|
+ grub_efi_shim_lock_t *shim_lock;
|
||
|
+
|
||
|
+ shim_lock = grub_efi_locate_protocol (&guid, NULL);
|
||
|
+
|
||
|
+ if (!shim_lock)
|
||
|
+ {
|
||
|
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "no shim lock protocol");
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (shim_lock->verify (data, size) == GRUB_EFI_SUCCESS)
|
||
|
+ {
|
||
|
+ grub_dprintf ("chain", "verify success\n");
|
||
|
+ return 1;
|
||
|
+ }
|
||
|
+
|
||
|
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "verify failed");
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static grub_efi_boolean_t
|
||
|
+read_header (void *data, grub_efi_uint32_t size, pe_coff_loader_image_context_t *context)
|
||
|
+{
|
||
|
+ grub_efi_guid_t guid = SHIM_LOCK_GUID;
|
||
|
+ grub_efi_shim_lock_t *shim_lock;
|
||
|
+ grub_efi_status_t status;
|
||
|
+
|
||
|
+ shim_lock = grub_efi_locate_protocol (&guid, NULL);
|
||
|
+
|
||
|
+ if (!shim_lock)
|
||
|
+ {
|
||
|
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "no shim lock protocol");
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ status = shim_lock->context (data, size, context);
|
||
|
+
|
||
|
+ if (status == GRUB_EFI_SUCCESS)
|
||
|
+ {
|
||
|
+ grub_dprintf ("chain", "context success\n");
|
||
|
+ return 1;
|
||
|
+ }
|
||
|
+
|
||
|
+ switch (status)
|
||
|
+ {
|
||
|
+ case GRUB_EFI_UNSUPPORTED:
|
||
|
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "context error unsupported");
|
||
|
+ break;
|
||
|
+ case GRUB_EFI_INVALID_PARAMETER:
|
||
|
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "context error invalid parameter");
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "context error code");
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static void*
|
||
|
+image_address (void *image, grub_efi_uint64_t sz, grub_efi_uint64_t adr)
|
||
|
+{
|
||
|
+ if (adr > sz)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ return ((grub_uint8_t*)image + adr);
|
||
|
+}
|
||
|
+
|
||
|
+static grub_efi_status_t
|
||
|
+relocate_coff (pe_coff_loader_image_context_t *context, void *data)
|
||
|
+{
|
||
|
+ struct grub_pe32_data_directory *reloc_base, *reloc_base_end;
|
||
|
+ grub_efi_uint64_t adjust;
|
||
|
+ grub_efi_uint16_t *reloc, *reloc_end;
|
||
|
+ char *fixup, *fixup_base, *fixup_data = NULL;
|
||
|
+ grub_efi_uint16_t *fixup_16;
|
||
|
+ grub_efi_uint32_t *fixup_32;
|
||
|
+ grub_efi_uint64_t *fixup_64;
|
||
|
+
|
||
|
+ grub_efi_uint64_t size = context->image_size;
|
||
|
+ void *image_end = (char *)data + size;
|
||
|
+
|
||
|
+ context->pe_hdr->optional_header.image_base = (grub_uint64_t)data;
|
||
|
+
|
||
|
+ if (context->number_of_rva_and_sizes <= 5 || context->reloc_dir->size == 0)
|
||
|
+ {
|
||
|
+ grub_dprintf ("chain", "no need to reloc, we are done\n");
|
||
|
+ return GRUB_EFI_SUCCESS;
|
||
|
+ }
|
||
|
+
|
||
|
+ reloc_base = image_address (data, size, context->reloc_dir->rva);
|
||
|
+ reloc_base_end = image_address (data, size, context->reloc_dir->rva + context->reloc_dir->size -1);
|
||
|
+
|
||
|
+ grub_dprintf ("chain", "reloc_base %p reloc_base_end %p\n", reloc_base, reloc_base_end);
|
||
|
+
|
||
|
+ if (!reloc_base || !reloc_base_end)
|
||
|
+ {
|
||
|
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc table overflows binary");
|
||
|
+ return GRUB_EFI_UNSUPPORTED;
|
||
|
+ }
|
||
|
+
|
||
|
+ adjust = (grub_uint64_t)data - context->image_address;
|
||
|
+
|
||
|
+ while (reloc_base < reloc_base_end)
|
||
|
+ {
|
||
|
+ reloc = (grub_uint16_t *)((char*)reloc_base + sizeof (struct grub_pe32_data_directory));
|
||
|
+ reloc_end = (grub_uint16_t *)((char*)reloc_base + reloc_base->size);
|
||
|
+
|
||
|
+ if ((void *)reloc_end < data || (void *)reloc_end > image_end)
|
||
|
+ {
|
||
|
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc table overflows binary");
|
||
|
+ return GRUB_EFI_UNSUPPORTED;
|
||
|
+ }
|
||
|
+
|
||
|
+ fixup_base = image_address(data, size, reloc_base->rva);
|
||
|
+
|
||
|
+ if (!fixup_base)
|
||
|
+ {
|
||
|
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid fixupbase");
|
||
|
+ return GRUB_EFI_UNSUPPORTED;
|
||
|
+ }
|
||
|
+
|
||
|
+ while (reloc < reloc_end)
|
||
|
+ {
|
||
|
+ fixup = fixup_base + (*reloc & 0xFFF);
|
||
|
+ switch ((*reloc) >> 12)
|
||
|
+ {
|
||
|
+ case GRUB_PE32_REL_BASED_ABSOLUTE:
|
||
|
+ break;
|
||
|
+ case GRUB_PE32_REL_BASED_HIGH:
|
||
|
+ fixup_16 = (grub_uint16_t *)fixup;
|
||
|
+ *fixup_16 = (grub_uint16_t) (*fixup_16 + ((grub_uint16_t)((grub_uint32_t)adjust >> 16)));
|
||
|
+ if (fixup_data != NULL)
|
||
|
+ {
|
||
|
+ *(grub_uint16_t *) fixup_data = *fixup_16;
|
||
|
+ fixup_data = fixup_data + sizeof (grub_uint16_t);
|
||
|
+ }
|
||
|
+ break;
|
||
|
+ case GRUB_PE32_REL_BASED_LOW:
|
||
|
+ fixup_16 = (grub_uint16_t *)fixup;
|
||
|
+ *fixup_16 = (grub_uint16_t) (*fixup_16 + (grub_uint16_t)adjust );
|
||
|
+ if (fixup_data != NULL)
|
||
|
+ {
|
||
|
+ *(grub_uint16_t *) fixup_data = *fixup_16;
|
||
|
+ fixup_data = fixup_data + sizeof (grub_uint16_t);
|
||
|
+ }
|
||
|
+ break;
|
||
|
+ case GRUB_PE32_REL_BASED_HIGHLOW:
|
||
|
+ fixup_32 = (grub_uint32_t *)fixup;
|
||
|
+ *fixup_32 = *fixup_32 + (grub_uint32_t)adjust;
|
||
|
+ if (fixup_data != NULL)
|
||
|
+ {
|
||
|
+ fixup_data = (char *)ALIGN_UP ((grub_addr_t)fixup_data, sizeof (grub_uint32_t));
|
||
|
+ *(grub_uint32_t *) fixup_data = *fixup_32;
|
||
|
+ fixup_data += sizeof (grub_uint32_t);
|
||
|
+ }
|
||
|
+ break;
|
||
|
+ case GRUB_PE32_REL_BASED_DIR64:
|
||
|
+ fixup_64 = (grub_uint64_t *)fixup;
|
||
|
+ *fixup_64 = *fixup_64 + (grub_uint64_t)adjust;
|
||
|
+ if (fixup_data != NULL)
|
||
|
+ {
|
||
|
+ fixup_data = (char *)ALIGN_UP ((grub_addr_t)fixup_data, sizeof (grub_uint64_t));
|
||
|
+ *(grub_uint64_t *) fixup_data = *fixup_64;
|
||
|
+ fixup_data += sizeof (grub_uint64_t);
|
||
|
+ }
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown relocation");
|
||
|
+ return GRUB_EFI_UNSUPPORTED;
|
||
|
+ }
|
||
|
+ reloc += 1;
|
||
|
+ }
|
||
|
+ reloc_base = (struct grub_pe32_data_directory *)reloc_end;
|
||
|
+ }
|
||
|
+
|
||
|
+ return GRUB_EFI_SUCCESS;
|
||
|
+}
|
||
|
+
|
||
|
+static grub_efi_device_path_t *
|
||
|
+grub_efi_get_media_file_path (grub_efi_device_path_t *dp)
|
||
|
+{
|
||
|
+ while (1)
|
||
|
+ {
|
||
|
+ grub_efi_uint8_t type;
|
||
|
+ grub_efi_uint8_t subtype;
|
||
|
+
|
||
|
+ if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp))
|
||
|
+ break;
|
||
|
+
|
||
|
+ type = GRUB_EFI_DEVICE_PATH_TYPE (dp);
|
||
|
+ subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp);
|
||
|
+
|
||
|
+ if (type == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE && subtype == GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE)
|
||
|
+ return dp;
|
||
|
+
|
||
|
+ dp = GRUB_EFI_NEXT_DEVICE_PATH (dp);
|
||
|
+ }
|
||
|
+
|
||
|
+ return NULL;
|
||
|
+}
|
||
|
+
|
||
|
+static grub_efi_boolean_t
|
||
|
+handle_image (struct grub_secureboot_chainloader_context *load_context)
|
||
|
+{
|
||
|
+ grub_efi_boot_services_t *b;
|
||
|
+ grub_efi_loaded_image_t *li, li_bak;
|
||
|
+ grub_efi_status_t efi_status;
|
||
|
+ void *data = (void *)(unsigned long)load_context->address;
|
||
|
+ grub_efi_uint32_t datasize = load_context->fsize;
|
||
|
+ char *buffer = NULL;
|
||
|
+ char *buffer_aligned = NULL;
|
||
|
+ grub_efi_uint32_t i, size;
|
||
|
+ struct grub_pe32_section_table *section;
|
||
|
+ char *base, *end;
|
||
|
+ pe_coff_loader_image_context_t context;
|
||
|
+ grub_uint32_t section_alignment;
|
||
|
+ grub_uint32_t buffer_size;
|
||
|
+
|
||
|
+ b = grub_efi_system_table->boot_services;
|
||
|
+
|
||
|
+ if (read_header (data, datasize, &context))
|
||
|
+ {
|
||
|
+ grub_dprintf ("chain", "Succeed to read header\n");
|
||
|
+ }
|
||
|
+ else
|
||
|
+ {
|
||
|
+ grub_dprintf ("chain", "Failed to read header\n");
|
||
|
+ goto error_exit;
|
||
|
+ }
|
||
|
+
|
||
|
+ section_alignment = context.pe_hdr->optional_header.section_alignment;
|
||
|
+ buffer_size = context.image_size + section_alignment;
|
||
|
+
|
||
|
+ efi_status = b->allocate_pool (GRUB_EFI_LOADER_DATA,
|
||
|
+ buffer_size, (void**)&buffer);
|
||
|
+
|
||
|
+ if (efi_status != GRUB_EFI_SUCCESS)
|
||
|
+ {
|
||
|
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
|
||
|
+ goto error_exit;
|
||
|
+ }
|
||
|
+
|
||
|
+ buffer_aligned = (char *)ALIGN_UP ((grub_addr_t)buffer, section_alignment);
|
||
|
+
|
||
|
+ grub_memcpy (buffer_aligned, data, context.size_of_headers);
|
||
|
+
|
||
|
+ section = context.first_section;
|
||
|
+ for (i = 0; i < context.number_of_sections; i++)
|
||
|
+ {
|
||
|
+ size = section->virtual_size;
|
||
|
+ if (size > section->raw_data_size)
|
||
|
+ size = section->raw_data_size;
|
||
|
+
|
||
|
+ base = image_address (buffer_aligned, context.image_size, section->virtual_address);
|
||
|
+ end = image_address (buffer_aligned, context.image_size, section->virtual_address + size - 1);
|
||
|
+
|
||
|
+ if (!base || !end)
|
||
|
+ {
|
||
|
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid section size");
|
||
|
+ goto error_exit;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (section->raw_data_size > 0)
|
||
|
+ grub_memcpy (base, (grub_efi_uint8_t*)data + section->raw_data_offset, size);
|
||
|
+
|
||
|
+ if (size < section->virtual_size)
|
||
|
+ grub_memset (base + size, 0, section->virtual_size - size);
|
||
|
+
|
||
|
+ grub_dprintf ("chain", "copied section %s\n", section->name);
|
||
|
+ section += 1;
|
||
|
+ }
|
||
|
+
|
||
|
+ efi_status = relocate_coff (&context, buffer_aligned);
|
||
|
+
|
||
|
+ if (efi_status != GRUB_EFI_SUCCESS)
|
||
|
+ {
|
||
|
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "relocation failed");
|
||
|
+ goto error_exit;
|
||
|
+ }
|
||
|
+
|
||
|
+ entry_point = image_address (buffer_aligned, context.image_size, context.entry_point);
|
||
|
+
|
||
|
+ if (!entry_point)
|
||
|
+ {
|
||
|
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid entry point");
|
||
|
+ goto error_exit;
|
||
|
+ }
|
||
|
+
|
||
|
+ li = grub_efi_get_loaded_image (grub_efi_image_handle);
|
||
|
+ if (!li)
|
||
|
+ {
|
||
|
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "no loaded image available");
|
||
|
+ goto error_exit;
|
||
|
+ }
|
||
|
+
|
||
|
+ grub_memcpy (&li_bak, li, sizeof (grub_efi_loaded_image_t));
|
||
|
+ li->image_base = buffer_aligned;
|
||
|
+ li->image_size = context.image_size;
|
||
|
+ li->load_options = load_context->cmdline;
|
||
|
+ li->load_options_size = load_context->cmdline_len;
|
||
|
+ li->file_path = grub_efi_get_media_file_path (load_context->file_path);
|
||
|
+ li->device_handle = load_context->dev_handle;
|
||
|
+ if (li->file_path)
|
||
|
+ {
|
||
|
+ grub_printf ("file path: ");
|
||
|
+ grub_efi_print_device_path (li->file_path);
|
||
|
+ }
|
||
|
+ else
|
||
|
+ {
|
||
|
+ grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching file path found");
|
||
|
+ goto error_exit;
|
||
|
+ }
|
||
|
+
|
||
|
+ efi_status = entry_point (grub_efi_image_handle, grub_efi_system_table);
|
||
|
+
|
||
|
+ grub_memcpy (li, &li_bak, sizeof (grub_efi_loaded_image_t));
|
||
|
+ efi_status = b->free_pool (buffer);
|
||
|
+
|
||
|
+ return 1;
|
||
|
+
|
||
|
+error_exit:
|
||
|
+ if (buffer)
|
||
|
+ b->free_pool (buffer);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+
|
||
|
+}
|
||
|
+
|
||
|
+static grub_err_t
|
||
|
+grub_secureboot_chainloader_unload (void* context)
|
||
|
+{
|
||
|
+ grub_efi_boot_services_t *b;
|
||
|
+ struct grub_secureboot_chainloader_context *sb_context = (struct grub_secureboot_chainloader_context *)context;
|
||
|
+
|
||
|
+ b = grub_efi_system_table->boot_services;
|
||
|
+ b->free_pages (sb_context->address, sb_context->pages);
|
||
|
+ grub_free (sb_context->file_path);
|
||
|
+ grub_free (sb_context->cmdline);
|
||
|
+ grub_free (sb_context);
|
||
|
+
|
||
|
+ grub_dl_unref (my_mod);
|
||
|
+ return GRUB_ERR_NONE;
|
||
|
+}
|
||
|
+
|
||
|
+static grub_err_t
|
||
|
+grub_secureboot_chainloader_boot (void *context)
|
||
|
+{
|
||
|
+ struct grub_secureboot_chainloader_context *sb_context = (struct grub_secureboot_chainloader_context *)context;
|
||
|
+
|
||
|
+ handle_image (sb_context);
|
||
|
+ grub_loader_unset ();
|
||
|
+ return grub_errno;
|
||
|
+}
|
||
|
+#endif
|
||
|
+
|
||
|
static grub_err_t
|
||
|
grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
|
||
|
int argc, char *argv[])
|
||
|
@@ -222,11 +651,12 @@
|
||
|
grub_efi_loaded_image_t *loaded_image;
|
||
|
char *filename;
|
||
|
void *boot_image = 0;
|
||
|
- grub_efi_handle_t dev_handle = 0;
|
||
|
grub_efi_physical_address_t address = 0;
|
||
|
grub_efi_uintn_t pages = 0;
|
||
|
grub_efi_char16_t *cmdline = NULL;
|
||
|
grub_efi_handle_t image_handle = NULL;
|
||
|
+ grub_ssize_t cmdline_len = 0;
|
||
|
+ grub_efi_handle_t dev_handle = 0;
|
||
|
|
||
|
if (argc == 0)
|
||
|
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
|
||
|
@@ -236,12 +666,39 @@
|
||
|
|
||
|
b = grub_efi_system_table->boot_services;
|
||
|
|
||
|
+ if (argc > 1)
|
||
|
+ {
|
||
|
+ int i;
|
||
|
+ grub_efi_char16_t *p16;
|
||
|
+
|
||
|
+ for (i = 1, cmdline_len = 0; i < argc; i++)
|
||
|
+ cmdline_len += grub_strlen (argv[i]) + 1;
|
||
|
+
|
||
|
+ cmdline_len *= sizeof (grub_efi_char16_t);
|
||
|
+ cmdline = p16 = grub_malloc (cmdline_len);
|
||
|
+ if (! cmdline)
|
||
|
+ goto fail;
|
||
|
+
|
||
|
+ for (i = 1; i < argc; i++)
|
||
|
+ {
|
||
|
+ char *p8;
|
||
|
+
|
||
|
+ p8 = argv[i];
|
||
|
+ while (*p8)
|
||
|
+ *(p16++) = *(p8++);
|
||
|
+
|
||
|
+ *(p16++) = ' ';
|
||
|
+ }
|
||
|
+ *(--p16) = 0;
|
||
|
+ }
|
||
|
+
|
||
|
file = grub_file_open (filename, GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE);
|
||
|
if (! file)
|
||
|
goto fail;
|
||
|
|
||
|
- /* Get the root device's device path. */
|
||
|
- dev = grub_device_open (0);
|
||
|
+ /* Get the device path from filename. */
|
||
|
+ char *devname = grub_file_get_device_name (filename);
|
||
|
+ dev = grub_device_open (devname);
|
||
|
if (dev == NULL)
|
||
|
;
|
||
|
else if (dev->disk)
|
||
|
@@ -343,6 +800,28 @@
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
+#ifdef SUPPORT_SECURE_BOOT
|
||
|
+ /* FIXME is secure boot possible also with universal binaries? */
|
||
|
+ if (debug_secureboot || (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED && grub_secure_validate ((void *)address, size)))
|
||
|
+ {
|
||
|
+ struct grub_secureboot_chainloader_context *sb_context;
|
||
|
+
|
||
|
+ sb_context = grub_malloc (sizeof (*sb_context));
|
||
|
+ if (!sb_context)
|
||
|
+ goto fail;
|
||
|
+ sb_context->cmdline = cmdline;
|
||
|
+ sb_context->cmdline_len = cmdline_len;
|
||
|
+ sb_context->fsize = size;
|
||
|
+ sb_context->dev_handle = dev_handle;
|
||
|
+ sb_context->address = address;
|
||
|
+ sb_context->pages = pages;
|
||
|
+ sb_context->file_path = file_path;
|
||
|
+ grub_file_close (file);
|
||
|
+ grub_loader_set_ex (grub_secureboot_chainloader_boot, grub_secureboot_chainloader_unload, sb_context, 0);
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+#endif
|
||
|
+
|
||
|
status = b->load_image (0, grub_efi_image_handle, file_path,
|
||
|
boot_image, size,
|
||
|
&image_handle);
|
||
|
@@ -368,33 +847,10 @@
|
||
|
loaded_image->device_handle = dev_handle;
|
||
|
|
||
|
/* Build load options with arguments from chainloader command line. */
|
||
|
- if (argc > 1)
|
||
|
+ if (cmdline)
|
||
|
{
|
||
|
- int i, len;
|
||
|
- grub_efi_char16_t *p16;
|
||
|
-
|
||
|
- for (i = 1, len = 0; i < argc; i++)
|
||
|
- len += grub_strlen (argv[i]) + 1;
|
||
|
-
|
||
|
- len *= sizeof (grub_efi_char16_t);
|
||
|
- cmdline = p16 = grub_malloc (len);
|
||
|
- if (! cmdline)
|
||
|
- goto fail;
|
||
|
-
|
||
|
- for (i = 1; i < argc; i++)
|
||
|
- {
|
||
|
- char *p8;
|
||
|
-
|
||
|
- p8 = argv[i];
|
||
|
- while (*p8)
|
||
|
- *(p16++) = *(p8++);
|
||
|
-
|
||
|
- *(p16++) = ' ';
|
||
|
- }
|
||
|
- *(--p16) = 0;
|
||
|
-
|
||
|
loaded_image->load_options = cmdline;
|
||
|
- loaded_image->load_options_size = len;
|
||
|
+ loaded_image->load_options_size = cmdline_len;
|
||
|
}
|
||
|
|
||
|
grub_file_close (file);
|