More EFI updates for v5.7 - Incorporate a stable branch with the EFI pieces of Hans's work on loading device firmware from EFI boot service memory regions
Unified · Split
| @@ -246,6 +246,7 @@ int __init efi_memblock_x86_reserve_range(void) · 245 unmodified lines | ||
| 246 | 246 | efi.memmap.desc_version); |
| 247 | 247 | |
| 248 | 248 | memblock_reserve(pmap, efi.memmap.nr_map * efi.memmap.desc_size); |
| 249 | + set_bit(EFI_PRESERVE_BS_REGIONS, &efi.flags); | |
| 249 | 250 | |
| 250 | 251 | return 0; |
| 251 | 252 | } |
| @@ -901,6 +902,7 @@ static void __init __efi_enter_virtual_mode(void) · 649 unmodified lines | ||
| 901 | 902 | goto err; |
| 902 | 903 | } |
| 903 | 904 | |
| 905 | + efi_check_for_embedded_firmwares(); | |
| 904 | 906 | efi_free_boot_services(); |
| 905 | 907 | |
| 906 | 908 | if (!efi_is_mixed()) |
| @@ -410,6 +410,10 @@ void __init efi_free_boot_services(void) · 409 unmodified lines | ||
| 410 | 410 | int num_entries = 0; |
| 411 | 411 | void *new, *new_md; |
| 412 | 412 | |
| 413 | + /* Keep all regions for /sys/kernel/debug/efi */ | |
| 414 | + if (efi_enabled(EFI_DBG)) | |
| 415 | + return; | |
| 416 | + | |
| 413 | 417 | for_each_efi_memory_desc(md) { |
| 414 | 418 | unsigned long long start = md->phys_addr; |
| 415 | 419 | unsigned long long size = md->num_pages << EFI_PAGE_SHIFT; |
| @@ -239,6 +239,11 @@ config EFI_DISABLE_PCI_DMA · 238 unmodified lines | ||
| 239 | 239 | |
| 240 | 240 | endmenu |
| 241 | 241 | |
| 242 | +config EFI_EMBEDDED_FIRMWARE | |
| 243 | + bool | |
| 244 | + depends on EFI | |
| 245 | + select CRYPTO_LIB_SHA256 | |
| 246 | + | |
| 242 | 247 | config UEFI_CPER |
| 243 | 248 | bool |
| 244 | 249 | |
| @@ -27,6 +27,7 @@ obj-$(CONFIG_EFI_TEST) += test/ · 26 unmodified lines | ||
| 27 | 27 | obj-$(CONFIG_EFI_DEV_PATH_PARSER) += dev-path-parser.o |
| 28 | 28 | obj-$(CONFIG_APPLE_PROPERTIES) += apple-properties.o |
| 29 | 29 | obj-$(CONFIG_EFI_RCI2_TABLE) += rci2-table.o |
| 30 | +obj-$(CONFIG_EFI_EMBEDDED_FIRMWARE) += embedded-firmware.o | |
| 30 | 31 | |
| 31 | 32 | fake_map-y += fake_mem.o |
| 32 | 33 | fake_map-$(CONFIG_X86) += x86_fake_mem.o |
| @@ -17,6 +17,7 @@ · 16 unmodified lines | ||
| 17 | 17 | #include <linux/kobject.h> |
| 18 | 18 | #include <linux/module.h> |
| 19 | 19 | #include <linux/init.h> |
| 20 | +#include <linux/debugfs.h> | |
| 20 | 21 | #include <linux/device.h> |
| 21 | 22 | #include <linux/efi.h> |
| 22 | 23 | #include <linux/of.h> |
| @@ -291,6 +292,59 @@ free_entry: · 268 unmodified lines | ||
| 291 | 292 | static inline int efivar_ssdt_load(void) { return 0; } |
| 292 | 293 | #endif |
| 293 | 294 | |
| 295 | +#ifdef CONFIG_DEBUG_FS | |
| 296 | + | |
| 297 | +#define EFI_DEBUGFS_MAX_BLOBS 32 | |
| 298 | + | |
| 299 | +static struct debugfs_blob_wrapper debugfs_blob[EFI_DEBUGFS_MAX_BLOBS]; | |
| 300 | + | |
| 301 | +static void __init efi_debugfs_init(void) | |
| 302 | +{ | |
| 303 | + struct dentry *efi_debugfs; | |
| 304 | + efi_memory_desc_t *md; | |
| 305 | + char name[32]; | |
| 306 | + int type_count[EFI_BOOT_SERVICES_DATA + 1] = {}; | |
| 307 | + int i = 0; | |
| 308 | + | |
| 309 | + efi_debugfs = debugfs_create_dir("efi", NULL); | |
| 310 | + if (IS_ERR_OR_NULL(efi_debugfs)) | |
| 311 | + return; | |
| 312 | + | |
| 313 | + for_each_efi_memory_desc(md) { | |
| 314 | + switch (md->type) { | |
| 315 | + case EFI_BOOT_SERVICES_CODE: | |
| 316 | + snprintf(name, sizeof(name), "boot_services_code%d", | |
| 317 | + type_count[md->type]++); | |
| 318 | + break; | |
| 319 | + case EFI_BOOT_SERVICES_DATA: | |
| 320 | + snprintf(name, sizeof(name), "boot_services_data%d", | |
| 321 | + type_count[md->type]++); | |
| 322 | + break; | |
| 323 | + default: | |
| 324 | + continue; | |
| 325 | + } | |
| 326 | + | |
| 327 | + if (i >= EFI_DEBUGFS_MAX_BLOBS) { | |
| 328 | + pr_warn("More then %d EFI boot service segments, only showing first %d in debugfs\n", | |
| 329 | + EFI_DEBUGFS_MAX_BLOBS, EFI_DEBUGFS_MAX_BLOBS); | |
| 330 | + break; | |
| 331 | + } | |
| 332 | + | |
| 333 | + debugfs_blob[i].size = md->num_pages << EFI_PAGE_SHIFT; | |
| 334 | + debugfs_blob[i].data = memremap(md->phys_addr, | |
| 335 | + debugfs_blob[i].size, | |
| 336 | + MEMREMAP_WB); | |
| 337 | + if (!debugfs_blob[i].data) | |
| 338 | + continue; | |
| 339 | + | |
| 340 | + debugfs_create_blob(name, 0400, efi_debugfs, &debugfs_blob[i]); | |
| 341 | + i++; | |
| 342 | + } | |
| 343 | +} | |
| 344 | +#else | |
| 345 | +static inline void efi_debugfs_init(void) {} | |
| 346 | +#endif | |
| 347 | + | |
| 294 | 348 | /* |
| 295 | 349 | * We register the efi subsystem with the firmware subsystem and the |
| 296 | 350 | * efivars subsystem with the efi subsystem, if the system was booted with |
| @@ -357,6 +411,9 @@ static int __init efisubsys_init(void) · 60 unmodified lines | ||
| 357 | 411 | goto err_remove_group; |
| 358 | 412 | } |
| 359 | 413 | |
| 414 | + if (efi_enabled(EFI_DBG) && efi_enabled(EFI_PRESERVE_BS_REGIONS)) | |
| 415 | + efi_debugfs_init(); | |
| 416 | + | |
| 360 | 417 | return 0; |
| 361 | 418 | |
| 362 | 419 | err_remove_group: |
| @@ -0,0 +1,147 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* | |
| 3 | + * Support for extracting embedded firmware for peripherals from EFI code, | |
| 4 | + * | |
| 5 | + * Copyright (c) 2018 Hans de Goede <hdegoede@redhat.com> | |
| 6 | + */ | |
| 7 | + | |
| 8 | +#include <linux/dmi.h> | |
| 9 | +#include <linux/efi.h> | |
| 10 | +#include <linux/efi_embedded_fw.h> | |
| 11 | +#include <linux/io.h> | |
| 12 | +#include <linux/slab.h> | |
| 13 | +#include <linux/types.h> | |
| 14 | +#include <linux/vmalloc.h> | |
| 15 | +#include <crypto/sha.h> | |
| 16 | + | |
| 17 | +/* Exported for use by lib/test_firmware.c only */ | |
| 18 | +LIST_HEAD(efi_embedded_fw_list); | |
| 19 | +EXPORT_SYMBOL_GPL(efi_embedded_fw_list); | |
| 20 | + | |
| 21 | +static bool checked_for_fw; | |
| 22 | + | |
| 23 | +static const struct dmi_system_id * const embedded_fw_table[] = { | |
| 24 | + NULL | |
| 25 | +}; | |
| 26 | + | |
| 27 | +/* | |
| 28 | + * Note the efi_check_for_embedded_firmwares() code currently makes the | |
| 29 | + * following 2 assumptions. This may needs to be revisited if embedded firmware | |
| 30 | + * is found where this is not true: | |
| 31 | + * 1) The firmware is only found in EFI_BOOT_SERVICES_CODE memory segments | |
| 32 | + * 2) The firmware always starts at an offset which is a multiple of 8 bytes | |
| 33 | + */ | |
| 34 | +static int __init efi_check_md_for_embedded_firmware( | |
| 35 | + efi_memory_desc_t *md, const struct efi_embedded_fw_desc *desc) | |
| 36 | +{ | |
| 37 | + struct sha256_state sctx; | |
| 38 | + struct efi_embedded_fw *fw; | |
| 39 | + u8 sha256[32]; | |
| 40 | + u64 i, size; | |
| 41 | + u8 *map; | |
| 42 | + | |
| 43 | + size = md->num_pages << EFI_PAGE_SHIFT; | |
| 44 | + map = memremap(md->phys_addr, size, MEMREMAP_WB); | |
| 45 | + if (!map) { | |
| 46 | + pr_err("Error mapping EFI mem at %#llx\n", md->phys_addr); | |
| 47 | + return -ENOMEM; | |
| 48 | + } | |
| 49 | + | |
| 50 | + for (i = 0; (i + desc->length) <= size; i += 8) { | |
| 51 | + if (memcmp(map + i, desc->prefix, EFI_EMBEDDED_FW_PREFIX_LEN)) | |
| 52 | + continue; | |
| 53 | + | |
| 54 | + sha256_init(&sctx); | |
| 55 | + sha256_update(&sctx, map + i, desc->length); | |
| 56 | + sha256_final(&sctx, sha256); | |
| 57 | + if (memcmp(sha256, desc->sha256, 32) == 0) | |
| 58 | + break; | |
| 59 | + } | |
| 60 | + if ((i + desc->length) > size) { | |
| 61 | + memunmap(map); | |
| 62 | + return -ENOENT; | |
| 63 | + } | |
| 64 | + | |
| 65 | + pr_info("Found EFI embedded fw '%s'\n", desc->name); | |
| 66 | + | |
| 67 | + fw = kmalloc(sizeof(*fw), GFP_KERNEL); | |
| 68 | + if (!fw) { | |
| 69 | + memunmap(map); | |
| 70 | + return -ENOMEM; | |
| 71 | + } | |
| 72 | + | |
| 73 | + fw->data = kmemdup(map + i, desc->length, GFP_KERNEL); | |
| 74 | + memunmap(map); | |
| 75 | + if (!fw->data) { | |
| 76 | + kfree(fw); | |
| 77 | + return -ENOMEM; | |
| 78 | + } | |
| 79 | + | |
| 80 | + fw->name = desc->name; | |
| 81 | + fw->length = desc->length; | |
| 82 | + list_add(&fw->list, &efi_embedded_fw_list); | |
| 83 | + | |
| 84 | + return 0; | |
| 85 | +} | |
| 86 | + | |
| 87 | +void __init efi_check_for_embedded_firmwares(void) | |
| 88 | +{ | |
| 89 | + const struct efi_embedded_fw_desc *fw_desc; | |
| 90 | + const struct dmi_system_id *dmi_id; | |
| 91 | + efi_memory_desc_t *md; | |
| 92 | + int i, r; | |
| 93 | + | |
| 94 | + for (i = 0; embedded_fw_table[i]; i++) { | |
| 95 | + dmi_id = dmi_first_match(embedded_fw_table[i]); | |
| 96 | + if (!dmi_id) | |
| 97 | + continue; | |
| 98 | + | |
| 99 | + fw_desc = dmi_id->driver_data; | |
| 100 | + | |
| 101 | + /* | |
| 102 | + * In some drivers the struct driver_data contains may contain | |
| 103 | + * other driver specific data after the fw_desc struct; and | |
| 104 | + * the fw_desc struct itself may be empty, skip these. | |
| 105 | + */ | |
| 106 | + if (!fw_desc->name) | |
| 107 | + continue; | |
| 108 | + | |
| 109 | + for_each_efi_memory_desc(md) { | |
| 110 | + if (md->type != EFI_BOOT_SERVICES_CODE) | |
| 111 | + continue; | |
| 112 | + | |
| 113 | + r = efi_check_md_for_embedded_firmware(md, fw_desc); | |
| 114 | + if (r == 0) | |
| 115 | + break; | |
| 116 | + } | |
| 117 | + } | |
| 118 | + | |
| 119 | + checked_for_fw = true; | |
| 120 | +} | |
| 121 | + | |
| 122 | +int efi_get_embedded_fw(const char *name, const u8 **data, size_t *size) | |
| 123 | +{ | |
| 124 | + struct efi_embedded_fw *iter, *fw = NULL; | |
| 125 | + | |
| 126 | + if (!checked_for_fw) { | |
| 127 | + pr_warn("Warning %s called while we did not check for embedded fw\n", | |
| 128 | + __func__); | |
| 129 | + return -ENOENT; | |
| 130 | + } | |
| 131 | + | |
| 132 | + list_for_each_entry(iter, &efi_embedded_fw_list, list) { | |
| 133 | + if (strcmp(name, iter->name) == 0) { | |
| 134 | + fw = iter; | |
| 135 | + break; | |
| 136 | + } | |
| 137 | + } | |
| 138 | + | |
| 139 | + if (!fw) | |
| 140 | + return -ENOENT; | |
| 141 | + | |
| 142 | + *data = fw->data; | |
| 143 | + *size = fw->length; | |
| 144 | + | |
| 145 | + return 0; | |
| 146 | +} | |
| 147 | +EXPORT_SYMBOL_GPL(efi_get_embedded_fw); | |
| @@ -775,6 +775,7 @@ extern int __init efi_setup_pcdp_console(char *); · 774 unmodified lines | ||
| 775 | 775 | #define EFI_NX_PE_DATA 9 /* Can runtime data regions be mapped non-executable? */ |
| 776 | 776 | #define EFI_MEM_ATTR 10 /* Did firmware publish an EFI_MEMORY_ATTRIBUTES table? */ |
| 777 | 777 | #define EFI_MEM_NO_SOFT_RESERVE 11 /* Is the kernel configured to ignore soft reservations? */ |
| 778 | +#define EFI_PRESERVE_BS_REGIONS 12 /* Are EFI boot-services memory segments available? */ | |
| 778 | 779 | |
| 779 | 780 | #ifdef CONFIG_EFI |
| 780 | 781 | /* |
| @@ -1097,6 +1098,12 @@ static inline void · 316 unmodified lines | ||
| 1097 | 1098 | efi_enable_reset_attack_mitigation(void) { } |
| 1098 | 1099 | #endif |
| 1099 | 1100 | |
| 1101 | +#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE | |
| 1102 | +void efi_check_for_embedded_firmwares(void); | |
| 1103 | +#else | |
| 1104 | +static inline void efi_check_for_embedded_firmwares(void) { } | |
| 1105 | +#endif | |
| 1106 | + | |
| 1100 | 1107 | efi_status_t efi_random_get_seed(void); |
| 1101 | 1108 | |
| 1102 | 1109 | void efi_retrieve_tpm2_eventlog(void); |
| @@ -0,0 +1,41 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0 */ | |
| 2 | +#ifndef _LINUX_EFI_EMBEDDED_FW_H | |
| 3 | +#define _LINUX_EFI_EMBEDDED_FW_H | |
| 4 | + | |
| 5 | +#include <linux/list.h> | |
| 6 | +#include <linux/mod_devicetable.h> | |
| 7 | + | |
| 8 | +#define EFI_EMBEDDED_FW_PREFIX_LEN 8 | |
| 9 | + | |
| 10 | +/* | |
| 11 | + * This struct and efi_embedded_fw_list are private to the efi-embedded fw | |
| 12 | + * implementation they are in this header for use by lib/test_firmware.c only! | |
| 13 | + */ | |
| 14 | +struct efi_embedded_fw { | |
| 15 | + struct list_head list; | |
| 16 | + const char *name; | |
| 17 | + const u8 *data; | |
| 18 | + size_t length; | |
| 19 | +}; | |
| 20 | + | |
| 21 | +extern struct list_head efi_embedded_fw_list; | |
| 22 | + | |
| 23 | +/** | |
| 24 | + * struct efi_embedded_fw_desc - This struct is used by the EFI embedded-fw | |
| 25 | + * code to search for embedded firmwares. | |
| 26 | + * | |
| 27 | + * @name: Name to register the firmware with if found | |
| 28 | + * @prefix: First 8 bytes of the firmware | |
| 29 | + * @length: Length of the firmware in bytes including prefix | |
| 30 | + * @sha256: SHA256 of the firmware | |
| 31 | + */ | |
| 32 | +struct efi_embedded_fw_desc { | |
| 33 | + const char *name; | |
| 34 | + u8 prefix[EFI_EMBEDDED_FW_PREFIX_LEN]; | |
| 35 | + u32 length; | |
| 36 | + u8 sha256[32]; | |
| 37 | +}; | |
| 38 | + | |
| 39 | +int efi_get_embedded_fw(const char *name, const u8 **dat, size_t *sz); | |
| 40 | + | |
| 41 | +#endif | |