From 23589dc78b3e35eaba8b2a12df03d018184a0a19 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 10 Jul 2012 11:58:52 -0400 Subject: [PATCH 01/85] Add support for linuxefi --- grub-core/Makefile.core.def | 8 + grub-core/kern/efi/mm.c | 32 +++ grub-core/loader/i386/efi/linux.c | 371 ++++++++++++++++++++++++++++++ include/grub/efi/efi.h | 3 + include/grub/i386/linux.h | 1 + 5 files changed, 415 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 2dfa22a92..68e47a727 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1733,6 +1733,14 @@ module = { enable = x86_64_efi; }; +module = { + name = linuxefi; + efi = loader/i386/efi/linux.c; + efi = lib/cmdline.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 20a47aaf5..efb15cc1b 100644 --- a/grub-core/kern/efi/mm.c +++ b/grub-core/kern/efi/mm.c @@ -49,6 +49,38 @@ static grub_efi_uintn_t finish_desc_size; static grub_efi_uint32_t finish_desc_version; int grub_efi_is_finished = 0; +/* 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 (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..b79e6320b --- /dev/null +++ b/grub-core/loader/i386/efi/linux.c @@ -0,0 +1,371 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; + + shim_lock = grub_efi_locate_protocol(&guid, NULL); + + if (!shim_lock) + return 1; + + if (shim_lock->verify(data, size) == GRUB_EFI_SUCCESS) + return 1; + + 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)initrd_mem, BYTES_TO_PAGES(params->ramdisk_size)); + if (linux_cmdline) + grub_efi_free_pages((grub_efi_physical_address_t)linux_cmdline, BYTES_TO_PAGES(params->cmdline_size + 1)); + if (kernel_mem) + grub_efi_free_pages((grub_efi_physical_address_t)kernel_mem, BYTES_TO_PAGES(kernel_size)); + if (params) + grub_efi_free_pages((grub_efi_physical_address_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_file_t *files = 0; + int i, nfiles = 0; + grub_size_t size = 0; + grub_uint8_t *ptr; + + 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; + } + + files = grub_zalloc (argc * sizeof (files[0])); + if (!files) + goto fail; + + for (i = 0; i < argc; i++) + { + grub_file_filter_disable_compression (); + files[i] = grub_file_open (argv[i]); + if (! files[i]) + goto fail; + nfiles++; + size += ALIGN_UP (grub_file_size (files[i]), 4); + } + + 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; + } + + params->ramdisk_size = size; + params->ramdisk_image = (grub_uint32_t)(grub_uint64_t) initrd_mem; + + ptr = initrd_mem; + + for (i = 0; i < nfiles; i++) + { + grub_ssize_t cursize = grub_file_size (files[i]); + if (grub_file_read (files[i], ptr, cursize) != cursize) + { + if (!grub_errno) + grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), + argv[i]); + goto fail; + } + ptr += cursize; + grub_memset (ptr, 0, ALIGN_UP_OVERHEAD (cursize, 4)); + ptr += ALIGN_UP_OVERHEAD (cursize, 4); + } + + params->ramdisk_size = size; + + fail: + for (i = 0; i < nfiles; i++) + grub_file_close (files[i]); + grub_free (files); + + if (initrd_mem && grub_errno) + grub_efi_free_pages((grub_efi_physical_address_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_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]); + 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_INVALID_COMMAND, 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; + } + + 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_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE)); + grub_create_loader_cmdline (argc, argv, + linux_cmdline + sizeof (LINUX_IMAGE) - 1, + lh.cmdline_size - (sizeof (LINUX_IMAGE) - 1)); + + lh.cmd_line_ptr = (grub_uint32_t)(grub_uint64_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_pages(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; + } + + 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_uint64_t) kernel_mem; + } + + 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)linux_cmdline, BYTES_TO_PAGES(lh.cmdline_size + 1)); + + if (kernel_mem && !loaded) + grub_efi_free_pages((grub_efi_physical_address_t)kernel_mem, BYTES_TO_PAGES(kernel_size)); + + if (params && !loaded) + grub_efi_free_pages((grub_efi_physical_address_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/include/grub/efi/efi.h b/include/grub/efi/efi.h index e9c601f34..764cd11f5 100644 --- a/include/grub/efi/efi.h +++ b/include/grub/efi/efi.h @@ -40,6 +40,9 @@ void EXPORT_FUNC(grub_efi_stall) (grub_efi_uintn_t microseconds); void * EXPORT_FUNC(grub_efi_allocate_pages) (grub_efi_physical_address_t address, 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); int diff --git a/include/grub/i386/linux.h b/include/grub/i386/linux.h index da0ca3b83..fc36bdaf3 100644 --- a/include/grub/i386/linux.h +++ b/include/grub/i386/linux.h @@ -139,6 +139,7 @@ struct linux_kernel_header grub_uint64_t setup_data; grub_uint64_t pref_address; grub_uint32_t init_size; + grub_uint32_t handover_offset; } GRUB_PACKED; /* Boot parameters for Linux based on 2.6.12. This is used by the setup From dec8c6a8aca0fef4060d604edc0eb097446346cb Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Tue, 23 Oct 2012 10:40:49 -0400 Subject: [PATCH 02/85] Don't allow insmod when secure boot is enabled. Hi, Fedora's patch to forbid insmod in UEFI Secure Boot environments is fine as far as it goes. However, the insmod command is not the only way that modules can be loaded. In particular, the 'normal' command, which implements the usual GRUB menu and the fully-featured command prompt, will implicitly load commands not currently loaded into memory. This permits trivial Secure Boot violations by writing commands implementing whatever you want to do and pointing $prefix at the malicious code. I'm currently test-building this patch (replacing your current grub-2.00-no-insmod-on-sb.patch), but this should be more correct. It moves the check into grub_dl_load_file. --- grub-core/kern/dl.c | 17 +++++++++++++++++ grub-core/kern/efi/efi.c | 28 ++++++++++++++++++++++++++++ include/grub/efi/efi.h | 1 + 3 files changed, 46 insertions(+) diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index e394cd96f..37fc6cf51 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -38,6 +38,14 @@ #define GRUB_MODULES_MACHINE_READONLY #endif +#ifdef GRUB_MACHINE_EMU +#include +#endif + +#ifdef GRUB_MACHINE_EFI +#include +#endif + #pragma GCC diagnostic ignored "-Wcast-align" @@ -686,6 +694,15 @@ grub_dl_load_file (const char *filename) void *core = 0; grub_dl_t mod = 0; +#ifdef GRUB_MACHINE_EFI + if (grub_efi_secure_boot ()) + { + grub_error (GRUB_ERR_ACCESS_DENIED, + "Secure Boot forbids loading module from %s", filename); + return 0; + } +#endif + grub_boot_time ("Loading module %s", filename); file = grub_file_open (filename); diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c index d467785fc..bce251df9 100644 --- a/grub-core/kern/efi/efi.c +++ b/grub-core/kern/efi/efi.c @@ -264,6 +264,34 @@ grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid, return NULL; } +grub_efi_boolean_t +grub_efi_secure_boot (void) +{ + grub_efi_guid_t efi_var_guid = GRUB_EFI_GLOBAL_VARIABLE_GUID; + grub_size_t datasize; + char *secure_boot = NULL; + char *setup_mode = NULL; + grub_efi_boolean_t ret = 0; + + secure_boot = grub_efi_get_variable("SecureBoot", &efi_var_guid, &datasize); + + if (datasize != 1 || !secure_boot) + goto out; + + setup_mode = grub_efi_get_variable("SetupMode", &efi_var_guid, &datasize); + + if (datasize != 1 || !setup_mode) + goto out; + + if (*secure_boot && !*setup_mode) + ret = 1; + + out: + grub_free (secure_boot); + grub_free (setup_mode); + return ret; +} + #pragma GCC diagnostic ignored "-Wcast-align" /* Search the mods section from the PE32/PE32+ image. This code uses diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h index 764cd11f5..62a3d9726 100644 --- a/include/grub/efi/efi.h +++ b/include/grub/efi/efi.h @@ -76,6 +76,7 @@ EXPORT_FUNC (grub_efi_set_variable) (const char *var, const grub_efi_guid_t *guid, void *data, grub_size_t datasize); +grub_efi_boolean_t EXPORT_FUNC (grub_efi_secure_boot) (void); int EXPORT_FUNC (grub_efi_compare_device_paths) (const grub_efi_device_path_t *dp1, const grub_efi_device_path_t *dp2); From f69a9e0fdcf63ac33906e2753e14152bab2fcd05 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Sun, 28 Sep 2014 21:26:21 -0700 Subject: [PATCH 03/85] gpt: start new GPT module This module is a new implementation for reading GUID Partition Tables which is much stricter than the existing part_gpt module and exports GPT data directly instead of the generic grub_partition structure. It will be the basis for modules that need to read/write/update GPT data. The current code does nothing more than read and verify the table. --- .gitignore | 1 + Makefile.util.def | 16 ++ grub-core/Makefile.core.def | 5 + grub-core/lib/gpt.c | 288 +++++++++++++++++++++ include/grub/gpt_partition.h | 60 +++++ tests/gpt_unit_test.c | 467 +++++++++++++++++++++++++++++++++++ 6 files changed, 837 insertions(+) create mode 100644 grub-core/lib/gpt.c create mode 100644 tests/gpt_unit_test.c diff --git a/.gitignore b/.gitignore index eca17bec9..5a9cce919 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,7 @@ gensymlist.sh gentrigtables gentrigtables.exe gettext_strings_test +gpt_unit_test grub-bin2h /grub-bios-setup /grub-bios-setup.exe diff --git a/Makefile.util.def b/Makefile.util.def index f9caccb97..48448c28d 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -1254,6 +1254,22 @@ program = { ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; +program = { + testcase; + name = gpt_unit_test; + common = tests/gpt_unit_test.c; + common = tests/lib/unit_test.c; + common = grub-core/disk/host.c; + common = grub-core/kern/emu/hostfs.c; + common = grub-core/lib/gpt.c; + common = grub-core/tests/lib/test.c; + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/gnulib/libgnu.a; + ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + program = { name = grub-menulst2cfg; mansection = 1; diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 68e47a727..5f339a7cf 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -821,6 +821,11 @@ module = { common = commands/gptsync.c; }; +module = { + name = gpt; + common = lib/gpt.c; +}; + module = { name = halt; nopc = commands/halt.c; diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c new file mode 100644 index 000000000..a308e8537 --- /dev/null +++ b/grub-core/lib/gpt.c @@ -0,0 +1,288 @@ +/* gpt.c - Read/Verify/Write GUID Partition Tables (GPT). */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2005,2006,2007,2008 Free Software Foundation, Inc. + * Copyright (C) 2014 CoreOS, 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_uint8_t grub_gpt_magic[] = GRUB_GPT_HEADER_MAGIC; + + +static grub_err_t +grub_gpt_header_crc32 (struct grub_gpt_header *gpt, grub_uint32_t *crc) +{ + grub_uint8_t *crc32_context; + grub_uint32_t old; + + crc32_context = grub_zalloc (GRUB_MD_CRC32->contextsize); + if (!crc32_context) + return grub_errno; + + /* crc32 must be computed with the field cleared. */ + old = gpt->crc32; + gpt->crc32 = 0; + GRUB_MD_CRC32->init (crc32_context); + GRUB_MD_CRC32->write (crc32_context, gpt, sizeof (*gpt)); + GRUB_MD_CRC32->final (crc32_context); + gpt->crc32 = old; + + /* GRUB_MD_CRC32 always uses big endian, gpt is always little. */ + *crc = grub_swap_bytes32 (*(grub_uint32_t *) + GRUB_MD_CRC32->read (crc32_context)); + + grub_free (crc32_context); + + return GRUB_ERR_NONE; +} + +/* Make sure the MBR is a protective MBR and not a normal MBR. */ +grub_err_t +grub_gpt_pmbr_check (struct grub_msdos_partition_mbr *mbr) +{ + unsigned int i; + + if (mbr->signature != + grub_cpu_to_le16_compile_time (GRUB_PC_PARTITION_SIGNATURE)) + return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid MBR signature"); + + for (i = 0; i < sizeof (mbr->entries); i++) + if (mbr->entries[i].type == GRUB_PC_PARTITION_TYPE_GPT_DISK) + return GRUB_ERR_NONE; + + return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid protective MBR"); +} + +grub_err_t +grub_gpt_header_check (struct grub_gpt_header *gpt, + unsigned int log_sector_size) +{ + grub_uint32_t crc = 0, size; + + if (grub_memcmp (gpt->magic, grub_gpt_magic, sizeof (grub_gpt_magic)) != 0) + return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid GPT signature"); + + if (gpt->version != GRUB_GPT_HEADER_VERSION) + return grub_error (GRUB_ERR_BAD_PART_TABLE, "unknown GPT version"); + + if (grub_gpt_header_crc32 (gpt, &crc)) + return grub_errno; + + if (gpt->crc32 != crc) + return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid GPT header crc32"); + + /* The header size must be between 92 and the sector size. */ + size = grub_le_to_cpu32 (gpt->headersize); + if (size < 92U || size > (1U << log_sector_size)) + return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid GPT header size"); + + /* The partition entry size must be a multiple of 128. */ + size = grub_le_to_cpu32 (gpt->partentry_size); + if (size < 128 || size % 128) + return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid GPT entry size"); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_gpt_read_primary (grub_disk_t disk, grub_gpt_t gpt) +{ + grub_disk_addr_t addr; + + /* TODO: The gpt partmap module searches for the primary header instead + * of relying on the disk's sector size. For now trust the disk driver + * but eventually this code should match the existing behavior. */ + gpt->log_sector_size = disk->log_sector_size; + + addr = grub_gpt_sector_to_addr (gpt, 1); + if (grub_disk_read (disk, addr, 0, sizeof (gpt->primary), &gpt->primary)) + return grub_errno; + + if (grub_gpt_header_check (&gpt->primary, gpt->log_sector_size)) + return grub_errno; + + gpt->status |= GRUB_GPT_PRIMARY_HEADER_VALID; + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_gpt_read_backup (grub_disk_t disk, grub_gpt_t gpt) +{ + grub_uint64_t sector; + grub_disk_addr_t addr; + + /* Assumes gpt->log_sector_size == disk->log_sector_size */ + if (disk->total_sectors != GRUB_DISK_SIZE_UNKNOWN) + sector = disk->total_sectors - 1; + else if (gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID) + sector = grub_le_to_cpu64 (gpt->primary.backup); + else + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unable to locate backup GPT"); + + addr = grub_gpt_sector_to_addr (gpt, sector); + if (grub_disk_read (disk, addr, 0, sizeof (gpt->backup), &gpt->backup)) + return grub_errno; + + if (grub_gpt_header_check (&gpt->backup, gpt->log_sector_size)) + return grub_errno; + + gpt->status |= GRUB_GPT_BACKUP_HEADER_VALID; + return GRUB_ERR_NONE; +} + +static struct grub_gpt_partentry * +grub_gpt_read_entries (grub_disk_t disk, grub_gpt_t gpt, + struct grub_gpt_header *header) +{ + struct grub_gpt_partentry *entries = NULL; + grub_uint8_t *crc32_context = NULL; + grub_uint32_t count, size, crc; + grub_disk_addr_t addr; + grub_size_t entries_size; + + /* Grub doesn't include calloc, hence the manual overflow check. */ + count = grub_le_to_cpu32 (header->maxpart); + size = grub_le_to_cpu32 (header->partentry_size); + entries_size = count *size; + if (size && entries_size / size != count) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto fail; + } + + entries = grub_malloc (entries_size); + if (!entries) + goto fail; + + addr = grub_gpt_sector_to_addr (gpt, grub_le_to_cpu64 (header->partitions)); + if (grub_disk_read (disk, addr, 0, entries_size, entries)) + goto fail; + + crc32_context = grub_zalloc (GRUB_MD_CRC32->contextsize); + if (!crc32_context) + goto fail; + + GRUB_MD_CRC32->init (crc32_context); + GRUB_MD_CRC32->write (crc32_context, entries, entries_size); + GRUB_MD_CRC32->final (crc32_context); + + crc = *(grub_uint32_t *) GRUB_MD_CRC32->read (crc32_context); + if (grub_swap_bytes32 (crc) != header->partentry_crc32) + { + grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid GPT entry crc32"); + goto fail; + } + + grub_free (crc32_context); + return entries; + +fail: + grub_free (entries); + grub_free (crc32_context); + return NULL; +} + +grub_gpt_t +grub_gpt_read (grub_disk_t disk) +{ + grub_gpt_t gpt; + struct grub_gpt_partentry *backup_entries; + + gpt = grub_zalloc (sizeof (*gpt)); + if (!gpt) + goto fail; + + if (grub_disk_read (disk, 0, 0, sizeof (gpt->mbr), &gpt->mbr)) + goto fail; + + /* Check the MBR but errors aren't reported beyond the status bit. */ + if (grub_gpt_pmbr_check (&gpt->mbr)) + grub_errno = GRUB_ERR_NONE; + else + gpt->status |= GRUB_GPT_PROTECTIVE_MBR; + + /* If both the primary and backup fail report the primary's error. */ + if (grub_gpt_read_primary (disk, gpt)) + { + grub_error_push (); + grub_gpt_read_backup (disk, gpt); + grub_error_pop (); + } + else + grub_gpt_read_backup (disk, gpt); + + /* If either succeeded clear any possible error from the other. */ + if (gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID || + gpt->status & GRUB_GPT_BACKUP_HEADER_VALID) + grub_errno = GRUB_ERR_NONE; + else + goto fail; + + /* Same error handling scheme for the entry tables. */ + gpt->entries = grub_gpt_read_entries (disk, gpt, &gpt->primary); + if (!gpt->entries) + { + grub_error_push (); + backup_entries = grub_gpt_read_entries (disk, gpt, &gpt->backup); + grub_error_pop (); + } + else + { + gpt->status |= GRUB_GPT_PRIMARY_ENTRIES_VALID; + backup_entries = grub_gpt_read_entries (disk, gpt, &gpt->backup); + } + + if (backup_entries) + { + gpt->status |= GRUB_GPT_BACKUP_ENTRIES_VALID; + + if (gpt->status & GRUB_GPT_PRIMARY_ENTRIES_VALID) + grub_free (backup_entries); + else + gpt->entries = backup_entries; + } + + if (gpt->status & GRUB_GPT_PRIMARY_ENTRIES_VALID || + gpt->status & GRUB_GPT_BACKUP_ENTRIES_VALID) + { + grub_errno = GRUB_ERR_NONE; + return gpt; + } + +fail: + grub_gpt_free (gpt); + return NULL; +} + +void +grub_gpt_free (grub_gpt_t gpt) +{ + if (!gpt) + return; + + grub_free (gpt->entries); + grub_free (gpt); +} diff --git a/include/grub/gpt_partition.h b/include/grub/gpt_partition.h index 1b32f6725..04ed2d7f1 100644 --- a/include/grub/gpt_partition.h +++ b/include/grub/gpt_partition.h @@ -21,6 +21,7 @@ #include #include +#include struct grub_gpt_part_type { @@ -50,6 +51,12 @@ typedef struct grub_gpt_part_type grub_gpt_part_type_t; { 0x85, 0xD2, 0xE1, 0xE9, 0x04, 0x34, 0xCF, 0xB3 } \ } +#define GRUB_GPT_HEADER_MAGIC \ + { 0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54 } + +#define GRUB_GPT_HEADER_VERSION \ + grub_cpu_to_le32_compile_time (0x00010000U) + struct grub_gpt_header { grub_uint8_t magic[8]; @@ -78,10 +85,63 @@ struct grub_gpt_partentry char name[72]; } GRUB_PACKED; +/* Basic GPT partmap module. */ grub_err_t grub_gpt_partition_map_iterate (grub_disk_t disk, grub_partition_iterate_hook_t hook, void *hook_data); +/* Advanced GPT library. */ +typedef enum grub_gpt_status + { + GRUB_GPT_PROTECTIVE_MBR = 0x01, + GRUB_GPT_HYBRID_MBR = 0x02, + GRUB_GPT_PRIMARY_HEADER_VALID = 0x04, + GRUB_GPT_PRIMARY_ENTRIES_VALID = 0x08, + GRUB_GPT_BACKUP_HEADER_VALID = 0x10, + GRUB_GPT_BACKUP_ENTRIES_VALID = 0x20, + } grub_gpt_status_t; + +#define GRUB_GPT_MBR_VALID (GRUB_GPT_PROTECTIVE_MBR|GRUB_GPT_HYBRID_MBR) + +/* UEFI requires the entries table to be at least 16384 bytes for a + * total of 128 entries given the standard 128 byte entry size. */ +#define GRUB_GPT_DEFAULT_ENTRIES_LENGTH 128 + +struct grub_gpt +{ + /* Bit field indicating which structures on disk are valid. */ + grub_gpt_status_t status; + + /* Protective or hybrid MBR. */ + struct grub_msdos_partition_mbr mbr; + + /* Each of the two GPT headers. */ + struct grub_gpt_header primary; + struct grub_gpt_header backup; + + /* Only need one entries table, on disk both copies are identical. */ + struct grub_gpt_partentry *entries; + + /* Logarithm of sector size, in case GPT and disk driver disagree. */ + unsigned int log_sector_size; +}; +typedef struct grub_gpt *grub_gpt_t; + +/* Translate GPT sectors to GRUB's 512 byte block addresses. */ +static inline grub_disk_addr_t +grub_gpt_sector_to_addr (grub_gpt_t gpt, grub_uint64_t sector) +{ + return (sector << (gpt->log_sector_size - GRUB_DISK_SECTOR_BITS)); +} + +/* Allocates and fills new grub_gpt structure, free with grub_gpt_free. */ +grub_gpt_t grub_gpt_read (grub_disk_t disk); + +void grub_gpt_free (grub_gpt_t gpt); + +grub_err_t grub_gpt_pmbr_check (struct grub_msdos_partition_mbr *mbr); +grub_err_t grub_gpt_header_check (struct grub_gpt_header *gpt, + unsigned int log_sector_size); #endif /* ! GRUB_GPT_PARTITION_HEADER */ diff --git a/tests/gpt_unit_test.c b/tests/gpt_unit_test.c new file mode 100644 index 000000000..a824cd967 --- /dev/null +++ b/tests/gpt_unit_test.c @@ -0,0 +1,467 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2014 CoreOS, 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* from gnulib */ +#include + + +/* GPT section sizes. */ +#define HEADER_SIZE (sizeof (struct grub_gpt_header)) +#define HEADER_PAD (GRUB_DISK_SECTOR_SIZE - HEADER_SIZE) +#define ENTRY_SIZE (sizeof (struct grub_gpt_partentry)) +#define TABLE_ENTRIES 0x80 +#define TABLE_SIZE (TABLE_ENTRIES * ENTRY_SIZE) +#define TABLE_SECTORS (TABLE_SIZE / GRUB_DISK_SECTOR_SIZE) + +/* Double check that the table size calculation was valid. */ +verify (TABLE_SECTORS * GRUB_DISK_SECTOR_SIZE == TABLE_SIZE); + +/* GPT section locations for a 1MiB disk. */ +#define DISK_SECTORS 0x800 +#define DISK_SIZE (GRUB_DISK_SECTOR_SIZE * DISK_SECTORS) +#define PRIMARY_HEADER_SECTOR 0x1 +#define PRIMARY_TABLE_SECTOR 0x2 +#define BACKUP_HEADER_SECTOR (DISK_SECTORS - 0x1) +#define BACKUP_TABLE_SECTOR (BACKUP_HEADER_SECTOR - TABLE_SECTORS) + +#define DATA_START_SECTOR (PRIMARY_TABLE_SECTOR + TABLE_SECTORS) +#define DATA_END_SECTOR (BACKUP_TABLE_SECTOR - 0x1) +#define DATA_SECTORS (BACKUP_TABLE_SECTOR - DATA_START_SECTOR) +#define DATA_SIZE (GRUB_DISK_SECTOR_SIZE * DATA_SECTORS) + +struct test_disk +{ + struct grub_msdos_partition_mbr mbr; + + struct grub_gpt_header primary_header; + grub_uint8_t primary_header_pad[HEADER_PAD]; + struct grub_gpt_partentry primary_entries[TABLE_ENTRIES]; + + grub_uint8_t data[DATA_SIZE]; + + struct grub_gpt_partentry backup_entries[TABLE_ENTRIES]; + struct grub_gpt_header backup_header; + grub_uint8_t backup_header_pad[HEADER_PAD]; +} GRUB_PACKED; + +/* Sanity check that all the above ugly math was correct. */ +verify (sizeof (struct test_disk) == DISK_SIZE); + +struct test_data +{ + int fd; + grub_device_t dev; + struct test_disk *raw; +}; + + +/* Sample primary GPT header for an empty 1MB disk. */ +static const struct grub_gpt_header example_primary = { + .magic = GRUB_GPT_HEADER_MAGIC, + .version = GRUB_GPT_HEADER_VERSION, + .headersize = sizeof (struct grub_gpt_header), + .crc32 = grub_cpu_to_le32_compile_time (0x7cd8642c), + .primary = grub_cpu_to_le64_compile_time (PRIMARY_HEADER_SECTOR), + .backup = grub_cpu_to_le64_compile_time (BACKUP_HEADER_SECTOR), + .start = grub_cpu_to_le64_compile_time (DATA_START_SECTOR), + .end = grub_cpu_to_le64_compile_time (DATA_END_SECTOR), + .guid = {0xad, 0x31, 0xc1, 0x69, 0xd6, 0x67, 0xc6, 0x46, + 0x93, 0xc4, 0x12, 0x4c, 0x75, 0x52, 0x56, 0xac}, + .partitions = grub_cpu_to_le64_compile_time (PRIMARY_TABLE_SECTOR), + .maxpart = grub_cpu_to_le32_compile_time (TABLE_ENTRIES), + .partentry_size = grub_cpu_to_le32_compile_time (ENTRY_SIZE), + .partentry_crc32 = grub_cpu_to_le32_compile_time (0xab54d286), +}; + +/* And the backup header. */ +static const struct grub_gpt_header example_backup = { + .magic = GRUB_GPT_HEADER_MAGIC, + .version = GRUB_GPT_HEADER_VERSION, + .headersize = sizeof (struct grub_gpt_header), + .crc32 = grub_cpu_to_le32_compile_time (0xcfaa4a27), + .primary = grub_cpu_to_le64_compile_time (BACKUP_HEADER_SECTOR), + .backup = grub_cpu_to_le64_compile_time (PRIMARY_HEADER_SECTOR), + .start = grub_cpu_to_le64_compile_time (DATA_START_SECTOR), + .end = grub_cpu_to_le64_compile_time (DATA_END_SECTOR), + .guid = {0xad, 0x31, 0xc1, 0x69, 0xd6, 0x67, 0xc6, 0x46, + 0x93, 0xc4, 0x12, 0x4c, 0x75, 0x52, 0x56, 0xac}, + .partitions = grub_cpu_to_le64_compile_time (BACKUP_TABLE_SECTOR), + .maxpart = grub_cpu_to_le32_compile_time (TABLE_ENTRIES), + .partentry_size = grub_cpu_to_le32_compile_time (ENTRY_SIZE), + .partentry_crc32 = grub_cpu_to_le32_compile_time (0xab54d286), +}; + +/* Sample protective MBR for the same 1MB disk. Note, this matches + * parted and fdisk behavior. The UEFI spec uses different values. */ +static const struct grub_msdos_partition_mbr example_pmbr = { + .entries = {{.flag = 0x00, + .start_head = 0x00, + .start_sector = 0x01, + .start_cylinder = 0x00, + .type = 0xee, + .end_head = 0xfe, + .end_sector = 0xff, + .end_cylinder = 0xff, + .start = grub_cpu_to_le32_compile_time (0x1), + .length = grub_cpu_to_le32_compile_time (DISK_SECTORS - 0x1), + }}, + .signature = grub_cpu_to_le16_compile_time (GRUB_PC_PARTITION_SIGNATURE), +}; + +/* If errors are left in grub's error stack things can get confused. */ +static void +assert_error_stack_empty (void) +{ + do + { + grub_test_assert (grub_errno == GRUB_ERR_NONE, + "error on stack: %s", grub_errmsg); + } + while (grub_error_pop ()); +} + +static grub_err_t +execute_command2 (const char *name, const char *arg1, const char *arg2) +{ + grub_command_t cmd; + grub_err_t err; + char *argv[2]; + + cmd = grub_command_find (name); + if (!cmd) + grub_fatal ("can't find command %s", name); + + argv[0] = strdup (arg1); + argv[1] = strdup (arg2); + err = (cmd->func) (cmd, 2, argv); + free (argv[0]); + free (argv[1]); + + return err; +} + +static void +sync_disk (struct test_data *data) +{ + if (msync (data->raw, DISK_SIZE, MS_SYNC | MS_INVALIDATE) < 0) + grub_fatal ("Syncing disk failed: %s", strerror (errno)); + + grub_disk_cache_invalidate_all (); +} + +static void +reset_disk (struct test_data *data) +{ + memset (data->raw, 0, DISK_SIZE); + + /* Initialize image with valid example tables. */ + memcpy (&data->raw->mbr, &example_pmbr, sizeof (data->raw->mbr)); + memcpy (&data->raw->primary_header, &example_primary, + sizeof (data->raw->primary_header)); + memcpy (&data->raw->backup_header, &example_backup, + sizeof (data->raw->backup_header)); + + sync_disk (data); +} + +static void +open_disk (struct test_data *data) +{ + const char *loop = "loop0"; + char template[] = "/tmp/grub_gpt_test.XXXXXX"; + char host[sizeof ("(host)") + sizeof (template)]; + + data->fd = mkstemp (template); + if (data->fd < 0) + grub_fatal ("Creating %s failed: %s", template, strerror (errno)); + + if (ftruncate (data->fd, DISK_SIZE) < 0) + { + int err = errno; + unlink (template); + grub_fatal ("Resizing %s failed: %s", template, strerror (err)); + } + + data->raw = mmap (NULL, DISK_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED, data->fd, 0); + if (data->raw == MAP_FAILED) + { + int err = errno; + unlink (template); + grub_fatal ("Maping %s failed: %s", template, strerror (err)); + } + + snprintf (host, sizeof (host), "(host)%s", template); + if (execute_command2 ("loopback", loop, host) != GRUB_ERR_NONE) + { + unlink (template); + grub_fatal ("loopback %s %s failed: %s", loop, host, grub_errmsg); + } + + if (unlink (template) < 0) + grub_fatal ("Unlinking %s failed: %s", template, strerror (errno)); + + reset_disk (data); + + data->dev = grub_device_open (loop); + if (!data->dev) + grub_fatal ("Opening %s failed: %s", loop, grub_errmsg); +} + +static void +close_disk (struct test_data *data) +{ + char *loop; + + assert_error_stack_empty (); + + if (munmap (data->raw, DISK_SIZE) || close (data->fd)) + grub_fatal ("Closing disk image failed: %s", strerror (errno)); + + loop = strdup (data->dev->disk->name); + grub_test_assert (grub_device_close (data->dev) == GRUB_ERR_NONE, + "Closing disk device failed: %s", grub_errmsg); + + grub_test_assert (execute_command2 ("loopback", "-d", loop) == + GRUB_ERR_NONE, "loopback -d %s failed: %s", loop, + grub_errmsg); + + free (loop); +} + +static grub_gpt_t +read_disk (struct test_data *data) +{ + grub_gpt_t gpt; + + gpt = grub_gpt_read (data->dev->disk); + if (gpt == NULL) + { + grub_print_error (); + grub_fatal ("grub_gpt_read failed"); + } + + + return gpt; +} + +static void +pmbr_test (void) +{ + struct grub_msdos_partition_mbr mbr; + + memset (&mbr, 0, sizeof (mbr)); + + /* Empty is invalid. */ + grub_gpt_pmbr_check (&mbr); + grub_test_assert (grub_errno == GRUB_ERR_BAD_PART_TABLE, + "unexpected error: %s", grub_errmsg); + grub_errno = GRUB_ERR_NONE; + + /* A table without a protective partition is invalid. */ + mbr.signature = grub_cpu_to_le16_compile_time (GRUB_PC_PARTITION_SIGNATURE); + grub_gpt_pmbr_check (&mbr); + grub_test_assert (grub_errno == GRUB_ERR_BAD_PART_TABLE, + "unexpected error: %s", grub_errmsg); + grub_errno = GRUB_ERR_NONE; + + /* A table with a protective type is ok. */ + memcpy (&mbr, &example_pmbr, sizeof (mbr)); + grub_gpt_pmbr_check (&mbr); + grub_test_assert (grub_errno == GRUB_ERR_NONE, + "unexpected error: %s", grub_errmsg); + grub_errno = GRUB_ERR_NONE; +} + +static void +header_test (void) +{ + struct grub_gpt_header primary, backup; + + /* Example headers should be valid. */ + memcpy (&primary, &example_primary, sizeof (primary)); + grub_gpt_header_check (&primary, GRUB_DISK_SECTOR_BITS); + grub_test_assert (grub_errno == GRUB_ERR_NONE, + "unexpected error: %s", grub_errmsg); + grub_errno = GRUB_ERR_NONE; + + memcpy (&backup, &example_backup, sizeof (backup)); + grub_gpt_header_check (&backup, GRUB_DISK_SECTOR_BITS); + grub_test_assert (grub_errno == GRUB_ERR_NONE, + "unexpected error: %s", grub_errmsg); + grub_errno = GRUB_ERR_NONE; + + /* Twiddle the GUID to invalidate the CRC. */ + primary.guid[0] = 0; + grub_gpt_header_check (&primary, GRUB_DISK_SECTOR_BITS); + grub_test_assert (grub_errno == GRUB_ERR_BAD_PART_TABLE, + "unexpected error: %s", grub_errmsg); + grub_errno = GRUB_ERR_NONE; + + backup.guid[0] = 0; + grub_gpt_header_check (&backup, GRUB_DISK_SECTOR_BITS); + grub_test_assert (grub_errno == GRUB_ERR_BAD_PART_TABLE, + "unexpected error: %s", grub_errmsg); + grub_errno = GRUB_ERR_NONE; +} + +static void +read_valid_test (void) +{ + struct test_data data; + grub_gpt_t gpt; + + open_disk (&data); + gpt = read_disk (&data); + grub_test_assert (gpt->status == (GRUB_GPT_PROTECTIVE_MBR | + GRUB_GPT_PRIMARY_HEADER_VALID | + GRUB_GPT_PRIMARY_ENTRIES_VALID | + GRUB_GPT_BACKUP_HEADER_VALID | + GRUB_GPT_BACKUP_ENTRIES_VALID), + "unexpected status: 0x%02x", gpt->status); + grub_gpt_free (gpt); + close_disk (&data); +} + +static void +read_invalid_entries_test (void) +{ + struct test_data data; + grub_gpt_t gpt; + + open_disk (&data); + + /* Corrupt the first entry in both tables. */ + memset (&data.raw->primary_entries[0], 0x55, + sizeof (data.raw->primary_entries[0])); + memset (&data.raw->backup_entries[0], 0x55, + sizeof (data.raw->backup_entries[0])); + sync_disk (&data); + + gpt = grub_gpt_read (data.dev->disk); + grub_test_assert (gpt == NULL, "no error reported for corrupt entries"); + grub_test_assert (grub_errno == GRUB_ERR_BAD_PART_TABLE, + "unexpected error: %s", grub_errmsg); + grub_errno = GRUB_ERR_NONE; + + close_disk (&data); +} + +static void +read_fallback_test (void) +{ + struct test_data data; + grub_gpt_t gpt; + + open_disk (&data); + + /* Corrupt the primary header. */ + memset (&data.raw->primary_header.guid, 0x55, + sizeof (data.raw->primary_header.guid)); + sync_disk (&data); + gpt = read_disk (&data); + grub_test_assert ((gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID) == 0, + "unreported corrupt primary header"); + grub_gpt_free (gpt); + reset_disk (&data); + + /* Corrupt the backup header. */ + memset (&data.raw->backup_header.guid, 0x55, + sizeof (data.raw->backup_header.guid)); + sync_disk (&data); + gpt = read_disk (&data); + grub_test_assert ((gpt->status & GRUB_GPT_BACKUP_HEADER_VALID) == 0, + "unreported corrupt backup header"); + grub_gpt_free (gpt); + reset_disk (&data); + + /* Corrupt the primary entry table. */ + memset (&data.raw->primary_entries[0], 0x55, + sizeof (data.raw->primary_entries[0])); + sync_disk (&data); + gpt = read_disk (&data); + grub_test_assert ((gpt->status & GRUB_GPT_PRIMARY_ENTRIES_VALID) == 0, + "unreported corrupt primary entries table"); + grub_gpt_free (gpt); + reset_disk (&data); + + /* Corrupt the backup entry table. */ + memset (&data.raw->backup_entries[0], 0x55, + sizeof (data.raw->backup_entries[0])); + sync_disk (&data); + gpt = read_disk (&data); + grub_test_assert ((gpt->status & GRUB_GPT_BACKUP_ENTRIES_VALID) == 0, + "unreported corrupt backup entries table"); + grub_gpt_free (gpt); + reset_disk (&data); + + /* If primary is corrupt and disk size is unknown fallback fails. */ + memset (&data.raw->primary_header.guid, 0x55, + sizeof (data.raw->primary_header.guid)); + sync_disk (&data); + data.dev->disk->total_sectors = GRUB_DISK_SIZE_UNKNOWN; + gpt = grub_gpt_read (data.dev->disk); + grub_test_assert (gpt == NULL, "no error reported"); + grub_test_assert (grub_errno == GRUB_ERR_BAD_PART_TABLE, + "unexpected error: %s", grub_errmsg); + grub_errno = GRUB_ERR_NONE; + + close_disk (&data); +} + +void +grub_unit_test_init (void) +{ + grub_init_all (); + grub_hostfs_init (); + grub_host_init (); + grub_test_register ("gpt_pmbr_test", pmbr_test); + grub_test_register ("gpt_header_test", header_test); + grub_test_register ("gpt_read_valid_test", read_valid_test); + grub_test_register ("gpt_read_invalid_test", read_invalid_entries_test); + grub_test_register ("gpt_read_fallback_test", read_fallback_test); +} + +void +grub_unit_test_fini (void) +{ + grub_test_unregister ("gpt_pmbr_test"); + grub_test_unregister ("gpt_header_test"); + grub_test_unregister ("gpt_read_valid_test"); + grub_test_unregister ("gpt_read_invalid_test"); + grub_test_unregister ("gpt_read_fallback_test"); + grub_fini_all (); +} From c26743a145c918958b862d580c4261735d1c1a6e Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Sat, 18 Oct 2014 15:39:13 -0700 Subject: [PATCH 04/85] gpt: rename misnamed header location fields The header location fields refer to 'this header' and 'alternate header' respectively, not 'primary header' and 'backup header'. The previous field names are backwards for the backup header. --- grub-core/lib/gpt.c | 2 +- include/grub/gpt_partition.h | 4 ++-- tests/gpt_unit_test.c | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index a308e8537..705bd77f9 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -137,7 +137,7 @@ grub_gpt_read_backup (grub_disk_t disk, grub_gpt_t gpt) if (disk->total_sectors != GRUB_DISK_SIZE_UNKNOWN) sector = disk->total_sectors - 1; else if (gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID) - sector = grub_le_to_cpu64 (gpt->primary.backup); + sector = grub_le_to_cpu64 (gpt->primary.alternate_lba); else return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "Unable to locate backup GPT"); diff --git a/include/grub/gpt_partition.h b/include/grub/gpt_partition.h index 04ed2d7f1..a7ef61875 100644 --- a/include/grub/gpt_partition.h +++ b/include/grub/gpt_partition.h @@ -64,8 +64,8 @@ struct grub_gpt_header grub_uint32_t headersize; grub_uint32_t crc32; grub_uint32_t unused1; - grub_uint64_t primary; - grub_uint64_t backup; + grub_uint64_t header_lba; + grub_uint64_t alternate_lba; grub_uint64_t start; grub_uint64_t end; grub_uint8_t guid[16]; diff --git a/tests/gpt_unit_test.c b/tests/gpt_unit_test.c index a824cd967..4d70868af 100644 --- a/tests/gpt_unit_test.c +++ b/tests/gpt_unit_test.c @@ -94,8 +94,8 @@ static const struct grub_gpt_header example_primary = { .version = GRUB_GPT_HEADER_VERSION, .headersize = sizeof (struct grub_gpt_header), .crc32 = grub_cpu_to_le32_compile_time (0x7cd8642c), - .primary = grub_cpu_to_le64_compile_time (PRIMARY_HEADER_SECTOR), - .backup = grub_cpu_to_le64_compile_time (BACKUP_HEADER_SECTOR), + .header_lba = grub_cpu_to_le64_compile_time (PRIMARY_HEADER_SECTOR), + .alternate_lba = grub_cpu_to_le64_compile_time (BACKUP_HEADER_SECTOR), .start = grub_cpu_to_le64_compile_time (DATA_START_SECTOR), .end = grub_cpu_to_le64_compile_time (DATA_END_SECTOR), .guid = {0xad, 0x31, 0xc1, 0x69, 0xd6, 0x67, 0xc6, 0x46, @@ -112,8 +112,8 @@ static const struct grub_gpt_header example_backup = { .version = GRUB_GPT_HEADER_VERSION, .headersize = sizeof (struct grub_gpt_header), .crc32 = grub_cpu_to_le32_compile_time (0xcfaa4a27), - .primary = grub_cpu_to_le64_compile_time (BACKUP_HEADER_SECTOR), - .backup = grub_cpu_to_le64_compile_time (PRIMARY_HEADER_SECTOR), + .header_lba = grub_cpu_to_le64_compile_time (BACKUP_HEADER_SECTOR), + .alternate_lba = grub_cpu_to_le64_compile_time (PRIMARY_HEADER_SECTOR), .start = grub_cpu_to_le64_compile_time (DATA_START_SECTOR), .end = grub_cpu_to_le64_compile_time (DATA_END_SECTOR), .guid = {0xad, 0x31, 0xc1, 0x69, 0xd6, 0x67, 0xc6, 0x46, From 94f04a532d2b0e2b81e47a92488ebb1613bda1a0 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Sat, 18 Oct 2014 16:46:17 -0700 Subject: [PATCH 05/85] gpt: record size of of the entries table The size of the entries table will be needed later when writing it back to disk. Restructure the entries reading code to flow a little better. --- grub-core/lib/gpt.c | 53 ++++++++++++++++-------------------- include/grub/gpt_partition.h | 5 +++- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index 705bd77f9..01df7f3e8 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -153,7 +153,7 @@ grub_gpt_read_backup (grub_disk_t disk, grub_gpt_t gpt) return GRUB_ERR_NONE; } -static struct grub_gpt_partentry * +static grub_err_t grub_gpt_read_entries (grub_disk_t disk, grub_gpt_t gpt, struct grub_gpt_header *header) { @@ -173,6 +173,10 @@ grub_gpt_read_entries (grub_disk_t disk, grub_gpt_t gpt, goto fail; } + /* Double check that the header was validated properly. */ + if (entries_size < GRUB_GPT_DEFAULT_ENTRIES_SIZE) + return grub_error (GRUB_ERR_BUG, "invalid GPT entries table size"); + entries = grub_malloc (entries_size); if (!entries) goto fail; @@ -197,19 +201,21 @@ grub_gpt_read_entries (grub_disk_t disk, grub_gpt_t gpt, } grub_free (crc32_context); - return entries; + grub_free (gpt->entries); + gpt->entries = entries; + gpt->entries_size = entries_size; + return GRUB_ERR_NONE; fail: grub_free (entries); grub_free (crc32_context); - return NULL; + return grub_errno; } grub_gpt_t grub_gpt_read (grub_disk_t disk) { grub_gpt_t gpt; - struct grub_gpt_partentry *backup_entries; gpt = grub_zalloc (sizeof (*gpt)); if (!gpt) @@ -241,36 +247,23 @@ grub_gpt_read (grub_disk_t disk) else goto fail; - /* Same error handling scheme for the entry tables. */ - gpt->entries = grub_gpt_read_entries (disk, gpt, &gpt->primary); - if (!gpt->entries) - { - grub_error_push (); - backup_entries = grub_gpt_read_entries (disk, gpt, &gpt->backup); - grub_error_pop (); - } - else - { - gpt->status |= GRUB_GPT_PRIMARY_ENTRIES_VALID; - backup_entries = grub_gpt_read_entries (disk, gpt, &gpt->backup); - } + /* Similarly, favor the value or error from the primary table. */ + if (gpt->status & GRUB_GPT_BACKUP_HEADER_VALID && + !grub_gpt_read_entries (disk, gpt, &gpt->backup)) + gpt->status |= GRUB_GPT_BACKUP_ENTRIES_VALID; - if (backup_entries) - { - gpt->status |= GRUB_GPT_BACKUP_ENTRIES_VALID; - - if (gpt->status & GRUB_GPT_PRIMARY_ENTRIES_VALID) - grub_free (backup_entries); - else - gpt->entries = backup_entries; - } + grub_errno = GRUB_ERR_NONE; + if (gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID && + !grub_gpt_read_entries (disk, gpt, &gpt->primary)) + gpt->status |= GRUB_GPT_PRIMARY_ENTRIES_VALID; if (gpt->status & GRUB_GPT_PRIMARY_ENTRIES_VALID || gpt->status & GRUB_GPT_BACKUP_ENTRIES_VALID) - { - grub_errno = GRUB_ERR_NONE; - return gpt; - } + grub_errno = GRUB_ERR_NONE; + else + goto fail; + + return gpt; fail: grub_gpt_free (gpt); diff --git a/include/grub/gpt_partition.h b/include/grub/gpt_partition.h index a7ef61875..7f41e22dd 100644 --- a/include/grub/gpt_partition.h +++ b/include/grub/gpt_partition.h @@ -106,7 +106,9 @@ typedef enum grub_gpt_status /* UEFI requires the entries table to be at least 16384 bytes for a * total of 128 entries given the standard 128 byte entry size. */ -#define GRUB_GPT_DEFAULT_ENTRIES_LENGTH 128 +#define GRUB_GPT_DEFAULT_ENTRIES_SIZE 16384 +#define GRUB_GPT_DEFAULT_ENTRIES_LENGTH \ + (GRUB_GPT_DEFAULT_ENTRIES_SIZE / sizeof (struct grub_gpt_partentry)) struct grub_gpt { @@ -122,6 +124,7 @@ struct grub_gpt /* Only need one entries table, on disk both copies are identical. */ struct grub_gpt_partentry *entries; + grub_size_t entries_size; /* Logarithm of sector size, in case GPT and disk driver disagree. */ unsigned int log_sector_size; From 3d066264ac13198e45dc151b863a9aac4c095225 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Sat, 18 Oct 2014 18:18:17 -0700 Subject: [PATCH 06/85] gpt: consolidate crc32 computation code The gcrypt API is overly verbose, wrap it up in a helper function to keep this rather common operation easy to use. --- grub-core/lib/gpt.c | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index 01df7f3e8..43a150942 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -32,22 +32,17 @@ static grub_uint8_t grub_gpt_magic[] = GRUB_GPT_HEADER_MAGIC; static grub_err_t -grub_gpt_header_crc32 (struct grub_gpt_header *gpt, grub_uint32_t *crc) +grub_gpt_lecrc32 (void *data, grub_size_t len, grub_uint32_t *crc) { grub_uint8_t *crc32_context; - grub_uint32_t old; crc32_context = grub_zalloc (GRUB_MD_CRC32->contextsize); if (!crc32_context) return grub_errno; - /* crc32 must be computed with the field cleared. */ - old = gpt->crc32; - gpt->crc32 = 0; GRUB_MD_CRC32->init (crc32_context); - GRUB_MD_CRC32->write (crc32_context, gpt, sizeof (*gpt)); + GRUB_MD_CRC32->write (crc32_context, data, len); GRUB_MD_CRC32->final (crc32_context); - gpt->crc32 = old; /* GRUB_MD_CRC32 always uses big endian, gpt is always little. */ *crc = grub_swap_bytes32 (*(grub_uint32_t *) @@ -58,6 +53,25 @@ grub_gpt_header_crc32 (struct grub_gpt_header *gpt, grub_uint32_t *crc) return GRUB_ERR_NONE; } +static grub_err_t +grub_gpt_header_lecrc32 (struct grub_gpt_header *header, grub_uint32_t *crc) +{ + grub_uint32_t old, new; + grub_err_t err; + + /* crc32 must be computed with the field cleared. */ + old = header->crc32; + header->crc32 = 0; + err = grub_gpt_lecrc32 (header, sizeof (*header), &new); + header->crc32 = old; + + if (err) + return err; + + *crc = new; + return GRUB_ERR_NONE; +} + /* Make sure the MBR is a protective MBR and not a normal MBR. */ grub_err_t grub_gpt_pmbr_check (struct grub_msdos_partition_mbr *mbr) @@ -87,7 +101,7 @@ grub_gpt_header_check (struct grub_gpt_header *gpt, if (gpt->version != GRUB_GPT_HEADER_VERSION) return grub_error (GRUB_ERR_BAD_PART_TABLE, "unknown GPT version"); - if (grub_gpt_header_crc32 (gpt, &crc)) + if (grub_gpt_header_lecrc32 (gpt, &crc)) return grub_errno; if (gpt->crc32 != crc) @@ -158,7 +172,6 @@ grub_gpt_read_entries (grub_disk_t disk, grub_gpt_t gpt, struct grub_gpt_header *header) { struct grub_gpt_partentry *entries = NULL; - grub_uint8_t *crc32_context = NULL; grub_uint32_t count, size, crc; grub_disk_addr_t addr; grub_size_t entries_size; @@ -185,22 +198,15 @@ grub_gpt_read_entries (grub_disk_t disk, grub_gpt_t gpt, if (grub_disk_read (disk, addr, 0, entries_size, entries)) goto fail; - crc32_context = grub_zalloc (GRUB_MD_CRC32->contextsize); - if (!crc32_context) + if (grub_gpt_lecrc32 (entries, entries_size, &crc)) goto fail; - GRUB_MD_CRC32->init (crc32_context); - GRUB_MD_CRC32->write (crc32_context, entries, entries_size); - GRUB_MD_CRC32->final (crc32_context); - - crc = *(grub_uint32_t *) GRUB_MD_CRC32->read (crc32_context); - if (grub_swap_bytes32 (crc) != header->partentry_crc32) + if (crc != header->partentry_crc32) { grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid GPT entry crc32"); goto fail; } - grub_free (crc32_context); grub_free (gpt->entries); gpt->entries = entries; gpt->entries_size = entries_size; @@ -208,7 +214,6 @@ grub_gpt_read_entries (grub_disk_t disk, grub_gpt_t gpt, fail: grub_free (entries); - grub_free (crc32_context); return grub_errno; } From dab6fac705bdad7e6ec130b24085189bcb15a5c9 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Sat, 18 Oct 2014 18:21:07 -0700 Subject: [PATCH 07/85] gpt: add new repair function to sync up primary and backup tables. --- grub-core/lib/gpt.c | 90 ++++++++++++++++++++++++++++++++++++ include/grub/gpt_partition.h | 3 ++ tests/gpt_unit_test.c | 49 ++++++++++++++++++++ 3 files changed, 142 insertions(+) diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index 43a150942..2d61df488 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -31,6 +31,20 @@ GRUB_MOD_LICENSE ("GPLv3+"); static grub_uint8_t grub_gpt_magic[] = GRUB_GPT_HEADER_MAGIC; +static grub_uint64_t +grub_gpt_size_to_sectors (grub_gpt_t gpt, grub_size_t size) +{ + unsigned int sector_size; + grub_uint64_t sectors; + + sector_size = 1U << gpt->log_sector_size; + sectors = size / sector_size; + if (size % sector_size) + sectors++; + + return sectors; +} + static grub_err_t grub_gpt_lecrc32 (void *data, grub_size_t len, grub_uint32_t *crc) { @@ -275,6 +289,82 @@ fail: return NULL; } +grub_err_t +grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt) +{ + grub_uint64_t backup_header, backup_entries; + grub_uint32_t crc; + + if (disk->log_sector_size != gpt->log_sector_size) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "GPT sector size must match disk sector size"); + + if (!(gpt->status & GRUB_GPT_PRIMARY_ENTRIES_VALID || + gpt->status & GRUB_GPT_BACKUP_ENTRIES_VALID)) + return grub_error (GRUB_ERR_BUG, "No valid GPT entries"); + + if (gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID) + { + backup_header = grub_le_to_cpu64 (gpt->primary.alternate_lba); + grub_memcpy (&gpt->backup, &gpt->primary, sizeof (gpt->backup)); + } + else if (gpt->status & GRUB_GPT_BACKUP_HEADER_VALID) + { + backup_header = grub_le_to_cpu64 (gpt->backup.header_lba); + grub_memcpy (&gpt->primary, &gpt->backup, sizeof (gpt->primary)); + } + else + return grub_error (GRUB_ERR_BUG, "No valid GPT header"); + + /* Relocate backup to end if disk whenever possible. */ + if (disk->total_sectors != GRUB_DISK_SIZE_UNKNOWN) + backup_header = disk->total_sectors - 1; + + backup_entries = backup_header - + grub_gpt_size_to_sectors (gpt, gpt->entries_size); + + /* Update/fixup header and partition table locations. */ + gpt->primary.header_lba = grub_cpu_to_le64_compile_time (1); + gpt->primary.alternate_lba = grub_cpu_to_le64 (backup_header); + gpt->primary.partitions = grub_cpu_to_le64_compile_time (2); + gpt->backup.header_lba = gpt->primary.alternate_lba; + gpt->backup.alternate_lba = gpt->primary.header_lba; + gpt->backup.partitions = grub_cpu_to_le64 (backup_entries); + + /* Writing headers larger than our header structure are unsupported. */ + gpt->primary.headersize = + grub_cpu_to_le32_compile_time (sizeof (gpt->primary)); + gpt->backup.headersize = + grub_cpu_to_le32_compile_time (sizeof (gpt->backup)); + + /* Recompute checksums. */ + if (grub_gpt_lecrc32 (gpt->entries, gpt->entries_size, &crc)) + return grub_errno; + + gpt->primary.partentry_crc32 = crc; + gpt->backup.partentry_crc32 = crc; + + if (grub_gpt_header_lecrc32 (&gpt->primary, &gpt->primary.crc32)) + return grub_errno; + + if (grub_gpt_header_lecrc32 (&gpt->backup, &gpt->backup.crc32)) + return grub_errno; + + /* Sanity check. */ + if (grub_gpt_header_check (&gpt->primary, gpt->log_sector_size)) + return grub_error (GRUB_ERR_BUG, "Generated invalid GPT primary header"); + + if (grub_gpt_header_check (&gpt->backup, gpt->log_sector_size)) + return grub_error (GRUB_ERR_BUG, "Generated invalid GPT backup header"); + + gpt->status |= (GRUB_GPT_PRIMARY_HEADER_VALID | + GRUB_GPT_PRIMARY_ENTRIES_VALID | + GRUB_GPT_BACKUP_HEADER_VALID | + GRUB_GPT_BACKUP_ENTRIES_VALID); + + return GRUB_ERR_NONE; +} + void grub_gpt_free (grub_gpt_t gpt) { diff --git a/include/grub/gpt_partition.h b/include/grub/gpt_partition.h index 7f41e22dd..62d027e4e 100644 --- a/include/grub/gpt_partition.h +++ b/include/grub/gpt_partition.h @@ -141,6 +141,9 @@ grub_gpt_sector_to_addr (grub_gpt_t gpt, grub_uint64_t sector) /* Allocates and fills new grub_gpt structure, free with grub_gpt_free. */ grub_gpt_t grub_gpt_read (grub_disk_t disk); +/* Sync up primary and backup headers, recompute checksums. */ +grub_err_t grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt); + void grub_gpt_free (grub_gpt_t gpt); grub_err_t grub_gpt_pmbr_check (struct grub_msdos_partition_mbr *mbr); diff --git a/tests/gpt_unit_test.c b/tests/gpt_unit_test.c index 4d70868af..83198bebf 100644 --- a/tests/gpt_unit_test.c +++ b/tests/gpt_unit_test.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -442,6 +443,52 @@ read_fallback_test (void) close_disk (&data); } +static void +repair_test (void) +{ + struct test_data data; + grub_gpt_t gpt; + + open_disk (&data); + + /* Erase/Repair primary. */ + memset (&data.raw->primary_header, 0, sizeof (data.raw->primary_header)); + sync_disk (&data); + gpt = read_disk (&data); + grub_gpt_repair (data.dev->disk, gpt); + grub_test_assert (grub_errno == GRUB_ERR_NONE, + "repair failed: %s", grub_errmsg); + if (memcmp (&gpt->primary, &example_primary, sizeof (gpt->primary))) + { + printf ("Invalid restored primary header:\n"); + hexdump (16, (char*)&gpt->primary, sizeof (gpt->primary)); + printf ("Expected primary header:\n"); + hexdump (16, (char*)&example_primary, sizeof (example_primary)); + grub_test_assert (0, "repair did not restore primary header"); + } + grub_gpt_free (gpt); + reset_disk (&data); + + /* Erase/Repair backup. */ + memset (&data.raw->backup_header, 0, sizeof (data.raw->backup_header)); + sync_disk (&data); + gpt = read_disk (&data); + grub_gpt_repair (data.dev->disk, gpt); + grub_test_assert (grub_errno == GRUB_ERR_NONE, + "repair failed: %s", grub_errmsg); + if (memcmp (&gpt->backup, &example_backup, sizeof (gpt->backup))) + { + printf ("Invalid restored backup header:\n"); + hexdump (16, (char*)&gpt->backup, sizeof (gpt->backup)); + printf ("Expected backup header:\n"); + hexdump (16, (char*)&example_backup, sizeof (example_backup)); + grub_test_assert (0, "repair did not restore backup header"); + } + grub_gpt_free (gpt); + reset_disk (&data); + + close_disk (&data); +} void grub_unit_test_init (void) { @@ -453,6 +500,7 @@ grub_unit_test_init (void) grub_test_register ("gpt_read_valid_test", read_valid_test); grub_test_register ("gpt_read_invalid_test", read_invalid_entries_test); grub_test_register ("gpt_read_fallback_test", read_fallback_test); + grub_test_register ("gpt_repair_test", repair_test); } void @@ -463,5 +511,6 @@ grub_unit_test_fini (void) grub_test_unregister ("gpt_read_valid_test"); grub_test_unregister ("gpt_read_invalid_test"); grub_test_unregister ("gpt_read_fallback_test"); + grub_test_unregister ("gpt_repair_test"); grub_fini_all (); } From 5e1829d4141343617b5e13e84298d118eac15bdf Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Sun, 19 Oct 2014 14:21:29 -0700 Subject: [PATCH 08/85] gpt: add write function and gptrepair command The first hint of something practical, a command that can restore any of the GPT structures from the alternate location. New test case must run under QEMU because the loopback device used by the other unit tests does not support writing. --- Makefile.util.def | 6 ++ grub-core/Makefile.core.def | 5 ++ grub-core/commands/gptrepair.c | 116 +++++++++++++++++++++++++++++++++ grub-core/lib/gpt.c | 44 +++++++++++-- include/grub/gpt_partition.h | 8 +++ tests/gptrepair_test.in | 102 +++++++++++++++++++++++++++++ 6 files changed, 277 insertions(+), 4 deletions(-) create mode 100644 grub-core/commands/gptrepair.c create mode 100644 tests/gptrepair_test.in diff --git a/Makefile.util.def b/Makefile.util.def index 48448c28d..8156fca5f 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -1159,6 +1159,12 @@ script = { common = tests/grub_cmd_tr.in; }; +script = { + testcase; + name = gptrepair_test; + common = tests/gptrepair_test.in; +}; + script = { testcase; name = file_filter_test; diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 5f339a7cf..123142dae 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -821,6 +821,11 @@ module = { common = commands/gptsync.c; }; +module = { + name = gptrepair; + common = commands/gptrepair.c; +}; + module = { name = gpt; common = lib/gpt.c; diff --git a/grub-core/commands/gptrepair.c b/grub-core/commands/gptrepair.c new file mode 100644 index 000000000..38392fd8f --- /dev/null +++ b/grub-core/commands/gptrepair.c @@ -0,0 +1,116 @@ +/* gptrepair.c - verify and restore GPT info from alternate location. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 Free Software Foundation, Inc. + * Copyright (C) 2014 CoreOS, 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 . + */ + +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static char * +trim_dev_name (char *name) +{ + grub_size_t len = grub_strlen (name); + if (len && name[0] == '(' && name[len - 1] == ')') + { + name[len - 1] = '\0'; + name = name + 1; + } + return name; +} + +static grub_err_t +grub_cmd_gptrepair (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + grub_device_t dev = NULL; + grub_gpt_t gpt = NULL; + char *dev_name; + grub_uint32_t primary_crc, backup_crc; + enum grub_gpt_status old_status; + + if (argc != 1 || !grub_strlen(args[0])) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); + + dev_name = trim_dev_name (args[0]); + dev = grub_device_open (dev_name); + if (!dev) + goto done; + + if (!dev->disk) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "not a disk"); + goto done; + } + + gpt = grub_gpt_read (dev->disk); + if (!gpt) + goto done; + + primary_crc = gpt->primary.crc32; + backup_crc = gpt->backup.crc32; + old_status = gpt->status; + + if (grub_gpt_repair (dev->disk, gpt)) + goto done; + + if (primary_crc == gpt->primary.crc32 && + backup_crc == gpt->backup.crc32 && + old_status && gpt->status) + { + grub_printf_ (N_("GPT already valid, %s unmodified.\n"), dev_name); + goto done; + } + + if (grub_gpt_write (dev->disk, gpt)) + goto done; + + if (!(old_status & GRUB_GPT_PRIMARY_VALID)) + grub_printf_ (N_("Primary GPT for %s repaired.\n"), dev_name); + + if (!(old_status & GRUB_GPT_BACKUP_VALID)) + grub_printf_ (N_("Backup GPT for %s repaired.\n"), dev_name); + +done: + if (gpt) + grub_gpt_free (gpt); + + if (dev) + grub_device_close (dev); + + return grub_errno; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(gptrepair) +{ + cmd = grub_register_command ("gptrepair", grub_cmd_gptrepair, + N_("DEVICE"), + N_("Verify and repair GPT on drive DEVICE.")); +} + +GRUB_MOD_FINI(gptrepair) +{ + grub_unregister_command (cmd); +} diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index 2d61df488..67ffdf703 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -357,10 +357,46 @@ grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt) if (grub_gpt_header_check (&gpt->backup, gpt->log_sector_size)) return grub_error (GRUB_ERR_BUG, "Generated invalid GPT backup header"); - gpt->status |= (GRUB_GPT_PRIMARY_HEADER_VALID | - GRUB_GPT_PRIMARY_ENTRIES_VALID | - GRUB_GPT_BACKUP_HEADER_VALID | - GRUB_GPT_BACKUP_ENTRIES_VALID); + gpt->status |= GRUB_GPT_BOTH_VALID; + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_gpt_write_table (grub_disk_t disk, grub_gpt_t gpt, + struct grub_gpt_header *header) +{ + grub_disk_addr_t addr; + + if (grub_le_to_cpu32 (header->headersize) != sizeof (*header)) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Header size is %u, must be %u", + grub_le_to_cpu32 (header->headersize), + sizeof (*header)); + + addr = grub_gpt_sector_to_addr (gpt, grub_le_to_cpu64 (header->header_lba)); + if (grub_disk_write (disk, addr, 0, sizeof (*header), header)) + return grub_errno; + + addr = grub_gpt_sector_to_addr (gpt, grub_le_to_cpu64 (header->partitions)); + if (grub_disk_write (disk, addr, 0, gpt->entries_size, gpt->entries)) + return grub_errno; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_gpt_write (grub_disk_t disk, grub_gpt_t gpt) +{ + /* TODO: update/repair protective MBRs too. */ + + if (!(gpt->status & GRUB_GPT_BOTH_VALID)) + return grub_error (GRUB_ERR_BAD_PART_TABLE, "Invalid GPT data"); + + if (grub_gpt_write_table (disk, gpt, &gpt->primary)) + return grub_errno; + + if (grub_gpt_write_table (disk, gpt, &gpt->backup)) + return grub_errno; return GRUB_ERR_NONE; } diff --git a/include/grub/gpt_partition.h b/include/grub/gpt_partition.h index 62d027e4e..3cac6df32 100644 --- a/include/grub/gpt_partition.h +++ b/include/grub/gpt_partition.h @@ -103,6 +103,11 @@ typedef enum grub_gpt_status } grub_gpt_status_t; #define GRUB_GPT_MBR_VALID (GRUB_GPT_PROTECTIVE_MBR|GRUB_GPT_HYBRID_MBR) +#define GRUB_GPT_PRIMARY_VALID \ + (GRUB_GPT_PRIMARY_HEADER_VALID|GRUB_GPT_PRIMARY_ENTRIES_VALID) +#define GRUB_GPT_BACKUP_VALID \ + (GRUB_GPT_BACKUP_HEADER_VALID|GRUB_GPT_BACKUP_ENTRIES_VALID) +#define GRUB_GPT_BOTH_VALID (GRUB_GPT_PRIMARY_VALID|GRUB_GPT_BACKUP_VALID) /* UEFI requires the entries table to be at least 16384 bytes for a * total of 128 entries given the standard 128 byte entry size. */ @@ -144,6 +149,9 @@ grub_gpt_t grub_gpt_read (grub_disk_t disk); /* Sync up primary and backup headers, recompute checksums. */ grub_err_t grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt); +/* Write headers and entry tables back to disk. */ +grub_err_t grub_gpt_write (grub_disk_t disk, grub_gpt_t gpt); + void grub_gpt_free (grub_gpt_t gpt); grub_err_t grub_gpt_pmbr_check (struct grub_msdos_partition_mbr *mbr); diff --git a/tests/gptrepair_test.in b/tests/gptrepair_test.in new file mode 100644 index 000000000..80b2de633 --- /dev/null +++ b/tests/gptrepair_test.in @@ -0,0 +1,102 @@ +#! /bin/sh +set -e + +# Copyright (C) 2010 Free Software Foundation, Inc. +# Copyright (C) 2014 CoreOS, 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 . + +parted=parted +grubshell=@builddir@/grub-shell + +. "@builddir@/grub-core/modinfo.sh" + +case "${grub_modinfo_target_cpu}-${grub_modinfo_platform}" in + mips-qemu_mips | mipsel-qemu_mips | i386-qemu | i386-multiboot | i386-coreboot | mipsel-loongson) + disk=ata0 + ;; + powerpc-ieee1275) + disk=ieee1275//pci@80000000/mac-io@4/ata-3@20000/disk@0 + # FIXME: QEMU firmware has bugs which prevent it from accessing hard disk w/o recognised label. + exit 0 + ;; + sparc64-ieee1275) + disk=ieee1275//pci@1fe\,0/pci-ata@5/ide0@500/disk@0 + # FIXME: QEMU firmware has bugs which prevent it from accessing hard disk w/o recognised label. + exit 0 + ;; + i386-ieee1275) + disk=ieee1275/d + # FIXME: QEMU firmware has bugs which prevent it from accessing hard disk w/o recognised label. + exit 0 + ;; + mips-arc) + # FIXME: ARC firmware has bugs which prevent it from accessing hard disk w/o dvh disklabel. + exit 0 ;; + mipsel-arc) + disk=arc/scsi0/disk0/rdisk0 + ;; + *) + disk=hd0 + ;; +esac +img1="`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"`" || exit 1 +img2="`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"`" || exit 1 +trap "rm -f '${img1}' '${ing2}'" EXIT + +create_disk_image () { + size=$1 + rm -f "${img1}" + dd if=/dev/zero of="${img1}" bs=512 count=1 seek=$((size - 1)) status=none + ${parted} -a none -s "${img1}" mklabel gpt + cp "${img1}" "${img2}" +} + +wipe_disk_area () { + sector=$1 + size=$2 + dd if=/dev/zero of="${img2}" bs=512 count=${size} seek=${sector} conv=notrunc status=none +} + +do_repair () { + output="`echo "gptrepair ($disk)" | "${grubshell}" --disk="${img2}"`" + if echo "${output}" | grep ^error; then + return 1 + fi + if echo "${output}" | grep -v GPT; then + echo "Unexpected output ${output}" + return 1 + fi + echo "${output}" +} + +echo "Nothing to repair:" +create_disk_image 100 +do_repair +cmp "${img1}" "${img2}" +echo + +echo "Repair primary (MBR left intact)" +create_disk_image 100 +wipe_disk_area 1 1 +do_repair +cmp "${img1}" "${img2}" +echo + +echo "Repair backup" +create_disk_image 100 +wipe_disk_area 99 1 +do_repair +cmp "${img1}" "${img2}" +echo From c1151f761fea5acd873adbf8aa301a43c0ba6284 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Sun, 19 Oct 2014 20:44:34 -0700 Subject: [PATCH 09/85] tests: fix path to words file on Gentoo/CoreOS By default there isn't a linux.words file, but there is words. --- tests/util/grub-fs-tester.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/util/grub-fs-tester.in b/tests/util/grub-fs-tester.in index 2337771a1..d768d66d1 100644 --- a/tests/util/grub-fs-tester.in +++ b/tests/util/grub-fs-tester.in @@ -241,8 +241,10 @@ for ((LOGSECSIZE=MINLOGSECSIZE;LOGSECSIZE<=MAXLOGSECSIZE;LOGSECSIZE=LOGSECSIZE + CFILESN=1 if test -f /usr/share/dict/american-english; then CFILESSRC[0]="/usr/share/dict/american-english" - else + elif test -f /usr/share/dict/linux.words; then CFILESSRC[0]="/usr/share/dict/linux.words" + else + CFILESSRC[0]="/usr/share/dict/words" fi case x"$fs" in # FS LIMITATION: 8.3 names From 2cd009dffe98c19672394608661767e4c3c84764 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Thu, 30 Oct 2014 20:55:21 -0700 Subject: [PATCH 10/85] gpt: add a new generic GUID type In order to do anything with partition GUIDs they need to be stored in a proper structure like the partition type GUIDs. Additionally add an initializer macro to simplify defining both GUID types. --- include/grub/gpt_partition.h | 36 +++++++++++++++++++----------------- tests/gpt_unit_test.c | 12 ++++++------ 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/include/grub/gpt_partition.h b/include/grub/gpt_partition.h index 3cac6df32..df076ca64 100644 --- a/include/grub/gpt_partition.h +++ b/include/grub/gpt_partition.h @@ -23,33 +23,35 @@ #include #include -struct grub_gpt_part_type +struct grub_gpt_guid { grub_uint32_t data1; grub_uint16_t data2; grub_uint16_t data3; grub_uint8_t data4[8]; } __attribute__ ((aligned(8))); -typedef struct grub_gpt_part_type grub_gpt_part_type_t; +typedef struct grub_gpt_guid grub_gpt_guid_t; +typedef struct grub_gpt_guid grub_gpt_part_type_t; + +#define GRUB_GPT_GUID_INIT(a, b, c, d1, d2, d3, d4, d5, d6, d7, d8) \ + { \ + grub_cpu_to_le32_compile_time (a), \ + grub_cpu_to_le16_compile_time (b), \ + grub_cpu_to_le16_compile_time (c), \ + { d1, d2, d3, d4, d5, d6, d7, d8 } \ + } #define GRUB_GPT_PARTITION_TYPE_EMPTY \ - { 0x0, 0x0, 0x0, \ - { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } \ - } + GRUB_GPT_GUID_INIT (0x0, 0x0, 0x0, \ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0) #define GRUB_GPT_PARTITION_TYPE_BIOS_BOOT \ - { grub_cpu_to_le32_compile_time (0x21686148), \ - grub_cpu_to_le16_compile_time (0x6449), \ - grub_cpu_to_le16_compile_time (0x6e6f), \ - { 0x74, 0x4e, 0x65, 0x65, 0x64, 0x45, 0x46, 0x49 } \ - } + GRUB_GPT_GUID_INIT (0x21686148, 0x6449, 0x6e6f, \ + 0x74, 0x4e, 0x65, 0x65, 0x64, 0x45, 0x46, 0x49) #define GRUB_GPT_PARTITION_TYPE_LDM \ - { grub_cpu_to_le32_compile_time (0x5808C8AAU),\ - grub_cpu_to_le16_compile_time (0x7E8F), \ - grub_cpu_to_le16_compile_time (0x42E0), \ - { 0x85, 0xD2, 0xE1, 0xE9, 0x04, 0x34, 0xCF, 0xB3 } \ - } + GRUB_GPT_GUID_INIT (0x5808c8aa, 0x7e8f, 0x42e0, \ + 0x85, 0xd2, 0xe1, 0xe9, 0x04, 0x34, 0xcf, 0xb3) #define GRUB_GPT_HEADER_MAGIC \ { 0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54 } @@ -68,7 +70,7 @@ struct grub_gpt_header grub_uint64_t alternate_lba; grub_uint64_t start; grub_uint64_t end; - grub_uint8_t guid[16]; + grub_gpt_guid_t guid; grub_uint64_t partitions; grub_uint32_t maxpart; grub_uint32_t partentry_size; @@ -78,7 +80,7 @@ struct grub_gpt_header struct grub_gpt_partentry { grub_gpt_part_type_t type; - grub_uint8_t guid[16]; + grub_gpt_guid_t guid; grub_uint64_t start; grub_uint64_t end; grub_uint64_t attrib; diff --git a/tests/gpt_unit_test.c b/tests/gpt_unit_test.c index 83198bebf..86e4364a5 100644 --- a/tests/gpt_unit_test.c +++ b/tests/gpt_unit_test.c @@ -99,8 +99,8 @@ static const struct grub_gpt_header example_primary = { .alternate_lba = grub_cpu_to_le64_compile_time (BACKUP_HEADER_SECTOR), .start = grub_cpu_to_le64_compile_time (DATA_START_SECTOR), .end = grub_cpu_to_le64_compile_time (DATA_END_SECTOR), - .guid = {0xad, 0x31, 0xc1, 0x69, 0xd6, 0x67, 0xc6, 0x46, - 0x93, 0xc4, 0x12, 0x4c, 0x75, 0x52, 0x56, 0xac}, + .guid = GRUB_GPT_GUID_INIT(0x69c131ad, 0x67d6, 0x46c6, + 0x93, 0xc4, 0x12, 0x4c, 0x75, 0x52, 0x56, 0xac), .partitions = grub_cpu_to_le64_compile_time (PRIMARY_TABLE_SECTOR), .maxpart = grub_cpu_to_le32_compile_time (TABLE_ENTRIES), .partentry_size = grub_cpu_to_le32_compile_time (ENTRY_SIZE), @@ -117,8 +117,8 @@ static const struct grub_gpt_header example_backup = { .alternate_lba = grub_cpu_to_le64_compile_time (PRIMARY_HEADER_SECTOR), .start = grub_cpu_to_le64_compile_time (DATA_START_SECTOR), .end = grub_cpu_to_le64_compile_time (DATA_END_SECTOR), - .guid = {0xad, 0x31, 0xc1, 0x69, 0xd6, 0x67, 0xc6, 0x46, - 0x93, 0xc4, 0x12, 0x4c, 0x75, 0x52, 0x56, 0xac}, + .guid = GRUB_GPT_GUID_INIT(0x69c131ad, 0x67d6, 0x46c6, + 0x93, 0xc4, 0x12, 0x4c, 0x75, 0x52, 0x56, 0xac), .partitions = grub_cpu_to_le64_compile_time (BACKUP_TABLE_SECTOR), .maxpart = grub_cpu_to_le32_compile_time (TABLE_ENTRIES), .partentry_size = grub_cpu_to_le32_compile_time (ENTRY_SIZE), @@ -326,13 +326,13 @@ header_test (void) grub_errno = GRUB_ERR_NONE; /* Twiddle the GUID to invalidate the CRC. */ - primary.guid[0] = 0; + primary.guid.data1 = 0; grub_gpt_header_check (&primary, GRUB_DISK_SECTOR_BITS); grub_test_assert (grub_errno == GRUB_ERR_BAD_PART_TABLE, "unexpected error: %s", grub_errmsg); grub_errno = GRUB_ERR_NONE; - backup.guid[0] = 0; + backup.guid.data1 = 0; grub_gpt_header_check (&backup, GRUB_DISK_SECTOR_BITS); grub_test_assert (grub_errno == GRUB_ERR_BAD_PART_TABLE, "unexpected error: %s", grub_errmsg); From 508b02fc8a1fe58413ec8938ed1a7b149b5855fe Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Mon, 3 Nov 2014 17:14:37 -0800 Subject: [PATCH 11/85] gpt: new gptprio.next command for selecting priority based partitions Basic usage would look something like this: gptprio.next -d usr_dev -u usr_uuid linuxefi ($usr_dev)/boot/vmlinuz mount.usr=PARTUUID=$usr_uuid After booting the system should set the 'successful' bit on the partition that was used. --- Makefile.util.def | 6 + grub-core/Makefile.core.def | 5 + grub-core/commands/gptprio.c | 238 +++++++++++++++++++++++++++++++++++ include/grub/gpt_partition.h | 49 ++++++++ tests/gptprio_test.in | 150 ++++++++++++++++++++++ 5 files changed, 448 insertions(+) create mode 100644 grub-core/commands/gptprio.c create mode 100644 tests/gptprio_test.in diff --git a/Makefile.util.def b/Makefile.util.def index 8156fca5f..9249f77be 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -1165,6 +1165,12 @@ script = { common = tests/gptrepair_test.in; }; +script = { + testcase; + name = gptprio_test; + common = tests/gptprio_test.in; +}; + script = { testcase; name = file_filter_test; diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 123142dae..e2fdc0233 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -826,6 +826,11 @@ module = { common = commands/gptrepair.c; }; +module = { + name = gptprio; + common = commands/gptprio.c; +}; + module = { name = gpt; common = lib/gpt.c; diff --git a/grub-core/commands/gptprio.c b/grub-core/commands/gptprio.c new file mode 100644 index 000000000..29bd11d68 --- /dev/null +++ b/grub-core/commands/gptprio.c @@ -0,0 +1,238 @@ +/* gptprio.c - manage priority based partition selection. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 Free Software Foundation, Inc. + * Copyright (C) 2014 CoreOS, 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static const struct grub_arg_option options_next[] = { + {"set-device", 'd', 0, + N_("Set a variable to the name of selected partition."), + N_("VARNAME"), ARG_TYPE_STRING}, + {"set-uuid", 'u', 0, + N_("Set a variable to the GPT UUID of selected partition."), + N_("VARNAME"), ARG_TYPE_STRING}, + {0, 0, 0, 0, 0, 0} +}; + +enum options_next +{ + NEXT_SET_DEVICE, + NEXT_SET_UUID, +}; + +static unsigned int +grub_gptprio_priority (struct grub_gpt_partentry *entry) +{ + return (unsigned int) grub_gpt_entry_attribute + (entry, GRUB_GPT_PART_ATTR_OFFSET_GPTPRIO_PRIORITY, 4); +} + +static unsigned int +grub_gptprio_tries_left (struct grub_gpt_partentry *entry) +{ + return (unsigned int) grub_gpt_entry_attribute + (entry, GRUB_GPT_PART_ATTR_OFFSET_GPTPRIO_TRIES_LEFT, 4); +} + +static void +grub_gptprio_set_tries_left (struct grub_gpt_partentry *entry, + unsigned int tries_left) +{ + grub_gpt_entry_set_attribute + (entry, tries_left, GRUB_GPT_PART_ATTR_OFFSET_GPTPRIO_TRIES_LEFT, 4); +} + +static unsigned int +grub_gptprio_successful (struct grub_gpt_partentry *entry) +{ + return (unsigned int) grub_gpt_entry_attribute + (entry, GRUB_GPT_PART_ATTR_OFFSET_GPTPRIO_SUCCESSFUL, 1); +} + +static grub_err_t +grub_find_next (const char *disk_name, + const grub_gpt_part_type_t *part_type, + char **part_name, char **part_guid) +{ + struct grub_gpt_partentry *part_found = NULL; + grub_device_t dev = NULL; + grub_gpt_t gpt = NULL; + grub_uint32_t i, part_index; + + dev = grub_device_open (disk_name); + if (!dev) + goto done; + + gpt = grub_gpt_read (dev->disk); + if (!gpt) + goto done; + + if (!(gpt->status & GRUB_GPT_BOTH_VALID)) + if (grub_gpt_repair (dev->disk, gpt)) + goto done; + + for (i = 0; i < grub_le_to_cpu32 (gpt->primary.maxpart); i++) + { + struct grub_gpt_partentry *part = &gpt->entries[i]; + + if (grub_memcmp (part_type, &part->type, sizeof (*part_type)) == 0) + { + unsigned int priority, tries_left, successful, old_priority = 0; + + priority = grub_gptprio_priority (part); + tries_left = grub_gptprio_tries_left (part); + successful = grub_gptprio_successful (part); + + if (part_found) + old_priority = grub_gptprio_priority (part_found); + + if ((tries_left || successful) && priority > old_priority) + { + part_index = i; + part_found = part; + } + } + } + + if (!part_found) + { + grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("no such partition")); + goto done; + } + + if (grub_gptprio_tries_left (part_found)) + { + unsigned int tries_left = grub_gptprio_tries_left (part_found); + + grub_gptprio_set_tries_left (part_found, tries_left - 1); + + if (grub_gpt_update_checksums (gpt)) + goto done; + + if (grub_gpt_write (dev->disk, gpt)) + goto done; + } + + *part_name = grub_xasprintf ("%s,gpt%u", disk_name, part_index + 1); + if (!*part_name) + goto done; + + *part_guid = + grub_xasprintf ("%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + grub_le_to_cpu32 (part_found->guid.data1), + grub_le_to_cpu16 (part_found->guid.data2), + grub_le_to_cpu16 (part_found->guid.data3), + part_found->guid.data4[0], + part_found->guid.data4[1], + part_found->guid.data4[2], + part_found->guid.data4[3], + part_found->guid.data4[4], + part_found->guid.data4[5], + part_found->guid.data4[6], + part_found->guid.data4[7]); + if (!*part_name) + goto done; + + grub_errno = GRUB_ERR_NONE; + +done: + grub_gpt_free (gpt); + + if (dev) + grub_device_close (dev); + + return grub_errno; +} + + + +static grub_err_t +grub_cmd_next (grub_extcmd_context_t ctxt, int argc, char **args) +{ + struct grub_arg_list *state = ctxt->state; + char *p, *root = NULL, *part_name = NULL, *part_guid = NULL; + + /* TODO: Add a uuid parser and a command line flag for providing type. */ + grub_gpt_part_type_t part_type = GRUB_GPT_PARTITION_TYPE_USR_X86_64; + + if (!state[NEXT_SET_DEVICE].set || !state[NEXT_SET_UUID].set) + { + grub_error (GRUB_ERR_INVALID_COMMAND, N_("-d and -u are required")); + goto done; + } + + if (argc == 0) + root = grub_strdup (grub_env_get ("root")); + else if (argc == 1) + root = grub_strdup (args[0]); + else + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unexpected arguments")); + goto done; + } + + if (!root) + goto done; + + /* To make using $root practical strip off the partition name. */ + p = grub_strchr (root, ','); + if (p) + *p = '\0'; + + if (grub_find_next (root, &part_type, &part_name, &part_guid)) + goto done; + + if (grub_env_set (state[NEXT_SET_DEVICE].arg, part_name)) + goto done; + + if (grub_env_set (state[NEXT_SET_UUID].arg, part_guid)) + goto done; + + grub_errno = GRUB_ERR_NONE; + +done: + grub_free (root); + grub_free (part_name); + grub_free (part_guid); + + return grub_errno; +} + +static grub_extcmd_t cmd_next; + +GRUB_MOD_INIT(gptprio) +{ + cmd_next = grub_register_extcmd ("gptprio.next", grub_cmd_next, 0, + N_("-d VARNAME -u VARNAME [DEVICE]"), + N_("Select next partition to boot."), + options_next); +} + +GRUB_MOD_FINI(gptprio) +{ + grub_unregister_extcmd (cmd_next); +} diff --git a/include/grub/gpt_partition.h b/include/grub/gpt_partition.h index df076ca64..e41c66539 100644 --- a/include/grub/gpt_partition.h +++ b/include/grub/gpt_partition.h @@ -53,6 +53,10 @@ typedef struct grub_gpt_guid grub_gpt_part_type_t; GRUB_GPT_GUID_INIT (0x5808c8aa, 0x7e8f, 0x42e0, \ 0x85, 0xd2, 0xe1, 0xe9, 0x04, 0x34, 0xcf, 0xb3) +#define GRUB_GPT_PARTITION_TYPE_USR_X86_64 \ + GRUB_GPT_GUID_INIT (0x5dfbf5f4, 0x2848, 0x4bac, \ + 0xaa, 0x5e, 0x0d, 0x9a, 0x20, 0xb7, 0x45, 0xa6) + #define GRUB_GPT_HEADER_MAGIC \ { 0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54 } @@ -87,6 +91,51 @@ struct grub_gpt_partentry char name[72]; } GRUB_PACKED; +enum grub_gpt_part_attr_offset +{ + /* Standard partition attribute bits defined by UEFI. */ + GRUB_GPT_PART_ATTR_OFFSET_REQUIRED = 0, + GRUB_GPT_PART_ATTR_OFFSET_NO_BLOCK_IO_PROTOCOL = 1, + GRUB_GPT_PART_ATTR_OFFSET_LEGACY_BIOS_BOOTABLE = 2, + + /* De facto standard attribute bits defined by Microsoft and reused by + * http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec */ + GRUB_GPT_PART_ATTR_OFFSET_READ_ONLY = 60, + GRUB_GPT_PART_ATTR_OFFSET_NO_AUTO = 63, + + /* Partition attributes for priority based selection, + * Currently only valid for PARTITION_TYPE_USR_X86_64. + * TRIES_LEFT and PRIORITY are 4 bit wide fields. */ + GRUB_GPT_PART_ATTR_OFFSET_GPTPRIO_PRIORITY = 48, + GRUB_GPT_PART_ATTR_OFFSET_GPTPRIO_TRIES_LEFT = 52, + GRUB_GPT_PART_ATTR_OFFSET_GPTPRIO_SUCCESSFUL = 56, +}; + +/* Helpers for reading/writing partition attributes. */ +static inline grub_uint64_t +grub_gpt_entry_attribute (struct grub_gpt_partentry *entry, + enum grub_gpt_part_attr_offset offset, + unsigned int bits) +{ + grub_uint64_t attrib = grub_le_to_cpu64 (entry->attrib); + + return (attrib >> offset) & ((1ULL << bits) - 1); +} + +static inline void +grub_gpt_entry_set_attribute (struct grub_gpt_partentry *entry, + grub_uint64_t value, + enum grub_gpt_part_attr_offset offset, + unsigned int bits) +{ + grub_uint64_t attrib, mask; + + mask = (((1ULL << bits) - 1) << offset); + attrib = grub_le_to_cpu64 (entry->attrib) & ~mask; + attrib |= ((value << offset) & mask); + entry->attrib = grub_cpu_to_le64 (attrib); +} + /* Basic GPT partmap module. */ grub_err_t grub_gpt_partition_map_iterate (grub_disk_t disk, diff --git a/tests/gptprio_test.in b/tests/gptprio_test.in new file mode 100644 index 000000000..f4aea0dc9 --- /dev/null +++ b/tests/gptprio_test.in @@ -0,0 +1,150 @@ +#! /bin/bash +set -e + +# Copyright (C) 2010 Free Software Foundation, Inc. +# Copyright (C) 2014 CoreOS, 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 . + +sgdisk=sgdisk +grubshell=@builddir@/grub-shell + +if ! which "${sgdisk}" >/dev/null 2>&1; then + echo "sgdisk not installed; cannot test gptprio." + exit 77 +fi + +. "@builddir@/grub-core/modinfo.sh" + +case "${grub_modinfo_target_cpu}-${grub_modinfo_platform}" in + mips-qemu_mips | mipsel-qemu_mips | i386-qemu | i386-multiboot | i386-coreboot | mipsel-loongson) + disk=ata0 + ;; + powerpc-ieee1275) + disk=ieee1275//pci@80000000/mac-io@4/ata-3@20000/disk@0 + # FIXME: QEMU firmware has bugs which prevent it from accessing hard disk w/o recognised label. + exit 0 + ;; + sparc64-ieee1275) + disk=ieee1275//pci@1fe\,0/pci-ata@5/ide0@500/disk@0 + # FIXME: QEMU firmware has bugs which prevent it from accessing hard disk w/o recognised label. + exit 0 + ;; + i386-ieee1275) + disk=ieee1275/d + # FIXME: QEMU firmware has bugs which prevent it from accessing hard disk w/o recognised label. + exit 0 + ;; + mips-arc) + # FIXME: ARC firmware has bugs which prevent it from accessing hard disk w/o dvh disklabel. + exit 0 ;; + mipsel-arc) + disk=arc/scsi0/disk0/rdisk0 + ;; + *) + disk=hd0 + ;; +esac +img1="`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"`" || exit 1 +trap "rm -f '${img1}'" EXIT + +prio_type="5dfbf5f4-2848-4bac-aa5e-0d9a20b745a6" +declare -a prio_uuid +prio_uuid[2]="9b003904-d006-4ab3-97f1-73f547b7af1a" +prio_uuid[3]="1aa5a658-5b02-414d-9b71-f7e6c151f0cd" +prio_uuid[4]="8aa0240d-98af-42b0-b32a-ccbe0572d62b" + +create_disk_image () { + rm -f "${img1}" + dd if=/dev/zero of="${img1}" bs=512 count=1 seek=100 status=none + ${sgdisk} \ + -n 1:0:+1 -c 1:ESP -t 1:ef00 \ + -n 2:0:+1 -c 2:A -t 2:"${prio_type}" -u 2:"${prio_uuid[2]}" \ + -n 3:0:+1 -c 3:B -t 3:"${prio_type}" -u 3:"${prio_uuid[3]}" \ + -n 4:0:+1 -c 4:C -t 4:"${prio_type}" -u 4:"${prio_uuid[4]}" \ + "${img1}" >/dev/null +} + + +fmt_prio () { + priority=$(( ( $1 & 15 ) << 48 )) + tries=$(( ( $2 & 15 ) << 52 )) + success=$(( ( $3 & 1 ) << 56 )) + printf %016x $(( priority | tries | success )) +} + +set_prio () { + part="$1" + attr=$(fmt_prio $2 $3 $4) + ${sgdisk} -A "${part}:=:${attr}" "${img1}" >/dev/null +} + +check_prio () { + part="$1" + expect=$(fmt_prio $2 $3 $4) + result=$(LANG=C ${sgdisk} -i "${part}" "${img1}" \ + | awk '/^Attribute flags: / {print $3}') + if [[ "${expect}" != "${result}" ]]; then + echo "Partition ${part} has attributes ${result}, not ${expect}" >&2 + exit 1 + fi +} + +run_next() { + "${grubshell}" --disk="${img1}" --modules=gptprio < Date: Sat, 15 Nov 2014 13:27:13 -0800 Subject: [PATCH 12/85] gpt: split out checksum recomputation For basic data modifications the full repair function is overkill. --- grub-core/lib/gpt.c | 30 ++++++++++++++++++++---------- include/grub/gpt_partition.h | 3 +++ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index 67ffdf703..198234071 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -293,7 +293,6 @@ grub_err_t grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt) { grub_uint64_t backup_header, backup_entries; - grub_uint32_t crc; if (disk->log_sector_size != gpt->log_sector_size) return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, @@ -331,13 +330,32 @@ grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt) gpt->backup.alternate_lba = gpt->primary.header_lba; gpt->backup.partitions = grub_cpu_to_le64 (backup_entries); + /* Recompute checksums. */ + if (grub_gpt_update_checksums (gpt)) + return grub_errno; + + /* Sanity check. */ + if (grub_gpt_header_check (&gpt->primary, gpt->log_sector_size)) + return grub_error (GRUB_ERR_BUG, "Generated invalid GPT primary header"); + + if (grub_gpt_header_check (&gpt->backup, gpt->log_sector_size)) + return grub_error (GRUB_ERR_BUG, "Generated invalid GPT backup header"); + + gpt->status |= GRUB_GPT_BOTH_VALID; + return GRUB_ERR_NONE; +} + +grub_err_t +grub_gpt_update_checksums (grub_gpt_t gpt) +{ + grub_uint32_t crc; + /* Writing headers larger than our header structure are unsupported. */ gpt->primary.headersize = grub_cpu_to_le32_compile_time (sizeof (gpt->primary)); gpt->backup.headersize = grub_cpu_to_le32_compile_time (sizeof (gpt->backup)); - /* Recompute checksums. */ if (grub_gpt_lecrc32 (gpt->entries, gpt->entries_size, &crc)) return grub_errno; @@ -350,14 +368,6 @@ grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt) if (grub_gpt_header_lecrc32 (&gpt->backup, &gpt->backup.crc32)) return grub_errno; - /* Sanity check. */ - if (grub_gpt_header_check (&gpt->primary, gpt->log_sector_size)) - return grub_error (GRUB_ERR_BUG, "Generated invalid GPT primary header"); - - if (grub_gpt_header_check (&gpt->backup, gpt->log_sector_size)) - return grub_error (GRUB_ERR_BUG, "Generated invalid GPT backup header"); - - gpt->status |= GRUB_GPT_BOTH_VALID; return GRUB_ERR_NONE; } diff --git a/include/grub/gpt_partition.h b/include/grub/gpt_partition.h index e41c66539..50592d6d0 100644 --- a/include/grub/gpt_partition.h +++ b/include/grub/gpt_partition.h @@ -200,6 +200,9 @@ grub_gpt_t grub_gpt_read (grub_disk_t disk); /* Sync up primary and backup headers, recompute checksums. */ grub_err_t grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt); +/* Recompute checksums, must be called after modifying GPT data. */ +grub_err_t grub_gpt_update_checksums (grub_gpt_t gpt); + /* Write headers and entry tables back to disk. */ grub_err_t grub_gpt_write (grub_disk_t disk, grub_gpt_t gpt); From d9bdbc10485a5c6f610569077631294683da4e34 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Thu, 27 Nov 2014 12:55:53 -0800 Subject: [PATCH 13/85] gpt: move gpt guid printing function to common library --- grub-core/commands/gptprio.c | 16 ++-------------- grub-core/lib/gpt.c | 13 +++++++++++++ include/grub/gpt_partition.h | 4 ++++ 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/grub-core/commands/gptprio.c b/grub-core/commands/gptprio.c index 29bd11d68..ce5840b4e 100644 --- a/grub-core/commands/gptprio.c +++ b/grub-core/commands/gptprio.c @@ -141,20 +141,8 @@ grub_find_next (const char *disk_name, if (!*part_name) goto done; - *part_guid = - grub_xasprintf ("%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", - grub_le_to_cpu32 (part_found->guid.data1), - grub_le_to_cpu16 (part_found->guid.data2), - grub_le_to_cpu16 (part_found->guid.data3), - part_found->guid.data4[0], - part_found->guid.data4[1], - part_found->guid.data4[2], - part_found->guid.data4[3], - part_found->guid.data4[4], - part_found->guid.data4[5], - part_found->guid.data4[6], - part_found->guid.data4[7]); - if (!*part_name) + *part_guid = grub_gpt_guid_to_str (&part_found->guid); + if (!*part_guid) goto done; grub_errno = GRUB_ERR_NONE; diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index 198234071..9a1835b84 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -31,6 +31,19 @@ GRUB_MOD_LICENSE ("GPLv3+"); static grub_uint8_t grub_gpt_magic[] = GRUB_GPT_HEADER_MAGIC; +char * +grub_gpt_guid_to_str (grub_gpt_guid_t *guid) +{ + return grub_xasprintf ("%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + grub_le_to_cpu32 (guid->data1), + grub_le_to_cpu16 (guid->data2), + grub_le_to_cpu16 (guid->data3), + guid->data4[0], guid->data4[1], + guid->data4[2], guid->data4[3], + guid->data4[4], guid->data4[5], + guid->data4[6], guid->data4[7]); +} + static grub_uint64_t grub_gpt_size_to_sectors (grub_gpt_t gpt, grub_size_t size) { diff --git a/include/grub/gpt_partition.h b/include/grub/gpt_partition.h index 50592d6d0..166fd4b55 100644 --- a/include/grub/gpt_partition.h +++ b/include/grub/gpt_partition.h @@ -33,6 +33,10 @@ struct grub_gpt_guid typedef struct grub_gpt_guid grub_gpt_guid_t; typedef struct grub_gpt_guid grub_gpt_part_type_t; +/* Format the raw little-endian GUID as a newly allocated string. */ +char * grub_gpt_guid_to_str (grub_gpt_guid_t *guid); + + #define GRUB_GPT_GUID_INIT(a, b, c, d1, d2, d3, d4, d5, d6, d7, d8) \ { \ grub_cpu_to_le32_compile_time (a), \ From ffb13159f1e88d8c66954c3dfbeb027f943b3b1d Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Thu, 27 Nov 2014 14:54:27 -0800 Subject: [PATCH 14/85] gpt: switch partition names to a 16 bit type In UEFI/GPT strings are UTF-16 so use a uint16 to make dealing with the string practical. --- include/grub/gpt_partition.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/grub/gpt_partition.h b/include/grub/gpt_partition.h index 166fd4b55..1142317e3 100644 --- a/include/grub/gpt_partition.h +++ b/include/grub/gpt_partition.h @@ -92,7 +92,7 @@ struct grub_gpt_partentry grub_uint64_t start; grub_uint64_t end; grub_uint64_t attrib; - char name[72]; + grub_uint16_t name[36]; } GRUB_PACKED; enum grub_gpt_part_attr_offset From febf4666fbabc3ab4eaab32f4972b45b5c64c06d Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Thu, 27 Nov 2014 15:49:57 -0800 Subject: [PATCH 15/85] tests: add some partitions to the gpt unit test data --- tests/gpt_unit_test.c | 65 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/tests/gpt_unit_test.c b/tests/gpt_unit_test.c index 86e4364a5..5692a5a52 100644 --- a/tests/gpt_unit_test.c +++ b/tests/gpt_unit_test.c @@ -89,12 +89,12 @@ struct test_data }; -/* Sample primary GPT header for an empty 1MB disk. */ +/* Sample primary GPT header for a 1MB disk. */ static const struct grub_gpt_header example_primary = { .magic = GRUB_GPT_HEADER_MAGIC, .version = GRUB_GPT_HEADER_VERSION, .headersize = sizeof (struct grub_gpt_header), - .crc32 = grub_cpu_to_le32_compile_time (0x7cd8642c), + .crc32 = grub_cpu_to_le32_compile_time (0xb985abe0), .header_lba = grub_cpu_to_le64_compile_time (PRIMARY_HEADER_SECTOR), .alternate_lba = grub_cpu_to_le64_compile_time (BACKUP_HEADER_SECTOR), .start = grub_cpu_to_le64_compile_time (DATA_START_SECTOR), @@ -104,7 +104,52 @@ static const struct grub_gpt_header example_primary = { .partitions = grub_cpu_to_le64_compile_time (PRIMARY_TABLE_SECTOR), .maxpart = grub_cpu_to_le32_compile_time (TABLE_ENTRIES), .partentry_size = grub_cpu_to_le32_compile_time (ENTRY_SIZE), - .partentry_crc32 = grub_cpu_to_le32_compile_time (0xab54d286), + .partentry_crc32 = grub_cpu_to_le32_compile_time (0x074e052c), +}; + +static const struct grub_gpt_partentry example_entries[TABLE_ENTRIES] = { + { + .type = GRUB_GPT_PARTITION_TYPE_EFI_SYSTEM, + .guid = GRUB_GPT_GUID_INIT (0xa0f1792e, 0xb4ce, 0x4136, 0xbc, 0xf2, + 0x1a, 0xfc, 0x13, 0x3c, 0x28, 0x28), + .start = grub_cpu_to_le64_compile_time (DATA_START_SECTOR), + .end = grub_cpu_to_le64_compile_time (0x3f), + .attrib = 0x0, + .name = { + grub_cpu_to_le16_compile_time ('E'), + grub_cpu_to_le16_compile_time ('F'), + grub_cpu_to_le16_compile_time ('I'), + grub_cpu_to_le16_compile_time (' '), + grub_cpu_to_le16_compile_time ('S'), + grub_cpu_to_le16_compile_time ('Y'), + grub_cpu_to_le16_compile_time ('S'), + grub_cpu_to_le16_compile_time ('T'), + grub_cpu_to_le16_compile_time ('E'), + grub_cpu_to_le16_compile_time ('M'), + 0x0, + } + }, + { + .type = GRUB_GPT_PARTITION_TYPE_BIOS_BOOT, + .guid = GRUB_GPT_GUID_INIT (0x876c898d, 0x1b40, 0x4727, 0xa1, 0x61, + 0xed, 0xf9, 0xb5, 0x48, 0x66, 0x74), + .start = grub_cpu_to_le64_compile_time (0x40), + .end = grub_cpu_to_le64_compile_time (0x7f), + .attrib = grub_cpu_to_le64_compile_time ( + 1ULL << GRUB_GPT_PART_ATTR_OFFSET_LEGACY_BIOS_BOOTABLE), + .name = { + grub_cpu_to_le16_compile_time ('B'), + grub_cpu_to_le16_compile_time ('I'), + grub_cpu_to_le16_compile_time ('O'), + grub_cpu_to_le16_compile_time ('S'), + grub_cpu_to_le16_compile_time (' '), + grub_cpu_to_le16_compile_time ('B'), + grub_cpu_to_le16_compile_time ('O'), + grub_cpu_to_le16_compile_time ('O'), + grub_cpu_to_le16_compile_time ('T'), + 0x0, + } + }, }; /* And the backup header. */ @@ -112,7 +157,7 @@ static const struct grub_gpt_header example_backup = { .magic = GRUB_GPT_HEADER_MAGIC, .version = GRUB_GPT_HEADER_VERSION, .headersize = sizeof (struct grub_gpt_header), - .crc32 = grub_cpu_to_le32_compile_time (0xcfaa4a27), + .crc32 = grub_cpu_to_le32_compile_time (0x0af785eb), .header_lba = grub_cpu_to_le64_compile_time (BACKUP_HEADER_SECTOR), .alternate_lba = grub_cpu_to_le64_compile_time (PRIMARY_HEADER_SECTOR), .start = grub_cpu_to_le64_compile_time (DATA_START_SECTOR), @@ -122,7 +167,7 @@ static const struct grub_gpt_header example_backup = { .partitions = grub_cpu_to_le64_compile_time (BACKUP_TABLE_SECTOR), .maxpart = grub_cpu_to_le32_compile_time (TABLE_ENTRIES), .partentry_size = grub_cpu_to_le32_compile_time (ENTRY_SIZE), - .partentry_crc32 = grub_cpu_to_le32_compile_time (0xab54d286), + .partentry_crc32 = grub_cpu_to_le32_compile_time (0x074e052c), }; /* Sample protective MBR for the same 1MB disk. Note, this matches @@ -192,6 +237,10 @@ reset_disk (struct test_data *data) memcpy (&data->raw->mbr, &example_pmbr, sizeof (data->raw->mbr)); memcpy (&data->raw->primary_header, &example_primary, sizeof (data->raw->primary_header)); + memcpy (&data->raw->primary_entries, &example_entries, + sizeof (data->raw->primary_entries)); + memcpy (&data->raw->backup_entries, &example_entries, + sizeof (data->raw->backup_entries)); memcpy (&data->raw->backup_header, &example_backup, sizeof (data->raw->backup_header)); @@ -270,11 +319,7 @@ read_disk (struct test_data *data) gpt = grub_gpt_read (data->dev->disk); if (gpt == NULL) - { - grub_print_error (); - grub_fatal ("grub_gpt_read failed"); - } - + grub_fatal ("grub_gpt_read failed: %s", grub_errmsg); return gpt; } From 67475f53e0ac4a844f793296ba2e4af707d5b20e Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Thu, 27 Nov 2014 16:34:21 -0800 Subject: [PATCH 16/85] gpt: add search by partition label and uuid commands Builds on the existing filesystem search code. Only for GPT right now. --- Makefile.util.def | 2 + grub-core/Makefile.core.def | 10 +++ grub-core/commands/search.c | 49 +++++++++++++++ grub-core/commands/search_part_label.c | 5 ++ grub-core/commands/search_part_uuid.c | 5 ++ grub-core/commands/search_wrap.c | 12 ++++ grub-core/lib/gpt.c | 64 ++++++++++++++++++++ include/grub/gpt_partition.h | 16 +++++ include/grub/search.h | 4 ++ tests/gpt_unit_test.c | 84 ++++++++++++++++++++++++++ 10 files changed, 251 insertions(+) create mode 100644 grub-core/commands/search_part_label.c create mode 100644 grub-core/commands/search_part_uuid.c diff --git a/Makefile.util.def b/Makefile.util.def index 9249f77be..bc0f178ff 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -1271,6 +1271,8 @@ program = { name = gpt_unit_test; common = tests/gpt_unit_test.c; common = tests/lib/unit_test.c; + common = grub-core/commands/search_part_label.c; + common = grub-core/commands/search_part_uuid.c; common = grub-core/disk/host.c; common = grub-core/kern/emu/hostfs.c; common = grub-core/lib/gpt.c; diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index e2fdc0233..ab3190c3e 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1013,6 +1013,16 @@ module = { common = commands/search_label.c; }; +module = { + name = search_part_uuid; + common = commands/search_part_uuid.c; +}; + +module = { + name = search_part_label; + common = commands/search_part_label.c; +}; + module = { name = setpci; common = commands/setpci.c; diff --git a/grub-core/commands/search.c b/grub-core/commands/search.c index 7dd32e445..09e165ed3 100644 --- a/grub-core/commands/search.c +++ b/grub-core/commands/search.c @@ -30,6 +30,9 @@ #include #include #include +#if defined(DO_SEARCH_PART_UUID) || defined(DO_SEARCH_PART_LABEL) +#include +#endif GRUB_MOD_LICENSE ("GPLv3+"); @@ -90,6 +93,44 @@ iterate_device (const char *name, void *data) } grub_free (buf); } +#elif defined(DO_SEARCH_PART_UUID) + { + grub_device_t dev; + char *quid; + + dev = grub_device_open (name); + if (dev) + { + if (grub_gpt_part_uuid (dev, &quid) == GRUB_ERR_NONE) + { + if (grub_strcasecmp (quid, ctx->key) == 0) + found = 1; + + grub_free (quid); + } + + grub_device_close (dev); + } + } +#elif defined(DO_SEARCH_PART_LABEL) + { + grub_device_t dev; + char *quid; + + dev = grub_device_open (name); + if (dev) + { + if (grub_gpt_part_label (dev, &quid) == GRUB_ERR_NONE) + { + if (grub_strcmp (quid, ctx->key) == 0) + found = 1; + + grub_free (quid); + } + + grub_device_close (dev); + } + } #else { /* SEARCH_FS_UUID or SEARCH_LABEL */ @@ -313,6 +354,10 @@ static grub_command_t cmd; #ifdef DO_SEARCH_FILE GRUB_MOD_INIT(search_fs_file) +#elif defined(DO_SEARCH_PART_UUID) +GRUB_MOD_INIT(search_part_uuid) +#elif defined(DO_SEARCH_PART_LABEL) +GRUB_MOD_INIT(search_part_label) #elif defined (DO_SEARCH_FS_UUID) GRUB_MOD_INIT(search_fs_uuid) #else @@ -327,6 +372,10 @@ GRUB_MOD_INIT(search_label) #ifdef DO_SEARCH_FILE GRUB_MOD_FINI(search_fs_file) +#elif defined(DO_SEARCH_PART_UUID) +GRUB_MOD_FINI(search_part_uuid) +#elif defined(DO_SEARCH_PART_LABEL) +GRUB_MOD_FINI(search_part_label) #elif defined (DO_SEARCH_FS_UUID) GRUB_MOD_FINI(search_fs_uuid) #else diff --git a/grub-core/commands/search_part_label.c b/grub-core/commands/search_part_label.c new file mode 100644 index 000000000..ca906cbd9 --- /dev/null +++ b/grub-core/commands/search_part_label.c @@ -0,0 +1,5 @@ +#define DO_SEARCH_PART_LABEL 1 +#define FUNC_NAME grub_search_part_label +#define COMMAND_NAME "search.part_label" +#define HELP_MESSAGE N_("Search devices by partition label. If VARIABLE is specified, the first device found is set to a variable.") +#include "search.c" diff --git a/grub-core/commands/search_part_uuid.c b/grub-core/commands/search_part_uuid.c new file mode 100644 index 000000000..2d1d3d0d7 --- /dev/null +++ b/grub-core/commands/search_part_uuid.c @@ -0,0 +1,5 @@ +#define DO_SEARCH_PART_UUID 1 +#define FUNC_NAME grub_search_part_uuid +#define COMMAND_NAME "search.part_uuid" +#define HELP_MESSAGE N_("Search devices by partition UUID. If VARIABLE is specified, the first device found is set to a variable.") +#include "search.c" diff --git a/grub-core/commands/search_wrap.c b/grub-core/commands/search_wrap.c index d7fd26b94..e3ff756df 100644 --- a/grub-core/commands/search_wrap.c +++ b/grub-core/commands/search_wrap.c @@ -36,6 +36,10 @@ static const struct grub_arg_option options[] = 0, 0}, {"fs-uuid", 'u', 0, N_("Search devices by a filesystem UUID."), 0, 0}, + {"part-label", 'L', 0, N_("Search devices by a partition label."), + 0, 0}, + {"part-uuid", 'U', 0, N_("Search devices by a partition UUID."), + 0, 0}, {"set", 's', GRUB_ARG_OPTION_OPTIONAL, N_("Set a variable to the first device found."), N_("VARNAME"), ARG_TYPE_STRING}, @@ -71,6 +75,8 @@ enum options SEARCH_FILE, SEARCH_LABEL, SEARCH_FS_UUID, + SEARCH_PART_LABEL, + SEARCH_PART_UUID, SEARCH_SET, SEARCH_NO_FLOPPY, SEARCH_HINT, @@ -186,6 +192,12 @@ grub_cmd_search (grub_extcmd_context_t ctxt, int argc, char **args) else if (state[SEARCH_FS_UUID].set) grub_search_fs_uuid (id, var, state[SEARCH_NO_FLOPPY].set, hints, nhints); + else if (state[SEARCH_PART_LABEL].set) + grub_search_part_label (id, var, state[SEARCH_NO_FLOPPY].set, + hints, nhints); + else if (state[SEARCH_PART_UUID].set) + grub_search_part_uuid (id, var, state[SEARCH_NO_FLOPPY].set, + hints, nhints); else if (state[SEARCH_FILE].set) grub_search_fs_file (id, var, state[SEARCH_NO_FLOPPY].set, hints, nhints); diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index 9a1835b84..10a4b852d 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -18,7 +18,9 @@ * along with GRUB. If not, see . */ +#include #include +#include #include #include #include @@ -44,6 +46,68 @@ grub_gpt_guid_to_str (grub_gpt_guid_t *guid) guid->data4[6], guid->data4[7]); } +static grub_err_t +grub_gpt_device_partentry (grub_device_t device, + struct grub_gpt_partentry *entry) +{ + grub_disk_t disk = device->disk; + grub_partition_t p; + grub_err_t err; + + if (!disk || !disk->partition) + return grub_error (GRUB_ERR_BUG, "not a partition"); + + if (grub_strcmp (disk->partition->partmap->name, "gpt")) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "not a GPT partition"); + + p = disk->partition; + disk->partition = p->parent; + err = grub_disk_read (disk, p->offset, p->index, sizeof (*entry), entry); + disk->partition = p; + + return err; +} + +grub_err_t +grub_gpt_part_label (grub_device_t device, char **label) +{ + struct grub_gpt_partentry entry; + const grub_size_t name_len = ARRAY_SIZE (entry.name); + const grub_size_t label_len = name_len * GRUB_MAX_UTF8_PER_UTF16 + 1; + grub_size_t i; + grub_uint8_t *end; + + if (grub_gpt_device_partentry (device, &entry)) + return grub_errno; + + *label = grub_malloc (label_len); + if (!*label) + return grub_errno; + + for (i = 0; i < name_len; i++) + entry.name[i] = grub_le_to_cpu16 (entry.name[i]); + + end = grub_utf16_to_utf8 ((grub_uint8_t *) *label, entry.name, name_len); + *end = '\0'; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_gpt_part_uuid (grub_device_t device, char **uuid) +{ + struct grub_gpt_partentry entry; + + if (grub_gpt_device_partentry (device, &entry)) + return grub_errno; + + *uuid = grub_gpt_guid_to_str (&entry.guid); + if (!*uuid) + return grub_errno; + + return GRUB_ERR_NONE; +} + static grub_uint64_t grub_gpt_size_to_sectors (grub_gpt_t gpt, grub_size_t size) { diff --git a/include/grub/gpt_partition.h b/include/grub/gpt_partition.h index 1142317e3..8ff62d67f 100644 --- a/include/grub/gpt_partition.h +++ b/include/grub/gpt_partition.h @@ -49,6 +49,10 @@ char * grub_gpt_guid_to_str (grub_gpt_guid_t *guid); GRUB_GPT_GUID_INIT (0x0, 0x0, 0x0, \ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0) +#define GRUB_GPT_PARTITION_TYPE_EFI_SYSTEM \ + GRUB_GPT_GUID_INIT (0xc12a7328, 0xf81f, 0x11d2, \ + 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b) + #define GRUB_GPT_PARTITION_TYPE_BIOS_BOOT \ GRUB_GPT_GUID_INIT (0x21686148, 0x6449, 0x6e6f, \ 0x74, 0x4e, 0x65, 0x65, 0x64, 0x45, 0x46, 0x49) @@ -216,4 +220,16 @@ grub_err_t grub_gpt_pmbr_check (struct grub_msdos_partition_mbr *mbr); grub_err_t grub_gpt_header_check (struct grub_gpt_header *gpt, unsigned int log_sector_size); + +/* Utilities for simple partition data lookups, usage is intended to + * be similar to fs->label and fs->uuid functions. */ + +/* Return the partition label of the device DEVICE in LABEL. + * The label is in a new buffer and should be freed by the caller. */ +grub_err_t grub_gpt_part_label (grub_device_t device, char **label); + +/* Return the partition uuid of the device DEVICE in UUID. + * The label is in a new buffer and should be freed by the caller. */ +grub_err_t grub_gpt_part_uuid (grub_device_t device, char **uuid); + #endif /* ! GRUB_GPT_PARTITION_HEADER */ diff --git a/include/grub/search.h b/include/grub/search.h index d80347df3..c2f40abe9 100644 --- a/include/grub/search.h +++ b/include/grub/search.h @@ -25,5 +25,9 @@ void grub_search_fs_uuid (const char *key, const char *var, int no_floppy, char **hints, unsigned nhints); void grub_search_label (const char *key, const char *var, int no_floppy, char **hints, unsigned nhints); +void grub_search_part_uuid (const char *key, const char *var, int no_floppy, + char **hints, unsigned nhints); +void grub_search_part_label (const char *key, const char *var, int no_floppy, + char **hints, unsigned nhints); #endif diff --git a/tests/gpt_unit_test.c b/tests/gpt_unit_test.c index 5692a5a52..deb55a926 100644 --- a/tests/gpt_unit_test.c +++ b/tests/gpt_unit_test.c @@ -21,10 +21,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -534,6 +536,84 @@ repair_test (void) close_disk (&data); } + +static void +search_label_test (void) +{ + struct test_data data; + const char *test_result; + char *expected_result; + + open_disk (&data); + + expected_result = grub_xasprintf ("%s,gpt1", data.dev->disk->name); + grub_env_unset ("test_result"); + grub_search_part_label ("EFI SYSTEM", "test_result", 0, NULL, 0); + test_result = grub_env_get ("test_result"); + grub_test_assert (test_result && strcmp (test_result, expected_result) == 0, + "wrong device: %s (%s)", test_result, expected_result); + grub_free (expected_result); + + expected_result = grub_xasprintf ("%s,gpt2", data.dev->disk->name); + grub_env_unset ("test_result"); + grub_search_part_label ("BIOS BOOT", "test_result", 0, NULL, 0); + test_result = grub_env_get ("test_result"); + grub_test_assert (test_result && strcmp (test_result, expected_result) == 0, + "wrong device: %s (%s)", test_result, expected_result); + grub_free (expected_result); + + grub_env_unset ("test_result"); + grub_search_part_label ("bogus name", "test_result", 0, NULL, 0); + test_result = grub_env_get ("test_result"); + grub_test_assert (test_result == NULL, + "unexpected device: %s", test_result); + grub_test_assert (grub_errno == GRUB_ERR_FILE_NOT_FOUND, + "unexpected error: %s", grub_errmsg); + grub_errno = GRUB_ERR_NONE; + + close_disk (&data); +} + +static void +search_uuid_test (void) +{ + struct test_data data; + const char gpt1_uuid[] = "A0F1792E-B4CE-4136-BCF2-1AFC133C2828"; + const char gpt2_uuid[] = "876c898d-1b40-4727-a161-edf9b5486674"; + const char bogus_uuid[] = "1534c928-c50e-4866-9daf-6a9fd7918a76"; + const char *test_result; + char *expected_result; + + open_disk (&data); + + expected_result = grub_xasprintf ("%s,gpt1", data.dev->disk->name); + grub_env_unset ("test_result"); + grub_search_part_uuid (gpt1_uuid, "test_result", 0, NULL, 0); + test_result = grub_env_get ("test_result"); + grub_test_assert (test_result && strcmp (test_result, expected_result) == 0, + "wrong device: %s (%s)", test_result, expected_result); + grub_free (expected_result); + + expected_result = grub_xasprintf ("%s,gpt2", data.dev->disk->name); + grub_env_unset ("test_result"); + grub_search_part_uuid (gpt2_uuid, "test_result", 0, NULL, 0); + test_result = grub_env_get ("test_result"); + grub_test_assert (test_result && strcmp (test_result, expected_result) == 0, + "wrong device: %s (%s)", test_result, expected_result); + grub_free (expected_result); + + grub_env_unset ("test_result"); + grub_search_part_uuid (bogus_uuid, "test_result", 0, NULL, 0); + test_result = grub_env_get ("test_result"); + grub_test_assert (test_result == NULL, + "unexpected device: %s", test_result); + grub_test_assert (grub_errno == GRUB_ERR_FILE_NOT_FOUND, + "unexpected error: %s", grub_errmsg); + grub_errno = GRUB_ERR_NONE; + + close_disk (&data); +} + void grub_unit_test_init (void) { @@ -546,6 +626,8 @@ grub_unit_test_init (void) grub_test_register ("gpt_read_invalid_test", read_invalid_entries_test); grub_test_register ("gpt_read_fallback_test", read_fallback_test); grub_test_register ("gpt_repair_test", repair_test); + grub_test_register ("gpt_search_label_test", search_label_test); + grub_test_register ("gpt_search_uuid_test", search_uuid_test); } void @@ -557,5 +639,7 @@ grub_unit_test_fini (void) grub_test_unregister ("gpt_read_invalid_test"); grub_test_unregister ("gpt_read_fallback_test"); grub_test_unregister ("gpt_repair_test"); + grub_test_unregister ("gpt_search_label_test"); + grub_test_unregister ("gpt_search_uuid_test"); grub_fini_all (); } From f4d00290ed75fc9ad4de46ea313023d59ee1d39d Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 16 Apr 2015 16:30:53 -0700 Subject: [PATCH 17/85] Fail validation if we can't find shim and Secure Boot is enabled If grub is signed with a key that's in the trusted EFI keyring, an attacker can point a boot entry at grub rather than at shim and grub will fail to locate the shim verification protocol. This would then allow booting an arbitrary kernel image. Fail validation if Secure Boot is enabled and we can't find the shim protocol in order to prevent this. --- grub-core/loader/i386/efi/linux.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index b79e6320b..57ccd4ec4 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -57,8 +57,12 @@ grub_linuxefi_secure_validate (void *data, grub_uint32_t size) shim_lock = grub_efi_locate_protocol(&guid, NULL); - if (!shim_lock) - return 1; + if (!shim_lock) { + if (grub_efi_secure_boot()) + return 0; + else + return 1; + } if (shim_lock->verify(data, size) == GRUB_EFI_SUCCESS) return 1; From 250af4351d1579b4048e9105f05e7c7a4528a753 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Fri, 17 Apr 2015 13:34:01 -0700 Subject: [PATCH 18/85] Add efi getenv command Add a command to obtain the contents of EFI firmware variables. --- grub-core/Makefile.core.def | 7 ++ grub-core/commands/efi/getenv.c | 153 ++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 grub-core/commands/efi/getenv.c diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index ab3190c3e..ba05e9a1a 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -816,6 +816,13 @@ module = { enable = x86_64_efi; }; +module = { + name = getenv; + common = commands/efi/getenv.c; + enable = i386_efi; + enable = x86_64_efi; +}; + module = { name = gptsync; common = commands/gptsync.c; diff --git a/grub-core/commands/efi/getenv.c b/grub-core/commands/efi/getenv.c new file mode 100644 index 000000000..5a829f5e4 --- /dev/null +++ b/grub-core/commands/efi/getenv.c @@ -0,0 +1,153 @@ +/* getenv.c - retrieve EFI variables. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 Free Software Foundation, Inc. + * Copyright (C) 2014 CoreOS, 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static const struct grub_arg_option options_getenv[] = { + {"var-name", 'e', 0, + N_("Environment variable to query"), + N_("VARNAME"), ARG_TYPE_STRING}, + {"var-guid", 'g', 0, + N_("GUID of environment variable to query"), + N_("GUID"), ARG_TYPE_STRING}, + {"binary", 'b', 0, + N_("Read binary data and represent it as hex"), + 0, ARG_TYPE_NONE}, + {0, 0, 0, 0, 0, 0} +}; + +enum options_getenv +{ + GETENV_VAR_NAME, + GETENV_VAR_GUID, + GETENV_BINARY, +}; + +static grub_err_t +grub_cmd_getenv (grub_extcmd_context_t ctxt, int argc, char **args) +{ + struct grub_arg_list *state = ctxt->state; + char *envvar = NULL, *guid = NULL, *bindata = NULL, *data = NULL; + grub_size_t datasize; + grub_efi_guid_t efi_var_guid; + grub_efi_boolean_t binary = state[GETENV_BINARY].set; + unsigned int i; + + if (!state[GETENV_VAR_NAME].set || !state[GETENV_VAR_GUID].set) + { + grub_error (GRUB_ERR_INVALID_COMMAND, N_("-e and -g are required")); + goto done; + } + + if (argc != 1) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unexpected arguments")); + goto done; + } + + envvar = state[GETENV_VAR_NAME].arg; + guid = state[GETENV_VAR_GUID].arg; + + if (grub_strlen(guid) != 36 || + guid[8] != '-' || + guid[13] != '-' || + guid[18] != '-' || + guid[23] != '-') + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid GUID")); + goto done; + } + + /* Forgive me father for I have sinned */ + guid[8] = 0; + efi_var_guid.data1 = grub_strtoul(guid, NULL, 16); + guid[13] = 0; + efi_var_guid.data2 = grub_strtoul(guid + 9, NULL, 16); + guid[18] = 0; + efi_var_guid.data3 = grub_strtoul(guid + 14, NULL, 16); + efi_var_guid.data4[7] = grub_strtoul(guid + 34, NULL, 16); + guid[34] = 0; + efi_var_guid.data4[6] = grub_strtoul(guid + 32, NULL, 16); + guid[32] = 0; + efi_var_guid.data4[5] = grub_strtoul(guid + 30, NULL, 16); + guid[30] = 0; + efi_var_guid.data4[4] = grub_strtoul(guid + 28, NULL, 16); + guid[28] = 0; + efi_var_guid.data4[3] = grub_strtoul(guid + 26, NULL, 16); + guid[26] = 0; + efi_var_guid.data4[2] = grub_strtoul(guid + 24, NULL, 16); + guid[23] = 0; + efi_var_guid.data4[1] = grub_strtoul(guid + 21, NULL, 16); + guid[21] = 0; + efi_var_guid.data4[0] = grub_strtoul(guid + 19, NULL, 16); + + data = grub_efi_get_variable(envvar, &efi_var_guid, &datasize); + + if (!data || !datasize) + { + grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("No such variable")); + goto done; + } + + if (binary) + { + bindata = grub_zalloc(datasize * 2 + 1); + for (i=0; i Date: Thu, 28 May 2015 11:15:30 -0700 Subject: [PATCH 19/85] Add verity hash passthrough Read the verity hash from the kernel binary and pass it to the running system via the kernel command line --- grub-core/loader/i386/efi/linux.c | 3 +++ grub-core/loader/i386/linux.c | 2 ++ grub-core/loader/i386/verity-hash.h | 25 +++++++++++++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 grub-core/loader/i386/verity-hash.h diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index 57ccd4ec4..48716cedf 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -27,6 +27,8 @@ #include #include +#include "../verity-hash.h" + GRUB_MOD_LICENSE ("GPLv3+"); static grub_dl_t my_mod; @@ -288,6 +290,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), linux_cmdline + sizeof (LINUX_IMAGE) - 1, lh.cmdline_size - (sizeof (LINUX_IMAGE) - 1)); + grub_pass_verity_hash(&lh, linux_cmdline); lh.cmd_line_ptr = (grub_uint32_t)(grub_uint64_t)linux_cmdline; handover_offset = lh.handover_offset; diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index 083f9417c..37287edad 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -36,6 +36,7 @@ #include #include +#include "verity-hash.h" GRUB_MOD_LICENSE ("GPLv3+"); #ifdef GRUB_MACHINE_PCBIOS @@ -1018,6 +1019,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), maximal_cmdline_size - (sizeof (LINUX_IMAGE) - 1)); + grub_pass_verity_hash(&lh, linux_cmdline); len = prot_file_size; if (grub_file_read (file, prot_mode_mem, len) != len && !grub_errno) grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), diff --git a/grub-core/loader/i386/verity-hash.h b/grub-core/loader/i386/verity-hash.h new file mode 100644 index 000000000..4027be6aa --- /dev/null +++ b/grub-core/loader/i386/verity-hash.h @@ -0,0 +1,25 @@ +#define VERITY_ARG " verity.usrhash=" +#define VERITY_HASH_OFFSET 0x40 +#define VERITY_HASH_LENGTH 64 + +static inline void grub_pass_verity_hash(struct linux_kernel_header *lh, + char *cmdline) +{ + char *buf = (char *)lh; + grub_size_t cmdline_len; + int i; + + for (i=VERITY_HASH_OFFSET; i '9') // Not a number + if (buf[i] < 'a' || buf[i] > 'f') // Not a hex letter + return; + } + + grub_memcpy (cmdline + grub_strlen(cmdline), VERITY_ARG, + sizeof (VERITY_ARG)); + cmdline_len = grub_strlen(cmdline); + grub_memcpy (cmdline + cmdline_len, buf + VERITY_HASH_OFFSET, + VERITY_HASH_LENGTH); + cmdline[cmdline_len + VERITY_HASH_LENGTH] = '\0'; +} From 57b4382f0cf10ffece1f0924e36ff00609c332cb Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 14 Jul 2015 16:58:51 -0700 Subject: [PATCH 20/85] Fix race in EFI validation The Secure Boot code currently reads the kernel from disk, validates the signature and then reads it from disk again. A sufficiently exciting storage device could modify the kernel between these two events and trigger the execution of an untrusted kernel. Avoid re-reading it in order to ensure this isn't a problem, and in the process speed up boot by not reading the kernel twice. --- grub-core/loader/i386/efi/linux.c | 43 +++++++------------------------ 1 file changed, 10 insertions(+), 33 deletions(-) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index 48716cedf..d3fbeeae8 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -193,7 +193,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_file_t file = 0; struct linux_kernel_header lh; grub_ssize_t len, start, filelen; - void *kernel; + void *kernel = NULL; grub_dl_ref (my_mod); @@ -230,10 +230,6 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } - grub_file_seek (file, 0); - - grub_free(kernel); - params = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(16384)); if (! params) @@ -242,15 +238,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } - memset (params, 0, 16384); + 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; - } + grub_memcpy (&lh, kernel, sizeof (lh)); if (lh.boot_flag != grub_cpu_to_le16 (0xaa55)) { @@ -311,27 +301,12 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } - 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; - } + grub_memcpy (kernel_mem, (char *)kernel + start, len); + grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0); + loaded=1; - 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_uint64_t) kernel_mem; - } - - memcpy(params, &lh, 2 * 512); + lh.code32_start = (grub_uint32_t)(grub_uint64_t) kernel_mem; + grub_memcpy (params, &lh, 2 * 512); params->type_of_loader = 0x21; @@ -340,6 +315,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), if (file) grub_file_close (file); + grub_free (kernel); + if (grub_errno != GRUB_ERR_NONE) { grub_dl_unref (my_mod); From 2db9e6064073149b91f582fc9d72153259052fe4 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 14 Jul 2015 17:06:35 -0700 Subject: [PATCH 21/85] Core TPM support Add support for performing basic TPM measurements. Right now this only supports extending PCRs statically and only on UEFI and BIOS systems, but will measure all modules as they're loaded. --- grub-core/Makefile.am | 1 + grub-core/Makefile.core.def | 3 + grub-core/kern/dl.c | 3 + grub-core/kern/efi/tpm.c | 282 +++++++++++++++++++++++++++++++++++ grub-core/kern/i386/pc/tpm.c | 132 ++++++++++++++++ grub-core/kern/tpm.c | 13 ++ include/grub/efi/tpm.h | 153 +++++++++++++++++++ include/grub/tpm.h | 91 +++++++++++ 8 files changed, 678 insertions(+) create mode 100644 grub-core/kern/efi/tpm.c create mode 100644 grub-core/kern/i386/pc/tpm.c create mode 100644 grub-core/kern/tpm.c create mode 100644 include/grub/efi/tpm.h create mode 100644 include/grub/tpm.h diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index 04e9395fd..23ce9e9fb 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -92,6 +92,7 @@ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/term.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/time.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/mm_private.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/net.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/tpm.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/memory.h if COND_i386_pc diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index ba05e9a1a..2da182f9b 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -126,6 +126,7 @@ kernel = { common = kern/rescue_parser.c; common = kern/rescue_reader.c; common = kern/term.c; + common = kern/tpm.c; noemu = kern/compiler-rt.c; noemu = kern/mm.c; @@ -173,6 +174,7 @@ kernel = { efi = term/efi/console.c; efi = kern/acpi.c; efi = kern/efi/acpi.c; + efi = kern/efi/tpm.c; i386_coreboot = kern/i386/pc/acpi.c; i386_multiboot = kern/i386/pc/acpi.c; i386_coreboot = kern/acpi.c; @@ -219,6 +221,7 @@ kernel = { i386_pc = kern/i386/pc/init.c; i386_pc = kern/i386/pc/mmap.c; + i386_pc = kern/i386/pc/tpm.c; i386_pc = term/i386/pc/console.c; i386_qemu = bus/pci.c; diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index 37fc6cf51..de0fcb4af 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -32,6 +32,7 @@ #include #include #include +#include /* Platforms where modules are in a readonly area of memory. */ #if defined(GRUB_MACHINE_QEMU) @@ -729,6 +730,8 @@ grub_dl_load_file (const char *filename) opens of the same device. */ grub_file_close (file); + grub_tpm_measure(core, size, GRUB_TPM_PCR, filename); + mod = grub_dl_load_core (core, size); grub_free (core); if (! mod) diff --git a/grub-core/kern/efi/tpm.c b/grub-core/kern/efi/tpm.c new file mode 100644 index 000000000..c9fb3c133 --- /dev/null +++ b/grub-core/kern/efi/tpm.c @@ -0,0 +1,282 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +static grub_efi_guid_t tpm_guid = EFI_TPM_GUID; +static grub_efi_guid_t tpm2_guid = EFI_TPM2_GUID; + +static grub_efi_boolean_t grub_tpm_present(grub_efi_tpm_protocol_t *tpm) +{ + grub_efi_status_t status; + TCG_EFI_BOOT_SERVICE_CAPABILITY caps; + grub_uint32_t flags; + grub_efi_physical_address_t eventlog, lastevent; + + caps.Size = (grub_uint8_t)sizeof(caps); + + status = efi_call_5(tpm->status_check, tpm, &caps, &flags, &eventlog, + &lastevent); + + if (status != GRUB_EFI_SUCCESS || caps.TPMDeactivatedFlag + || !caps.TPMPresentFlag) + return 0; + + return 1; +} + +static grub_efi_boolean_t grub_tpm2_present(grub_efi_tpm2_protocol_t *tpm) +{ + grub_efi_status_t status; + EFI_TCG2_BOOT_SERVICE_CAPABILITY caps; + + caps.Size = (grub_uint8_t)sizeof(caps); + + status = efi_call_2(tpm->get_capability, tpm, &caps); + + if (status != GRUB_EFI_SUCCESS || !caps.TPMPresentFlag) + return 0; + + return 1; +} + +static grub_efi_boolean_t grub_tpm_handle_find(grub_efi_handle_t *tpm_handle, + grub_efi_uint8_t *protocol_version) +{ + grub_efi_handle_t *handles; + grub_efi_uintn_t num_handles; + + handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &tpm_guid, NULL, + &num_handles); + if (handles && num_handles > 0) { + *tpm_handle = handles[0]; + *protocol_version = 1; + return 1; + } + + handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &tpm2_guid, NULL, + &num_handles); + if (handles && num_handles > 0) { + *tpm_handle = handles[0]; + *protocol_version = 2; + return 1; + } + + return 0; +} + +static grub_err_t +grub_tpm1_execute(grub_efi_handle_t tpm_handle, + PassThroughToTPM_InputParamBlock *inbuf, + PassThroughToTPM_OutputParamBlock *outbuf) +{ + grub_efi_status_t status; + grub_efi_tpm_protocol_t *tpm; + grub_uint32_t inhdrsize = sizeof(*inbuf) - sizeof(inbuf->TPMOperandIn); + grub_uint32_t outhdrsize = sizeof(*outbuf) - sizeof(outbuf->TPMOperandOut); + + tpm = grub_efi_open_protocol (tpm_handle, &tpm_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + if (!grub_tpm_present(tpm)) + return 0; + + /* UEFI TPM protocol takes the raw operand block, no param block header */ + status = efi_call_5 (tpm->pass_through_to_tpm, tpm, + inbuf->IPBLength - inhdrsize, inbuf->TPMOperandIn, + outbuf->OPBLength - outhdrsize, outbuf->TPMOperandOut); + + switch (status) { + case GRUB_EFI_SUCCESS: + return 0; + case GRUB_EFI_DEVICE_ERROR: + return grub_error (GRUB_ERR_IO, N_("Command failed")); + case GRUB_EFI_INVALID_PARAMETER: + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Invalid parameter")); + case GRUB_EFI_BUFFER_TOO_SMALL: + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Output buffer too small")); + case GRUB_EFI_NOT_FOUND: + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("TPM unavailable")); + default: + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("Unknown TPM error")); + } +} + +static grub_err_t +grub_tpm2_execute(grub_efi_handle_t tpm_handle, + PassThroughToTPM_InputParamBlock *inbuf, + PassThroughToTPM_OutputParamBlock *outbuf) +{ + grub_efi_status_t status; + grub_efi_tpm2_protocol_t *tpm; + grub_uint32_t inhdrsize = sizeof(*inbuf) - sizeof(inbuf->TPMOperandIn); + grub_uint32_t outhdrsize = sizeof(*outbuf) - sizeof(outbuf->TPMOperandOut); + + tpm = grub_efi_open_protocol (tpm_handle, &tpm2_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + if (!grub_tpm2_present(tpm)) + return 0; + + /* UEFI TPM protocol takes the raw operand block, no param block header */ + status = efi_call_5 (tpm->submit_command, tpm, + inbuf->IPBLength - inhdrsize, inbuf->TPMOperandIn, + outbuf->OPBLength - outhdrsize, outbuf->TPMOperandOut); + + switch (status) { + case GRUB_EFI_SUCCESS: + return 0; + case GRUB_EFI_DEVICE_ERROR: + return grub_error (GRUB_ERR_IO, N_("Command failed")); + case GRUB_EFI_INVALID_PARAMETER: + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Invalid parameter")); + case GRUB_EFI_BUFFER_TOO_SMALL: + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Output buffer too small")); + case GRUB_EFI_NOT_FOUND: + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("TPM unavailable")); + default: + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("Unknown TPM error")); + } +} + +grub_err_t +grub_tpm_execute(PassThroughToTPM_InputParamBlock *inbuf, + PassThroughToTPM_OutputParamBlock *outbuf) +{ + grub_efi_handle_t tpm_handle; + grub_uint8_t protocol_version; + + /* It's not a hard failure for there to be no TPM */ + if (!grub_tpm_handle_find(&tpm_handle, &protocol_version)) + return 0; + + if (protocol_version == 1) { + return grub_tpm1_execute(tpm_handle, inbuf, outbuf); + } else { + return grub_tpm2_execute(tpm_handle, inbuf, outbuf); + } +} + +typedef struct { + grub_uint32_t pcrindex; + grub_uint32_t eventtype; + grub_uint8_t digest[20]; + grub_uint32_t eventsize; + grub_uint8_t event[1]; +} Event; + + +static grub_err_t +grub_tpm1_log_event(grub_efi_handle_t tpm_handle, unsigned char *buf, + grub_size_t size, grub_uint8_t pcr, + const char *description) +{ + Event *event; + grub_efi_status_t status; + grub_efi_tpm_protocol_t *tpm; + grub_efi_physical_address_t lastevent; + grub_uint32_t algorithm; + grub_uint32_t eventnum = 0; + + tpm = grub_efi_open_protocol (tpm_handle, &tpm_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + if (!grub_tpm_present(tpm)) + return 0; + + event = grub_zalloc(sizeof (Event) + grub_strlen(description) + 1); + if (!event) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("cannot allocate TPM event buffer")); + + event->pcrindex = pcr; + event->eventtype = EV_IPL; + event->eventsize = grub_strlen(description) + 1; + grub_memcpy(event->event, description, event->eventsize); + + algorithm = TCG_ALG_SHA; + status = efi_call_7 (tpm->log_extend_event, tpm, buf, (grub_uint64_t) size, + algorithm, event, &eventnum, &lastevent); + + switch (status) { + case GRUB_EFI_SUCCESS: + return 0; + case GRUB_EFI_DEVICE_ERROR: + return grub_error (GRUB_ERR_IO, N_("Command failed")); + case GRUB_EFI_INVALID_PARAMETER: + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Invalid parameter")); + case GRUB_EFI_BUFFER_TOO_SMALL: + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Output buffer too small")); + case GRUB_EFI_NOT_FOUND: + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("TPM unavailable")); + default: + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("Unknown TPM error")); + } +} + +static grub_err_t +grub_tpm2_log_event(grub_efi_handle_t tpm_handle, unsigned char *buf, + grub_size_t size, grub_uint8_t pcr, + const char *description) +{ + EFI_TCG2_EVENT *event; + grub_efi_status_t status; + grub_efi_tpm2_protocol_t *tpm; + + tpm = grub_efi_open_protocol (tpm_handle, &tpm2_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + if (!grub_tpm2_present(tpm)) + return 0; + + event = grub_zalloc(sizeof (EFI_TCG2_EVENT) + grub_strlen(description) + 1); + if (!event) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("cannot allocate TPM event buffer")); + + event->Header.HeaderSize = sizeof(EFI_TCG2_EVENT_HEADER); + event->Header.HeaderVersion = 1; + event->Header.PCRIndex = pcr; + event->Header.EventType = EV_IPL; + event->Size = sizeof(*event) - sizeof(event->Event) + grub_strlen(description) + 1; + grub_memcpy(event->Event, description, grub_strlen(description) + 1); + + status = efi_call_5 (tpm->hash_log_extend_event, tpm, 0, buf, + (grub_uint64_t) size, event); + + switch (status) { + case GRUB_EFI_SUCCESS: + return 0; + case GRUB_EFI_DEVICE_ERROR: + return grub_error (GRUB_ERR_IO, N_("Command failed")); + case GRUB_EFI_INVALID_PARAMETER: + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Invalid parameter")); + case GRUB_EFI_BUFFER_TOO_SMALL: + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Output buffer too small")); + case GRUB_EFI_NOT_FOUND: + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("TPM unavailable")); + default: + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("Unknown TPM error")); + } +} + +grub_err_t +grub_tpm_log_event(unsigned char *buf, grub_size_t size, grub_uint8_t pcr, + const char *description) +{ + grub_efi_handle_t tpm_handle; + grub_efi_uint8_t protocol_version; + + if (!grub_tpm_handle_find(&tpm_handle, &protocol_version)) + return 0; + + if (protocol_version == 1) { + return grub_tpm1_log_event(tpm_handle, buf, size, pcr, description); + } else { + return grub_tpm2_log_event(tpm_handle, buf, size, pcr, description); + } +} diff --git a/grub-core/kern/i386/pc/tpm.c b/grub-core/kern/i386/pc/tpm.c new file mode 100644 index 000000000..8c6c1e6ec --- /dev/null +++ b/grub-core/kern/i386/pc/tpm.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include +#include +#include + +#define TCPA_MAGIC 0x41504354 + +int tpm_present(void); + +int tpm_present(void) +{ + struct grub_bios_int_registers regs; + + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + regs.eax = 0xbb00; + regs.ebx = TCPA_MAGIC; + grub_bios_interrupt (0x1a, ®s); + + if (regs.eax == 0) + return 1; + + return 0; +} + +grub_err_t +grub_tpm_execute(PassThroughToTPM_InputParamBlock *inbuf, + PassThroughToTPM_OutputParamBlock *outbuf) +{ + struct grub_bios_int_registers regs; + grub_addr_t inaddr, outaddr; + + if (!tpm_present()) + return 0; + + inaddr = (grub_addr_t) inbuf; + outaddr = (grub_addr_t) outbuf; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + regs.eax = 0xbb02; + regs.ebx = TCPA_MAGIC; + regs.ecx = 0; + regs.edx = 0; + regs.es = (inaddr & 0xffff0000) >> 4; + regs.edi = inaddr & 0xffff; + regs.ds = outaddr >> 4; + regs.esi = outaddr & 0xf; + + grub_bios_interrupt (0x1a, ®s); + + if (regs.eax) + return grub_error (GRUB_ERR_IO, N_("TPM error %x\n"), regs.eax); + + return 0; +} + +typedef struct { + grub_uint32_t pcrindex; + grub_uint32_t eventtype; + grub_uint8_t digest[20]; + grub_uint32_t eventdatasize; + grub_uint8_t event[0]; +} GRUB_PACKED Event; + +typedef struct { + grub_uint16_t ipblength; + grub_uint16_t reserved; + grub_uint32_t hashdataptr; + grub_uint32_t hashdatalen; + grub_uint32_t pcr; + grub_uint32_t reserved2; + grub_uint32_t logdataptr; + grub_uint32_t logdatalen; +} GRUB_PACKED EventIncoming; + +typedef struct { + grub_uint16_t opblength; + grub_uint16_t reserved; + grub_uint32_t eventnum; + grub_uint8_t hashvalue[20]; +} GRUB_PACKED EventOutgoing; + +grub_err_t +grub_tpm_log_event(unsigned char *buf, grub_size_t size, grub_uint8_t pcr, + const char *description) +{ + struct grub_bios_int_registers regs; + EventIncoming incoming; + EventOutgoing outgoing; + Event *event; + grub_uint32_t datalength; + + if (!tpm_present()) + return 0; + + datalength = grub_strlen(description); + event = grub_zalloc(datalength + sizeof(Event)); + if (!event) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("cannot allocate TPM event buffer")); + + event->pcrindex = pcr; + event->eventtype = 0x0d; + event->eventdatasize = grub_strlen(description); + grub_memcpy(event->event, description, datalength); + + incoming.ipblength = sizeof(incoming); + incoming.hashdataptr = (grub_uint32_t)buf; + incoming.hashdatalen = size; + incoming.pcr = pcr; + incoming.logdataptr = (grub_uint32_t)event; + incoming.logdatalen = datalength + sizeof(Event); + + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + regs.eax = 0xbb01; + regs.ebx = TCPA_MAGIC; + regs.ecx = 0; + regs.edx = 0; + regs.es = (((grub_addr_t) &incoming) & 0xffff0000) >> 4; + regs.edi = ((grub_addr_t) &incoming) & 0xffff; + regs.ds = (((grub_addr_t) &outgoing) & 0xffff0000) >> 4; + regs.esi = ((grub_addr_t) &outgoing) & 0xffff; + + grub_bios_interrupt (0x1a, ®s); + + grub_free(event); + + if (regs.eax) + return grub_error (GRUB_ERR_IO, N_("TPM error %x\n"), regs.eax); + + return 0; +} diff --git a/grub-core/kern/tpm.c b/grub-core/kern/tpm.c new file mode 100644 index 000000000..1a991876c --- /dev/null +++ b/grub-core/kern/tpm.c @@ -0,0 +1,13 @@ +#include +#include +#include +#include +#include +#include + +grub_err_t +grub_tpm_measure (unsigned char *buf, grub_size_t size, grub_uint8_t pcr, + const char *description) +{ + return grub_tpm_log_event(buf, size, pcr, description); +} diff --git a/include/grub/efi/tpm.h b/include/grub/efi/tpm.h new file mode 100644 index 000000000..e2aff4a3c --- /dev/null +++ b/include/grub/efi/tpm.h @@ -0,0 +1,153 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2015 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 . + */ + +#ifndef GRUB_EFI_TPM_HEADER +#define GRUB_EFI_TPM_HEADER 1 + +#define EFI_TPM_GUID {0xf541796d, 0xa62e, 0x4954, {0xa7, 0x75, 0x95, 0x84, 0xf6, 0x1b, 0x9c, 0xdd }}; +#define EFI_TPM2_GUID {0x607f766c, 0x7455, 0x42be, {0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f }}; + +typedef struct { + grub_efi_uint8_t Major; + grub_efi_uint8_t Minor; + grub_efi_uint8_t RevMajor; + grub_efi_uint8_t RevMinor; +} TCG_VERSION; + +typedef struct _TCG_EFI_BOOT_SERVICE_CAPABILITY { + grub_efi_uint8_t Size; /// Size of this structure. + TCG_VERSION StructureVersion; + TCG_VERSION ProtocolSpecVersion; + grub_efi_uint8_t HashAlgorithmBitmap; /// Hash algorithms . + char TPMPresentFlag; /// 00h = TPM not present. + char TPMDeactivatedFlag; /// 01h = TPM currently deactivated. +} TCG_EFI_BOOT_SERVICE_CAPABILITY; + +typedef struct { + grub_efi_uint32_t PCRIndex; + grub_efi_uint32_t EventType; + grub_efi_uint8_t digest[20]; + grub_efi_uint32_t EventSize; + grub_efi_uint8_t Event[1]; +} TCG_PCR_EVENT; + +struct grub_efi_tpm_protocol +{ + grub_efi_status_t (*status_check) (struct grub_efi_tpm_protocol *this, + TCG_EFI_BOOT_SERVICE_CAPABILITY *ProtocolCapability, + grub_efi_uint32_t *TCGFeatureFlags, + grub_efi_physical_address_t *EventLogLocation, + grub_efi_physical_address_t *EventLogLastEntry); + grub_efi_status_t (*hash_all) (struct grub_efi_tpm_protocol *this, + grub_efi_uint8_t *HashData, + grub_efi_uint64_t HashLen, + grub_efi_uint32_t AlgorithmId, + grub_efi_uint64_t *HashedDataLen, + grub_efi_uint8_t **HashedDataResult); + grub_efi_status_t (*log_event) (struct grub_efi_tpm_protocol *this, + TCG_PCR_EVENT *TCGLogData, + grub_efi_uint32_t *EventNumber, + grub_efi_uint32_t Flags); + grub_efi_status_t (*pass_through_to_tpm) (struct grub_efi_tpm_protocol *this, + grub_efi_uint32_t TpmInputParameterBlockSize, + grub_efi_uint8_t *TpmInputParameterBlock, + grub_efi_uint32_t TpmOutputParameterBlockSize, + grub_efi_uint8_t *TpmOutputParameterBlock); + grub_efi_status_t (*log_extend_event) (struct grub_efi_tpm_protocol *this, + grub_efi_physical_address_t HashData, + grub_efi_uint64_t HashDataLen, + grub_efi_uint32_t AlgorithmId, + TCG_PCR_EVENT *TCGLogData, + grub_efi_uint32_t *EventNumber, + grub_efi_physical_address_t *EventLogLastEntry); +}; + +typedef struct grub_efi_tpm_protocol grub_efi_tpm_protocol_t; + +typedef grub_efi_uint32_t EFI_TCG2_EVENT_LOG_BITMAP; +typedef grub_efi_uint32_t EFI_TCG2_EVENT_LOG_FORMAT; +typedef grub_efi_uint32_t EFI_TCG2_EVENT_ALGORITHM_BITMAP; + +typedef struct tdEFI_TCG2_VERSION { + grub_efi_uint8_t Major; + grub_efi_uint8_t Minor; +} GRUB_PACKED EFI_TCG2_VERSION; + +typedef struct tdEFI_TCG2_BOOT_SERVICE_CAPABILITY { + grub_efi_uint8_t Size; + EFI_TCG2_VERSION StructureVersion; + EFI_TCG2_VERSION ProtocolVersion; + EFI_TCG2_EVENT_ALGORITHM_BITMAP HashAlgorithmBitmap; + EFI_TCG2_EVENT_LOG_BITMAP SupportedEventLogs; + grub_efi_boolean_t TPMPresentFlag; + grub_efi_uint16_t MaxCommandSize; + grub_efi_uint16_t MaxResponseSize; + grub_efi_uint32_t ManufacturerID; + grub_efi_uint32_t NumberOfPcrBanks; + EFI_TCG2_EVENT_ALGORITHM_BITMAP ActivePcrBanks; +} EFI_TCG2_BOOT_SERVICE_CAPABILITY; + +typedef grub_efi_uint32_t TCG_PCRINDEX; +typedef grub_efi_uint32_t TCG_EVENTTYPE; + +typedef struct tdEFI_TCG2_EVENT_HEADER { + grub_efi_uint32_t HeaderSize; + grub_efi_uint16_t HeaderVersion; + TCG_PCRINDEX PCRIndex; + TCG_EVENTTYPE EventType; +} GRUB_PACKED EFI_TCG2_EVENT_HEADER; + +typedef struct tdEFI_TCG2_EVENT { + grub_efi_uint32_t Size; + EFI_TCG2_EVENT_HEADER Header; + grub_efi_uint8_t Event[1]; +} GRUB_PACKED EFI_TCG2_EVENT; + +struct grub_efi_tpm2_protocol +{ + grub_efi_status_t (*get_capability) (struct grub_efi_tpm2_protocol *this, + EFI_TCG2_BOOT_SERVICE_CAPABILITY *ProtocolCapability); + grub_efi_status_t (*get_event_log) (struct grub_efi_tpm2_protocol *this, + EFI_TCG2_EVENT_LOG_FORMAT EventLogFormat, + grub_efi_physical_address_t *EventLogLocation, + grub_efi_physical_address_t *EventLogLastEntry, + grub_efi_boolean_t *EventLogTruncated); + grub_efi_status_t (*hash_log_extend_event) (struct grub_efi_tpm2_protocol *this, + grub_efi_uint64_t Flags, + grub_efi_physical_address_t *DataToHash, + grub_efi_uint64_t DataToHashLen, + EFI_TCG2_EVENT *EfiTcgEvent); + grub_efi_status_t (*submit_command) (struct grub_efi_tpm2_protocol *this, + grub_efi_uint32_t InputParameterBlockSize, + grub_efi_uint8_t *InputParameterBlock, + grub_efi_uint32_t OutputParameterBlockSize, + grub_efi_uint8_t *OutputParameterBlock); + grub_efi_status_t (*get_active_pcr_blanks) (struct grub_efi_tpm2_protocol *this, + grub_efi_uint32_t *ActivePcrBanks); + grub_efi_status_t (*set_active_pcr_banks) (struct grub_efi_tpm2_protocol *this, + grub_efi_uint32_t ActivePcrBanks); + grub_efi_status_t (*get_result_of_set_active_pcr_banks) (struct grub_efi_tpm2_protocol *this, + grub_efi_uint32_t *OperationPresent, + grub_efi_uint32_t *Response); +}; + +typedef struct grub_efi_tpm2_protocol grub_efi_tpm2_protocol_t; + +#define TCG_ALG_SHA 0x00000004 + +#endif diff --git a/include/grub/tpm.h b/include/grub/tpm.h new file mode 100644 index 000000000..40d3cf65b --- /dev/null +++ b/include/grub/tpm.h @@ -0,0 +1,91 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2015 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 . + */ + +#ifndef GRUB_TPM_HEADER +#define GRUB_TPM_HEADER 1 + +#define SHA1_DIGEST_SIZE 20 + +#define TPM_BASE 0x0 +#define TPM_SUCCESS TPM_BASE +#define TPM_AUTHFAIL (TPM_BASE + 0x1) +#define TPM_BADINDEX (TPM_BASE + 0x2) + +#define GRUB_TPM_PCR 9 +#define GRUB_KERNEL_PCR 10 +#define GRUB_INITRD_PCR 11 +#define GRUB_CMDLINE_PCR 12 + +#define TPM_TAG_RQU_COMMAND 0x00C1 +#define TPM_ORD_Extend 0x14 + +#define EV_IPL 0x0d + +/* TCG_PassThroughToTPM Input Parameter Block */ +typedef struct { + grub_uint16_t IPBLength; + grub_uint16_t Reserved1; + grub_uint16_t OPBLength; + grub_uint16_t Reserved2; + grub_uint8_t TPMOperandIn[1]; +} GRUB_PACKED PassThroughToTPM_InputParamBlock; + +/* TCG_PassThroughToTPM Output Parameter Block */ +typedef struct { + grub_uint16_t OPBLength; + grub_uint16_t Reserved; + grub_uint8_t TPMOperandOut[1]; +} GRUB_PACKED PassThroughToTPM_OutputParamBlock; + +typedef struct { + grub_uint16_t tag; + grub_uint32_t paramSize; + grub_uint32_t ordinal; + grub_uint32_t pcrNum; + grub_uint8_t inDigest[SHA1_DIGEST_SIZE]; /* The 160 bit value representing the event to be recorded. */ +} GRUB_PACKED ExtendIncoming; + +/* TPM_Extend Outgoing Operand */ +typedef struct { + grub_uint16_t tag; + grub_uint32_t paramSize; + grub_uint32_t returnCode; + grub_uint8_t outDigest[SHA1_DIGEST_SIZE]; /* The PCR value after execution of the command. */ +} GRUB_PACKED ExtendOutgoing; + +grub_err_t EXPORT_FUNC(grub_tpm_measure) (unsigned char *buf, grub_size_t size, + grub_uint8_t pcr, + const char *description); +#if defined (GRUB_MACHINE_EFI) || defined (GRUB_MACHINE_PCBIOS) +grub_err_t grub_tpm_execute(PassThroughToTPM_InputParamBlock *inbuf, + PassThroughToTPM_OutputParamBlock *outbuf); +grub_err_t grub_tpm_log_event(unsigned char *buf, grub_size_t size, + grub_uint8_t pcr, const char *description); +#else +static inline grub_err_t grub_tpm_execute(PassThroughToTPM_InputParamBlock *inbuf, + PassThroughToTPM_OutputParamBlock *outbuf) { return 0; }; +static inline grub_err_t grub_tpm_log_event(unsigned char *buf, + grub_size_t size, + grub_uint8_t pcr, + const char *description) +{ + return 0; +}; +#endif + +#endif From a5693087f0d1a99bfbd6de8a4e7e97a863f7f153 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 16 Jul 2015 15:22:34 -0700 Subject: [PATCH 22/85] Measure kernel + initrd Measure the kernel and initrd when loaded on UEFI systems --- grub-core/loader/i386/efi/linux.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index d3fbeeae8..32c20bc26 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "../verity-hash.h" @@ -168,6 +169,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), argv[i]); goto fail; } + grub_tpm_measure (ptr, cursize, GRUB_INITRD_PCR, "UEFI Linux initrd"); ptr += cursize; grub_memset (ptr, 0, ALIGN_UP_OVERHEAD (cursize, 4)); ptr += ALIGN_UP_OVERHEAD (cursize, 4); @@ -223,6 +225,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } + grub_tpm_measure (kernel, filelen, GRUB_KERNEL_PCR, "UEFI Linux kernel"); + if (! grub_linuxefi_secure_validate (kernel, filelen)) { grub_error (GRUB_ERR_INVALID_COMMAND, N_("%s has invalid signature"), argv[0]); From d1270a2ba31cc3dd747d410a907f272ff03a6d68 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Fri, 31 Jul 2015 15:03:11 -0700 Subject: [PATCH 23/85] gpt: clean up little-endian crc32 computation - Remove problematic cast from *uint8_t to *uint32_t (alignment issue). - Remove dynamic allocation and associated error handling paths. - Match parameter ordering to existing grub_crypto_hash function. --- grub-core/lib/gpt.c | 51 ++++++++++++--------------------------------- 1 file changed, 13 insertions(+), 38 deletions(-) diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index 10a4b852d..aedc4f7a1 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -122,45 +122,29 @@ grub_gpt_size_to_sectors (grub_gpt_t gpt, grub_size_t size) return sectors; } -static grub_err_t -grub_gpt_lecrc32 (void *data, grub_size_t len, grub_uint32_t *crc) +static void +grub_gpt_lecrc32 (grub_uint32_t *crc, const void *data, grub_size_t len) { - grub_uint8_t *crc32_context; + grub_uint32_t crc32_val; - crc32_context = grub_zalloc (GRUB_MD_CRC32->contextsize); - if (!crc32_context) - return grub_errno; - - GRUB_MD_CRC32->init (crc32_context); - GRUB_MD_CRC32->write (crc32_context, data, len); - GRUB_MD_CRC32->final (crc32_context); + grub_crypto_hash (GRUB_MD_CRC32, &crc32_val, data, len); /* GRUB_MD_CRC32 always uses big endian, gpt is always little. */ - *crc = grub_swap_bytes32 (*(grub_uint32_t *) - GRUB_MD_CRC32->read (crc32_context)); - - grub_free (crc32_context); - - return GRUB_ERR_NONE; + *crc = grub_swap_bytes32 (crc32_val); } -static grub_err_t -grub_gpt_header_lecrc32 (struct grub_gpt_header *header, grub_uint32_t *crc) +static void +grub_gpt_header_lecrc32 (grub_uint32_t *crc, struct grub_gpt_header *header) { grub_uint32_t old, new; - grub_err_t err; /* crc32 must be computed with the field cleared. */ old = header->crc32; header->crc32 = 0; - err = grub_gpt_lecrc32 (header, sizeof (*header), &new); + grub_gpt_lecrc32 (&new, header, sizeof (*header)); header->crc32 = old; - if (err) - return err; - *crc = new; - return GRUB_ERR_NONE; } /* Make sure the MBR is a protective MBR and not a normal MBR. */ @@ -192,9 +176,7 @@ grub_gpt_header_check (struct grub_gpt_header *gpt, if (gpt->version != GRUB_GPT_HEADER_VERSION) return grub_error (GRUB_ERR_BAD_PART_TABLE, "unknown GPT version"); - if (grub_gpt_header_lecrc32 (gpt, &crc)) - return grub_errno; - + grub_gpt_header_lecrc32 (&crc, gpt); if (gpt->crc32 != crc) return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid GPT header crc32"); @@ -289,9 +271,7 @@ grub_gpt_read_entries (grub_disk_t disk, grub_gpt_t gpt, if (grub_disk_read (disk, addr, 0, entries_size, entries)) goto fail; - if (grub_gpt_lecrc32 (entries, entries_size, &crc)) - goto fail; - + grub_gpt_lecrc32 (&crc, entries, entries_size); if (crc != header->partentry_crc32) { grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid GPT entry crc32"); @@ -433,17 +413,12 @@ grub_gpt_update_checksums (grub_gpt_t gpt) gpt->backup.headersize = grub_cpu_to_le32_compile_time (sizeof (gpt->backup)); - if (grub_gpt_lecrc32 (gpt->entries, gpt->entries_size, &crc)) - return grub_errno; - + grub_gpt_lecrc32 (&crc, gpt->entries, gpt->entries_size); gpt->primary.partentry_crc32 = crc; gpt->backup.partentry_crc32 = crc; - if (grub_gpt_header_lecrc32 (&gpt->primary, &gpt->primary.crc32)) - return grub_errno; - - if (grub_gpt_header_lecrc32 (&gpt->backup, &gpt->backup.crc32)) - return grub_errno; + grub_gpt_header_lecrc32 (&gpt->primary.crc32, &gpt->primary); + grub_gpt_header_lecrc32 (&gpt->backup.crc32, &gpt->backup); return GRUB_ERR_NONE; } From 6b79d94608bd7d5cf94776319610db4f9daa06d6 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Sun, 9 Aug 2015 15:48:51 -0700 Subject: [PATCH 24/85] Add BIOS boot measurement Measure the on-disk grub core on BIOS systems - unlike UEFI, the firmware can't do this stage for us. --- grub-core/boot/i386/pc/boot.S | 30 ++++++++++++++++++++- grub-core/boot/i386/pc/diskboot.S | 44 +++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/grub-core/boot/i386/pc/boot.S b/grub-core/boot/i386/pc/boot.S index 2bd0b2d28..4c63247e8 100644 --- a/grub-core/boot/i386/pc/boot.S +++ b/grub-core/boot/i386/pc/boot.S @@ -24,11 +24,14 @@ * defines for the code go here */ +#define TPM 1 + /* Print message string */ #define MSG(x) movw $x, %si; call LOCAL(message) #define ERR(x) movw $x, %si; jmp LOCAL(error_message) .macro floppy +#ifndef TPM part_start: LOCAL(probe_values): @@ -85,6 +88,7 @@ fd_probe_error_string: .asciz "Floppy" movb MACRO_DOLLAR(79), %ch jmp LOCAL(final_init) +#endif .endm .macro scratch @@ -255,6 +259,7 @@ real_start: /* set %si to the disk address packet */ movw $disk_address_packet, %si +#ifndef TPM /* check if LBA is supported */ movb $0x41, %ah movw $0x55aa, %bx @@ -274,6 +279,7 @@ real_start: andw $1, %cx jz LOCAL(chs_mode) +#endif LOCAL(lba_mode): xorw %ax, %ax @@ -317,6 +323,9 @@ LOCAL(lba_mode): jmp LOCAL(copy_buffer) LOCAL(chs_mode): +#ifdef TPM + jmp LOCAL(general_error) +#else /* * Determine the hard disk geometry from the BIOS! * We do this first, so that LS-120 IDE floppies work correctly. @@ -428,7 +437,7 @@ setup_sectors: jc LOCAL(read_error) movw %es, %bx - +#endif /* TPM */ LOCAL(copy_buffer): /* * We need to save %cx and %si because the startup code in @@ -451,6 +460,25 @@ LOCAL(copy_buffer): popw %ds popa +#ifdef TPM + pusha + + movw $0xBB00, %ax /* TCG_StatusCheck */ + int $0x1A + test %eax, %eax + jnz boot /* No TPM or TPM deactivated */ + + movw $0xBB07, %ax /* TCG_CompactHashLogExtendEvent */ + movw $GRUB_BOOT_MACHINE_KERNEL_ADDR, %di + xorl %esi, %esi + movl $0x41504354, %ebx /* TCPA */ + movl $0x200, %ecx /* Measure 512 bytes */ + movl $0x8, %edx /* PCR 8 */ + int $0x1A + + popa +#endif +boot: /* boot kernel */ jmp *(LOCAL(kernel_address)) diff --git a/grub-core/boot/i386/pc/diskboot.S b/grub-core/boot/i386/pc/diskboot.S index 1ee4cf5b2..3a324ea74 100644 --- a/grub-core/boot/i386/pc/diskboot.S +++ b/grub-core/boot/i386/pc/diskboot.S @@ -19,6 +19,8 @@ #include #include +#define TPM 1 + /* * defines for the code go here */ @@ -58,6 +60,21 @@ _start: /* this sets up for the first run through "bootloop" */ movw $LOCAL(firstlist), %di +#ifdef TPM + /* clear EAX to remove potential garbage */ + xorl %eax, %eax + /* 8(%di) = number of sectors to read */ + movw 8(%di), %ax + + /* Multiply number of sectors to read with 512 bytes. EAX is 32bit + * which is large enough to hold values of up to 4GB. I doubt there + * will ever be a core.img larger than that. ;-) */ + shll $9, %eax + + /* write result to bytes_to_measure var */ + movl %eax, bytes_to_measure +#endif + /* save the sector number of the second sector in %ebp */ movl (%di), %ebp @@ -295,6 +312,29 @@ LOCAL(copy_buffer): /* END OF MAIN LOOP */ LOCAL(bootit): +#ifdef TPM + pusha + movw $0xBB07, %ax /* TCG_CompactHashLogExtendEvent */ + + movw $0x0, %bx + movw %bx, %es + + /* We've already measured the first 512 bytes, now measure the rest */ + xorl %edi, %edi + movw $(GRUB_BOOT_MACHINE_KERNEL_ADDR + 0x200), %di + + movl $0x41504354, %ebx /* EBX = "TCPA" */ + + /* %ecx = The length, in bytes, of the buffer to measure */ + movl $bytes_to_measure, %esi + movl (%esi), %ecx + xorl %esi, %esi + movl $0x9, %edx /* PCR 9 */ + + int $0x1A + + popa +#endif /* print a newline */ MSG(notification_done) popw %dx /* this makes sure %dl is our "boot" drive */ @@ -329,6 +369,10 @@ geometry_error_string: .asciz "Geom" read_error_string: .asciz "Read" general_error_string: .asciz " Error" +#ifdef TPM +bytes_to_measure: .long 0 +#endif + /* * message: write the string pointed to by %si * From 794c5b35d0bcf87dd2d90374cd264ff8f237ea5f Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Sun, 9 Aug 2015 16:12:39 -0700 Subject: [PATCH 25/85] Rework linux command We want a single buffer that contains the entire kernel image in order to perform a TPM measurement. Allocate one and copy the entire kernel into it before pulling out the individual blocks later on. --- grub-core/loader/i386/linux.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index 37287edad..091f3b31d 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -681,12 +681,13 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_file_t file = 0; struct linux_kernel_header lh; grub_uint8_t setup_sects; - grub_size_t real_size, prot_size, prot_file_size; + grub_size_t real_size, prot_size, prot_file_size, kernel_offset; grub_ssize_t len; int i; grub_size_t align, min_align; int relocatable; grub_uint64_t preferred_address = GRUB_LINUX_BZIMAGE_ADDR; + grub_uint8_t *kernel = NULL; grub_dl_ref (my_mod); @@ -700,7 +701,15 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), if (! file) goto fail; - if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh)) + len = grub_file_size (file); + kernel = grub_malloc (len); + if (!kernel) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer")); + goto fail; + } + + if (grub_file_read (file, kernel, len) != len) { if (!grub_errno) grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), @@ -708,6 +717,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } + grub_memcpy (&lh, kernel, sizeof (lh)); + kernel_offset = sizeof (lh); + if (lh.boot_flag != grub_cpu_to_le16_compile_time (0xaa55)) { grub_error (GRUB_ERR_BAD_OS, "invalid magic number"); @@ -807,13 +819,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), linux_params.ps_mouse = linux_params.padding10 = 0; len = sizeof (linux_params) - sizeof (lh); - if (grub_file_read (file, (char *) &linux_params + sizeof (lh), len) != len) - { - if (!grub_errno) - grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), - argv[0]); - goto fail; - } + + grub_memcpy (&linux_params + sizeof (lh), kernel + kernel_offset, len); + kernel_offset += len; linux_params.type_of_loader = GRUB_LINUX_BOOT_LOADER_TYPE; @@ -872,7 +880,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), /* The other parameters are filled when booting. */ - grub_file_seek (file, real_size + GRUB_DISK_SECTOR_SIZE); + kernel_offset = real_size + GRUB_DISK_SECTOR_SIZE; grub_dprintf ("linux", "bzImage, setup=0x%x, size=0x%x\n", (unsigned) real_size, (unsigned) prot_size); @@ -1021,9 +1029,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_pass_verity_hash(&lh, linux_cmdline); len = prot_file_size; - if (grub_file_read (file, prot_mode_mem, len) != len && !grub_errno) - grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), - argv[0]); + grub_memcpy (prot_mode_mem, kernel + kernel_offset, len); if (grub_errno == GRUB_ERR_NONE) { @@ -1034,6 +1040,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), fail: + grub_free (kernel); + if (file) grub_file_close (file); From bccf37f5270ff4e5c4f7001453853c74eadfa431 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Sun, 9 Aug 2015 16:20:58 -0700 Subject: [PATCH 26/85] Rework linux16 command We want a single buffer that contains the entire kernel image in order to perform a TPM measurement. Allocate one and copy the entire kernel int it before pulling out the individual blocks later on. --- grub-core/loader/i386/pc/linux.c | 34 ++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/grub-core/loader/i386/pc/linux.c b/grub-core/loader/i386/pc/linux.c index a293b17aa..1ac9cd101 100644 --- a/grub-core/loader/i386/pc/linux.c +++ b/grub-core/loader/i386/pc/linux.c @@ -123,13 +123,14 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_file_t file = 0; struct linux_kernel_header lh; grub_uint8_t setup_sects; - grub_size_t real_size; + grub_size_t real_size, kernel_offset = 0; grub_ssize_t len; int i; char *grub_linux_prot_chunk; int grub_linux_is_bzimage; grub_addr_t grub_linux_prot_target; grub_err_t err; + grub_uint8_t *kernel = NULL; grub_dl_ref (my_mod); @@ -143,7 +144,15 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), if (! file) goto fail; - if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh)) + len = grub_file_size (file); + kernel = grub_malloc (len); + if (!kernel) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer")); + goto fail; + } + + if (grub_file_read (file, kernel, len) != len) { if (!grub_errno) grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), @@ -151,6 +160,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } + grub_memcpy (&lh, kernel, sizeof (lh)); + kernel_offset = sizeof (lh); + if (lh.boot_flag != grub_cpu_to_le16_compile_time (0xaa55)) { grub_error (GRUB_ERR_BAD_OS, "invalid magic number"); @@ -314,13 +326,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_memmove (grub_linux_real_chunk, &lh, sizeof (lh)); len = real_size + GRUB_DISK_SECTOR_SIZE - sizeof (lh); - if (grub_file_read (file, grub_linux_real_chunk + sizeof (lh), len) != len) - { - if (!grub_errno) - grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), - argv[0]); - goto fail; - } + grub_memcpy (grub_linux_real_chunk + sizeof (lh), kernel + kernel_offset, + len); + kernel_offset += len; if (lh.header != grub_cpu_to_le32_compile_time (GRUB_LINUX_MAGIC_SIGNATURE) || grub_le_to_cpu16 (lh.version) < 0x0200) @@ -355,10 +363,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), } len = grub_linux16_prot_size; - if (grub_file_read (file, grub_linux_prot_chunk, grub_linux16_prot_size) - != (grub_ssize_t) grub_linux16_prot_size && !grub_errno) - grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), - argv[0]); + grub_memcpy (grub_linux_prot_chunk, kernel + kernel_offset, len); + kernel_offset += len; if (grub_errno == GRUB_ERR_NONE) { @@ -368,6 +374,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), fail: + grub_free (kernel); + if (file) grub_file_close (file); From 5433dc99b3cc2652fb51e95d7f4b0c5f43e13bf1 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Sun, 9 Aug 2015 16:28:29 -0700 Subject: [PATCH 27/85] Measure kernel and initrd on BIOS systems Measure the kernel and initrd when loaded on BIOS systems --- grub-core/loader/i386/linux.c | 5 +++++ grub-core/loader/i386/pc/linux.c | 3 +++ grub-core/loader/linux.c | 2 ++ 3 files changed, 10 insertions(+) diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index 091f3b31d..8c96ff9f4 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "verity-hash.h" GRUB_MOD_LICENSE ("GPLv3+"); @@ -717,7 +718,10 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } + grub_tpm_measure (kernel, len, GRUB_KERNEL_PCR, "Linux Kernel"); + grub_memcpy (&lh, kernel, sizeof (lh)); + kernel_offset = sizeof (lh); if (lh.boot_flag != grub_cpu_to_le16_compile_time (0xaa55)) @@ -1030,6 +1034,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_pass_verity_hash(&lh, linux_cmdline); len = prot_file_size; grub_memcpy (prot_mode_mem, kernel + kernel_offset, len); + kernel_offset += len; if (grub_errno == GRUB_ERR_NONE) { diff --git a/grub-core/loader/i386/pc/linux.c b/grub-core/loader/i386/pc/linux.c index 1ac9cd101..bd73addba 100644 --- a/grub-core/loader/i386/pc/linux.c +++ b/grub-core/loader/i386/pc/linux.c @@ -35,6 +35,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -160,6 +161,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } + grub_tpm_measure (kernel, len, GRUB_KERNEL_PCR, "BIOS Linux Kernel"); + grub_memcpy (&lh, kernel, sizeof (lh)); kernel_offset = sizeof (lh); diff --git a/grub-core/loader/linux.c b/grub-core/loader/linux.c index be6fa0f4d..3005c0d19 100644 --- a/grub-core/loader/linux.c +++ b/grub-core/loader/linux.c @@ -4,6 +4,7 @@ #include #include #include +#include struct newc_head { @@ -288,6 +289,7 @@ grub_initrd_load (struct grub_linux_initrd_context *initrd_ctx, grub_initrd_close (initrd_ctx); return grub_errno; } + grub_tpm_measure (ptr, cursize, GRUB_INITRD_PCR, "Linux Initrd"); ptr += cursize; } if (newc) From 7f587ef7f958f5d579359c626fa467d52a971fb9 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Sun, 9 Aug 2015 16:32:29 -0700 Subject: [PATCH 28/85] Measure the kernel commandline Measure the kernel commandline to ensure that it hasn't been modified --- grub-core/lib/cmdline.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/grub-core/lib/cmdline.c b/grub-core/lib/cmdline.c index d5e10ee87..ac6e0b797 100644 --- a/grub-core/lib/cmdline.c +++ b/grub-core/lib/cmdline.c @@ -19,6 +19,7 @@ #include #include +#include static unsigned int check_arg (char *c, int *has_space) { @@ -67,7 +68,7 @@ int grub_create_loader_cmdline (int argc, char *argv[], char *buf, { int i, space; unsigned int arg_size; - char *c; + char *c, *orig = buf; for (i = 0; i < argc; i++) { @@ -104,5 +105,8 @@ int grub_create_loader_cmdline (int argc, char *argv[], char *buf, *buf = 0; + grub_tpm_measure ((void *)orig, grub_strlen (orig), GRUB_CMDLINE_PCR, + "Kernel Commandline"); + return i; } From 206172d9f10005730b2025e726aebd8bd9a0f566 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 10 Aug 2015 15:27:12 -0700 Subject: [PATCH 29/85] Measure commands Measure each command executed by grub, which includes script execution. --- grub-core/script/execute.c | 25 +++++++++++++++++++++++-- include/grub/tpm.h | 1 + 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/grub-core/script/execute.c b/grub-core/script/execute.c index a8502d907..1ad6cad56 100644 --- a/grub-core/script/execute.c +++ b/grub-core/script/execute.c @@ -27,6 +27,7 @@ #include #include #include +#include /* Max digits for a char is 3 (0xFF is 255), similarly for an int it is sizeof (int) * 3, and one extra for a possible -ve sign. */ @@ -929,8 +930,9 @@ grub_script_execute_cmdline (struct grub_script_cmd *cmd) grub_err_t ret = 0; grub_script_function_t func = 0; char errnobuf[18]; - char *cmdname; - int argc; + char *cmdname, *cmdstring; + int argc, offset = 0, cmdlen = 0; + unsigned int i; char **args; int invert; struct grub_script_argv argv = { 0, 0, 0 }; @@ -939,6 +941,25 @@ grub_script_execute_cmdline (struct grub_script_cmd *cmd) if (grub_script_arglist_to_argv (cmdline->arglist, &argv) || ! argv.args[0]) return grub_errno; + for (i = 0; i < argv.argc; i++) { + cmdlen += grub_strlen (argv.args[i]) + 1; + } + + cmdstring = grub_malloc (cmdlen); + if (!cmdstring) + { + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("cannot allocate command buffer")); + } + + for (i = 0; i < argv.argc; i++) { + offset += grub_snprintf (cmdstring + offset, cmdlen - offset, "%s ", + argv.args[i]); + } + cmdstring[cmdlen-1]= '\0'; + grub_tpm_measure ((unsigned char *)cmdstring, cmdlen, GRUB_COMMAND_PCR, + cmdstring); + grub_free(cmdstring); invert = 0; argc = argv.argc - 1; args = argv.args + 1; diff --git a/include/grub/tpm.h b/include/grub/tpm.h index 40d3cf65b..7fc9d77d2 100644 --- a/include/grub/tpm.h +++ b/include/grub/tpm.h @@ -30,6 +30,7 @@ #define GRUB_KERNEL_PCR 10 #define GRUB_INITRD_PCR 11 #define GRUB_CMDLINE_PCR 12 +#define GRUB_COMMAND_PCR 13 #define TPM_TAG_RQU_COMMAND 0x00C1 #define TPM_ORD_Extend 0x14 From bacbed2c07f4b4e21c70310814a75fa9a1c3a155 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Mon, 31 Aug 2015 15:23:39 -0700 Subject: [PATCH 30/85] gpt: minor cleanup --- include/grub/gpt_partition.h | 2 +- tests/gpt_unit_test.c | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/grub/gpt_partition.h b/include/grub/gpt_partition.h index 8ff62d67f..21359f08a 100644 --- a/include/grub/gpt_partition.h +++ b/include/grub/gpt_partition.h @@ -229,7 +229,7 @@ grub_err_t grub_gpt_header_check (struct grub_gpt_header *gpt, grub_err_t grub_gpt_part_label (grub_device_t device, char **label); /* Return the partition uuid of the device DEVICE in UUID. - * The label is in a new buffer and should be freed by the caller. */ + * The uuid is in a new buffer and should be freed by the caller. */ grub_err_t grub_gpt_part_uuid (grub_device_t device, char **uuid); #endif /* ! GRUB_GPT_PARTITION_HEADER */ diff --git a/tests/gpt_unit_test.c b/tests/gpt_unit_test.c index deb55a926..7a1af46e1 100644 --- a/tests/gpt_unit_test.c +++ b/tests/gpt_unit_test.c @@ -538,7 +538,7 @@ repair_test (void) } static void -search_label_test (void) +search_part_label_test (void) { struct test_data data; const char *test_result; @@ -575,7 +575,7 @@ search_label_test (void) } static void -search_uuid_test (void) +search_part_uuid_test (void) { struct test_data data; const char gpt1_uuid[] = "A0F1792E-B4CE-4136-BCF2-1AFC133C2828"; @@ -626,8 +626,8 @@ grub_unit_test_init (void) grub_test_register ("gpt_read_invalid_test", read_invalid_entries_test); grub_test_register ("gpt_read_fallback_test", read_fallback_test); grub_test_register ("gpt_repair_test", repair_test); - grub_test_register ("gpt_search_label_test", search_label_test); - grub_test_register ("gpt_search_uuid_test", search_uuid_test); + grub_test_register ("gpt_search_part_label_test", search_part_label_test); + grub_test_register ("gpt_search_uuid_test", search_part_uuid_test); } void @@ -639,7 +639,7 @@ grub_unit_test_fini (void) grub_test_unregister ("gpt_read_invalid_test"); grub_test_unregister ("gpt_read_fallback_test"); grub_test_unregister ("gpt_repair_test"); - grub_test_unregister ("gpt_search_label_test"); - grub_test_unregister ("gpt_search_uuid_test"); + grub_test_unregister ("gpt_search_part_label_test"); + grub_test_unregister ("gpt_search_part_uuid_test"); grub_fini_all (); } From 1545295ad49d2aff2b75c6c0e7db58214351768e Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Mon, 31 Aug 2015 15:15:48 -0700 Subject: [PATCH 31/85] gpt: add search by disk uuid command --- Makefile.util.def | 1 + grub-core/Makefile.core.def | 5 ++++ grub-core/commands/search.c | 28 +++++++++++++++++++++-- grub-core/commands/search_disk_uuid.c | 5 ++++ grub-core/commands/search_wrap.c | 6 +++++ grub-core/lib/gpt.c | 21 +++++++++++++++++ include/grub/gpt_partition.h | 4 ++++ include/grub/search.h | 2 ++ tests/gpt_unit_test.c | 33 +++++++++++++++++++++++++++ 9 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 grub-core/commands/search_disk_uuid.c diff --git a/Makefile.util.def b/Makefile.util.def index bc0f178ff..4b1b4c410 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -1273,6 +1273,7 @@ program = { common = tests/lib/unit_test.c; common = grub-core/commands/search_part_label.c; common = grub-core/commands/search_part_uuid.c; + common = grub-core/commands/search_disk_uuid.c; common = grub-core/disk/host.c; common = grub-core/kern/emu/hostfs.c; common = grub-core/lib/gpt.c; diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 2da182f9b..cc7938b1d 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1033,6 +1033,11 @@ module = { common = commands/search_part_label.c; }; +module = { + name = search_disk_uuid; + common = commands/search_disk_uuid.c; +}; + module = { name = setpci; common = commands/setpci.c; diff --git a/grub-core/commands/search.c b/grub-core/commands/search.c index 09e165ed3..83837b564 100644 --- a/grub-core/commands/search.c +++ b/grub-core/commands/search.c @@ -30,7 +30,8 @@ #include #include #include -#if defined(DO_SEARCH_PART_UUID) || defined(DO_SEARCH_PART_LABEL) +#if defined(DO_SEARCH_PART_UUID) || defined(DO_SEARCH_PART_LABEL) || \ + defined(DO_SEARCH_DISK_UUID) #include #endif @@ -69,7 +70,7 @@ iterate_device (const char *name, void *data) name[0] == 'f' && name[1] == 'd' && name[2] >= '0' && name[2] <= '9') return 1; -#ifdef DO_SEARCH_FS_UUID +#if defined(DO_SEARCH_FS_UUID) || defined(DO_SEARCH_DISK_UUID) #define compare_fn grub_strcasecmp #else #define compare_fn grub_strcmp @@ -128,6 +129,25 @@ iterate_device (const char *name, void *data) grub_free (quid); } + grub_device_close (dev); + } + } +#elif defined(DO_SEARCH_DISK_UUID) + { + grub_device_t dev; + char *quid; + + dev = grub_device_open (name); + if (dev) + { + if (grub_gpt_disk_uuid (dev, &quid) == GRUB_ERR_NONE) + { + if (grub_strcmp (quid, ctx->key) == 0) + found = 1; + + grub_free (quid); + } + grub_device_close (dev); } } @@ -360,6 +380,8 @@ GRUB_MOD_INIT(search_part_uuid) GRUB_MOD_INIT(search_part_label) #elif defined (DO_SEARCH_FS_UUID) GRUB_MOD_INIT(search_fs_uuid) +#elif defined (DO_SEARCH_DISK_UUID) +GRUB_MOD_INIT(search_disk_uuid) #else GRUB_MOD_INIT(search_label) #endif @@ -378,6 +400,8 @@ GRUB_MOD_FINI(search_part_uuid) GRUB_MOD_FINI(search_part_label) #elif defined (DO_SEARCH_FS_UUID) GRUB_MOD_FINI(search_fs_uuid) +#elif defined (DO_SEARCH_DISK_UUID) +GRUB_MOD_FINI(search_disk_uuid) #else GRUB_MOD_FINI(search_label) #endif diff --git a/grub-core/commands/search_disk_uuid.c b/grub-core/commands/search_disk_uuid.c new file mode 100644 index 000000000..fba96f6b8 --- /dev/null +++ b/grub-core/commands/search_disk_uuid.c @@ -0,0 +1,5 @@ +#define DO_SEARCH_DISK_UUID 1 +#define FUNC_NAME grub_search_disk_uuid +#define COMMAND_NAME "search.disk_uuid" +#define HELP_MESSAGE N_("Search devices by disk UUID. If VARIABLE is specified, the first device found is set to a variable.") +#include "search.c" diff --git a/grub-core/commands/search_wrap.c b/grub-core/commands/search_wrap.c index e3ff756df..d931c56c5 100644 --- a/grub-core/commands/search_wrap.c +++ b/grub-core/commands/search_wrap.c @@ -40,6 +40,8 @@ static const struct grub_arg_option options[] = 0, 0}, {"part-uuid", 'U', 0, N_("Search devices by a partition UUID."), 0, 0}, + {"disk-uuid", 'U', 0, N_("Search devices by a disk UUID."), + 0, 0}, {"set", 's', GRUB_ARG_OPTION_OPTIONAL, N_("Set a variable to the first device found."), N_("VARNAME"), ARG_TYPE_STRING}, @@ -77,6 +79,7 @@ enum options SEARCH_FS_UUID, SEARCH_PART_LABEL, SEARCH_PART_UUID, + SEARCH_DISK_UUID, SEARCH_SET, SEARCH_NO_FLOPPY, SEARCH_HINT, @@ -198,6 +201,9 @@ grub_cmd_search (grub_extcmd_context_t ctxt, int argc, char **args) else if (state[SEARCH_PART_UUID].set) grub_search_part_uuid (id, var, state[SEARCH_NO_FLOPPY].set, hints, nhints); + else if (state[SEARCH_DISK_UUID].set) + grub_search_disk_uuid (id, var, state[SEARCH_NO_FLOPPY].set, + hints, nhints); else if (state[SEARCH_FILE].set) grub_search_fs_file (id, var, state[SEARCH_NO_FLOPPY].set, hints, nhints); diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index aedc4f7a1..e162bafd3 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -108,6 +108,27 @@ grub_gpt_part_uuid (grub_device_t device, char **uuid) return GRUB_ERR_NONE; } +grub_err_t +grub_gpt_disk_uuid (grub_device_t device, char **uuid) +{ + grub_gpt_t gpt = grub_gpt_read (device->disk); + if (!gpt) + goto done; + + grub_errno = GRUB_ERR_NONE; + + if (gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID) + *uuid = grub_gpt_guid_to_str (&gpt->primary.guid); + else if (gpt->status & GRUB_GPT_BACKUP_HEADER_VALID) + *uuid = grub_gpt_guid_to_str (&gpt->backup.guid); + else + grub_errno = grub_error (GRUB_ERR_BUG, "No valid GPT header"); + +done: + grub_gpt_free (gpt); + return grub_errno; +} + static grub_uint64_t grub_gpt_size_to_sectors (grub_gpt_t gpt, grub_size_t size) { diff --git a/include/grub/gpt_partition.h b/include/grub/gpt_partition.h index 21359f08a..4a6ed25b3 100644 --- a/include/grub/gpt_partition.h +++ b/include/grub/gpt_partition.h @@ -232,4 +232,8 @@ grub_err_t grub_gpt_part_label (grub_device_t device, char **label); * The uuid is in a new buffer and should be freed by the caller. */ grub_err_t grub_gpt_part_uuid (grub_device_t device, char **uuid); +/* Return the disk uuid of the device DEVICE in UUID. + * The uuid is in a new buffer and should be freed by the caller. */ +grub_err_t grub_gpt_disk_uuid (grub_device_t device, char **uuid); + #endif /* ! GRUB_GPT_PARTITION_HEADER */ diff --git a/include/grub/search.h b/include/grub/search.h index c2f40abe9..7f69d25d1 100644 --- a/include/grub/search.h +++ b/include/grub/search.h @@ -29,5 +29,7 @@ void grub_search_part_uuid (const char *key, const char *var, int no_floppy, char **hints, unsigned nhints); void grub_search_part_label (const char *key, const char *var, int no_floppy, char **hints, unsigned nhints); +void grub_search_disk_uuid (const char *key, const char *var, int no_floppy, + char **hints, unsigned nhints); #endif diff --git a/tests/gpt_unit_test.c b/tests/gpt_unit_test.c index 7a1af46e1..60f601729 100644 --- a/tests/gpt_unit_test.c +++ b/tests/gpt_unit_test.c @@ -614,6 +614,37 @@ search_part_uuid_test (void) close_disk (&data); } +static void +search_disk_uuid_test (void) +{ + struct test_data data; + const char disk_uuid[] = "69c131ad-67d6-46c6-93c4-124c755256ac"; + const char bogus_uuid[] = "1534c928-c50e-4866-9daf-6a9fd7918a76"; + const char *test_result; + char *expected_result; + + open_disk (&data); + + expected_result = grub_xasprintf ("%s", data.dev->disk->name); + grub_env_unset ("test_result"); + grub_search_disk_uuid (disk_uuid, "test_result", 0, NULL, 0); + test_result = grub_env_get ("test_result"); + grub_test_assert (test_result && strcmp (test_result, expected_result) == 0, + "wrong device: %s (%s)", test_result, expected_result); + grub_free (expected_result); + + grub_env_unset ("test_result"); + grub_search_disk_uuid (bogus_uuid, "test_result", 0, NULL, 0); + test_result = grub_env_get ("test_result"); + grub_test_assert (test_result == NULL, + "unexpected device: %s", test_result); + grub_test_assert (grub_errno == GRUB_ERR_FILE_NOT_FOUND, + "unexpected error: %s", grub_errmsg); + grub_errno = GRUB_ERR_NONE; + + close_disk (&data); +} + void grub_unit_test_init (void) { @@ -628,6 +659,7 @@ grub_unit_test_init (void) grub_test_register ("gpt_repair_test", repair_test); grub_test_register ("gpt_search_part_label_test", search_part_label_test); grub_test_register ("gpt_search_uuid_test", search_part_uuid_test); + grub_test_register ("gpt_search_disk_uuid_test", search_disk_uuid_test); } void @@ -641,5 +673,6 @@ grub_unit_test_fini (void) grub_test_unregister ("gpt_repair_test"); grub_test_unregister ("gpt_search_part_label_test"); grub_test_unregister ("gpt_search_part_uuid_test"); + grub_test_unregister ("gpt_search_disk_uuid_test"); grub_fini_all (); } From 3cb28afab6a2484e7724fa4e5cdaf252d7938ffb Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 1 Sep 2015 16:02:55 -0700 Subject: [PATCH 32/85] Measure multiboot images and modules --- grub-core/loader/i386/multiboot_mbi.c | 3 +++ grub-core/loader/multiboot.c | 2 ++ grub-core/loader/multiboot_mbi2.c | 3 +++ 3 files changed, 8 insertions(+) diff --git a/grub-core/loader/i386/multiboot_mbi.c b/grub-core/loader/i386/multiboot_mbi.c index fd7b41b0c..5d00aed65 100644 --- a/grub-core/loader/i386/multiboot_mbi.c +++ b/grub-core/loader/i386/multiboot_mbi.c @@ -36,6 +36,7 @@ #include #include #include +#include #ifdef GRUB_MACHINE_EFI #include @@ -173,6 +174,8 @@ grub_multiboot_load (grub_file_t file, const char *filename) return grub_errno; } + grub_tpm_measure((unsigned char*)buffer, len, GRUB_KERNEL_PCR, filename); + header = find_header (buffer, len); if (header == 0) diff --git a/grub-core/loader/multiboot.c b/grub-core/loader/multiboot.c index bd9d5b3e6..f176340b5 100644 --- a/grub-core/loader/multiboot.c +++ b/grub-core/loader/multiboot.c @@ -42,6 +42,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -424,6 +425,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), } grub_file_close (file); + grub_tpm_measure (module, size, GRUB_KERNEL_PCR, argv[0]); return GRUB_ERR_NONE; } diff --git a/grub-core/loader/multiboot_mbi2.c b/grub-core/loader/multiboot_mbi2.c index b0679a9f6..2466979de 100644 --- a/grub-core/loader/multiboot_mbi2.c +++ b/grub-core/loader/multiboot_mbi2.c @@ -36,6 +36,7 @@ #include #include #include +#include #if defined (GRUB_MACHINE_EFI) #include @@ -131,6 +132,8 @@ grub_multiboot_load (grub_file_t file, const char *filename) COMPILE_TIME_ASSERT (MULTIBOOT_HEADER_ALIGN % 4 == 0); + grub_tpm_measure ((unsigned char *)mld.buffer, len, GRUB_KERNEL_PCR, filename); + header = find_header (mld.buffer, len); if (header == 0) From 357f451338cfb051b930a508daf91ba6e02534ef Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Wed, 14 Oct 2015 08:24:00 -0700 Subject: [PATCH 33/85] Add fwconfig command Add a command to read values from the qemu fwcfg store. This allows data to be passed from the qemu command line to grub. Example use: echo '(hd0,1)' >rootdev qemu -fw_cfg opt/rootdev,file=rootdev fwconfig opt/rootdev root --- grub-core/Makefile.core.def | 6 ++ grub-core/commands/fwconfig.c | 121 ++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 grub-core/commands/fwconfig.c diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index cc7938b1d..d2ff2b738 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -2403,3 +2403,9 @@ module = { common = loader/i386/xen_file64.c; extra_dist = loader/i386/xen_fileXX.c; }; + +module = { + name = fwconfig; + common = commands/fwconfig.c; + enable = x86; +}; diff --git a/grub-core/commands/fwconfig.c b/grub-core/commands/fwconfig.c new file mode 100644 index 000000000..289d167fd --- /dev/null +++ b/grub-core/commands/fwconfig.c @@ -0,0 +1,121 @@ +/* fwconfig.c - command to read config from qemu fwconfig */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2015 CoreOS, 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define SELECTOR 0x510 +#define DATA 0x511 + +#define SIGNATURE_INDEX 0x00 +#define DIRECTORY_INDEX 0x19 + +static grub_extcmd_t cmd_read_fwconfig; + +struct grub_qemu_fwcfgfile { + grub_uint32_t size; + grub_uint16_t select; + grub_uint16_t reserved; + char name[56]; +}; + +static const struct grub_arg_option options[] = + { + {0, 'v', 0, N_("Save read value into variable VARNAME."), + N_("VARNAME"), ARG_TYPE_STRING}, + {0, 0, 0, 0, 0, 0} + }; + +static grub_err_t +grub_cmd_fwconfig (grub_extcmd_context_t ctxt, int argc, char **argv) +{ + grub_uint32_t i, j, value = 0; + struct grub_qemu_fwcfgfile file; + char fwsig[4], signature[4] = { 'Q', 'E', 'M', 'U' }; + + if (argc != 2) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected")); + + /* Verify that we have meaningful hardware here */ + grub_outw(SIGNATURE_INDEX, SELECTOR); + for (i=0; i Date: Tue, 5 Jan 2016 18:45:46 -0800 Subject: [PATCH 34/85] Add smbios command Incorporate the smbios command from https://raw.githubusercontent.com/dm0-/gnuxc/master/patches/grub-2.02~beta2-smbios-module.patch so we can extract the machine UUID and serial number. --- docs/grub.texi | 69 +++++ grub-core/Makefile.core.def | 15 ++ grub-core/commands/efi/loadbios.c | 18 +- grub-core/commands/efi/lsefisystab.c | 1 + grub-core/commands/efi/smbios.c | 59 +++++ grub-core/commands/i386/pc/smbios.c | 50 ++++ grub-core/commands/smbios.c | 372 +++++++++++++++++++++++++++ grub-core/efiemu/i386/pc/cfgtables.c | 34 +-- include/grub/efi/api.h | 5 + include/grub/smbios.h | 68 +++++ 10 files changed, 673 insertions(+), 18 deletions(-) create mode 100644 grub-core/commands/efi/smbios.c create mode 100644 grub-core/commands/i386/pc/smbios.c create mode 100644 grub-core/commands/smbios.c create mode 100644 include/grub/smbios.h diff --git a/docs/grub.texi b/docs/grub.texi index e935af33e..eedf4319a 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -3864,6 +3864,7 @@ you forget a command, you can run the command @command{help} * sha256sum:: Compute or check SHA256 hash * sha512sum:: Compute or check SHA512 hash * sleep:: Wait for a specified number of seconds +* smbios:: Retrieve SMBIOS information * source:: Read a configuration file in same context * test:: Check file types and compare values * true:: Do nothing, successfully @@ -4986,6 +4987,74 @@ if timeout was interrupted by @key{ESC}. @end deffn +@node smbios +@subsection smbios + +@deffn Command smbios @ + [@option{--type} @var{type}] @ + [@option{--handle} @var{handle}] @ + [@option{--match} @var{match}] @ + (@option{--get-byte} | @option{--get-word} | @option{--get-dword} | @ + @option{--get-qword} | @option{--get-string} | @option{--get-uuid}) @ + @var{offset} @ + [@option{--set} @var{variable}] +Retrieve SMBIOS information. + +The @command{smbios} command returns the value of a field in an SMBIOS +structure. The following options determine which structure to select. + +@itemize @bullet +@item +Specifying @option{--type} will select structures with a matching +@var{type}. The type can be any integer from 0 to 255. +@item +Specifying @option{--handle} will select structures with a matching +@var{handle}. The handle can be any integer from 0 to 65535. +@item +Specifying @option{--match} will select structure number @var{match} in the +filtered list of structures; e.g. @code{smbios --type 4 --match 2} will select +the second Process Information (Type 4) structure. The list is always ordered +the same as the hardware's SMBIOS table. The match number must be a positive +integer. If unspecified, the first matching structure will be selected. +@end itemize + +The remaining options determine which field in the selected SMBIOS structure to +return. Only one of these options may be specified at a time. + +@itemize @bullet +@item +When given @option{--get-byte}, return the value of the byte +at @var{offset} bytes into the selected SMBIOS structure. +@item +When given @option{--get-word}, return the value of the word (two bytes) +at @var{offset} bytes into the selected SMBIOS structure. +@item +When given @option{--get-dword}, return the value of the dword (four bytes) +at @var{offset} bytes into the selected SMBIOS structure. +@item +When given @option{--get-qword}, return the value of the qword (eight bytes) +at @var{offset} bytes into the selected SMBIOS structure. +@item +When given @option{--get-string}, return the string with its index found +at @var{offset} bytes into the selected SMBIOS structure. +@item +When given @option{--get-uuid}, return the value of the UUID (sixteen bytes) +at @var{offset} bytes into the selected SMBIOS structure. +@end itemize + +The default action is to print the value of the requested field to the console, +but a variable name can be specified with @option{--set} to store the value +instead of printing it. + +For example, this will store and then display the system manufacturer's name. + +@example +smbios --type 1 --get-string 4 --set system_manufacturer +echo $system_manufacturer +@end example +@end deffn + + @node source @subsection source diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index d2ff2b738..b20ae4dbf 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1055,6 +1055,21 @@ module = { common = commands/sleep.c; }; +module = { + name = smbios; + + common = commands/smbios.c; + efi = commands/efi/smbios.c; + i386_pc = commands/i386/pc/smbios.c; + i386_coreboot = commands/i386/pc/smbios.c; + i386_multiboot = commands/i386/pc/smbios.c; + + enable = efi; + enable = i386_pc; + enable = i386_coreboot; + enable = i386_multiboot; +}; + module = { name = suspend; ieee1275 = commands/ieee1275/suspend.c; diff --git a/grub-core/commands/efi/loadbios.c b/grub-core/commands/efi/loadbios.c index 132cadbc7..b34cf15f9 100644 --- a/grub-core/commands/efi/loadbios.c +++ b/grub-core/commands/efi/loadbios.c @@ -30,6 +30,7 @@ GRUB_MOD_LICENSE ("GPLv3+"); static grub_efi_guid_t acpi_guid = GRUB_EFI_ACPI_TABLE_GUID; static grub_efi_guid_t acpi2_guid = GRUB_EFI_ACPI_20_TABLE_GUID; static grub_efi_guid_t smbios_guid = GRUB_EFI_SMBIOS_TABLE_GUID; +static grub_efi_guid_t smbios3_guid = GRUB_EFI_SMBIOS3_TABLE_GUID; #define EBDA_SEG_ADDR 0x40e #define LOW_MEM_ADDR 0x413 @@ -93,7 +94,7 @@ static void fake_bios_data (int use_rom) { unsigned i; - void *acpi, *smbios; + void *acpi, *smbios, *smbios3; grub_uint16_t *ebda_seg_ptr, *low_mem_ptr; ebda_seg_ptr = (grub_uint16_t *) EBDA_SEG_ADDR; @@ -103,6 +104,7 @@ fake_bios_data (int use_rom) acpi = 0; smbios = 0; + smbios3 = 0; for (i = 0; i < grub_efi_system_table->num_table_entries; i++) { grub_efi_packed_guid_t *guid = @@ -127,6 +129,11 @@ fake_bios_data (int use_rom) smbios = grub_efi_system_table->configuration_table[i].vendor_table; grub_dprintf ("efi", "SMBIOS: %p\n", smbios); } + else if (! grub_memcmp (guid, &smbios3_guid, sizeof (grub_efi_guid_t))) + { + smbios3 = grub_efi_system_table->configuration_table[i].vendor_table; + grub_dprintf ("efi", "SMBIOS3: %p\n", smbios3); + } } *ebda_seg_ptr = FAKE_EBDA_SEG; @@ -137,8 +144,13 @@ fake_bios_data (int use_rom) if (acpi) grub_memcpy ((char *) ((FAKE_EBDA_SEG << 4) + 16), acpi, 1024 - 16); - if ((use_rom) && (smbios)) - grub_memcpy ((char *) SBIOS_ADDR, (char *) smbios + 16, 16); + if (use_rom) + { + if (smbios) + grub_memcpy ((char *) SBIOS_ADDR, (char *) smbios, 31); + if (smbios3) + grub_memcpy ((char *) SBIOS_ADDR + 32, (char *) smbios3, 24); + } } static grub_err_t diff --git a/grub-core/commands/efi/lsefisystab.c b/grub-core/commands/efi/lsefisystab.c index df1030221..7c039c509 100644 --- a/grub-core/commands/efi/lsefisystab.c +++ b/grub-core/commands/efi/lsefisystab.c @@ -48,6 +48,7 @@ static const struct guid_mapping guid_mappings[] = { GRUB_EFI_MPS_TABLE_GUID, "MPS"}, { GRUB_EFI_SAL_TABLE_GUID, "SAL"}, { GRUB_EFI_SMBIOS_TABLE_GUID, "SMBIOS"}, + { GRUB_EFI_SMBIOS3_TABLE_GUID, "SMBIOS3"}, { GRUB_EFI_SYSTEM_RESOURCE_TABLE_GUID, "SYSTEM RESOURCE TABLE"}, { GRUB_EFI_TIANO_CUSTOM_DECOMPRESS_GUID, "TIANO CUSTOM DECOMPRESS"}, { GRUB_EFI_TSC_FREQUENCY_GUID, "TSC FREQUENCY"}, diff --git a/grub-core/commands/efi/smbios.c b/grub-core/commands/efi/smbios.c new file mode 100644 index 000000000..927702209 --- /dev/null +++ b/grub-core/commands/efi/smbios.c @@ -0,0 +1,59 @@ +/* smbios.c - get smbios tables. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2015 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 . + */ + +#include +#include +#include +#include + +struct grub_smbios_eps * +grub_machine_smbios_get_eps (void) +{ + unsigned i; + static grub_efi_packed_guid_t smbios_guid = GRUB_EFI_SMBIOS_TABLE_GUID; + + for (i = 0; i < grub_efi_system_table->num_table_entries; i++) + { + grub_efi_packed_guid_t *guid = + &grub_efi_system_table->configuration_table[i].vendor_guid; + + if (! grub_memcmp (guid, &smbios_guid, sizeof (grub_efi_packed_guid_t))) + return (struct grub_smbios_eps *) + grub_efi_system_table->configuration_table[i].vendor_table; + } + return 0; +} + +struct grub_smbios_eps3 * +grub_machine_smbios_get_eps3 (void) +{ + unsigned i; + static grub_efi_packed_guid_t smbios3_guid = GRUB_EFI_SMBIOS3_TABLE_GUID; + + for (i = 0; i < grub_efi_system_table->num_table_entries; i++) + { + grub_efi_packed_guid_t *guid = + &grub_efi_system_table->configuration_table[i].vendor_guid; + + if (! grub_memcmp (guid, &smbios3_guid, sizeof (grub_efi_packed_guid_t))) + return (struct grub_smbios_eps3 *) + grub_efi_system_table->configuration_table[i].vendor_table; + } + return 0; +} diff --git a/grub-core/commands/i386/pc/smbios.c b/grub-core/commands/i386/pc/smbios.c new file mode 100644 index 000000000..3bdf3d766 --- /dev/null +++ b/grub-core/commands/i386/pc/smbios.c @@ -0,0 +1,50 @@ +/* smbios.c - get smbios tables. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2015 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 . + */ + +#include +#include +#include + +struct grub_smbios_eps * +grub_machine_smbios_get_eps (void) +{ + grub_uint8_t *ptr; + + grub_dprintf ("smbios", "Looking for SMBIOS EPS. Scanning BIOS\n"); + for (ptr = (grub_uint8_t *) 0xf0000; ptr < (grub_uint8_t *) 0x100000; + ptr += 16) + if (grub_memcmp (ptr, "_SM_", 4) == 0 + && grub_byte_checksum (ptr, sizeof (struct grub_smbios_eps)) == 0) + return (struct grub_smbios_eps *) ptr; + return 0; +} + +struct grub_smbios_eps3 * +grub_machine_smbios_get_eps3 (void) +{ + grub_uint8_t *ptr; + + grub_dprintf ("smbios", "Looking for SMBIOS3 EPS. Scanning BIOS\n"); + for (ptr = (grub_uint8_t *) 0xf0000; ptr < (grub_uint8_t *) 0x100000; + ptr += 16) + if (grub_memcmp (ptr, "_SM3_", 5) == 0 + && grub_byte_checksum (ptr, sizeof (struct grub_smbios_eps3)) == 0) + return (struct grub_smbios_eps3 *) ptr; + return 0; +} diff --git a/grub-core/commands/smbios.c b/grub-core/commands/smbios.c new file mode 100644 index 000000000..82186319a --- /dev/null +++ b/grub-core/commands/smbios.c @@ -0,0 +1,372 @@ +/* smbios.c - retrieve smbios information. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013,2014,2015 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + + +/* Locate the SMBIOS entry point structure depending on the hardware. */ +struct grub_smbios_eps * +grub_smbios_get_eps (void) +{ + static struct grub_smbios_eps *eps = NULL; + if (eps != NULL) + return eps; + eps = grub_machine_smbios_get_eps (); + return eps; +} + +/* Locate the SMBIOS3 entry point structure depending on the hardware. */ +struct grub_smbios_eps3 * +grub_smbios_get_eps3 (void) +{ + static struct grub_smbios_eps3 *eps = NULL; + if (eps != NULL) + return eps; + eps = grub_machine_smbios_get_eps3 (); + return eps; +} + +/* Abstract useful values found in either the SMBIOS3 or SMBIOS EPS. */ +static struct { + grub_addr_t start; + grub_addr_t end; + grub_uint16_t structures; +} table_desc = {0, 0, 0}; + + +/* + * These functions convert values from the various SMBIOS structure field types + * into a string formatted to be returned to the user. They expect that the + * structure and offset were already validated. The given buffer stores the + * newly formatted string if needed. When the requested data is successfully + * retrieved and formatted, the pointer to the string is returned; otherwise, + * NULL is returned on failure. + */ + +static const char * +grub_smbios_format_byte (char *buffer, grub_size_t size, + const grub_uint8_t *structure, grub_uint8_t offset) +{ + grub_snprintf (buffer, size, "%u", structure[offset]); + return (const char *)buffer; +} + +static const char * +grub_smbios_format_word (char *buffer, grub_size_t size, + const grub_uint8_t *structure, grub_uint8_t offset) +{ + grub_uint16_t value = grub_get_unaligned16 (structure + offset); + grub_snprintf (buffer, size, "%u", value); + return (const char *)buffer; +} + +static const char * +grub_smbios_format_dword (char *buffer, grub_size_t size, + const grub_uint8_t *structure, grub_uint8_t offset) +{ + grub_uint32_t value = grub_get_unaligned32 (structure + offset); + grub_snprintf (buffer, size, "%" PRIuGRUB_UINT32_T, value); + return (const char *)buffer; +} + +static const char * +grub_smbios_format_qword (char *buffer, grub_size_t size, + const grub_uint8_t *structure, grub_uint8_t offset) +{ + grub_uint64_t value = grub_get_unaligned64 (structure + offset); + grub_snprintf (buffer, size, "%" PRIuGRUB_UINT64_T, value); + return (const char *)buffer; +} + +/* The matching string pointer is returned directly to avoid extra copying. */ +static const char * +grub_smbios_get_string (char *buffer __attribute__ ((unused)), + grub_size_t size __attribute__ ((unused)), + const grub_uint8_t *structure, grub_uint8_t offset) +{ + const grub_uint8_t *ptr = structure + structure[1]; + const grub_uint8_t *table_end = (const grub_uint8_t *)table_desc.end; + const grub_uint8_t referenced_string_number = structure[offset]; + grub_uint8_t i; + + /* A string referenced with zero is interpreted as unset. */ + if (referenced_string_number == 0) + return NULL; + + /* Search the string set. */ + for (i = 1; *ptr != 0 && ptr < table_end; i++) + if (i == referenced_string_number) + { + const char *str = (const char *)ptr; + while (*ptr++ != 0) + if (ptr >= table_end) + return NULL; /* The string isn't terminated. */ + return str; + } + else + while (*ptr++ != 0 && ptr < table_end); + + /* The string number is greater than the number of strings in the set. */ + return NULL; +} + +static const char * +grub_smbios_format_uuid (char *buffer, grub_size_t size, + const grub_uint8_t *structure, grub_uint8_t offset) +{ + const grub_uint8_t *f = structure + offset; /* little-endian fields */ + const grub_uint8_t *g = f + 8; /* byte-by-byte fields */ + grub_snprintf (buffer, size, + "%02x%02x%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x-%02x%02x%02x%02x%02x%02x", + f[3], f[2], f[1], f[0], f[5], f[4], f[7], f[6], + g[0], g[1], g[2], g[3], g[4], g[5], g[6], g[7]); + return (const char *)buffer; +} + + +/* List the field formatting functions and the number of bytes they need. */ +#define MAXIMUM_FORMAT_LENGTH (sizeof ("ffffffff-ffff-ffff-ffff-ffffffffffff")) +static const struct { + const char *(*format) (char *buffer, grub_size_t size, + const grub_uint8_t *structure, grub_uint8_t offset); + grub_uint8_t field_length; +} field_extractors[] = { + {grub_smbios_format_byte, 1}, + {grub_smbios_format_word, 2}, + {grub_smbios_format_dword, 4}, + {grub_smbios_format_qword, 8}, + {grub_smbios_get_string, 1}, + {grub_smbios_format_uuid, 16} +}; + +/* List command options, with structure field getters ordered as above. */ +#define FIRST_GETTER_OPT (3) +#define SETTER_OPT (FIRST_GETTER_OPT + ARRAY_SIZE(field_extractors)) +static const struct grub_arg_option options[] = { + {"type", 't', 0, N_("Match entries with the given type."), + N_("type"), ARG_TYPE_INT}, + {"handle", 'h', 0, N_("Match entries with the given handle."), + N_("handle"), ARG_TYPE_INT}, + {"match", 'm', 0, N_("Select a structure when several match."), + N_("match"), ARG_TYPE_INT}, + {"get-byte", 'b', 0, N_("Get the byte's value at the given offset."), + N_("offset"), ARG_TYPE_INT}, + {"get-word", 'w', 0, N_("Get two bytes' value at the given offset."), + N_("offset"), ARG_TYPE_INT}, + {"get-dword", 'd', 0, N_("Get four bytes' value at the given offset."), + N_("offset"), ARG_TYPE_INT}, + {"get-qword", 'q', 0, N_("Get eight bytes' value at the given offset."), + N_("offset"), ARG_TYPE_INT}, + {"get-string", 's', 0, N_("Get the string specified at the given offset."), + N_("offset"), ARG_TYPE_INT}, + {"get-uuid", 'u', 0, N_("Get the UUID's value at the given offset."), + N_("offset"), ARG_TYPE_INT}, + {"set", '\0', 0, N_("Store the value in the given variable name."), + N_("variable"), ARG_TYPE_STRING}, + {0, 0, 0, 0, 0, 0} +}; + + +/* + * Return a matching SMBIOS structure. + * + * This method can use up to three criteria for selecting a structure: + * - The "type" field (use -1 to ignore) + * - The "handle" field (use -1 to ignore) + * - Which to return if several match (use 0 to ignore) + * + * The return value is a pointer to the first matching structure. If no + * structures match the given parameters, NULL is returned. + */ +static const grub_uint8_t * +grub_smbios_match_structure (const grub_int16_t type, + const grub_int32_t handle, + const grub_uint16_t match) +{ + const grub_uint8_t *ptr = (const grub_uint8_t *)table_desc.start; + const grub_uint8_t *table_end = (const grub_uint8_t *)table_desc.end; + grub_uint16_t structures = table_desc.structures; + grub_uint16_t structure_count = 0; + grub_uint16_t matches = 0; + + while (ptr < table_end + && ptr[1] >= 4 /* Valid structures include the 4-byte header. */ + && (structure_count++ < structures || structures == 0)) + { + grub_uint16_t structure_handle = grub_get_unaligned16 (ptr + 2); + grub_uint8_t structure_type = ptr[0]; + + if ((handle < 0 || handle == structure_handle) + && (type < 0 || type == structure_type) + && (match == 0 || match == ++matches)) + return ptr; + + else + { + ptr += ptr[1]; + while ((*ptr++ != 0 || *ptr++ != 0) && ptr < table_end); + } + + if (structure_type == GRUB_SMBIOS_TYPE_END_OF_TABLE) + break; + } + + return NULL; +} + + +static grub_err_t +grub_cmd_smbios (grub_extcmd_context_t ctxt, + int argc __attribute__ ((unused)), + char **argv __attribute__ ((unused))) +{ + struct grub_arg_list *state = ctxt->state; + + grub_int16_t type = -1; + grub_int32_t handle = -1; + grub_uint16_t match = 0; + grub_uint8_t offset = 0; + + const grub_uint8_t *structure; + const char *value; + char buffer[MAXIMUM_FORMAT_LENGTH]; + grub_int32_t option; + grub_int8_t field_type = -1; + grub_uint8_t i; + + if (table_desc.start == 0) + return grub_error (GRUB_ERR_IO, + N_("the SMBIOS entry point structure was not found")); + + /* Read the given filtering options. */ + if (state[0].set) + { + option = grub_strtol (state[0].arg, NULL, 0); + if (option < 0 || option > 255) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("the type must be between 0 and 255")); + type = (grub_int16_t)option; + } + if (state[1].set) + { + option = grub_strtol (state[1].arg, NULL, 0); + if (option < 0 || option > 65535) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("the handle must be between 0 and 65535")); + handle = (grub_int32_t)option; + } + if (state[2].set) + { + option = grub_strtol (state[2].arg, NULL, 0); + if (option <= 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("the match must be a positive integer")); + match = (grub_uint16_t)option; + } + + /* Determine the data type of the structure field to retrieve. */ + for (i = 0; i < ARRAY_SIZE(field_extractors); i++) + if (state[FIRST_GETTER_OPT + i].set) + { + if (field_type >= 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("only one --get option is usable at a time")); + field_type = i; + } + + /* Require a choice of a structure field to return. */ + if (field_type < 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("one of the --get options is required")); + + /* Locate a matching SMBIOS structure. */ + structure = grub_smbios_match_structure (type, handle, match); + if (structure == NULL) + return grub_error (GRUB_ERR_IO, + N_("no structure matched the given options")); + + /* Ensure the requested byte offset is inside the structure. */ + option = grub_strtol (state[FIRST_GETTER_OPT + field_type].arg, NULL, 0); + if (option < 0 || option >= structure[1]) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("the given offset is outside the structure")); + + /* Ensure the requested data type at the offset is inside the structure. */ + offset = (grub_uint8_t)option; + if (offset + field_extractors[field_type].field_length > structure[1]) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("the field ends outside the structure")); + + /* Format the requested structure field into a readable string. */ + value = field_extractors[field_type].format (buffer, sizeof (buffer), + structure, offset); + if (value == NULL) + return grub_error (GRUB_ERR_IO, + N_("failed to retrieve the structure field")); + + /* Store or print the formatted value. */ + if (state[SETTER_OPT].set) + grub_env_set (state[SETTER_OPT].arg, value); + else + grub_printf ("%s\n", value); + + return GRUB_ERR_NONE; +} + + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT(smbios) +{ + struct grub_smbios_eps3 *eps3; + struct grub_smbios_eps *eps; + + if ((eps3 = grub_smbios_get_eps3 ())) + { + table_desc.start = (grub_addr_t)eps3->table_address; + table_desc.end = table_desc.start + eps3->maximum_table_length; + table_desc.structures = 0; /* SMBIOS3 drops the structure count. */ + } + else if ((eps = grub_smbios_get_eps ())) + { + table_desc.start = (grub_addr_t)eps->intermediate.table_address; + table_desc.end = table_desc.start + eps->intermediate.table_length; + table_desc.structures = eps->intermediate.structures; + } + + cmd = grub_register_extcmd ("smbios", grub_cmd_smbios, 0, + N_("[-t type] [-h handle] [-m match] " + "(-b|-w|-d|-q|-s|-u) offset " + "[--set variable]"), + N_("Retrieve SMBIOS information."), options); +} + +GRUB_MOD_FINI(smbios) +{ + grub_unregister_extcmd (cmd); +} diff --git a/grub-core/efiemu/i386/pc/cfgtables.c b/grub-core/efiemu/i386/pc/cfgtables.c index 492c07c46..1d8833115 100644 --- a/grub-core/efiemu/i386/pc/cfgtables.c +++ b/grub-core/efiemu/i386/pc/cfgtables.c @@ -22,18 +22,22 @@ #include #include #include +#include grub_err_t grub_machine_efiemu_init_tables (void) { - grub_uint8_t *ptr; void *table; grub_err_t err; grub_efi_guid_t smbios = GRUB_EFI_SMBIOS_TABLE_GUID; + grub_efi_guid_t smbios3 = GRUB_EFI_SMBIOS3_TABLE_GUID; grub_efi_guid_t acpi20 = GRUB_EFI_ACPI_20_TABLE_GUID; grub_efi_guid_t acpi = GRUB_EFI_ACPI_TABLE_GUID; err = grub_efiemu_unregister_configuration_table (smbios); + if (err) + return err; + err = grub_efiemu_unregister_configuration_table (smbios3); if (err) return err; err = grub_efiemu_unregister_configuration_table (acpi); @@ -43,6 +47,20 @@ grub_machine_efiemu_init_tables (void) if (err) return err; + table = grub_smbios_get_eps (); + if (table) + { + err = grub_efiemu_register_configuration_table (smbios, 0, 0, table); + if (err) + return err; + } + table = grub_smbios_get_eps3 (); + if (table) + { + err = grub_efiemu_register_configuration_table (smbios3, 0, 0, table); + if (err) + return err; + } table = grub_acpi_get_rsdpv1 (); if (table) { @@ -58,19 +76,5 @@ grub_machine_efiemu_init_tables (void) return err; } - for (ptr = (grub_uint8_t *) 0xf0000; ptr < (grub_uint8_t *) 0x100000; - ptr += 16) - if (grub_memcmp (ptr, "_SM_", 4) == 0 - && grub_byte_checksum (ptr, *(ptr + 5)) == 0) - break; - - if (ptr < (grub_uint8_t *) 0x100000) - { - grub_dprintf ("efiemu", "Registering SMBIOS\n"); - err = grub_efiemu_register_configuration_table (smbios, 0, 0, ptr); - if (err) - return err; - } - return GRUB_ERR_NONE; } diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index c7c9f0e1d..356d855e2 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -314,6 +314,11 @@ { 0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \ } +#define GRUB_EFI_SMBIOS3_TABLE_GUID \ + { 0xf2fd1544, 0x9794, 0x4a2c, \ + { 0x99, 0x2e, 0xe5, 0xbb, 0xcf, 0x20, 0xe3, 0x94 } \ + } + #define GRUB_EFI_SAL_TABLE_GUID \ { 0xeb9d2d32, 0x2d88, 0x11d3, \ { 0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \ diff --git a/include/grub/smbios.h b/include/grub/smbios.h new file mode 100644 index 000000000..4b82fa845 --- /dev/null +++ b/include/grub/smbios.h @@ -0,0 +1,68 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2015 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 . + */ + +#ifndef GRUB_SMBIOS_HEADER +#define GRUB_SMBIOS_HEADER 1 + +#include + +#define GRUB_SMBIOS_TYPE_END_OF_TABLE ((grub_uint8_t)127) + +struct grub_smbios_ieps +{ + grub_uint8_t anchor[5]; /* "_DMI_" */ + grub_uint8_t checksum; + grub_uint16_t table_length; + grub_uint32_t table_address; + grub_uint16_t structures; + grub_uint8_t revision; +} GRUB_PACKED; + +struct grub_smbios_eps +{ + grub_uint8_t anchor[4]; /* "_SM_" */ + grub_uint8_t checksum; + grub_uint8_t length; /* 0x1f */ + grub_uint8_t version_major; + grub_uint8_t version_minor; + grub_uint16_t maximum_structure_size; + grub_uint8_t revision; + grub_uint8_t formatted[5]; + struct grub_smbios_ieps intermediate; +} GRUB_PACKED; + +struct grub_smbios_eps3 +{ + grub_uint8_t anchor[5]; /* "_SM3_" */ + grub_uint8_t checksum; + grub_uint8_t length; /* 0x18 */ + grub_uint8_t version_major; + grub_uint8_t version_minor; + grub_uint8_t docrev; + grub_uint8_t revision; + grub_uint8_t reserved; + grub_uint32_t maximum_table_length; + grub_uint64_t table_address; +} GRUB_PACKED; + +struct grub_smbios_eps *grub_smbios_get_eps (void); +struct grub_smbios_eps3 *grub_smbios_get_eps3 (void); +struct grub_smbios_eps *grub_machine_smbios_get_eps (void); +struct grub_smbios_eps3 *grub_machine_smbios_get_eps3 (void); + +#endif /* ! GRUB_SMBIOS_HEADER */ From d779b3e0fcbe70d099c0317d4a7cab3db59a4045 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 7 Jan 2016 15:31:36 -0800 Subject: [PATCH 35/85] Fix hex representation of binary variable contents The getenv code was mishandling the conversion of binary to hex. Grub's sprintf() doesn't seem to support the full set of format conversions, so fix this in the nasty way. --- grub-core/commands/efi/getenv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/commands/efi/getenv.c b/grub-core/commands/efi/getenv.c index 5a829f5e4..7eb2c4506 100644 --- a/grub-core/commands/efi/getenv.c +++ b/grub-core/commands/efi/getenv.c @@ -119,7 +119,7 @@ grub_cmd_getenv (grub_extcmd_context_t ctxt, int argc, char **args) { bindata = grub_zalloc(datasize * 2 + 1); for (i=0; i Date: Thu, 7 Jan 2016 15:33:36 -0800 Subject: [PATCH 36/85] Allow passing of trusted keys via variables Add support for adding gpg keys to the trusted database with a new command called "trust_var". This takes the contents of a variable (in ascii-encoded hex) and interprets it as a gpg public key. --- grub-core/commands/verify.c | 94 ++++++++++++++++++++++++++++++------- 1 file changed, 77 insertions(+), 17 deletions(-) diff --git a/grub-core/commands/verify.c b/grub-core/commands/verify.c index 67cb1c785..d72692b97 100644 --- a/grub-core/commands/verify.c +++ b/grub-core/commands/verify.c @@ -52,6 +52,20 @@ static const struct grub_arg_option options[] = {0, 0, 0, 0, 0, 0} }; +static grub_ssize_t +pseudo_read (struct grub_file *file, char *buf, grub_size_t len) +{ + grub_memcpy (buf, (grub_uint8_t *) file->data + file->offset, len); + return len; +} + +/* Filesystem descriptor. */ +struct grub_fs pseudo_fs = + { + .name = "pseudo", + .read = pseudo_read +}; + static grub_err_t read_packet_header (grub_file_t sig, grub_uint8_t *out_type, grub_size_t *len) { @@ -700,6 +714,64 @@ grub_cmd_trust (grub_extcmd_context_t ctxt, return GRUB_ERR_NONE; } +static grub_err_t +grub_cmd_trust_var (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + struct grub_file pseudo_file; + const char *var; + char *data; + struct grub_public_key *pk = NULL; + unsigned int i, idx0, idx1; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + + var = grub_env_get (args[0]); + if (!var) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unknown variable")); + + data = grub_zalloc (grub_strlen (var) / 2); + if (!data) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate memory for key")); + + /* For the want of sscanf() */ + for (i = 0; i < grub_strlen (var); i += 2) + { + if (var[i] < 0x40) + idx0 = var[i] - 0x30; + else + idx0 = var[i] - 0x57; + + if (var[i+1] < 0x40) + idx1 = var[i+1] - 0x30; + else + idx1 = var[i+1] - 0x57; + + data[i/2] = ((idx0 << 4) & 0xf0) | (idx1 & 0x0f); + } + + grub_memset (&pseudo_file, 0, sizeof (pseudo_file)); + + pseudo_file.fs = &pseudo_fs; + pseudo_file.size = grub_strlen (var) / 2; + pseudo_file.data = data; + + pk = grub_load_public_key (&pseudo_file); + if (!pk) + { + grub_free(data); + return grub_errno; + } + + pk->next = grub_pk_trusted; + grub_pk_trusted = pk; + + grub_free(data); + return GRUB_ERR_NONE; + +} + static grub_err_t grub_cmd_list (grub_command_t cmd __attribute__ ((unused)), int argc __attribute__ ((unused)), @@ -954,24 +1026,8 @@ grub_env_write_sec (struct grub_env_var *var __attribute__ ((unused)), return grub_strdup (sec ? "enforce" : "no"); } -static grub_ssize_t -pseudo_read (struct grub_file *file, char *buf, grub_size_t len) -{ - grub_memcpy (buf, (grub_uint8_t *) file->data + file->offset, len); - return len; -} - - -/* Filesystem descriptor. */ -struct grub_fs pseudo_fs = - { - .name = "pseudo", - .read = pseudo_read -}; - - static grub_extcmd_t cmd, cmd_trust; -static grub_command_t cmd_distrust, cmd_list; +static grub_command_t cmd_trust_var, cmd_distrust, cmd_list; GRUB_MOD_INIT(verify) { @@ -1024,6 +1080,9 @@ GRUB_MOD_INIT(verify) N_("[-s|--skip-sig] PUBKEY_FILE"), N_("Add PUBKEY_FILE to trusted keys."), options); + cmd_trust_var = grub_register_command ("trust_var", grub_cmd_trust_var, + N_("PUBKEY_VAR"), + N_("Add the contents of PUBKEY_VAR to trusted keys.")); cmd_list = grub_register_command ("list_trusted", grub_cmd_list, 0, N_("Show the list of trusted keys.")); @@ -1037,6 +1096,7 @@ GRUB_MOD_FINI(verify) grub_file_filter_unregister (GRUB_FILE_FILTER_PUBKEY); grub_unregister_extcmd (cmd); grub_unregister_extcmd (cmd_trust); + grub_unregister_command (cmd_trust_var); grub_unregister_command (cmd_list); grub_unregister_command (cmd_distrust); } From 0987f9d173c8d69befe5782fc7770222e2a13d1f Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 7 Jan 2016 17:27:15 -0800 Subject: [PATCH 37/85] Allow non-default ports for HTTP requests Add support for passing ports in HTTP requests. This takes the form of: (http,serverip:portnum)/file --- grub-core/net/http.c | 8 ++++++-- grub-core/net/net.c | 10 +++++++++- include/grub/net.h | 1 + 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/grub-core/net/http.c b/grub-core/net/http.c index 5aa4ad3be..389a78ee0 100644 --- a/grub-core/net/http.c +++ b/grub-core/net/http.c @@ -309,7 +309,7 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial) { http_data_t data = file->data; grub_uint8_t *ptr; - int i; + int i, port; struct grub_net_buff *nb; grub_err_t err; @@ -390,8 +390,12 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial) grub_netbuff_put (nb, 2); grub_memcpy (ptr, "\r\n", 2); + if (file->device->net->port) + port = file->device->net->port; + else + port = HTTP_PORT; data->sock = grub_net_tcp_open (file->device->net->server, - HTTP_PORT, http_receive, + port, http_receive, http_err, http_err, file); if (!data->sock) diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 10773fc34..cc1e57744 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -1261,7 +1261,7 @@ grub_net_open_real (const char *name) grub_net_app_level_t proto; const char *protname, *server; grub_size_t protnamelen; - int try; + int try, port = 0; if (grub_strncmp (name, "pxe:", sizeof ("pxe:") - 1) == 0) { @@ -1278,7 +1278,14 @@ grub_net_open_real (const char *name) else { const char *comma; + char *colon; comma = grub_strchr (name, ','); + colon = grub_strchr (name, ':'); + if (colon) + { + port = (int) grub_strtol(colon+1, NULL, 10); + *colon = '\0'; + } if (comma) { protnamelen = comma - name; @@ -1316,6 +1323,7 @@ grub_net_open_real (const char *name) grub_free (ret); return NULL; } + ret->port = port; ret->fs = &grub_net_fs; return ret; } diff --git a/include/grub/net.h b/include/grub/net.h index 2192fa186..c51750973 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -276,6 +276,7 @@ typedef struct grub_net grub_fs_t fs; int eof; int stall; + int port; } *grub_net_t; extern grub_net_t (*EXPORT_VAR (grub_net_open)) (const char *name); From fd0a4f5881baefb272f4a9e9b0881a769c858fd4 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 7 Jan 2016 22:55:13 -0800 Subject: [PATCH 38/85] Send a user class identifier in bootp requests It's helpful to determine that a request was sent by grub in order to permit the server to provide different information at different stages of the boot process. Send GRUB2 as a type 77 DHCP option when sending bootp packets in order to make this possible. --- grub-core/net/bootp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index 9e2fdb795..068db685c 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -25,6 +25,11 @@ #include #include +static grub_uint8_t grub_userclass[] = {GRUB_NET_BOOTP_RFC1048_MAGIC_0, + GRUB_NET_BOOTP_RFC1048_MAGIC_1, + GRUB_NET_BOOTP_RFC1048_MAGIC_2, + GRUB_NET_BOOTP_RFC1048_MAGIC_3, + 0x4D, 0x05, 'G', 'R', 'U', 'B', '2'}; static void parse_dhcp_vendor (const char *name, const void *vend, int limit, int *mask) { @@ -536,6 +541,7 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)), pack->seconds = grub_cpu_to_be16 (t); grub_memcpy (&pack->mac_addr, &ifaces[j].hwaddress.mac, 6); + grub_memcpy (&pack->vendor, grub_userclass, sizeof(grub_userclass)); grub_netbuff_push (nb, sizeof (*udph)); From 902aec6758be5d3668df6fe7a5bcb144bd7a6ef3 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Wed, 10 Feb 2016 15:43:33 -0800 Subject: [PATCH 39/85] Allow protocol to be separated from host with a semicolon Some DHCP servers (such as dnsmasq) tokenise parameters with commas, making it impossible to pass boot files with commas in them. Allow using a semicolon to separate the protocol from host if a comma wasn't found. --- grub-core/net/net.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/grub-core/net/net.c b/grub-core/net/net.c index cc1e57744..0a2cef7e4 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -1280,6 +1280,10 @@ grub_net_open_real (const char *name) const char *comma; char *colon; comma = grub_strchr (name, ','); + if (!comma) + { + comma = grub_strchr (name, ';'); + } colon = grub_strchr (name, ':'); if (colon) { From 527cbe513a275f88fc604aeac142ee0cda4b09f8 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Wed, 2 Mar 2016 17:24:57 -0800 Subject: [PATCH 40/85] Tag the bootp request as a DHCP discover --- grub-core/net/bootp.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index 068db685c..3de40d7fa 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -25,11 +25,14 @@ #include #include -static grub_uint8_t grub_userclass[] = {GRUB_NET_BOOTP_RFC1048_MAGIC_0, - GRUB_NET_BOOTP_RFC1048_MAGIC_1, - GRUB_NET_BOOTP_RFC1048_MAGIC_2, - GRUB_NET_BOOTP_RFC1048_MAGIC_3, - 0x4D, 0x05, 'G', 'R', 'U', 'B', '2'}; +static grub_uint8_t dhcp_option_header[] = {GRUB_NET_BOOTP_RFC1048_MAGIC_0, + GRUB_NET_BOOTP_RFC1048_MAGIC_1, + GRUB_NET_BOOTP_RFC1048_MAGIC_2, + GRUB_NET_BOOTP_RFC1048_MAGIC_3}; +static grub_uint8_t grub_userclass[] = {0x4D, 0x05, 'G', 'R', 'U', 'B', '2'}; +static grub_uint8_t grub_dhcpdiscover[] = {0x35, 0x01, 0x01}; +static grub_uint8_t grub_dhcptime[] = {0x33, 0x04, 0x00, 0x00, 0x0e, 0x10}; + static void parse_dhcp_vendor (const char *name, const void *vend, int limit, int *mask) { @@ -504,10 +507,14 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)), struct udphdr *udph; grub_net_network_level_address_t target; grub_net_link_level_address_t ll_target; + grub_uint8_t *offset; if (!ifaces[j].prev) continue; - nb = grub_netbuff_alloc (sizeof (*pack) + 64 + 128); + nb = grub_netbuff_alloc (sizeof (*pack) + sizeof(dhcp_option_header) + + sizeof(grub_userclass) + + sizeof(grub_dhcpdiscover) + + sizeof(grub_dhcptime) + 64 + 128); if (!nb) { grub_netbuff_free (nb); @@ -541,7 +548,14 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)), pack->seconds = grub_cpu_to_be16 (t); grub_memcpy (&pack->mac_addr, &ifaces[j].hwaddress.mac, 6); - grub_memcpy (&pack->vendor, grub_userclass, sizeof(grub_userclass)); + offset = (grub_uint8_t *)&pack->vendor; + grub_memcpy (offset, dhcp_option_header, sizeof(dhcp_option_header)); + offset += sizeof(dhcp_option_header); + grub_memcpy (offset, grub_dhcpdiscover, sizeof(grub_dhcpdiscover)); + offset += sizeof(grub_dhcpdiscover); + grub_memcpy (offset, grub_userclass, sizeof(grub_userclass)); + offset += sizeof(grub_userclass); + grub_memcpy (offset, grub_dhcptime, sizeof(grub_dhcptime)); grub_netbuff_push (nb, sizeof (*udph)); From 3340fc72a63461172e624b0e930ecf9d0effb978 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Wed, 2 Mar 2016 17:29:17 -0800 Subject: [PATCH 41/85] Don't allocate a new address buffer if we receive multiple responses The current logic in the DNS resolution code allocates an address buffer based on the number of addresses in the response packet. If we receive multiple response packets in response to a single query packet, this means that we will reallocate a new buffer large enough for only the addresses in that specific packet, discarding any previous results in the process. Worse, we still keep track of the *total* number of addresses resolved in response to this query, not merely the number in the packet being currently processed. Use realloc() rather than malloc() to avoid overwriting the existing data, and allocate a buffer large enough for the total set of addresses rather than merely the number in this specific response. --- grub-core/net/dns.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grub-core/net/dns.c b/grub-core/net/dns.c index 5d9afe093..5deb1efd3 100644 --- a/grub-core/net/dns.c +++ b/grub-core/net/dns.c @@ -285,8 +285,8 @@ recv_hook (grub_net_udp_socket_t sock __attribute__ ((unused)), ptr++; ptr += 4; } - *data->addresses = grub_malloc (sizeof ((*data->addresses)[0]) - * grub_be_to_cpu16 (head->ancount)); + *data->addresses = grub_realloc (*data->addresses, sizeof ((*data->addresses)[0]) + * (grub_be_to_cpu16 (head->ancount) + *data->naddresses)); if (!*data->addresses) { grub_errno = GRUB_ERR_NONE; From f203942ba2cbd73bf0a524a1672d6eeabded3200 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Wed, 23 Mar 2016 16:49:42 -0700 Subject: [PATCH 42/85] Fix boot when there's no TPM If the firmware has TPM support but has no TPM, we're jumping to core.img without popping the registers back onto the stack. Fix that. --- grub-core/boot/i386/pc/boot.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/boot/i386/pc/boot.S b/grub-core/boot/i386/pc/boot.S index 4c63247e8..47a461ed5 100644 --- a/grub-core/boot/i386/pc/boot.S +++ b/grub-core/boot/i386/pc/boot.S @@ -476,9 +476,9 @@ LOCAL(copy_buffer): movl $0x8, %edx /* PCR 8 */ int $0x1A +boot: popa #endif -boot: /* boot kernel */ jmp *(LOCAL(kernel_address)) From 8e1d90283f9ee95947814dac726ccb004454941c Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Wed, 23 Mar 2016 17:03:43 -0700 Subject: [PATCH 43/85] Rework TPM measurements Rework TPM measurements to use fewer PCRs. After discussion with upstream, it's preferable to avoid using so many PCRs. Instead, measure into PCRs 8 and 9 but use a prefix in the event log to indicate which subsystem carried out the measurements. --- grub-core/kern/dl.c | 2 +- grub-core/kern/tpm.c | 10 ++++++++-- grub-core/lib/cmdline.c | 4 ++-- grub-core/loader/i386/efi/linux.c | 4 ++-- grub-core/loader/i386/linux.c | 2 +- grub-core/loader/i386/multiboot_mbi.c | 2 +- grub-core/loader/i386/pc/linux.c | 2 +- grub-core/loader/linux.c | 2 +- grub-core/loader/multiboot.c | 2 +- grub-core/loader/multiboot_mbi2.c | 2 +- grub-core/script/execute.c | 4 ++-- include/grub/tpm.h | 9 +++------ 12 files changed, 24 insertions(+), 21 deletions(-) diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index de0fcb4af..facbc7829 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -730,7 +730,7 @@ grub_dl_load_file (const char *filename) opens of the same device. */ grub_file_close (file); - grub_tpm_measure(core, size, GRUB_TPM_PCR, filename); + grub_tpm_measure(core, size, GRUB_BINARY_PCR, "grub_module", filename); mod = grub_dl_load_core (core, size); grub_free (core); diff --git a/grub-core/kern/tpm.c b/grub-core/kern/tpm.c index 1a991876c..cb5a81203 100644 --- a/grub-core/kern/tpm.c +++ b/grub-core/kern/tpm.c @@ -7,7 +7,13 @@ grub_err_t grub_tpm_measure (unsigned char *buf, grub_size_t size, grub_uint8_t pcr, - const char *description) + const char *kind, const char *description) { - return grub_tpm_log_event(buf, size, pcr, description); + grub_err_t ret; + char *desc = grub_xasprintf("%s %s", kind, description); + if (!desc) + return GRUB_ERR_OUT_OF_MEMORY; + ret = grub_tpm_log_event(buf, size, pcr, description); + grub_free(desc); + return ret; } diff --git a/grub-core/lib/cmdline.c b/grub-core/lib/cmdline.c index ac6e0b797..3791f3aa9 100644 --- a/grub-core/lib/cmdline.c +++ b/grub-core/lib/cmdline.c @@ -105,8 +105,8 @@ int grub_create_loader_cmdline (int argc, char *argv[], char *buf, *buf = 0; - grub_tpm_measure ((void *)orig, grub_strlen (orig), GRUB_CMDLINE_PCR, - "Kernel Commandline"); + grub_tpm_measure ((void *)orig, grub_strlen (orig), GRUB_ASCII_PCR, + "grub_kernel_cmdline", orig); return i; } diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index 32c20bc26..e89d466f6 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -169,7 +169,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), argv[i]); goto fail; } - grub_tpm_measure (ptr, cursize, GRUB_INITRD_PCR, "UEFI Linux initrd"); + grub_tpm_measure (ptr, cursize, GRUB_BINARY_PCR, "grub_linuxefi", "Initrd"); ptr += cursize; grub_memset (ptr, 0, ALIGN_UP_OVERHEAD (cursize, 4)); ptr += ALIGN_UP_OVERHEAD (cursize, 4); @@ -225,7 +225,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } - grub_tpm_measure (kernel, filelen, GRUB_KERNEL_PCR, "UEFI Linux kernel"); + grub_tpm_measure (kernel, filelen, GRUB_BINARY_PCR, "grub_linuxefi", "Kernel"); if (! grub_linuxefi_secure_validate (kernel, filelen)) { diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index 8c96ff9f4..48f3957b6 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -718,7 +718,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } - grub_tpm_measure (kernel, len, GRUB_KERNEL_PCR, "Linux Kernel"); + grub_tpm_measure (kernel, len, GRUB_BINARY_PCR, "grub_linux", "Kernel"); grub_memcpy (&lh, kernel, sizeof (lh)); diff --git a/grub-core/loader/i386/multiboot_mbi.c b/grub-core/loader/i386/multiboot_mbi.c index 5d00aed65..0981a2cfd 100644 --- a/grub-core/loader/i386/multiboot_mbi.c +++ b/grub-core/loader/i386/multiboot_mbi.c @@ -174,7 +174,7 @@ grub_multiboot_load (grub_file_t file, const char *filename) return grub_errno; } - grub_tpm_measure((unsigned char*)buffer, len, GRUB_KERNEL_PCR, filename); + grub_tpm_measure((unsigned char*)buffer, len, GRUB_BINARY_PCR, "grub_multiboot", filename); header = find_header (buffer, len); diff --git a/grub-core/loader/i386/pc/linux.c b/grub-core/loader/i386/pc/linux.c index bd73addba..b9dd80bb3 100644 --- a/grub-core/loader/i386/pc/linux.c +++ b/grub-core/loader/i386/pc/linux.c @@ -161,7 +161,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } - grub_tpm_measure (kernel, len, GRUB_KERNEL_PCR, "BIOS Linux Kernel"); + grub_tpm_measure (kernel, len, GRUB_BINARY_PCR, "grub_linux16", "Kernel"); grub_memcpy (&lh, kernel, sizeof (lh)); kernel_offset = sizeof (lh); diff --git a/grub-core/loader/linux.c b/grub-core/loader/linux.c index 3005c0d19..78c41e334 100644 --- a/grub-core/loader/linux.c +++ b/grub-core/loader/linux.c @@ -289,7 +289,7 @@ grub_initrd_load (struct grub_linux_initrd_context *initrd_ctx, grub_initrd_close (initrd_ctx); return grub_errno; } - grub_tpm_measure (ptr, cursize, GRUB_INITRD_PCR, "Linux Initrd"); + grub_tpm_measure (ptr, cursize, GRUB_BINARY_PCR, "grub_initrd", "Initrd"); ptr += cursize; } if (newc) diff --git a/grub-core/loader/multiboot.c b/grub-core/loader/multiboot.c index f176340b5..11297bb17 100644 --- a/grub-core/loader/multiboot.c +++ b/grub-core/loader/multiboot.c @@ -425,7 +425,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), } grub_file_close (file); - grub_tpm_measure (module, size, GRUB_KERNEL_PCR, argv[0]); + grub_tpm_measure (module, size, GRUB_BINARY_PCR, "grub_multiboot", argv[0]); return GRUB_ERR_NONE; } diff --git a/grub-core/loader/multiboot_mbi2.c b/grub-core/loader/multiboot_mbi2.c index 2466979de..07ded8129 100644 --- a/grub-core/loader/multiboot_mbi2.c +++ b/grub-core/loader/multiboot_mbi2.c @@ -132,7 +132,7 @@ grub_multiboot_load (grub_file_t file, const char *filename) COMPILE_TIME_ASSERT (MULTIBOOT_HEADER_ALIGN % 4 == 0); - grub_tpm_measure ((unsigned char *)mld.buffer, len, GRUB_KERNEL_PCR, filename); + grub_tpm_measure ((unsigned char *)mld.buffer, len, GRUB_BINARY_PCR, "grub_multiboot", filename); header = find_header (mld.buffer, len); diff --git a/grub-core/script/execute.c b/grub-core/script/execute.c index 1ad6cad56..a76684d69 100644 --- a/grub-core/script/execute.c +++ b/grub-core/script/execute.c @@ -957,8 +957,8 @@ grub_script_execute_cmdline (struct grub_script_cmd *cmd) argv.args[i]); } cmdstring[cmdlen-1]= '\0'; - grub_tpm_measure ((unsigned char *)cmdstring, cmdlen, GRUB_COMMAND_PCR, - cmdstring); + grub_tpm_measure ((unsigned char *)cmdstring, cmdlen, GRUB_ASCII_PCR, + "grub_cmd", cmdstring); grub_free(cmdstring); invert = 0; argc = argv.argc - 1; diff --git a/include/grub/tpm.h b/include/grub/tpm.h index 7fc9d77d2..ecb2d09ff 100644 --- a/include/grub/tpm.h +++ b/include/grub/tpm.h @@ -26,11 +26,8 @@ #define TPM_AUTHFAIL (TPM_BASE + 0x1) #define TPM_BADINDEX (TPM_BASE + 0x2) -#define GRUB_TPM_PCR 9 -#define GRUB_KERNEL_PCR 10 -#define GRUB_INITRD_PCR 11 -#define GRUB_CMDLINE_PCR 12 -#define GRUB_COMMAND_PCR 13 +#define GRUB_ASCII_PCR 8 +#define GRUB_BINARY_PCR 9 #define TPM_TAG_RQU_COMMAND 0x00C1 #define TPM_ORD_Extend 0x14 @@ -70,7 +67,7 @@ typedef struct { } GRUB_PACKED ExtendOutgoing; grub_err_t EXPORT_FUNC(grub_tpm_measure) (unsigned char *buf, grub_size_t size, - grub_uint8_t pcr, + grub_uint8_t pcr, const char *kind, const char *description); #if defined (GRUB_MACHINE_EFI) || defined (GRUB_MACHINE_PCBIOS) grub_err_t grub_tpm_execute(PassThroughToTPM_InputParamBlock *inbuf, From 6182d13091276099cb454bb5654450720742e7f3 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 29 Mar 2016 15:36:49 -0700 Subject: [PATCH 44/85] Fix event log prefix We're not passing the prefixed version of the description to the event log. Fix that. --- grub-core/kern/tpm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/kern/tpm.c b/grub-core/kern/tpm.c index cb5a81203..e5e8fced6 100644 --- a/grub-core/kern/tpm.c +++ b/grub-core/kern/tpm.c @@ -13,7 +13,7 @@ grub_tpm_measure (unsigned char *buf, grub_size_t size, grub_uint8_t pcr, char *desc = grub_xasprintf("%s %s", kind, description); if (!desc) return GRUB_ERR_OUT_OF_MEMORY; - ret = grub_tpm_log_event(buf, size, pcr, description); + ret = grub_tpm_log_event(buf, size, pcr, desc); grub_free(desc); return ret; } From 10c77ec6b04978904d8bed6ffe61b3d4c5b76ac6 Mon Sep 17 00:00:00 2001 From: Nick Owens Date: Thu, 14 Apr 2016 14:47:39 -0700 Subject: [PATCH 45/85] grub-core: enable getenv for all efi targets --- grub-core/Makefile.core.def | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index b20ae4dbf..80b3bceae 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -822,8 +822,7 @@ module = { module = { name = getenv; common = commands/efi/getenv.c; - enable = i386_efi; - enable = x86_64_efi; + enable = efi; }; module = { From f441ad6625ac61d14bd3518336bbbcee6f69bb5d Mon Sep 17 00:00:00 2001 From: Nick Owens Date: Wed, 1 Jun 2016 13:55:45 -0700 Subject: [PATCH 46/85] set cmddevice when cmdpath is set --- grub-core/kern/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c index 9cad0c448..682d598f1 100644 --- a/grub-core/kern/main.c +++ b/grub-core/kern/main.c @@ -131,6 +131,9 @@ grub_set_prefix_and_root (void) { char *cmdpath; + grub_env_set ("cmddevice", fwdevice); + grub_env_export ("cmddevice"); + cmdpath = grub_xasprintf ("(%s)%s", fwdevice, fwpath ? : ""); if (cmdpath) { From 94731a80d73d44b4d230f2fc15ecbb5b784e7a5f Mon Sep 17 00:00:00 2001 From: Nick Owens Date: Fri, 8 Jul 2016 15:39:02 -0700 Subject: [PATCH 47/85] net: add client arch and fix user class/terminator send client arch in bootp requests, for now BIOS and x64/aarch64 EFI is supported. fix a bug introduced in 4d5d7be005bb5c15c07472461b528dea65a58cc6 where user class was encoded improperly, although this didn't seem to have any detrimental effects. properly insert an option terminator. --- grub-core/net/bootp.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index 3de40d7fa..0da8e2499 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -25,11 +25,21 @@ #include #include +#if !defined(GRUB_MACHINE_EFI) && (defined(__i386__) || defined(__x86_64__)) +#define GRUB_NET_BOOTP_ARCH 0x0000 +#elif defined(GRUB_MACHINE_EFI) && defined(__x86_64__) +#define GRUB_NET_BOOTP_ARCH 0x0007 +#elif defined(GRUB_MACHINE_EFI) && defined(__aarch64__) +#define GRUB_NET_BOOTP_ARCH 0x000B +#else +#error "unknown bootp architecture" +#endif + static grub_uint8_t dhcp_option_header[] = {GRUB_NET_BOOTP_RFC1048_MAGIC_0, GRUB_NET_BOOTP_RFC1048_MAGIC_1, GRUB_NET_BOOTP_RFC1048_MAGIC_2, GRUB_NET_BOOTP_RFC1048_MAGIC_3}; -static grub_uint8_t grub_userclass[] = {0x4D, 0x05, 'G', 'R', 'U', 'B', '2'}; +static grub_uint8_t grub_userclass[] = {0x4D, 0x06, 0x05, 'G', 'R', 'U', 'B', '2'}; static grub_uint8_t grub_dhcpdiscover[] = {0x35, 0x01, 0x01}; static grub_uint8_t grub_dhcptime[] = {0x33, 0x04, 0x00, 0x00, 0x0e, 0x10}; @@ -557,6 +567,16 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)), offset += sizeof(grub_userclass); grub_memcpy (offset, grub_dhcptime, sizeof(grub_dhcptime)); + /* insert Client System Architecture (option 93) */ + offset += sizeof(grub_dhcptime); + offset[0] = 93; + offset[1] = 2; + offset[2] = (GRUB_NET_BOOTP_ARCH >> 8); + offset[3] = (GRUB_NET_BOOTP_ARCH & 0xFF); + + /* option terminator */ + offset[4] = 255; + grub_netbuff_push (nb, sizeof (*udph)); udph = (struct udphdr *) nb->data; From 6d4ea47541db4e0a1eab81de8843a491973e6b40 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Mon, 25 Jul 2016 14:59:29 -0700 Subject: [PATCH 48/85] gpt: do not use disk sizes GRUB will reject as invalid later on GRUB assumes that no disk is ever larger than 1EiB and rejects reads/writes to such locations. Unfortunately this is not conveyed in the usual way with the special GRUB_DISK_SIZE_UNKNOWN value. --- grub-core/lib/gpt.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index e162bafd3..3e17f2771 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -143,6 +143,28 @@ grub_gpt_size_to_sectors (grub_gpt_t gpt, grub_size_t size) return sectors; } +/* Copied from grub-core/kern/disk_common.c grub_disk_adjust_range so we can + * avoid attempting to use disk->total_sectors when GRUB won't let us. + * TODO: Why is disk->total_sectors not set to GRUB_DISK_SIZE_UNKNOWN? */ +static int +grub_gpt_disk_size_valid (grub_disk_t disk) +{ + grub_disk_addr_t total_sectors; + + /* Transform total_sectors to number of 512B blocks. */ + total_sectors = disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS); + + /* Some drivers have problems with disks above reasonable. + Treat unknown as 1EiB disk. While on it, clamp the size to 1EiB. + Just one condition is enough since GRUB_DISK_UNKNOWN_SIZE << ls is always + above 9EiB. + */ + if (total_sectors > (1ULL << 51)) + return 0; + + return 1; +} + static void grub_gpt_lecrc32 (grub_uint32_t *crc, const void *data, grub_size_t len) { @@ -242,7 +264,7 @@ grub_gpt_read_backup (grub_disk_t disk, grub_gpt_t gpt) grub_disk_addr_t addr; /* Assumes gpt->log_sector_size == disk->log_sector_size */ - if (disk->total_sectors != GRUB_DISK_SIZE_UNKNOWN) + if (grub_gpt_disk_size_valid(disk)) sector = disk->total_sectors - 1; else if (gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID) sector = grub_le_to_cpu64 (gpt->primary.alternate_lba); @@ -394,7 +416,7 @@ grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt) return grub_error (GRUB_ERR_BUG, "No valid GPT header"); /* Relocate backup to end if disk whenever possible. */ - if (disk->total_sectors != GRUB_DISK_SIZE_UNKNOWN) + if (grub_gpt_disk_size_valid(disk)) backup_header = disk->total_sectors - 1; backup_entries = backup_header - From 66ec5893d79c9e496e2168526ab354805fe37397 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Wed, 10 Aug 2016 17:02:51 -0700 Subject: [PATCH 49/85] biosdisk: add verbose debug logging --- grub-core/disk/i386/pc/biosdisk.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/grub-core/disk/i386/pc/biosdisk.c b/grub-core/disk/i386/pc/biosdisk.c index f0aadd111..0b5826805 100644 --- a/grub-core/disk/i386/pc/biosdisk.c +++ b/grub-core/disk/i386/pc/biosdisk.c @@ -278,10 +278,14 @@ grub_biosdisk_call_hook (grub_disk_dev_iterate_hook_t hook, void *hook_data, char name[10]; if (cd_drive && drive == cd_drive) - return hook ("cd", hook_data); + { + grub_dprintf ("biosdisk", "iterating cd\n"); + return hook ("cd", hook_data); + } grub_snprintf (name, sizeof (name), (drive & 0x80) ? "hd%d" : "fd%d", drive & (~0x80)); + grub_dprintf ("biosdisk", "iterating %s\n", name); return hook (name, hook_data); } @@ -301,7 +305,7 @@ grub_biosdisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, if (grub_biosdisk_rw_standard (0x02, drive, 0, 0, 1, 1, GRUB_MEMORY_MACHINE_SCRATCH_SEG) != 0) { - grub_dprintf ("disk", "Read error when probing drive 0x%2x\n", drive); + grub_dprintf ("biosdisk", "Read error when probing drive 0x%2x\n", drive); break; } @@ -336,6 +340,8 @@ grub_biosdisk_open (const char *name, grub_disk_t disk) int drive; struct grub_biosdisk_data *data; + grub_dprintf ("biosdisk", "opening %s\n", name); + drive = grub_biosdisk_get_drive (name); if (drive < 0) return grub_errno; @@ -393,6 +399,11 @@ grub_biosdisk_open (const char *name, grub_disk_t disk) (1 << disk->log_sector_size) < drp->bytes_per_sector; disk->log_sector_size++); } + + grub_dprintf ("biosdisk", + "LBA total = 0x%llx block size = 0x%lx\n", + (unsigned long long) total_sectors, + 1L << disk->log_sector_size); } } } @@ -428,6 +439,9 @@ grub_biosdisk_open (const char *name, grub_disk_t disk) if (! total_sectors) total_sectors = ((grub_uint64_t) data->cylinders) * data->heads * data->sectors; + + grub_dprintf ("biosdisk", "C/H/S %lu/%lu/%lu\n", + data->cylinders, data->heads, data->sectors); } disk->total_sectors = total_sectors; @@ -440,12 +454,15 @@ grub_biosdisk_open (const char *name, grub_disk_t disk) disk->data = data; + grub_dprintf ("biosdisk", "opening %s succeeded\n", name); + return GRUB_ERR_NONE; } static void grub_biosdisk_close (grub_disk_t disk) { + grub_dprintf ("biosdisk", "closing %s\n", disk->name); grub_free (disk->data); } @@ -577,6 +594,9 @@ static grub_err_t grub_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector, grub_size_t size, char *buf) { + grub_dprintf ("biosdisk", "reading 0x%lx sectors at 0x%llx from %s\n", + (unsigned long) size, (unsigned long long) sector, disk->name); + while (size) { grub_size_t len; @@ -607,6 +627,9 @@ grub_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector, { struct grub_biosdisk_data *data = disk->data; + grub_dprintf ("biosdisk", "writing 0x%lx sectors at 0x%llx to %s\n", + (unsigned long) size, (unsigned long long) sector, disk->name); + if (data->flags & GRUB_BIOSDISK_FLAG_CDROM) return grub_error (GRUB_ERR_IO, N_("cannot write to CD-ROM")); From 99959fa2fb8bfafadc1fa5aec773a8d605a1df4e Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Wed, 10 Aug 2016 18:26:03 -0700 Subject: [PATCH 50/85] gpt: add verbose debug logging --- grub-core/lib/gpt.c | 117 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 109 insertions(+), 8 deletions(-) diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index 3e17f2771..c2821b563 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -207,6 +207,18 @@ grub_gpt_pmbr_check (struct grub_msdos_partition_mbr *mbr) return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid protective MBR"); } +static grub_uint64_t +grub_gpt_entries_sectors (struct grub_gpt_header *gpt, + unsigned int log_sector_size) +{ + grub_uint64_t sector_bytes, entries_bytes; + + sector_bytes = 1ULL << log_sector_size; + entries_bytes = (grub_uint64_t) grub_le_to_cpu32 (gpt->maxpart) * + (grub_uint64_t) grub_le_to_cpu32 (gpt->partentry_size); + return grub_divmod64(entries_bytes + sector_bytes - 1, sector_bytes, NULL); +} + grub_err_t grub_gpt_header_check (struct grub_gpt_header *gpt, unsigned int log_sector_size) @@ -236,6 +248,64 @@ grub_gpt_header_check (struct grub_gpt_header *gpt, return GRUB_ERR_NONE; } +static grub_err_t +grub_gpt_check_primary (grub_gpt_t gpt) +{ + grub_uint64_t backup, primary, entries, entries_len, start, end; + + primary = grub_le_to_cpu64 (gpt->primary.header_lba); + backup = grub_le_to_cpu64 (gpt->primary.alternate_lba); + entries = grub_le_to_cpu64 (gpt->primary.partitions); + entries_len = grub_gpt_entries_sectors(&gpt->primary, gpt->log_sector_size); + start = grub_le_to_cpu64 (gpt->primary.start); + end = grub_le_to_cpu64 (gpt->primary.end); + + grub_dprintf ("gpt", "Primary GPT layout:\n" + "primary header = 0x%llx backup header = 0x%llx\n" + "entries location = 0x%llx length = 0x%llx\n" + "first usable = 0x%llx last usable = 0x%llx\n", + (unsigned long long) primary, + (unsigned long long) backup, + (unsigned long long) entries, + (unsigned long long) entries_len, + (unsigned long long) start, + (unsigned long long) end); + + if (grub_gpt_header_check (&gpt->primary, gpt->log_sector_size)) + return grub_errno; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_gpt_check_backup (grub_gpt_t gpt) +{ + grub_uint64_t backup, primary, entries, entries_len, start, end; + + backup = grub_le_to_cpu64 (gpt->backup.header_lba); + primary = grub_le_to_cpu64 (gpt->backup.alternate_lba); + entries = grub_le_to_cpu64 (gpt->backup.partitions); + entries_len = grub_gpt_entries_sectors(&gpt->backup, gpt->log_sector_size); + start = grub_le_to_cpu64 (gpt->backup.start); + end = grub_le_to_cpu64 (gpt->backup.end); + + grub_dprintf ("gpt", "Backup GPT layout:\n" + "primary header = 0x%llx backup header = 0x%llx\n" + "entries location = 0x%llx length = 0x%llx\n" + "first usable = 0x%llx last usable = 0x%llx\n", + (unsigned long long) primary, + (unsigned long long) backup, + (unsigned long long) entries, + (unsigned long long) entries_len, + (unsigned long long) start, + (unsigned long long) end); + + if (grub_gpt_header_check (&gpt->backup, gpt->log_sector_size)) + return grub_errno; + + return GRUB_ERR_NONE; +} + static grub_err_t grub_gpt_read_primary (grub_disk_t disk, grub_gpt_t gpt) { @@ -246,11 +316,13 @@ grub_gpt_read_primary (grub_disk_t disk, grub_gpt_t gpt) * but eventually this code should match the existing behavior. */ gpt->log_sector_size = disk->log_sector_size; + grub_dprintf ("gpt", "reading primary GPT from sector 0x1\n"); + addr = grub_gpt_sector_to_addr (gpt, 1); if (grub_disk_read (disk, addr, 0, sizeof (gpt->primary), &gpt->primary)) return grub_errno; - if (grub_gpt_header_check (&gpt->primary, gpt->log_sector_size)) + if (grub_gpt_check_primary (gpt)) return grub_errno; gpt->status |= GRUB_GPT_PRIMARY_HEADER_VALID; @@ -272,11 +344,14 @@ grub_gpt_read_backup (grub_disk_t disk, grub_gpt_t gpt) return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "Unable to locate backup GPT"); + grub_dprintf ("gpt", "reading backup GPT from sector 0x%llx\n", + (unsigned long long) sector); + addr = grub_gpt_sector_to_addr (gpt, sector); if (grub_disk_read (disk, addr, 0, sizeof (gpt->backup), &gpt->backup)) return grub_errno; - if (grub_gpt_header_check (&gpt->backup, gpt->log_sector_size)) + if (grub_gpt_check_backup (gpt)) return grub_errno; gpt->status |= GRUB_GPT_BACKUP_HEADER_VALID; @@ -289,6 +364,7 @@ grub_gpt_read_entries (grub_disk_t disk, grub_gpt_t gpt, { struct grub_gpt_partentry *entries = NULL; grub_uint32_t count, size, crc; + grub_uint64_t sector; grub_disk_addr_t addr; grub_size_t entries_size; @@ -310,7 +386,12 @@ grub_gpt_read_entries (grub_disk_t disk, grub_gpt_t gpt, if (!entries) goto fail; - addr = grub_gpt_sector_to_addr (gpt, grub_le_to_cpu64 (header->partitions)); + sector = grub_le_to_cpu64 (header->partitions); + grub_dprintf ("gpt", "reading GPT %lu entries from sector 0x%llx\n", + (unsigned long) count, + (unsigned long long) sector); + + addr = grub_gpt_sector_to_addr (gpt, sector); if (grub_disk_read (disk, addr, 0, entries_size, entries)) goto fail; @@ -336,6 +417,8 @@ grub_gpt_read (grub_disk_t disk) { grub_gpt_t gpt; + grub_dprintf ("gpt", "reading GPT from %s\n", disk->name); + gpt = grub_zalloc (sizeof (*gpt)); if (!gpt) goto fail; @@ -369,12 +452,18 @@ grub_gpt_read (grub_disk_t disk) /* Similarly, favor the value or error from the primary table. */ if (gpt->status & GRUB_GPT_BACKUP_HEADER_VALID && !grub_gpt_read_entries (disk, gpt, &gpt->backup)) - gpt->status |= GRUB_GPT_BACKUP_ENTRIES_VALID; + { + grub_dprintf ("gpt", "read valid backup GPT from %s\n", disk->name); + gpt->status |= GRUB_GPT_BACKUP_ENTRIES_VALID; + } grub_errno = GRUB_ERR_NONE; if (gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID && !grub_gpt_read_entries (disk, gpt, &gpt->primary)) - gpt->status |= GRUB_GPT_PRIMARY_ENTRIES_VALID; + { + grub_dprintf ("gpt", "read valid primary GPT from %s\n", disk->name); + gpt->status |= GRUB_GPT_PRIMARY_ENTRIES_VALID; + } if (gpt->status & GRUB_GPT_PRIMARY_ENTRIES_VALID || gpt->status & GRUB_GPT_BACKUP_ENTRIES_VALID) @@ -394,21 +483,25 @@ grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt) { grub_uint64_t backup_header, backup_entries; + grub_dprintf ("gpt", "repairing GPT for %s\n", disk->name); + if (disk->log_sector_size != gpt->log_sector_size) return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "GPT sector size must match disk sector size"); if (!(gpt->status & GRUB_GPT_PRIMARY_ENTRIES_VALID || - gpt->status & GRUB_GPT_BACKUP_ENTRIES_VALID)) + gpt->status & GRUB_GPT_BACKUP_ENTRIES_VALID)) return grub_error (GRUB_ERR_BUG, "No valid GPT entries"); if (gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID) { + grub_dprintf ("gpt", "primary GPT header is valid\n"); backup_header = grub_le_to_cpu64 (gpt->primary.alternate_lba); grub_memcpy (&gpt->backup, &gpt->primary, sizeof (gpt->backup)); } else if (gpt->status & GRUB_GPT_BACKUP_HEADER_VALID) { + grub_dprintf ("gpt", "backup GPT header is valid\n"); backup_header = grub_le_to_cpu64 (gpt->backup.header_lba); grub_memcpy (&gpt->primary, &gpt->backup, sizeof (gpt->primary)); } @@ -418,9 +511,13 @@ grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt) /* Relocate backup to end if disk whenever possible. */ if (grub_gpt_disk_size_valid(disk)) backup_header = disk->total_sectors - 1; + grub_dprintf ("gpt", "backup GPT header will be located at 0x%llx\n", + (unsigned long long) backup_header); backup_entries = backup_header - grub_gpt_size_to_sectors (gpt, gpt->entries_size); + grub_dprintf ("gpt", "backup GPT entries will be located at 0x%llx\n", + (unsigned long long) backup_entries); /* Update/fixup header and partition table locations. */ gpt->primary.header_lba = grub_cpu_to_le64_compile_time (1); @@ -435,13 +532,15 @@ grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt) return grub_errno; /* Sanity check. */ - if (grub_gpt_header_check (&gpt->primary, gpt->log_sector_size)) + if (grub_gpt_check_primary (gpt)) return grub_error (GRUB_ERR_BUG, "Generated invalid GPT primary header"); - if (grub_gpt_header_check (&gpt->backup, gpt->log_sector_size)) + if (grub_gpt_check_backup (gpt)) return grub_error (GRUB_ERR_BUG, "Generated invalid GPT backup header"); gpt->status |= GRUB_GPT_BOTH_VALID; + grub_dprintf ("gpt", "repairing GPT for %s successful\n", disk->name); + return GRUB_ERR_NONE; } @@ -497,9 +596,11 @@ grub_gpt_write (grub_disk_t disk, grub_gpt_t gpt) if (!(gpt->status & GRUB_GPT_BOTH_VALID)) return grub_error (GRUB_ERR_BAD_PART_TABLE, "Invalid GPT data"); + grub_dprintf ("gpt", "writing primary GPT to %s\n", disk->name); if (grub_gpt_write_table (disk, gpt, &gpt->primary)) return grub_errno; + grub_dprintf ("gpt", "writing backup GPT to %s\n", disk->name); if (grub_gpt_write_table (disk, gpt, &gpt->backup)) return grub_errno; From f6b89ec3156a549999a13b3d15e9a67b4a9bf824 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Wed, 10 Aug 2016 18:26:03 -0700 Subject: [PATCH 51/85] gpt: improve validation of GPT headers Adds basic validation of all the disk locations in the headers, reducing the chance of corrupting weird locations on disk. --- grub-core/lib/gpt.c | 48 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index c2821b563..f83fe29ac 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -224,6 +224,7 @@ grub_gpt_header_check (struct grub_gpt_header *gpt, unsigned int log_sector_size) { grub_uint32_t crc = 0, size; + grub_uint64_t start, end; if (grub_memcmp (gpt->magic, grub_gpt_magic, sizeof (grub_gpt_magic)) != 0) return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid GPT signature"); @@ -245,9 +246,35 @@ grub_gpt_header_check (struct grub_gpt_header *gpt, if (size < 128 || size % 128) return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid GPT entry size"); + /* And of course there better be some space for partitions! */ + start = grub_le_to_cpu64 (gpt->start); + end = grub_le_to_cpu64 (gpt->end); + if (start > end) + return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid usable sectors"); + return GRUB_ERR_NONE; } +static int +grub_gpt_headers_equal (grub_gpt_t gpt) +{ + /* Assume headers passed grub_gpt_header_check so skip magic and version. + * Individual fields must be checked instead of just using memcmp because + * crc32, header, alternate, and partitions will all normally differ. */ + + if (gpt->primary.headersize != gpt->backup.headersize || + gpt->primary.header_lba != gpt->backup.alternate_lba || + gpt->primary.start != gpt->backup.start || + gpt->primary.end != gpt->backup.end || + gpt->primary.maxpart != gpt->backup.maxpart || + gpt->primary.partentry_size != gpt->backup.partentry_size || + gpt->primary.partentry_crc32 != gpt->backup.partentry_crc32) + return 0; + + return grub_memcmp(&gpt->primary.guid, &gpt->backup.guid, + sizeof(grub_gpt_guid_t)) == 0; +} + static grub_err_t grub_gpt_check_primary (grub_gpt_t gpt) { @@ -273,6 +300,12 @@ grub_gpt_check_primary (grub_gpt_t gpt) if (grub_gpt_header_check (&gpt->primary, gpt->log_sector_size)) return grub_errno; + if (primary != 1) + return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid primary GPT LBA"); + if (entries <= 1 || entries+entries_len > start) + return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid entries location"); + if (backup <= end) + return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid backup GPT LBA"); return GRUB_ERR_NONE; } @@ -302,6 +335,12 @@ grub_gpt_check_backup (grub_gpt_t gpt) if (grub_gpt_header_check (&gpt->backup, gpt->log_sector_size)) return grub_errno; + if (primary != 1) + return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid primary GPT LBA"); + if (entries <= end || entries+entries_len > backup) + return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid entries location"); + if (backup <= end) + return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid backup GPT LBA"); return GRUB_ERR_NONE; } @@ -354,6 +393,15 @@ grub_gpt_read_backup (grub_disk_t disk, grub_gpt_t gpt) if (grub_gpt_check_backup (gpt)) return grub_errno; + /* Ensure the backup header thinks it is located where we found it. */ + if (grub_le_to_cpu64 (gpt->backup.header_lba) != sector) + return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid backup GPT LBA"); + + /* If both primary and backup are valid but differ prefer the primary. */ + if ((gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID) && + !grub_gpt_headers_equal(gpt)) + return grub_error (GRUB_ERR_BAD_PART_TABLE, "backup GPT of of sync"); + gpt->status |= GRUB_GPT_BACKUP_HEADER_VALID; return GRUB_ERR_NONE; } From fa18d3a292bdcd61012d549c61e25d557481a05e Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Thu, 11 Aug 2016 15:02:21 -0700 Subject: [PATCH 52/85] gpt: refuse to write to sector 0 --- grub-core/lib/gpt.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index f83fe29ac..b7449911a 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -626,10 +626,17 @@ grub_gpt_write_table (grub_disk_t disk, grub_gpt_t gpt, sizeof (*header)); addr = grub_gpt_sector_to_addr (gpt, grub_le_to_cpu64 (header->header_lba)); + if (addr == 0) + return grub_error (GRUB_ERR_BUG, + "Refusing to write GPT header to address 0x0"); if (grub_disk_write (disk, addr, 0, sizeof (*header), header)) return grub_errno; addr = grub_gpt_sector_to_addr (gpt, grub_le_to_cpu64 (header->partitions)); + if (addr < 2) + return grub_error (GRUB_ERR_BUG, + "Refusing to write GPT entries to address 0x%llx", + (unsigned long long) addr); if (grub_disk_write (disk, addr, 0, gpt->entries_size, gpt->entries)) return grub_errno; From 2cb9b7fcaa0521712d07e517751f440729d1f730 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Sat, 20 Aug 2016 16:41:00 -0700 Subject: [PATCH 53/85] fwconfig: fix unused argument warning --- grub-core/commands/fwconfig.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/grub-core/commands/fwconfig.c b/grub-core/commands/fwconfig.c index 289d167fd..3abe59f2b 100644 --- a/grub-core/commands/fwconfig.c +++ b/grub-core/commands/fwconfig.c @@ -50,7 +50,8 @@ static const struct grub_arg_option options[] = }; static grub_err_t -grub_cmd_fwconfig (grub_extcmd_context_t ctxt, int argc, char **argv) +grub_cmd_fwconfig (grub_extcmd_context_t ctxt __attribute__ ((unused)), + int argc, char **argv) { grub_uint32_t i, j, value = 0; struct grub_qemu_fwcfgfile file; From b1ef48849c8dc12756793567520dfd3654539a27 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Sat, 20 Aug 2016 17:42:12 -0700 Subject: [PATCH 54/85] gpt: properly detect and repair invalid tables GPT_BOTH_VALID is 4 bits so simple a boolean check is not sufficient. This broken condition allowed gptprio to trust bogus disk locations in headers that were marked invalid causing arbitrary disk corruption. --- grub-core/commands/gptprio.c | 2 +- grub-core/lib/gpt.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/grub-core/commands/gptprio.c b/grub-core/commands/gptprio.c index ce5840b4e..6b61bb56d 100644 --- a/grub-core/commands/gptprio.c +++ b/grub-core/commands/gptprio.c @@ -91,7 +91,7 @@ grub_find_next (const char *disk_name, if (!gpt) goto done; - if (!(gpt->status & GRUB_GPT_BOTH_VALID)) + if ((gpt->status & GRUB_GPT_BOTH_VALID) != GRUB_GPT_BOTH_VALID) if (grub_gpt_repair (dev->disk, gpt)) goto done; diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index b7449911a..0daf3f8de 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -648,7 +648,7 @@ grub_gpt_write (grub_disk_t disk, grub_gpt_t gpt) { /* TODO: update/repair protective MBRs too. */ - if (!(gpt->status & GRUB_GPT_BOTH_VALID)) + if ((gpt->status & GRUB_GPT_BOTH_VALID) != GRUB_GPT_BOTH_VALID) return grub_error (GRUB_ERR_BAD_PART_TABLE, "Invalid GPT data"); grub_dprintf ("gpt", "writing primary GPT to %s\n", disk->name); From 297b31785090ef78ca37486fed3b77befe6500d9 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Sun, 21 Aug 2016 18:24:58 -0700 Subject: [PATCH 55/85] tpm: fix warnings when compiling for platforms other than pc and efi --- include/grub/tpm.h | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/include/grub/tpm.h b/include/grub/tpm.h index ecb2d09ff..972a5edc8 100644 --- a/include/grub/tpm.h +++ b/include/grub/tpm.h @@ -75,12 +75,17 @@ grub_err_t grub_tpm_execute(PassThroughToTPM_InputParamBlock *inbuf, grub_err_t grub_tpm_log_event(unsigned char *buf, grub_size_t size, grub_uint8_t pcr, const char *description); #else -static inline grub_err_t grub_tpm_execute(PassThroughToTPM_InputParamBlock *inbuf, - PassThroughToTPM_OutputParamBlock *outbuf) { return 0; }; -static inline grub_err_t grub_tpm_log_event(unsigned char *buf, - grub_size_t size, - grub_uint8_t pcr, - const char *description) +static inline grub_err_t grub_tpm_execute( + PassThroughToTPM_InputParamBlock *inbuf __attribute__ ((unused)), + PassThroughToTPM_OutputParamBlock *outbuf __attribute__ ((unused))) +{ + return 0; +}; +static inline grub_err_t grub_tpm_log_event( + unsigned char *buf __attribute__ ((unused)), + grub_size_t size __attribute__ ((unused)), + grub_uint8_t pcr __attribute__ ((unused)), + const char *description __attribute__ ((unused))) { return 0; }; From 9af98c2bfd31a73b899268e67f01bca785681d52 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Mon, 22 Aug 2016 16:44:30 -0700 Subject: [PATCH 56/85] gptrepair_test: fix typo in cleanup trap --- tests/gptrepair_test.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/gptrepair_test.in b/tests/gptrepair_test.in index 80b2de633..805dc171a 100644 --- a/tests/gptrepair_test.in +++ b/tests/gptrepair_test.in @@ -53,7 +53,7 @@ case "${grub_modinfo_target_cpu}-${grub_modinfo_platform}" in esac img1="`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"`" || exit 1 img2="`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"`" || exit 1 -trap "rm -f '${img1}' '${ing2}'" EXIT +trap "rm -f '${img1}' '${img2}'" EXIT create_disk_image () { size=$1 From d457364d1d811ad262519cf6dde3d098caf7c778 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Mon, 22 Aug 2016 16:45:10 -0700 Subject: [PATCH 57/85] gptprio_test: check GPT is repaired when appropriate --- tests/gptprio_test.in | 63 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/tests/gptprio_test.in b/tests/gptprio_test.in index f4aea0dc9..c5cf0f3b7 100644 --- a/tests/gptprio_test.in +++ b/tests/gptprio_test.in @@ -66,8 +66,9 @@ prio_uuid[3]="1aa5a658-5b02-414d-9b71-f7e6c151f0cd" prio_uuid[4]="8aa0240d-98af-42b0-b32a-ccbe0572d62b" create_disk_image () { + size=$1 rm -f "${img1}" - dd if=/dev/zero of="${img1}" bs=512 count=1 seek=100 status=none + dd if=/dev/zero of="${img1}" bs=512 count=1 seek=$((size - 1)) status=none ${sgdisk} \ -n 1:0:+1 -c 1:ESP -t 1:ef00 \ -n 2:0:+1 -c 2:A -t 2:"${prio_type}" -u 2:"${prio_uuid[2]}" \ @@ -76,6 +77,35 @@ create_disk_image () { "${img1}" >/dev/null } +wipe_disk_area () { + sector=$1 + size=$2 + dd if=/dev/zero of="${img1}" bs=512 count=${size} seek=${sector} conv=notrunc status=none +} + +is_zero () { + sector=$1 + size=$2 + cmp -s -i $((sector * 512)) -n $((size * 512)) /dev/zero "${img1}" +} + +check_is_zero () { + sector=$1 + size=$2 + if ! is_zero "$@"; then + echo "$size sector(s) starting at $sector should be all zero" + exit 1 + fi +} + +check_not_zero () { + sector=$1 + size=$2 + if is_zero "$@"; then + echo "$size sector(s) starting at $sector should not be all zero" + exit 1 + fi +} fmt_prio () { priority=$(( ( $1 & 15 ) << 48 )) @@ -93,10 +123,10 @@ set_prio () { check_prio () { part="$1" expect=$(fmt_prio $2 $3 $4) - result=$(LANG=C ${sgdisk} -i "${part}" "${img1}" \ + result=$(LANG=C ${sgdisk} -i "${part}" "${img1}" 2>&1 \ | awk '/^Attribute flags: / {print $3}') if [[ "${expect}" != "${result}" ]]; then - echo "Partition ${part} has attributes ${result}, not ${expect}" >&2 + echo "Partition ${part} has attributes ${result:-??}, not ${expect}" exit 1 fi } @@ -133,6 +163,33 @@ create_disk_image 100 set_prio 2 3 2 1 check_prio 2 3 2 1 +# Check gptprio works without modifying the disk when no update is required. +# Leaves any existing corruption as is, repairing in the OS is better. +create_disk_image 100 +set_prio 2 1 0 1 +wipe_disk_area 99 1 +check_next 2 1 0 1 +check_is_zero 99 1 + +create_disk_image 100 +set_prio 2 1 0 1 +wipe_disk_area 1 1 +check_next 2 1 0 1 +check_is_zero 1 1 + +# When writes do need to be made go ahead and perform the repair. +create_disk_image 100 +set_prio 2 1 1 0 +wipe_disk_area 99 1 +check_next 2 1 0 0 +check_not_zero 99 1 + +create_disk_image 100 +set_prio 2 1 1 0 +wipe_disk_area 1 1 +check_next 2 1 0 0 +check_not_zero 1 1 + # Try two partitions before falling before falling back to a third create_disk_image 100 set_prio 2 3 3 0 From 3a3e45823dd677b428ceb40d8963676aff63f8d2 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Mon, 22 Aug 2016 18:30:56 -0700 Subject: [PATCH 58/85] fix checking alternate_lba --- grub-core/lib/gpt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index 0daf3f8de..39c5f50ba 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -264,6 +264,7 @@ grub_gpt_headers_equal (grub_gpt_t gpt) if (gpt->primary.headersize != gpt->backup.headersize || gpt->primary.header_lba != gpt->backup.alternate_lba || + gpt->primary.alternate_lba != gpt->backup.header_lba || gpt->primary.start != gpt->backup.start || gpt->primary.end != gpt->backup.end || gpt->primary.maxpart != gpt->backup.maxpart || From 72b178950d313d567dfdf11f403199370d81a9f3 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Wed, 24 Aug 2016 16:14:20 -0700 Subject: [PATCH 59/85] gpt: fix partition table indexing and validation Portions of the code attempted to handle the fact that GPT entries on disk may be larger than the currently defined struct while others assumed the data could be indexed by the struct size directly. This never came up because no utility uses a size larger than 128 bytes but for the sake of safety we need to do this by the spec. --- grub-core/commands/gptprio.c | 6 +- grub-core/lib/gpt.c | 51 +++++++++++++-- include/grub/gpt_partition.h | 11 +++- tests/gpt_unit_test.c | 120 +++++++++++++++++++++++++++++++++++ 4 files changed, 176 insertions(+), 12 deletions(-) diff --git a/grub-core/commands/gptprio.c b/grub-core/commands/gptprio.c index 6b61bb56d..548925a08 100644 --- a/grub-core/commands/gptprio.c +++ b/grub-core/commands/gptprio.c @@ -78,7 +78,7 @@ grub_find_next (const char *disk_name, const grub_gpt_part_type_t *part_type, char **part_name, char **part_guid) { - struct grub_gpt_partentry *part_found = NULL; + struct grub_gpt_partentry *part, *part_found = NULL; grub_device_t dev = NULL; grub_gpt_t gpt = NULL; grub_uint32_t i, part_index; @@ -95,10 +95,8 @@ grub_find_next (const char *disk_name, if (grub_gpt_repair (dev->disk, gpt)) goto done; - for (i = 0; i < grub_le_to_cpu32 (gpt->primary.maxpart); i++) + for (i = 0; (part = grub_gpt_get_partentry (gpt, i)) != NULL; i++) { - struct grub_gpt_partentry *part = &gpt->entries[i]; - if (grub_memcmp (part_type, &part->type, sizeof (*part_type)) == 0) { unsigned int priority, tries_left, successful, old_priority = 0; diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index 39c5f50ba..48cc53de4 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -207,6 +207,13 @@ grub_gpt_pmbr_check (struct grub_msdos_partition_mbr *mbr) return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid protective MBR"); } +static grub_uint64_t +grub_gpt_entries_size (struct grub_gpt_header *gpt) +{ + return (grub_uint64_t) grub_le_to_cpu32 (gpt->maxpart) * + (grub_uint64_t) grub_le_to_cpu32 (gpt->partentry_size); +} + static grub_uint64_t grub_gpt_entries_sectors (struct grub_gpt_header *gpt, unsigned int log_sector_size) @@ -214,11 +221,16 @@ grub_gpt_entries_sectors (struct grub_gpt_header *gpt, grub_uint64_t sector_bytes, entries_bytes; sector_bytes = 1ULL << log_sector_size; - entries_bytes = (grub_uint64_t) grub_le_to_cpu32 (gpt->maxpart) * - (grub_uint64_t) grub_le_to_cpu32 (gpt->partentry_size); + entries_bytes = grub_gpt_entries_size (gpt); return grub_divmod64(entries_bytes + sector_bytes - 1, sector_bytes, NULL); } +static int +is_pow2 (grub_uint32_t n) +{ + return (n & (n - 1)) == 0; +} + grub_err_t grub_gpt_header_check (struct grub_gpt_header *gpt, unsigned int log_sector_size) @@ -236,16 +248,23 @@ grub_gpt_header_check (struct grub_gpt_header *gpt, if (gpt->crc32 != crc) return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid GPT header crc32"); - /* The header size must be between 92 and the sector size. */ + /* The header size "must be greater than or equal to 92 and must be less + * than or equal to the logical block size." */ size = grub_le_to_cpu32 (gpt->headersize); if (size < 92U || size > (1U << log_sector_size)) return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid GPT header size"); - /* The partition entry size must be a multiple of 128. */ + /* The partition entry size must be "a value of 128*(2^n) where n is an + * integer greater than or equal to zero (e.g., 128, 256, 512, etc.)." */ size = grub_le_to_cpu32 (gpt->partentry_size); - if (size < 128 || size % 128) + if (size < 128U || size % 128U || !is_pow2 (size / 128U)) return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid GPT entry size"); + /* The minimum entries table size is specified in terms of bytes, + * regardless of how large the individual entry size is. */ + if (grub_gpt_entries_size (gpt) < GRUB_GPT_DEFAULT_ENTRIES_SIZE) + return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid GPT entry table size"); + /* And of course there better be some space for partitions! */ start = grub_le_to_cpu64 (gpt->start); end = grub_le_to_cpu64 (gpt->end); @@ -411,7 +430,7 @@ static grub_err_t grub_gpt_read_entries (grub_disk_t disk, grub_gpt_t gpt, struct grub_gpt_header *header) { - struct grub_gpt_partentry *entries = NULL; + void *entries = NULL; grub_uint32_t count, size, crc; grub_uint64_t sector; grub_disk_addr_t addr; @@ -527,6 +546,26 @@ fail: return NULL; } +struct grub_gpt_partentry * +grub_gpt_get_partentry (grub_gpt_t gpt, grub_uint32_t n) +{ + struct grub_gpt_header *header; + grub_size_t offset; + + if (gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID) + header = &gpt->primary; + else if (gpt->status & GRUB_GPT_BACKUP_HEADER_VALID) + header = &gpt->backup; + else + return NULL; + + if (n >= grub_le_to_cpu32 (header->maxpart)) + return NULL; + + offset = (grub_size_t) grub_le_to_cpu32 (header->partentry_size) * n; + return (struct grub_gpt_partentry *) ((char *) gpt->entries + offset); +} + grub_err_t grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt) { diff --git a/include/grub/gpt_partition.h b/include/grub/gpt_partition.h index 4a6ed25b3..cc3a201a5 100644 --- a/include/grub/gpt_partition.h +++ b/include/grub/gpt_partition.h @@ -186,8 +186,10 @@ struct grub_gpt struct grub_gpt_header primary; struct grub_gpt_header backup; - /* Only need one entries table, on disk both copies are identical. */ - struct grub_gpt_partentry *entries; + /* Only need one entries table, on disk both copies are identical. + * The on disk entry size may be larger than our partentry struct so + * the table cannot be indexed directly. */ + void *entries; grub_size_t entries_size; /* Logarithm of sector size, in case GPT and disk driver disagree. */ @@ -205,6 +207,11 @@ grub_gpt_sector_to_addr (grub_gpt_t gpt, grub_uint64_t sector) /* Allocates and fills new grub_gpt structure, free with grub_gpt_free. */ grub_gpt_t grub_gpt_read (grub_disk_t disk); +/* Helper for indexing into the entries table. + * Returns NULL when the end of the table has been reached. */ +struct grub_gpt_partentry * grub_gpt_get_partentry (grub_gpt_t gpt, + grub_uint32_t n); + /* Sync up primary and backup headers, recompute checksums. */ grub_err_t grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt); diff --git a/tests/gpt_unit_test.c b/tests/gpt_unit_test.c index 60f601729..9cf3414c2 100644 --- a/tests/gpt_unit_test.c +++ b/tests/gpt_unit_test.c @@ -40,6 +40,13 @@ /* from gnulib */ #include +/* Confirm that the GPT structures conform to the sizes in the spec: + * The header size "must be greater than or equal to 92 and must be less + * than or equal to the logical block size." + * The partition entry size must be "a value of 128*(2^n) where n is an + * integer greater than or equal to zero (e.g., 128, 256, 512, etc.)." */ +verify (sizeof (struct grub_gpt_header) == 92); +verify (sizeof (struct grub_gpt_partentry) == 128); /* GPT section sizes. */ #define HEADER_SIZE (sizeof (struct grub_gpt_header)) @@ -537,6 +544,113 @@ repair_test (void) close_disk (&data); } +static void +iterate_partitions_test (void) +{ + struct test_data data; + struct grub_gpt_partentry *p; + grub_gpt_t gpt; + grub_uint32_t n; + + open_disk (&data); + gpt = read_disk (&data); + + for (n = 0; (p = grub_gpt_get_partentry (gpt, n)) != NULL; n++) + grub_test_assert (memcmp (p, &example_entries[n], sizeof (*p)) == 0, + "unexpected partition %d data", n); + + grub_test_assert (n == TABLE_ENTRIES, "unexpected partition limit: %d", n); + + grub_gpt_free (gpt); + close_disk (&data); +} + +static void +large_partitions_test (void) +{ + struct test_data data; + struct grub_gpt_partentry *p; + grub_gpt_t gpt; + grub_uint32_t n; + + open_disk (&data); + + /* Double the entry size, cut the number of entries in half. */ + data.raw->primary_header.maxpart = + data.raw->backup_header.maxpart = + grub_cpu_to_le32_compile_time (TABLE_ENTRIES/2); + data.raw->primary_header.partentry_size = + data.raw->backup_header.partentry_size = + grub_cpu_to_le32_compile_time (ENTRY_SIZE*2); + data.raw->primary_header.partentry_crc32 = + data.raw->backup_header.partentry_crc32 = + grub_cpu_to_le32_compile_time (0xf2c45af8); + data.raw->primary_header.crc32 = grub_cpu_to_le32_compile_time (0xde00cc8f); + data.raw->backup_header.crc32 = grub_cpu_to_le32_compile_time (0x6d72e284); + + memset (&data.raw->primary_entries, 0, + sizeof (data.raw->primary_entries)); + for (n = 0; n < TABLE_ENTRIES/2; n++) + memcpy (&data.raw->primary_entries[n*2], &example_entries[n], + sizeof (data.raw->primary_entries[0])); + memcpy (&data.raw->backup_entries, &data.raw->primary_entries, + sizeof (data.raw->backup_entries)); + + sync_disk(&data); + gpt = read_disk (&data); + + for (n = 0; (p = grub_gpt_get_partentry (gpt, n)) != NULL; n++) + grub_test_assert (memcmp (p, &example_entries[n], sizeof (*p)) == 0, + "unexpected partition %d data", n); + + grub_test_assert (n == TABLE_ENTRIES/2, "unexpected partition limit: %d", n); + + grub_gpt_free (gpt); + + /* Editing memory beyond the entry structure should still change the crc. */ + data.raw->primary_entries[1].attrib = 0xff; + + sync_disk(&data); + gpt = read_disk (&data); + grub_test_assert (gpt->status == (GRUB_GPT_PROTECTIVE_MBR | + GRUB_GPT_PRIMARY_HEADER_VALID | + GRUB_GPT_BACKUP_HEADER_VALID | + GRUB_GPT_BACKUP_ENTRIES_VALID), + "unexpected status: 0x%02x", gpt->status); + grub_gpt_free (gpt); + + close_disk (&data); +} + +static void +invalid_partsize_test (void) +{ + struct grub_gpt_header header = { + .magic = GRUB_GPT_HEADER_MAGIC, + .version = GRUB_GPT_HEADER_VERSION, + .headersize = sizeof (struct grub_gpt_header), + .crc32 = grub_cpu_to_le32_compile_time (0x1ff2a054), + .header_lba = grub_cpu_to_le64_compile_time (PRIMARY_HEADER_SECTOR), + .alternate_lba = grub_cpu_to_le64_compile_time (BACKUP_HEADER_SECTOR), + .start = grub_cpu_to_le64_compile_time (DATA_START_SECTOR), + .end = grub_cpu_to_le64_compile_time (DATA_END_SECTOR), + .guid = GRUB_GPT_GUID_INIT(0x69c131ad, 0x67d6, 0x46c6, + 0x93, 0xc4, 0x12, 0x4c, 0x75, 0x52, 0x56, 0xac), + .partitions = grub_cpu_to_le64_compile_time (PRIMARY_TABLE_SECTOR), + .maxpart = grub_cpu_to_le32_compile_time (TABLE_ENTRIES), + /* Triple the entry size, which is not valid. */ + .partentry_size = grub_cpu_to_le32_compile_time (ENTRY_SIZE*3), + .partentry_crc32 = grub_cpu_to_le32_compile_time (0x074e052c), + }; + + grub_gpt_header_check(&header, GRUB_DISK_SECTOR_BITS); + grub_test_assert (grub_errno == GRUB_ERR_BAD_PART_TABLE, + "unexpected error: %s", grub_errmsg); + grub_test_assert (strcmp(grub_errmsg, "invalid GPT entry size") == 0, + "unexpected error: %s", grub_errmsg); + grub_errno = GRUB_ERR_NONE; +} + static void search_part_label_test (void) { @@ -657,6 +771,9 @@ grub_unit_test_init (void) grub_test_register ("gpt_read_invalid_test", read_invalid_entries_test); grub_test_register ("gpt_read_fallback_test", read_fallback_test); grub_test_register ("gpt_repair_test", repair_test); + grub_test_register ("gpt_iterate_partitions_test", iterate_partitions_test); + grub_test_register ("gpt_large_partitions_test", large_partitions_test); + grub_test_register ("gpt_invalid_partsize_test", invalid_partsize_test); grub_test_register ("gpt_search_part_label_test", search_part_label_test); grub_test_register ("gpt_search_uuid_test", search_part_uuid_test); grub_test_register ("gpt_search_disk_uuid_test", search_disk_uuid_test); @@ -671,6 +788,9 @@ grub_unit_test_fini (void) grub_test_unregister ("gpt_read_invalid_test"); grub_test_unregister ("gpt_read_fallback_test"); grub_test_unregister ("gpt_repair_test"); + grub_test_unregister ("gpt_iterate_partitions_test"); + grub_test_unregister ("gpt_large_partitions_test"); + grub_test_unregister ("gpt_invalid_partsize_test"); grub_test_unregister ("gpt_search_part_label_test"); grub_test_unregister ("gpt_search_part_uuid_test"); grub_test_unregister ("gpt_search_disk_uuid_test"); From 1d358a2061f40ad89567754f4787d0c76001d48a Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Tue, 23 Aug 2016 13:09:14 -0700 Subject: [PATCH 60/85] gpt: prefer disk size from header over firmware The firmware and the OS may disagree on the disk configuration and size. Although such a setup should be avoided users are unlikely to know about the problem, assuming everything behaves like the OS. Tolerate this as best we can and trust the reported on-disk location over the firmware when looking for the backup GPT. If the location is inaccessible report the error as best we can and move on. --- grub-core/lib/gpt.c | 18 +++++++++++++----- tests/gpt_unit_test.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index 48cc53de4..63189f390 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -395,13 +395,21 @@ grub_gpt_read_backup (grub_disk_t disk, grub_gpt_t gpt) grub_disk_addr_t addr; /* Assumes gpt->log_sector_size == disk->log_sector_size */ - if (grub_gpt_disk_size_valid(disk)) + if (gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID) + { + sector = grub_le_to_cpu64 (gpt->primary.alternate_lba); + if (grub_gpt_disk_size_valid (disk) && sector >= disk->total_sectors) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + "backup GPT located at 0x%llx, " + "beyond last disk sector at 0x%llx", + (unsigned long long) sector, + (unsigned long long) disk->total_sectors - 1); + } + else if (grub_gpt_disk_size_valid (disk)) sector = disk->total_sectors - 1; - else if (gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID) - sector = grub_le_to_cpu64 (gpt->primary.alternate_lba); else - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unable to locate backup GPT"); + return grub_error (GRUB_ERR_OUT_OF_RANGE, + "size of disk unknown, cannot locate backup GPT"); grub_dprintf ("gpt", "reading backup GPT from sector 0x%llx\n", (unsigned long long) sector); diff --git a/tests/gpt_unit_test.c b/tests/gpt_unit_test.c index 9cf3414c2..218b18697 100644 --- a/tests/gpt_unit_test.c +++ b/tests/gpt_unit_test.c @@ -544,6 +544,46 @@ repair_test (void) close_disk (&data); } +/* Finding/reading/writing the backup GPT may be difficult if the OS and + * BIOS report different sizes for the same disk. We need to gracefully + * recognize this and avoid causing trouble for the OS. */ +static void +weird_disk_size_test (void) +{ + struct test_data data; + grub_gpt_t gpt; + + open_disk (&data); + + /* Chop off 65536 bytes (128 512B sectors) which may happen when the + * BIOS thinks you are using a software RAID system that reserves that + * area for metadata when in fact you are not and using the bare disk. */ + grub_test_assert(data.dev->disk->total_sectors == DISK_SECTORS, + "unexpected disk size: 0x%llx", + (unsigned long long) data.dev->disk->total_sectors); + data.dev->disk->total_sectors -= 128; + + gpt = read_disk (&data); + assert_error_stack_empty (); + /* Reading the alternate_lba should have been blocked and reading + * the (new) end of disk should have found no useful data. */ + grub_test_assert ((gpt->status & GRUB_GPT_BACKUP_HEADER_VALID) == 0, + "unreported missing backup header"); + + /* We should be able to reconstruct the backup header and the location + * of the backup should remain unchanged, trusting the GPT data over + * what the BIOS is telling us. Further changes are left to the OS. */ + grub_gpt_repair (data.dev->disk, gpt); + grub_test_assert (grub_errno == GRUB_ERR_NONE, + "repair failed: %s", grub_errmsg); + grub_test_assert (memcmp (&gpt->primary, &example_primary, + sizeof (gpt->primary)) == 0, + "repair corrupted primary header"); + + grub_gpt_free (gpt); + close_disk (&data); +} + static void iterate_partitions_test (void) { @@ -774,6 +814,7 @@ grub_unit_test_init (void) grub_test_register ("gpt_iterate_partitions_test", iterate_partitions_test); grub_test_register ("gpt_large_partitions_test", large_partitions_test); grub_test_register ("gpt_invalid_partsize_test", invalid_partsize_test); + grub_test_register ("gpt_weird_disk_size_test", weird_disk_size_test); grub_test_register ("gpt_search_part_label_test", search_part_label_test); grub_test_register ("gpt_search_uuid_test", search_part_uuid_test); grub_test_register ("gpt_search_disk_uuid_test", search_disk_uuid_test); @@ -791,6 +832,7 @@ grub_unit_test_fini (void) grub_test_unregister ("gpt_iterate_partitions_test"); grub_test_unregister ("gpt_large_partitions_test"); grub_test_unregister ("gpt_invalid_partsize_test"); + grub_test_unregister ("gpt_weird_disk_size_test"); grub_test_unregister ("gpt_search_part_label_test"); grub_test_unregister ("gpt_search_part_uuid_test"); grub_test_unregister ("gpt_search_disk_uuid_test"); From 2ed905dc03c757c92064486b380f59166cc704e8 Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Thu, 25 Aug 2016 17:21:18 -0700 Subject: [PATCH 61/85] gpt: add helper for picking a valid header Eliminate some repetition in primary vs. backup header acquisition. --- grub-core/lib/gpt.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index 63189f390..d49fcb81e 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -108,21 +108,32 @@ grub_gpt_part_uuid (grub_device_t device, char **uuid) return GRUB_ERR_NONE; } +static struct grub_gpt_header * +grub_gpt_get_header (grub_gpt_t gpt) +{ + if (gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID) + return &gpt->primary; + else if (gpt->status & GRUB_GPT_BACKUP_HEADER_VALID) + return &gpt->backup; + + grub_error (GRUB_ERR_BUG, "No valid GPT header"); + return NULL; +} + grub_err_t grub_gpt_disk_uuid (grub_device_t device, char **uuid) { + struct grub_gpt_header *header; + grub_gpt_t gpt = grub_gpt_read (device->disk); if (!gpt) goto done; - grub_errno = GRUB_ERR_NONE; + header = grub_gpt_get_header (gpt); + if (!header) + goto done; - if (gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID) - *uuid = grub_gpt_guid_to_str (&gpt->primary.guid); - else if (gpt->status & GRUB_GPT_BACKUP_HEADER_VALID) - *uuid = grub_gpt_guid_to_str (&gpt->backup.guid); - else - grub_errno = grub_error (GRUB_ERR_BUG, "No valid GPT header"); + *uuid = grub_gpt_guid_to_str (&header->guid); done: grub_gpt_free (gpt); @@ -560,11 +571,8 @@ grub_gpt_get_partentry (grub_gpt_t gpt, grub_uint32_t n) struct grub_gpt_header *header; grub_size_t offset; - if (gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID) - header = &gpt->primary; - else if (gpt->status & GRUB_GPT_BACKUP_HEADER_VALID) - header = &gpt->backup; - else + header = grub_gpt_get_header (gpt); + if (!header) return NULL; if (n >= grub_le_to_cpu32 (header->maxpart)) From 4af1d7a8b7d0cefa41a1ea4df050b161ea6cdf50 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Tue, 20 Sep 2016 13:06:05 -0700 Subject: [PATCH 62/85] gptrepair: fix status checking None of these status bit checks were correct. Fix and simplify. --- grub-core/commands/gptrepair.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/grub-core/commands/gptrepair.c b/grub-core/commands/gptrepair.c index 38392fd8f..66ac3f7c7 100644 --- a/grub-core/commands/gptrepair.c +++ b/grub-core/commands/gptrepair.c @@ -46,8 +46,6 @@ grub_cmd_gptrepair (grub_command_t cmd __attribute__ ((unused)), grub_device_t dev = NULL; grub_gpt_t gpt = NULL; char *dev_name; - grub_uint32_t primary_crc, backup_crc; - enum grub_gpt_status old_status; if (argc != 1 || !grub_strlen(args[0])) return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); @@ -67,29 +65,25 @@ grub_cmd_gptrepair (grub_command_t cmd __attribute__ ((unused)), if (!gpt) goto done; - primary_crc = gpt->primary.crc32; - backup_crc = gpt->backup.crc32; - old_status = gpt->status; - - if (grub_gpt_repair (dev->disk, gpt)) - goto done; - - if (primary_crc == gpt->primary.crc32 && - backup_crc == gpt->backup.crc32 && - old_status && gpt->status) + if ((gpt->status & GRUB_GPT_BOTH_VALID) == GRUB_GPT_BOTH_VALID) { grub_printf_ (N_("GPT already valid, %s unmodified.\n"), dev_name); goto done; } + if ((gpt->status & GRUB_GPT_PRIMARY_VALID) != GRUB_GPT_PRIMARY_VALID) + grub_printf_ (N_("Found invalid primary GPT on %s\n"), dev_name); + + if ((gpt->status & GRUB_GPT_BACKUP_VALID) != GRUB_GPT_BACKUP_VALID) + grub_printf_ (N_("Found invalid backup GPT on %s\n"), dev_name); + + if (grub_gpt_repair (dev->disk, gpt)) + goto done; + if (grub_gpt_write (dev->disk, gpt)) goto done; - if (!(old_status & GRUB_GPT_PRIMARY_VALID)) - grub_printf_ (N_("Primary GPT for %s repaired.\n"), dev_name); - - if (!(old_status & GRUB_GPT_BACKUP_VALID)) - grub_printf_ (N_("Backup GPT for %s repaired.\n"), dev_name); + grub_printf_ (N_("Repaired GPT on %s\n"), dev_name); done: if (gpt) From a794435ae9f5b1a2e0281d36b10545c6e643fd8d Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Tue, 20 Sep 2016 12:43:01 -0700 Subject: [PATCH 63/85] gpt: use inline functions for checking status bits This should prevent bugs like 6078f836 and 4268f3da. --- grub-core/commands/gptprio.c | 2 +- grub-core/commands/gptrepair.c | 6 +++--- grub-core/lib/gpt.c | 9 +++++++-- include/grub/gpt_partition.h | 35 +++++++++++++++++++++++++++------- 4 files changed, 39 insertions(+), 13 deletions(-) diff --git a/grub-core/commands/gptprio.c b/grub-core/commands/gptprio.c index 548925a08..25f867a81 100644 --- a/grub-core/commands/gptprio.c +++ b/grub-core/commands/gptprio.c @@ -91,7 +91,7 @@ grub_find_next (const char *disk_name, if (!gpt) goto done; - if ((gpt->status & GRUB_GPT_BOTH_VALID) != GRUB_GPT_BOTH_VALID) + if (!grub_gpt_both_valid(gpt)) if (grub_gpt_repair (dev->disk, gpt)) goto done; diff --git a/grub-core/commands/gptrepair.c b/grub-core/commands/gptrepair.c index 66ac3f7c7..c17c7346c 100644 --- a/grub-core/commands/gptrepair.c +++ b/grub-core/commands/gptrepair.c @@ -65,16 +65,16 @@ grub_cmd_gptrepair (grub_command_t cmd __attribute__ ((unused)), if (!gpt) goto done; - if ((gpt->status & GRUB_GPT_BOTH_VALID) == GRUB_GPT_BOTH_VALID) + if (grub_gpt_both_valid (gpt)) { grub_printf_ (N_("GPT already valid, %s unmodified.\n"), dev_name); goto done; } - if ((gpt->status & GRUB_GPT_PRIMARY_VALID) != GRUB_GPT_PRIMARY_VALID) + if (!grub_gpt_primary_valid (gpt)) grub_printf_ (N_("Found invalid primary GPT on %s\n"), dev_name); - if ((gpt->status & GRUB_GPT_BACKUP_VALID) != GRUB_GPT_BACKUP_VALID) + if (!grub_gpt_backup_valid (gpt)) grub_printf_ (N_("Found invalid backup GPT on %s\n"), dev_name); if (grub_gpt_repair (dev->disk, gpt)) diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index d49fcb81e..473a0140e 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -639,10 +639,15 @@ grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt) if (grub_gpt_check_primary (gpt)) return grub_error (GRUB_ERR_BUG, "Generated invalid GPT primary header"); + gpt->status |= (GRUB_GPT_PRIMARY_HEADER_VALID | + GRUB_GPT_PRIMARY_ENTRIES_VALID); + if (grub_gpt_check_backup (gpt)) return grub_error (GRUB_ERR_BUG, "Generated invalid GPT backup header"); - gpt->status |= GRUB_GPT_BOTH_VALID; + gpt->status |= (GRUB_GPT_BACKUP_HEADER_VALID | + GRUB_GPT_BACKUP_ENTRIES_VALID); + grub_dprintf ("gpt", "repairing GPT for %s successful\n", disk->name); return GRUB_ERR_NONE; @@ -704,7 +709,7 @@ grub_gpt_write (grub_disk_t disk, grub_gpt_t gpt) { /* TODO: update/repair protective MBRs too. */ - if ((gpt->status & GRUB_GPT_BOTH_VALID) != GRUB_GPT_BOTH_VALID) + if (!grub_gpt_both_valid (gpt)) return grub_error (GRUB_ERR_BAD_PART_TABLE, "Invalid GPT data"); grub_dprintf ("gpt", "writing primary GPT to %s\n", disk->name); diff --git a/include/grub/gpt_partition.h b/include/grub/gpt_partition.h index cc3a201a5..39388ce6e 100644 --- a/include/grub/gpt_partition.h +++ b/include/grub/gpt_partition.h @@ -161,13 +161,6 @@ typedef enum grub_gpt_status GRUB_GPT_BACKUP_ENTRIES_VALID = 0x20, } grub_gpt_status_t; -#define GRUB_GPT_MBR_VALID (GRUB_GPT_PROTECTIVE_MBR|GRUB_GPT_HYBRID_MBR) -#define GRUB_GPT_PRIMARY_VALID \ - (GRUB_GPT_PRIMARY_HEADER_VALID|GRUB_GPT_PRIMARY_ENTRIES_VALID) -#define GRUB_GPT_BACKUP_VALID \ - (GRUB_GPT_BACKUP_HEADER_VALID|GRUB_GPT_BACKUP_ENTRIES_VALID) -#define GRUB_GPT_BOTH_VALID (GRUB_GPT_PRIMARY_VALID|GRUB_GPT_BACKUP_VALID) - /* UEFI requires the entries table to be at least 16384 bytes for a * total of 128 entries given the standard 128 byte entry size. */ #define GRUB_GPT_DEFAULT_ENTRIES_SIZE 16384 @@ -197,6 +190,34 @@ struct grub_gpt }; typedef struct grub_gpt *grub_gpt_t; +/* Helpers for checking the gpt status field. */ +static inline int +grub_gpt_mbr_valid (grub_gpt_t gpt) +{ + return ((gpt->status & GRUB_GPT_PROTECTIVE_MBR) || + (gpt->status & GRUB_GPT_HYBRID_MBR)); +} + +static inline int +grub_gpt_primary_valid (grub_gpt_t gpt) +{ + return ((gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID) && + (gpt->status & GRUB_GPT_PRIMARY_ENTRIES_VALID)); +} + +static inline int +grub_gpt_backup_valid (grub_gpt_t gpt) +{ + return ((gpt->status & GRUB_GPT_BACKUP_HEADER_VALID) && + (gpt->status & GRUB_GPT_BACKUP_ENTRIES_VALID)); +} + +static inline int +grub_gpt_both_valid (grub_gpt_t gpt) +{ + return grub_gpt_primary_valid (gpt) && grub_gpt_backup_valid (gpt); +} + /* Translate GPT sectors to GRUB's 512 byte block addresses. */ static inline grub_disk_addr_t grub_gpt_sector_to_addr (grub_gpt_t gpt, grub_uint64_t sector) From 38cc185319b74d7d33ad380fe4d519fb0b0c85a6 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Tue, 20 Sep 2016 13:40:11 -0700 Subject: [PATCH 64/85] gpt: allow repair function to noop Simplifies usage a little. --- grub-core/commands/gptprio.c | 5 ++--- grub-core/lib/gpt.c | 4 ++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/grub-core/commands/gptprio.c b/grub-core/commands/gptprio.c index 25f867a81..a439552e1 100644 --- a/grub-core/commands/gptprio.c +++ b/grub-core/commands/gptprio.c @@ -91,9 +91,8 @@ grub_find_next (const char *disk_name, if (!gpt) goto done; - if (!grub_gpt_both_valid(gpt)) - if (grub_gpt_repair (dev->disk, gpt)) - goto done; + if (grub_gpt_repair (dev->disk, gpt)) + goto done; for (i = 0; (part = grub_gpt_get_partentry (gpt, i)) != NULL; i++) { diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index 473a0140e..d9a30782a 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -587,6 +587,10 @@ grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt) { grub_uint64_t backup_header, backup_entries; + /* Skip if there is nothing to do. */ + if (grub_gpt_both_valid (gpt)) + return GRUB_ERR_NONE; + grub_dprintf ("gpt", "repairing GPT for %s\n", disk->name); if (disk->log_sector_size != gpt->log_sector_size) From 2aeadda52929bb47089ef99c2bad0f928eadeffa Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Wed, 21 Sep 2016 13:22:06 -0700 Subject: [PATCH 65/85] gpt: do not use an enum for status bit values --- include/grub/gpt_partition.h | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/include/grub/gpt_partition.h b/include/grub/gpt_partition.h index 39388ce6e..ee435d73b 100644 --- a/include/grub/gpt_partition.h +++ b/include/grub/gpt_partition.h @@ -151,15 +151,14 @@ grub_gpt_partition_map_iterate (grub_disk_t disk, void *hook_data); /* Advanced GPT library. */ -typedef enum grub_gpt_status - { - GRUB_GPT_PROTECTIVE_MBR = 0x01, - GRUB_GPT_HYBRID_MBR = 0x02, - GRUB_GPT_PRIMARY_HEADER_VALID = 0x04, - GRUB_GPT_PRIMARY_ENTRIES_VALID = 0x08, - GRUB_GPT_BACKUP_HEADER_VALID = 0x10, - GRUB_GPT_BACKUP_ENTRIES_VALID = 0x20, - } grub_gpt_status_t; + +/* Status bits for the grub_gpt.status field. */ +#define GRUB_GPT_PROTECTIVE_MBR 0x01 +#define GRUB_GPT_HYBRID_MBR 0x02 +#define GRUB_GPT_PRIMARY_HEADER_VALID 0x04 +#define GRUB_GPT_PRIMARY_ENTRIES_VALID 0x08 +#define GRUB_GPT_BACKUP_HEADER_VALID 0x10 +#define GRUB_GPT_BACKUP_ENTRIES_VALID 0x20 /* UEFI requires the entries table to be at least 16384 bytes for a * total of 128 entries given the standard 128 byte entry size. */ @@ -170,7 +169,7 @@ typedef enum grub_gpt_status struct grub_gpt { /* Bit field indicating which structures on disk are valid. */ - grub_gpt_status_t status; + unsigned status; /* Protective or hybrid MBR. */ struct grub_msdos_partition_mbr mbr; From 34652e500d64dc747ca17091b4490f9adf93ff82 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Wed, 21 Sep 2016 13:44:11 -0700 Subject: [PATCH 66/85] gpt: check header and entries status bits together Use the new status function which checks *_HEADER_VALID and *_ENTRIES_VALID bits together. It doesn't make sense for the header and entries bits to mismatch so don't allow for it. --- grub-core/lib/gpt.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index d9a30782a..41992f945 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -597,24 +597,20 @@ grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt) return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "GPT sector size must match disk sector size"); - if (!(gpt->status & GRUB_GPT_PRIMARY_ENTRIES_VALID || - gpt->status & GRUB_GPT_BACKUP_ENTRIES_VALID)) - return grub_error (GRUB_ERR_BUG, "No valid GPT entries"); - - if (gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID) + if (grub_gpt_primary_valid (gpt)) { - grub_dprintf ("gpt", "primary GPT header is valid\n"); + grub_dprintf ("gpt", "primary GPT is valid\n"); backup_header = grub_le_to_cpu64 (gpt->primary.alternate_lba); grub_memcpy (&gpt->backup, &gpt->primary, sizeof (gpt->backup)); } - else if (gpt->status & GRUB_GPT_BACKUP_HEADER_VALID) + else if (grub_gpt_backup_valid (gpt)) { - grub_dprintf ("gpt", "backup GPT header is valid\n"); + grub_dprintf ("gpt", "backup GPT is valid\n"); backup_header = grub_le_to_cpu64 (gpt->backup.header_lba); grub_memcpy (&gpt->primary, &gpt->backup, sizeof (gpt->primary)); } else - return grub_error (GRUB_ERR_BUG, "No valid GPT header"); + return grub_error (GRUB_ERR_BUG, "No valid GPT"); /* Relocate backup to end if disk whenever possible. */ if (grub_gpt_disk_size_valid(disk)) From 753dd9201306e8cd7092a1231ceb194524397b04 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Wed, 21 Sep 2016 13:52:52 -0700 Subject: [PATCH 67/85] gpt: be more careful about relocating backup header The header was being relocated without checking the new location is actually safe. If the BIOS thinks the disk is smaller than the OS then repair may relocate the header into allocated space, failing the final validation check. So only move it if the disk has grown. Additionally, if the backup is valid then we can assume its current location is good enough and leave it as-is. --- grub-core/lib/gpt.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index 41992f945..118f7c7b6 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -600,7 +600,17 @@ grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt) if (grub_gpt_primary_valid (gpt)) { grub_dprintf ("gpt", "primary GPT is valid\n"); + + /* Relocate backup to end if disk if the disk has grown. */ backup_header = grub_le_to_cpu64 (gpt->primary.alternate_lba); + if (grub_gpt_disk_size_valid (disk) && + disk->total_sectors - 1 > backup_header) + { + backup_header = disk->total_sectors - 1; + grub_dprintf ("gpt", "backup GPT header relocated to 0x%llx\n", + (unsigned long long) backup_header); + } + grub_memcpy (&gpt->backup, &gpt->primary, sizeof (gpt->backup)); } else if (grub_gpt_backup_valid (gpt)) @@ -612,12 +622,6 @@ grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt) else return grub_error (GRUB_ERR_BUG, "No valid GPT"); - /* Relocate backup to end if disk whenever possible. */ - if (grub_gpt_disk_size_valid(disk)) - backup_header = disk->total_sectors - 1; - grub_dprintf ("gpt", "backup GPT header will be located at 0x%llx\n", - (unsigned long long) backup_header); - backup_entries = backup_header - grub_gpt_size_to_sectors (gpt, gpt->entries_size); grub_dprintf ("gpt", "backup GPT entries will be located at 0x%llx\n", From f1f618740d1379000b04130a632f4d53bc2392b8 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Wed, 21 Sep 2016 14:33:48 -0700 Subject: [PATCH 68/85] gpt: selectively update fields during repair Just a little cleanup/refactor to skip touching data we don't need to. --- grub-core/lib/gpt.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index 118f7c7b6..984bd9b9e 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -585,8 +585,6 @@ grub_gpt_get_partentry (grub_gpt_t gpt, grub_uint32_t n) grub_err_t grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt) { - grub_uint64_t backup_header, backup_entries; - /* Skip if there is nothing to do. */ if (grub_gpt_both_valid (gpt)) return GRUB_ERR_NONE; @@ -599,6 +597,8 @@ grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt) if (grub_gpt_primary_valid (gpt)) { + grub_uint64_t backup_header; + grub_dprintf ("gpt", "primary GPT is valid\n"); /* Relocate backup to end if disk if the disk has grown. */ @@ -609,32 +609,28 @@ grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt) backup_header = disk->total_sectors - 1; grub_dprintf ("gpt", "backup GPT header relocated to 0x%llx\n", (unsigned long long) backup_header); + + gpt->primary.alternate_lba = grub_cpu_to_le64 (backup_header); } grub_memcpy (&gpt->backup, &gpt->primary, sizeof (gpt->backup)); + gpt->backup.header_lba = gpt->primary.alternate_lba; + gpt->backup.alternate_lba = gpt->primary.header_lba; + gpt->backup.partitions = grub_cpu_to_le64 (backup_header - + grub_gpt_size_to_sectors (gpt, gpt->entries_size)); } else if (grub_gpt_backup_valid (gpt)) { grub_dprintf ("gpt", "backup GPT is valid\n"); - backup_header = grub_le_to_cpu64 (gpt->backup.header_lba); + grub_memcpy (&gpt->primary, &gpt->backup, sizeof (gpt->primary)); + gpt->primary.header_lba = gpt->backup.alternate_lba; + gpt->primary.alternate_lba = gpt->backup.header_lba; + gpt->primary.partitions = grub_cpu_to_le64_compile_time (2); } else return grub_error (GRUB_ERR_BUG, "No valid GPT"); - backup_entries = backup_header - - grub_gpt_size_to_sectors (gpt, gpt->entries_size); - grub_dprintf ("gpt", "backup GPT entries will be located at 0x%llx\n", - (unsigned long long) backup_entries); - - /* Update/fixup header and partition table locations. */ - gpt->primary.header_lba = grub_cpu_to_le64_compile_time (1); - gpt->primary.alternate_lba = grub_cpu_to_le64 (backup_header); - gpt->primary.partitions = grub_cpu_to_le64_compile_time (2); - gpt->backup.header_lba = gpt->primary.alternate_lba; - gpt->backup.alternate_lba = gpt->primary.header_lba; - gpt->backup.partitions = grub_cpu_to_le64 (backup_entries); - /* Recompute checksums. */ if (grub_gpt_update_checksums (gpt)) return grub_errno; From 285368e3753b1dbd631c1f5a4a127b7321a6941f Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Wed, 21 Sep 2016 14:55:19 -0700 Subject: [PATCH 69/85] gpt: always revalidate when recomputing checksums This ensures all code modifying GPT data include the same sanity check that repair does. If revalidation fails the status flags are left in the appropriate state. --- grub-core/lib/gpt.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index 984bd9b9e..82c87bf73 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -631,23 +631,9 @@ grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt) else return grub_error (GRUB_ERR_BUG, "No valid GPT"); - /* Recompute checksums. */ if (grub_gpt_update_checksums (gpt)) return grub_errno; - /* Sanity check. */ - if (grub_gpt_check_primary (gpt)) - return grub_error (GRUB_ERR_BUG, "Generated invalid GPT primary header"); - - gpt->status |= (GRUB_GPT_PRIMARY_HEADER_VALID | - GRUB_GPT_PRIMARY_ENTRIES_VALID); - - if (grub_gpt_check_backup (gpt)) - return grub_error (GRUB_ERR_BUG, "Generated invalid GPT backup header"); - - gpt->status |= (GRUB_GPT_BACKUP_HEADER_VALID | - GRUB_GPT_BACKUP_ENTRIES_VALID); - grub_dprintf ("gpt", "repairing GPT for %s successful\n", disk->name); return GRUB_ERR_NONE; @@ -658,6 +644,12 @@ grub_gpt_update_checksums (grub_gpt_t gpt) { grub_uint32_t crc; + /* Clear status bits, require revalidation of everything. */ + gpt->status &= ~(GRUB_GPT_PRIMARY_HEADER_VALID | + GRUB_GPT_PRIMARY_ENTRIES_VALID | + GRUB_GPT_BACKUP_HEADER_VALID | + GRUB_GPT_BACKUP_ENTRIES_VALID); + /* Writing headers larger than our header structure are unsupported. */ gpt->primary.headersize = grub_cpu_to_le32_compile_time (sizeof (gpt->primary)); @@ -671,6 +663,18 @@ grub_gpt_update_checksums (grub_gpt_t gpt) grub_gpt_header_lecrc32 (&gpt->primary.crc32, &gpt->primary); grub_gpt_header_lecrc32 (&gpt->backup.crc32, &gpt->backup); + if (grub_gpt_check_primary (gpt)) + return grub_error (GRUB_ERR_BUG, "Generated invalid GPT primary header"); + + gpt->status |= (GRUB_GPT_PRIMARY_HEADER_VALID | + GRUB_GPT_PRIMARY_ENTRIES_VALID); + + if (grub_gpt_check_backup (gpt)) + return grub_error (GRUB_ERR_BUG, "Generated invalid GPT backup header"); + + gpt->status |= (GRUB_GPT_BACKUP_HEADER_VALID | + GRUB_GPT_BACKUP_ENTRIES_VALID); + return GRUB_ERR_NONE; } From f19f5cc49dc00752f6b267c2d580a25c31697afb Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Wed, 21 Sep 2016 15:01:09 -0700 Subject: [PATCH 70/85] gpt: include backup-in-sync check in revalidation --- grub-core/lib/gpt.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index 82c87bf73..089ddcf28 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -373,6 +373,11 @@ grub_gpt_check_backup (grub_gpt_t gpt) if (backup <= end) return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid backup GPT LBA"); + /* If both primary and backup are valid but differ prefer the primary. */ + if ((gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID) && + !grub_gpt_headers_equal (gpt)) + return grub_error (GRUB_ERR_BAD_PART_TABLE, "backup GPT out of sync"); + return GRUB_ERR_NONE; } @@ -436,11 +441,6 @@ grub_gpt_read_backup (grub_disk_t disk, grub_gpt_t gpt) if (grub_le_to_cpu64 (gpt->backup.header_lba) != sector) return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid backup GPT LBA"); - /* If both primary and backup are valid but differ prefer the primary. */ - if ((gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID) && - !grub_gpt_headers_equal(gpt)) - return grub_error (GRUB_ERR_BAD_PART_TABLE, "backup GPT of of sync"); - gpt->status |= GRUB_GPT_BACKUP_HEADER_VALID; return GRUB_ERR_NONE; } From 7b25acebc343895adf942975bba5a52ef3408437 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Wed, 21 Sep 2016 15:29:55 -0700 Subject: [PATCH 71/85] gpt: read entries table at the same time as the header I personally think this reads easier. Also has the side effect of directly comparing the primary and backup tables instead of presuming they are equal if the crc32 matches. --- grub-core/lib/gpt.c | 69 +++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index 089ddcf28..259c62f44 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -32,6 +32,11 @@ GRUB_MOD_LICENSE ("GPLv3+"); static grub_uint8_t grub_gpt_magic[] = GRUB_GPT_HEADER_MAGIC; +static grub_err_t +grub_gpt_read_entries (grub_disk_t disk, grub_gpt_t gpt, + struct grub_gpt_header *header, + void **ret_entries, + grub_size_t *ret_entries_size); char * grub_gpt_guid_to_str (grub_gpt_guid_t *guid) @@ -401,12 +406,21 @@ grub_gpt_read_primary (grub_disk_t disk, grub_gpt_t gpt) return grub_errno; gpt->status |= GRUB_GPT_PRIMARY_HEADER_VALID; + + if (grub_gpt_read_entries (disk, gpt, &gpt->primary, + &gpt->entries, &gpt->entries_size)) + return grub_errno; + + gpt->status |= GRUB_GPT_PRIMARY_ENTRIES_VALID; + return GRUB_ERR_NONE; } static grub_err_t grub_gpt_read_backup (grub_disk_t disk, grub_gpt_t gpt) { + void *entries = NULL; + grub_size_t entries_size; grub_uint64_t sector; grub_disk_addr_t addr; @@ -442,12 +456,35 @@ grub_gpt_read_backup (grub_disk_t disk, grub_gpt_t gpt) return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid backup GPT LBA"); gpt->status |= GRUB_GPT_BACKUP_HEADER_VALID; + + if (grub_gpt_read_entries (disk, gpt, &gpt->backup, + &entries, &entries_size)) + return grub_errno; + + if (gpt->status & GRUB_GPT_PRIMARY_ENTRIES_VALID) + { + if (entries_size != gpt->entries_size || + grub_memcmp (entries, gpt->entries, entries_size) != 0) + return grub_error (GRUB_ERR_BAD_PART_TABLE, "backup GPT out of sync"); + + grub_free (entries); + } + else + { + gpt->entries = entries; + gpt->entries_size = entries_size; + } + + gpt->status |= GRUB_GPT_BACKUP_ENTRIES_VALID; + return GRUB_ERR_NONE; } static grub_err_t grub_gpt_read_entries (grub_disk_t disk, grub_gpt_t gpt, - struct grub_gpt_header *header) + struct grub_gpt_header *header, + void **ret_entries, + grub_size_t *ret_entries_size) { void *entries = NULL; grub_uint32_t count, size, crc; @@ -489,9 +526,8 @@ grub_gpt_read_entries (grub_disk_t disk, grub_gpt_t gpt, goto fail; } - grub_free (gpt->entries); - gpt->entries = entries; - gpt->entries_size = entries_size; + *ret_entries = entries; + *ret_entries_size = entries_size; return GRUB_ERR_NONE; fail: @@ -530,30 +566,7 @@ grub_gpt_read (grub_disk_t disk) grub_gpt_read_backup (disk, gpt); /* If either succeeded clear any possible error from the other. */ - if (gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID || - gpt->status & GRUB_GPT_BACKUP_HEADER_VALID) - grub_errno = GRUB_ERR_NONE; - else - goto fail; - - /* Similarly, favor the value or error from the primary table. */ - if (gpt->status & GRUB_GPT_BACKUP_HEADER_VALID && - !grub_gpt_read_entries (disk, gpt, &gpt->backup)) - { - grub_dprintf ("gpt", "read valid backup GPT from %s\n", disk->name); - gpt->status |= GRUB_GPT_BACKUP_ENTRIES_VALID; - } - - grub_errno = GRUB_ERR_NONE; - if (gpt->status & GRUB_GPT_PRIMARY_HEADER_VALID && - !grub_gpt_read_entries (disk, gpt, &gpt->primary)) - { - grub_dprintf ("gpt", "read valid primary GPT from %s\n", disk->name); - gpt->status |= GRUB_GPT_PRIMARY_ENTRIES_VALID; - } - - if (gpt->status & GRUB_GPT_PRIMARY_ENTRIES_VALID || - gpt->status & GRUB_GPT_BACKUP_ENTRIES_VALID) + if (grub_gpt_primary_valid (gpt) || grub_gpt_backup_valid (gpt)) grub_errno = GRUB_ERR_NONE; else goto fail; From edd01f055a8a8f922491ba7077bf26fcaf015516 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Wed, 21 Sep 2016 16:02:53 -0700 Subject: [PATCH 72/85] gpt: report all revalidation errors Before returning an error that the primary or backup GPT is invalid push the existing error onto the stack so the user will be told what is bad. --- grub-core/lib/gpt.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index 259c62f44..5cae72207 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -677,13 +677,19 @@ grub_gpt_update_checksums (grub_gpt_t gpt) grub_gpt_header_lecrc32 (&gpt->backup.crc32, &gpt->backup); if (grub_gpt_check_primary (gpt)) - return grub_error (GRUB_ERR_BUG, "Generated invalid GPT primary header"); + { + grub_error_push (); + return grub_error (GRUB_ERR_BUG, "Generated invalid GPT primary header"); + } gpt->status |= (GRUB_GPT_PRIMARY_HEADER_VALID | GRUB_GPT_PRIMARY_ENTRIES_VALID); if (grub_gpt_check_backup (gpt)) - return grub_error (GRUB_ERR_BUG, "Generated invalid GPT backup header"); + { + grub_error_push (); + return grub_error (GRUB_ERR_BUG, "Generated invalid GPT backup header"); + } gpt->status |= (GRUB_GPT_BACKUP_HEADER_VALID | GRUB_GPT_BACKUP_ENTRIES_VALID); From 176fe49cf03ffdd72b8bd174a149032c3867ddde Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Thu, 22 Sep 2016 10:00:27 -0700 Subject: [PATCH 73/85] gpt: rename and update documentation for grub_gpt_update The function now does more than just recompute checksums so give it a more general name to reflect that. --- grub-core/commands/gptprio.c | 2 +- grub-core/lib/gpt.c | 4 ++-- include/grub/gpt_partition.h | 7 ++++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/grub-core/commands/gptprio.c b/grub-core/commands/gptprio.c index a439552e1..4a24fa62d 100644 --- a/grub-core/commands/gptprio.c +++ b/grub-core/commands/gptprio.c @@ -127,7 +127,7 @@ grub_find_next (const char *disk_name, grub_gptprio_set_tries_left (part_found, tries_left - 1); - if (grub_gpt_update_checksums (gpt)) + if (grub_gpt_update (gpt)) goto done; if (grub_gpt_write (dev->disk, gpt)) diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index 5cae72207..519e05d89 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -644,7 +644,7 @@ grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt) else return grub_error (GRUB_ERR_BUG, "No valid GPT"); - if (grub_gpt_update_checksums (gpt)) + if (grub_gpt_update (gpt)) return grub_errno; grub_dprintf ("gpt", "repairing GPT for %s successful\n", disk->name); @@ -653,7 +653,7 @@ grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt) } grub_err_t -grub_gpt_update_checksums (grub_gpt_t gpt) +grub_gpt_update (grub_gpt_t gpt) { grub_uint32_t crc; diff --git a/include/grub/gpt_partition.h b/include/grub/gpt_partition.h index ee435d73b..4730fe362 100644 --- a/include/grub/gpt_partition.h +++ b/include/grub/gpt_partition.h @@ -232,11 +232,12 @@ grub_gpt_t grub_gpt_read (grub_disk_t disk); struct grub_gpt_partentry * grub_gpt_get_partentry (grub_gpt_t gpt, grub_uint32_t n); -/* Sync up primary and backup headers, recompute checksums. */ +/* Sync and update primary and backup headers if either are invalid. */ grub_err_t grub_gpt_repair (grub_disk_t disk, grub_gpt_t gpt); -/* Recompute checksums, must be called after modifying GPT data. */ -grub_err_t grub_gpt_update_checksums (grub_gpt_t gpt); +/* Recompute checksums and revalidate everything, must be called after + * modifying any GPT data. */ +grub_err_t grub_gpt_update (grub_gpt_t gpt); /* Write headers and entry tables back to disk. */ grub_err_t grub_gpt_write (grub_disk_t disk, grub_gpt_t gpt); From eb28d32081be2d224874c430345e7ef97bfbba07 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Thu, 22 Sep 2016 11:18:42 -0700 Subject: [PATCH 74/85] gpt: write backup GPT first, skip if inaccessible. Writing the primary GPT before the backup may lead to a confusing situation: booting a freshly updated system could consistently fail and next boot will fall back to the old system if writing the primary works but writing the backup fails. If the backup is written first and fails the primary is left in the old state so the next boot will re-try and possibly fail in the exact same way. Making that repeatable should make it easier for users to identify the error. Additionally if the firmware and OS disagree on the disk size, making the backup inaccessible to GRUB, then just skip writing the backup. When this happens the automatic call to `coreos-setgoodroot` after boot will take care of repairing the backup. --- grub-core/lib/gpt.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/grub-core/lib/gpt.c b/grub-core/lib/gpt.c index 519e05d89..95538d5a3 100644 --- a/grub-core/lib/gpt.c +++ b/grub-core/lib/gpt.c @@ -730,19 +730,39 @@ grub_gpt_write_table (grub_disk_t disk, grub_gpt_t gpt, grub_err_t grub_gpt_write (grub_disk_t disk, grub_gpt_t gpt) { + grub_uint64_t backup_header; + /* TODO: update/repair protective MBRs too. */ if (!grub_gpt_both_valid (gpt)) return grub_error (GRUB_ERR_BAD_PART_TABLE, "Invalid GPT data"); + /* Write the backup GPT first so if writing fails the update is aborted + * and the primary is left intact. However if the backup location is + * inaccessible we have to just skip and hope for the best, the backup + * will need to be repaired in the OS. */ + backup_header = grub_le_to_cpu64 (gpt->backup.header_lba); + if (grub_gpt_disk_size_valid (disk) && + backup_header >= disk->total_sectors) + { + grub_printf ("warning: backup GPT located at 0x%llx, " + "beyond last disk sector at 0x%llx\n", + (unsigned long long) backup_header, + (unsigned long long) disk->total_sectors - 1); + grub_printf ("warning: only writing primary GPT, " + "the backup GPT must be repaired from the OS\n"); + } + else + { + grub_dprintf ("gpt", "writing backup GPT to %s\n", disk->name); + if (grub_gpt_write_table (disk, gpt, &gpt->backup)) + return grub_errno; + } + grub_dprintf ("gpt", "writing primary GPT to %s\n", disk->name); if (grub_gpt_write_table (disk, gpt, &gpt->primary)) return grub_errno; - grub_dprintf ("gpt", "writing backup GPT to %s\n", disk->name); - if (grub_gpt_write_table (disk, gpt, &gpt->backup)) - return grub_errno; - return GRUB_ERR_NONE; } From 976501a7d4305e20d50421610510a95033813ee9 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 13 Oct 2016 13:55:26 -0700 Subject: [PATCH 75/85] Make TPM errors less fatal Handle TPM errors, and stop trying to use the TPM once we hit one. --- grub-core/kern/dl.c | 1 + grub-core/kern/i386/pc/tpm.c | 21 +++++++++++++++++---- grub-core/lib/cmdline.c | 1 + grub-core/loader/i386/efi/linux.c | 2 ++ grub-core/loader/i386/linux.c | 1 + grub-core/loader/i386/multiboot_mbi.c | 1 + grub-core/loader/i386/pc/linux.c | 1 + grub-core/loader/linux.c | 2 ++ grub-core/loader/multiboot.c | 1 + grub-core/loader/multiboot_mbi2.c | 1 + grub-core/script/execute.c | 1 + 11 files changed, 29 insertions(+), 4 deletions(-) diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index facbc7829..d41343843 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -731,6 +731,7 @@ grub_dl_load_file (const char *filename) grub_file_close (file); grub_tpm_measure(core, size, GRUB_BINARY_PCR, "grub_module", filename); + grub_print_error(); mod = grub_dl_load_core (core, size); grub_free (core); diff --git a/grub-core/kern/i386/pc/tpm.c b/grub-core/kern/i386/pc/tpm.c index 8c6c1e6ec..f6f264aff 100644 --- a/grub-core/kern/i386/pc/tpm.c +++ b/grub-core/kern/i386/pc/tpm.c @@ -7,21 +7,28 @@ #define TCPA_MAGIC 0x41504354 +static int tpm_presence = -1; + int tpm_present(void); int tpm_present(void) { struct grub_bios_int_registers regs; + if (tpm_presence != -1) + return tpm_presence; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; regs.eax = 0xbb00; regs.ebx = TCPA_MAGIC; grub_bios_interrupt (0x1a, ®s); if (regs.eax == 0) - return 1; + tpm_presence = 1; + else + tpm_presence = 0; - return 0; + return tpm_presence; } grub_err_t @@ -49,7 +56,10 @@ grub_tpm_execute(PassThroughToTPM_InputParamBlock *inbuf, grub_bios_interrupt (0x1a, ®s); if (regs.eax) - return grub_error (GRUB_ERR_IO, N_("TPM error %x\n"), regs.eax); + { + tpm_presence = 0; + return grub_error (GRUB_ERR_IO, N_("TPM error %x, disabling TPM"), regs.eax); + } return 0; } @@ -126,7 +136,10 @@ grub_tpm_log_event(unsigned char *buf, grub_size_t size, grub_uint8_t pcr, grub_free(event); if (regs.eax) - return grub_error (GRUB_ERR_IO, N_("TPM error %x\n"), regs.eax); + { + tpm_presence = 0; + return grub_error (GRUB_ERR_IO, N_("TPM error %x, disabling TPM"), regs.eax); + } return 0; } diff --git a/grub-core/lib/cmdline.c b/grub-core/lib/cmdline.c index 3791f3aa9..f090ea20d 100644 --- a/grub-core/lib/cmdline.c +++ b/grub-core/lib/cmdline.c @@ -107,6 +107,7 @@ int grub_create_loader_cmdline (int argc, char *argv[], char *buf, grub_tpm_measure ((void *)orig, grub_strlen (orig), GRUB_ASCII_PCR, "grub_kernel_cmdline", orig); + grub_print_error(); return i; } diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index e89d466f6..e572d2351 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -170,6 +170,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), goto fail; } grub_tpm_measure (ptr, cursize, GRUB_BINARY_PCR, "grub_linuxefi", "Initrd"); + grub_print_error(); ptr += cursize; grub_memset (ptr, 0, ALIGN_UP_OVERHEAD (cursize, 4)); ptr += ALIGN_UP_OVERHEAD (cursize, 4); @@ -226,6 +227,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), } grub_tpm_measure (kernel, filelen, GRUB_BINARY_PCR, "grub_linuxefi", "Kernel"); + grub_print_error(); if (! grub_linuxefi_secure_validate (kernel, filelen)) { diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index 48f3957b6..dcb7ca3e7 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -719,6 +719,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), } grub_tpm_measure (kernel, len, GRUB_BINARY_PCR, "grub_linux", "Kernel"); + grub_print_error(); grub_memcpy (&lh, kernel, sizeof (lh)); diff --git a/grub-core/loader/i386/multiboot_mbi.c b/grub-core/loader/i386/multiboot_mbi.c index 0981a2cfd..42372bf05 100644 --- a/grub-core/loader/i386/multiboot_mbi.c +++ b/grub-core/loader/i386/multiboot_mbi.c @@ -175,6 +175,7 @@ grub_multiboot_load (grub_file_t file, const char *filename) } grub_tpm_measure((unsigned char*)buffer, len, GRUB_BINARY_PCR, "grub_multiboot", filename); + grub_print_error(); header = find_header (buffer, len); diff --git a/grub-core/loader/i386/pc/linux.c b/grub-core/loader/i386/pc/linux.c index b9dd80bb3..a20c66346 100644 --- a/grub-core/loader/i386/pc/linux.c +++ b/grub-core/loader/i386/pc/linux.c @@ -162,6 +162,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), } grub_tpm_measure (kernel, len, GRUB_BINARY_PCR, "grub_linux16", "Kernel"); + grub_print_error(); grub_memcpy (&lh, kernel, sizeof (lh)); kernel_offset = sizeof (lh); diff --git a/grub-core/loader/linux.c b/grub-core/loader/linux.c index 78c41e334..c2c7cfcd0 100644 --- a/grub-core/loader/linux.c +++ b/grub-core/loader/linux.c @@ -290,6 +290,8 @@ grub_initrd_load (struct grub_linux_initrd_context *initrd_ctx, return grub_errno; } grub_tpm_measure (ptr, cursize, GRUB_BINARY_PCR, "grub_initrd", "Initrd"); + grub_print_error(); + ptr += cursize; } if (newc) diff --git a/grub-core/loader/multiboot.c b/grub-core/loader/multiboot.c index 11297bb17..a97ed8739 100644 --- a/grub-core/loader/multiboot.c +++ b/grub-core/loader/multiboot.c @@ -426,6 +426,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), grub_file_close (file); grub_tpm_measure (module, size, GRUB_BINARY_PCR, "grub_multiboot", argv[0]); + grub_print_error(); return GRUB_ERR_NONE; } diff --git a/grub-core/loader/multiboot_mbi2.c b/grub-core/loader/multiboot_mbi2.c index 07ded8129..c62ced3dc 100644 --- a/grub-core/loader/multiboot_mbi2.c +++ b/grub-core/loader/multiboot_mbi2.c @@ -133,6 +133,7 @@ grub_multiboot_load (grub_file_t file, const char *filename) COMPILE_TIME_ASSERT (MULTIBOOT_HEADER_ALIGN % 4 == 0); grub_tpm_measure ((unsigned char *)mld.buffer, len, GRUB_BINARY_PCR, "grub_multiboot", filename); + grub_print_error(); header = find_header (mld.buffer, len); diff --git a/grub-core/script/execute.c b/grub-core/script/execute.c index a76684d69..488bc6bc8 100644 --- a/grub-core/script/execute.c +++ b/grub-core/script/execute.c @@ -959,6 +959,7 @@ grub_script_execute_cmdline (struct grub_script_cmd *cmd) cmdstring[cmdlen-1]= '\0'; grub_tpm_measure ((unsigned char *)cmdstring, cmdlen, GRUB_ASCII_PCR, "grub_cmd", cmdstring); + grub_print_error(); grub_free(cmdstring); invert = 0; argc = argv.argc - 1; From 55dd139eda63f4867747728918659838437194f8 Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Wed, 19 Oct 2016 14:12:55 -0700 Subject: [PATCH 76/85] loader: validate cmdline string length before appending verity arg --- grub-core/loader/i386/efi/linux.c | 2 +- grub-core/loader/i386/linux.c | 2 +- grub-core/loader/i386/verity-hash.h | 14 ++++++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index e572d2351..d195c59bf 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -286,7 +286,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), linux_cmdline + sizeof (LINUX_IMAGE) - 1, lh.cmdline_size - (sizeof (LINUX_IMAGE) - 1)); - grub_pass_verity_hash(&lh, linux_cmdline); + grub_pass_verity_hash(&lh, linux_cmdline, lh.cmdline_size); lh.cmd_line_ptr = (grub_uint32_t)(grub_uint64_t)linux_cmdline; handover_offset = lh.handover_offset; diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index dcb7ca3e7..7fe4e9aba 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -1032,7 +1032,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), maximal_cmdline_size - (sizeof (LINUX_IMAGE) - 1)); - grub_pass_verity_hash(&lh, linux_cmdline); + grub_pass_verity_hash(&lh, linux_cmdline, maximal_cmdline_size); len = prot_file_size; grub_memcpy (prot_mode_mem, kernel + kernel_offset, len); kernel_offset += len; diff --git a/grub-core/loader/i386/verity-hash.h b/grub-core/loader/i386/verity-hash.h index 4027be6aa..afbfd14d6 100644 --- a/grub-core/loader/i386/verity-hash.h +++ b/grub-core/loader/i386/verity-hash.h @@ -1,9 +1,11 @@ #define VERITY_ARG " verity.usrhash=" +#define VERITY_ARG_LENGTH (sizeof (VERITY_ARG) - 1) #define VERITY_HASH_OFFSET 0x40 #define VERITY_HASH_LENGTH 64 static inline void grub_pass_verity_hash(struct linux_kernel_header *lh, - char *cmdline) + char *cmdline, + grub_size_t cmdline_max_len) { char *buf = (char *)lh; grub_size_t cmdline_len; @@ -16,10 +18,14 @@ static inline void grub_pass_verity_hash(struct linux_kernel_header *lh, return; } - grub_memcpy (cmdline + grub_strlen(cmdline), VERITY_ARG, - sizeof (VERITY_ARG)); cmdline_len = grub_strlen(cmdline); + if (cmdline_len + VERITY_ARG_LENGTH + VERITY_HASH_LENGTH > cmdline_max_len) + return; + + grub_memcpy (cmdline + cmdline_len, VERITY_ARG, VERITY_ARG_LENGTH); + cmdline_len += VERITY_ARG_LENGTH; grub_memcpy (cmdline + cmdline_len, buf + VERITY_HASH_OFFSET, VERITY_HASH_LENGTH); - cmdline[cmdline_len + VERITY_HASH_LENGTH] = '\0'; + cmdline_len += VERITY_HASH_LENGTH; + cmdline[cmdline_len] = '\0'; } From 2b62e81a73228f1cf9b4cbc153b93d2b372df66d Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Wed, 19 Oct 2016 14:48:50 -0700 Subject: [PATCH 77/85] loader: add support for passing verity hash to xen kernels This only supports DomU Linux bzImage, ignoring bare ELF images and Dom0 Xen+Linux but those cases are not applicable to us on CoreOS. --- grub-core/loader/i386/xen.c | 4 +++- grub-core/loader/i386/xen_file.c | 13 +++++++++++++ include/grub/xen_file.h | 3 +++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/grub-core/loader/i386/xen.c b/grub-core/loader/i386/xen.c index 3073f64d5..e15f1d604 100644 --- a/grub-core/loader/i386/xen.c +++ b/grub-core/loader/i386/xen.c @@ -652,7 +652,9 @@ grub_cmd_xen (grub_command_t cmd __attribute__ ((unused)), if (!file) return grub_errno; - elf = grub_xen_file (file); + elf = grub_xen_file_and_cmdline (file, + (char *) xen_state.next_start.cmd_line, + sizeof (xen_state.next_start.cmd_line) - 1); if (!elf) goto fail; diff --git a/grub-core/loader/i386/xen_file.c b/grub-core/loader/i386/xen_file.c index 99fad4cad..ca1464d44 100644 --- a/grub-core/loader/i386/xen_file.c +++ b/grub-core/loader/i386/xen_file.c @@ -20,10 +20,20 @@ #include #include +#include "verity-hash.h" + #define XZ_MAGIC "\3757zXZ\0" grub_elf_t grub_xen_file (grub_file_t file) +{ + return grub_xen_file_and_cmdline (file, NULL, 0); +} + +grub_elf_t +grub_xen_file_and_cmdline (grub_file_t file, + char *cmdline, + grub_size_t cmdline_max_len) { grub_elf_t elf; struct linux_kernel_header lh; @@ -64,6 +74,9 @@ grub_xen_file (grub_file_t file) (unsigned long long) payload_offset, (unsigned long long) lh.payload_length); + if (cmdline) + grub_pass_verity_hash (&lh, cmdline, cmdline_max_len); + grub_file_seek (file, payload_offset); if (grub_file_read (file, &magic, sizeof (magic)) != sizeof (magic)) diff --git a/include/grub/xen_file.h b/include/grub/xen_file.h index 658799952..f8d8b19a7 100644 --- a/include/grub/xen_file.h +++ b/include/grub/xen_file.h @@ -24,6 +24,9 @@ #include grub_elf_t grub_xen_file (grub_file_t file); +grub_elf_t grub_xen_file_and_cmdline (grub_file_t file, + char *cmdline, + grub_size_t cmdline_max_len); struct grub_xen_file_info { From e7254abd7db05a500cb6208ac0aec9fab9c6047c Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Wed, 25 Jan 2017 14:54:46 -0800 Subject: [PATCH 78/85] loader: verity-hash.h fixups o Add some comments. o Change image buffer type to (const void *). o Add new macro VERITY_CMDLINE_LENGTH. Signed-off-by: Geoff Levand --- grub-core/loader/i386/verity-hash.h | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/grub-core/loader/i386/verity-hash.h b/grub-core/loader/i386/verity-hash.h index afbfd14d6..7222d5b87 100644 --- a/grub-core/loader/i386/verity-hash.h +++ b/grub-core/loader/i386/verity-hash.h @@ -1,13 +1,26 @@ +/* CoreOS verity hash */ + #define VERITY_ARG " verity.usrhash=" #define VERITY_ARG_LENGTH (sizeof (VERITY_ARG) - 1) -#define VERITY_HASH_OFFSET 0x40 #define VERITY_HASH_LENGTH 64 +#define VERITY_CMDLINE_LENGTH ((VERITY_ARG_LENGTH)+(VERITY_HASH_LENGTH)) +#define VERITY_HASH_OFFSET 0x40 -static inline void grub_pass_verity_hash(struct linux_kernel_header *lh, + +/** + * grub_pass_verity_hash - Reads the CoreOS verity hash value from a well known + * kernel image offset and adds a kernel command line argument for it. + * + * @pImage: Kernel image buffer. + * @cmdline: Kernel command line buffer. + * @cmdline_max_len: Kernel command line buffer length. + */ + +static inline void grub_pass_verity_hash(const void *pImage, char *cmdline, grub_size_t cmdline_max_len) { - char *buf = (char *)lh; + const char *buf = pImage; grub_size_t cmdline_len; int i; @@ -19,7 +32,7 @@ static inline void grub_pass_verity_hash(struct linux_kernel_header *lh, } cmdline_len = grub_strlen(cmdline); - if (cmdline_len + VERITY_ARG_LENGTH + VERITY_HASH_LENGTH > cmdline_max_len) + if (cmdline_len + VERITY_CMDLINE_LENGTH > cmdline_max_len) return; grub_memcpy (cmdline + cmdline_len, VERITY_ARG, VERITY_ARG_LENGTH); From 330cb8e74deddaba94c17c8b058fc3f92ab4158a Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Wed, 25 Jan 2017 14:54:46 -0800 Subject: [PATCH 79/85] loader: Move verity-hash.h to include Signed-off-by: Geoff Levand --- grub-core/loader/i386/efi/linux.c | 2 +- grub-core/loader/i386/linux.c | 3 ++- grub-core/loader/i386/xen_file.c | 2 +- {grub-core/loader/i386 => include/grub}/verity-hash.h | 0 4 files changed, 4 insertions(+), 3 deletions(-) rename {grub-core/loader/i386 => include/grub}/verity-hash.h (100%) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index d195c59bf..22a3618d8 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -28,7 +28,7 @@ #include #include -#include "../verity-hash.h" +#include GRUB_MOD_LICENSE ("GPLv3+"); diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index 7fe4e9aba..5fdfea312 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -37,7 +37,8 @@ #include #include -#include "verity-hash.h" +#include + GRUB_MOD_LICENSE ("GPLv3+"); #ifdef GRUB_MACHINE_PCBIOS diff --git a/grub-core/loader/i386/xen_file.c b/grub-core/loader/i386/xen_file.c index ca1464d44..510d99c94 100644 --- a/grub-core/loader/i386/xen_file.c +++ b/grub-core/loader/i386/xen_file.c @@ -20,7 +20,7 @@ #include #include -#include "verity-hash.h" +#include #define XZ_MAGIC "\3757zXZ\0" diff --git a/grub-core/loader/i386/verity-hash.h b/include/grub/verity-hash.h similarity index 100% rename from grub-core/loader/i386/verity-hash.h rename to include/grub/verity-hash.h From df45313e15126fd9d2d53438ea0253fd76588199 Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Wed, 25 Jan 2017 14:54:46 -0800 Subject: [PATCH 80/85] loader: Add arm64 verity Signed-off-by: Geoff Levand --- grub-core/loader/arm64/linux.c | 6 +++++- include/grub/verity-hash.h | 9 ++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c index 9519d2e4d..432f5c073 100644 --- a/grub-core/loader/arm64/linux.c +++ b/grub-core/loader/arm64/linux.c @@ -32,6 +32,8 @@ #include #include +#include + GRUB_MOD_LICENSE ("GPLv3+"); static grub_dl_t my_mod; @@ -297,7 +299,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_dprintf ("linux", "kernel @ %p\n", kernel_addr); - cmdline_size = grub_loader_cmdline_size (argc, argv) + sizeof (LINUX_IMAGE); + cmdline_size = grub_loader_cmdline_size (argc, argv) + sizeof (LINUX_IMAGE) + + VERITY_CMDLINE_LENGTH; linux_args = grub_malloc (cmdline_size); if (!linux_args) { @@ -311,6 +314,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), if (grub_errno == GRUB_ERR_NONE) { + grub_pass_verity_hash (kernel_addr, linux_args, cmdline_size); grub_loader_set (grub_linux_boot, grub_linux_unload, 0); loaded = 1; } diff --git a/include/grub/verity-hash.h b/include/grub/verity-hash.h index 7222d5b87..f79bb5d4c 100644 --- a/include/grub/verity-hash.h +++ b/include/grub/verity-hash.h @@ -4,7 +4,14 @@ #define VERITY_ARG_LENGTH (sizeof (VERITY_ARG) - 1) #define VERITY_HASH_LENGTH 64 #define VERITY_CMDLINE_LENGTH ((VERITY_ARG_LENGTH)+(VERITY_HASH_LENGTH)) -#define VERITY_HASH_OFFSET 0x40 + +#if defined(__aarch64__) +# define VERITY_HASH_OFFSET 512 +#elif defined(__i386__) +# define VERITY_HASH_OFFSET 0x40 +#else +# error Unsupported arch +#endif /** From 3626296357cb7cd405e9d5b7496f4b463ce5b744 Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Mon, 30 Jan 2017 12:15:16 -0800 Subject: [PATCH 81/85] Fix missing VERITY_HASH_OFFSET on amd64 --- include/grub/verity-hash.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/grub/verity-hash.h b/include/grub/verity-hash.h index f79bb5d4c..448d9aff0 100644 --- a/include/grub/verity-hash.h +++ b/include/grub/verity-hash.h @@ -7,7 +7,7 @@ #if defined(__aarch64__) # define VERITY_HASH_OFFSET 512 -#elif defined(__i386__) +#elif defined(__i386__) || defined(__amd64__) # define VERITY_HASH_OFFSET 0x40 #else # error Unsupported arch From fefd7d6acf21640fa052509a8491176c2cc1316c Mon Sep 17 00:00:00 2001 From: 1337ninja <1337ninja@users.noreply.github.com> Date: Sun, 9 Jul 2017 18:38:15 +0530 Subject: [PATCH 82/85] Fix use after free --- grub-core/normal/crypto.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/normal/crypto.c b/grub-core/normal/crypto.c index 2bfd67c8e..e6d345f33 100644 --- a/grub-core/normal/crypto.c +++ b/grub-core/normal/crypto.c @@ -147,8 +147,8 @@ read_crypto_list (const char *prefix) if (! cur->modname) { grub_errno = GRUB_ERR_NONE; - grub_free (cur); grub_free (cur->name); + grub_free (cur); continue; } cur->next = crypto_specs; From 0f7291e01856e3cd37a85a173c7fed017117c5ca Mon Sep 17 00:00:00 2001 From: Dennis Chen Date: Wed, 8 Nov 2017 09:13:00 +0000 Subject: [PATCH 83/85] Prototype fixing for (*hash_log_extend_event) According to the section 6.6.1 'Prototype' in 'TCG EFI Protocol Spec', the 3rd parameter of the (*hash_log_extend_event) should be 'EFI_PHYSICAL_ADDRESS' which is 'grub_efi_physical_address_t' in the real implementation. So this patch drop the pointer mark '*' from this prototype. Signed-off-by: Dennis Chen --- include/grub/efi/tpm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/grub/efi/tpm.h b/include/grub/efi/tpm.h index e2aff4a3c..63d8a0fe7 100644 --- a/include/grub/efi/tpm.h +++ b/include/grub/efi/tpm.h @@ -129,7 +129,7 @@ struct grub_efi_tpm2_protocol grub_efi_boolean_t *EventLogTruncated); grub_efi_status_t (*hash_log_extend_event) (struct grub_efi_tpm2_protocol *this, grub_efi_uint64_t Flags, - grub_efi_physical_address_t *DataToHash, + grub_efi_physical_address_t DataToHash, grub_efi_uint64_t DataToHashLen, EFI_TCG2_EVENT *EfiTcgEvent); grub_efi_status_t (*submit_command) (struct grub_efi_tpm2_protocol *this, From 1e08408cdefa1b076be46d28aedc766c9daffa7d Mon Sep 17 00:00:00 2001 From: Dennis Chen Date: Wed, 8 Nov 2017 09:28:49 +0000 Subject: [PATCH 84/85] Fix the build issue in TPM module The original code use deprecated 'Event' data structure with the wrong member variable names, which result in the build error. This patch fix it by using 'TCG_PCR_EVENT'. Signed-off-by: Dennis Chen --- grub-core/kern/efi/tpm.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/grub-core/kern/efi/tpm.c b/grub-core/kern/efi/tpm.c index c9fb3c133..7af07ddb5 100644 --- a/grub-core/kern/efi/tpm.c +++ b/grub-core/kern/efi/tpm.c @@ -175,7 +175,7 @@ grub_tpm1_log_event(grub_efi_handle_t tpm_handle, unsigned char *buf, grub_size_t size, grub_uint8_t pcr, const char *description) { - Event *event; + TCG_PCR_EVENT *event; grub_efi_status_t status; grub_efi_tpm_protocol_t *tpm; grub_efi_physical_address_t lastevent; @@ -188,18 +188,18 @@ grub_tpm1_log_event(grub_efi_handle_t tpm_handle, unsigned char *buf, if (!grub_tpm_present(tpm)) return 0; - event = grub_zalloc(sizeof (Event) + grub_strlen(description) + 1); + event = grub_zalloc(sizeof (TCG_PCR_EVENT) + grub_strlen(description) + 1); if (!event) return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate TPM event buffer")); - event->pcrindex = pcr; - event->eventtype = EV_IPL; - event->eventsize = grub_strlen(description) + 1; - grub_memcpy(event->event, description, event->eventsize); + event->PCRIndex = pcr; + event->EventType = EV_IPL; + event->EventSize = grub_strlen(description) + 1; + grub_memcpy(event->Event, description, event->EventSize); algorithm = TCG_ALG_SHA; - status = efi_call_7 (tpm->log_extend_event, tpm, buf, (grub_uint64_t) size, + status = efi_call_7 (tpm->log_extend_event, tpm, (grub_efi_physical_address_t)buf, (grub_uint64_t) size, algorithm, event, &eventnum, &lastevent); switch (status) { @@ -245,7 +245,7 @@ grub_tpm2_log_event(grub_efi_handle_t tpm_handle, unsigned char *buf, event->Size = sizeof(*event) - sizeof(event->Event) + grub_strlen(description) + 1; grub_memcpy(event->Event, description, grub_strlen(description) + 1); - status = efi_call_5 (tpm->hash_log_extend_event, tpm, 0, buf, + status = efi_call_5 (tpm->hash_log_extend_event, tpm, 0, (grub_efi_physical_address_t)buf, (grub_uint64_t) size, event); switch (status) { From 81911402ce57a0c10c0319839650b6b5f814ef2b Mon Sep 17 00:00:00 2001 From: Dennis Chen Date: Wed, 8 Nov 2017 09:45:39 +0000 Subject: [PATCH 85/85] Remove the deprecated 'Event' struct 'Event' struct will be not used any more, instead we use the 'TCG_PCR_EVENT', so this patch remove the older 'Event' data struct. Signed-off-by: Dennis Chen --- grub-core/kern/efi/tpm.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/grub-core/kern/efi/tpm.c b/grub-core/kern/efi/tpm.c index 7af07ddb5..532e534e4 100644 --- a/grub-core/kern/efi/tpm.c +++ b/grub-core/kern/efi/tpm.c @@ -161,15 +161,6 @@ grub_tpm_execute(PassThroughToTPM_InputParamBlock *inbuf, } } -typedef struct { - grub_uint32_t pcrindex; - grub_uint32_t eventtype; - grub_uint8_t digest[20]; - grub_uint32_t eventsize; - grub_uint8_t event[1]; -} Event; - - static grub_err_t grub_tpm1_log_event(grub_efi_handle_t tpm_handle, unsigned char *buf, grub_size_t size, grub_uint8_t pcr,