Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | // SPDX-License-Identifier: GPL-2.0 /* * Support for extracting embedded firmware for peripherals from EFI code, * * Copyright (c) 2018 Hans de Goede <hdegoede@redhat.com> */ #include <linux/dmi.h> #include <linux/efi.h> #include <linux/efi_embedded_fw.h> #include <linux/io.h> #include <linux/slab.h> #include <linux/types.h> #include <linux/vmalloc.h> #include <crypto/sha2.h> /* Exported for use by lib/test_firmware.c only */ LIST_HEAD(efi_embedded_fw_list); EXPORT_SYMBOL_NS_GPL(efi_embedded_fw_list, TEST_FIRMWARE); bool efi_embedded_fw_checked; EXPORT_SYMBOL_NS_GPL(efi_embedded_fw_checked, TEST_FIRMWARE); static const struct dmi_system_id * const embedded_fw_table[] = { #ifdef CONFIG_TOUCHSCREEN_DMI touchscreen_dmi_table, #endif NULL }; /* * Note the efi_check_for_embedded_firmwares() code currently makes the * following 2 assumptions. This may needs to be revisited if embedded firmware * is found where this is not true: * 1) The firmware is only found in EFI_BOOT_SERVICES_CODE memory segments * 2) The firmware always starts at an offset which is a multiple of 8 bytes */ static int __init efi_check_md_for_embedded_firmware( efi_memory_desc_t *md, const struct efi_embedded_fw_desc *desc) { struct efi_embedded_fw *fw; u8 hash[32]; u64 i, size; u8 *map; size = md->num_pages << EFI_PAGE_SHIFT; map = memremap(md->phys_addr, size, MEMREMAP_WB); if (!map) { pr_err("Error mapping EFI mem at %#llx\n", md->phys_addr); return -ENOMEM; } for (i = 0; (i + desc->length) <= size; i += 8) { if (memcmp(map + i, desc->prefix, EFI_EMBEDDED_FW_PREFIX_LEN)) continue; sha256(map + i, desc->length, hash); if (memcmp(hash, desc->sha256, 32) == 0) break; } if ((i + desc->length) > size) { memunmap(map); return -ENOENT; } pr_info("Found EFI embedded fw '%s'\n", desc->name); fw = kmalloc(sizeof(*fw), GFP_KERNEL); if (!fw) { memunmap(map); return -ENOMEM; } fw->data = kmemdup(map + i, desc->length, GFP_KERNEL); memunmap(map); if (!fw->data) { kfree(fw); return -ENOMEM; } fw->name = desc->name; fw->length = desc->length; list_add(&fw->list, &efi_embedded_fw_list); return 0; } void __init efi_check_for_embedded_firmwares(void) { const struct efi_embedded_fw_desc *fw_desc; const struct dmi_system_id *dmi_id; efi_memory_desc_t *md; int i, r; for (i = 0; embedded_fw_table[i]; i++) { dmi_id = dmi_first_match(embedded_fw_table[i]); if (!dmi_id) continue; fw_desc = dmi_id->driver_data; /* * In some drivers the struct driver_data contains may contain * other driver specific data after the fw_desc struct; and * the fw_desc struct itself may be empty, skip these. */ if (!fw_desc->name) continue; for_each_efi_memory_desc(md) { if (md->type != EFI_BOOT_SERVICES_CODE) continue; r = efi_check_md_for_embedded_firmware(md, fw_desc); if (r == 0) break; } } efi_embedded_fw_checked = true; } int efi_get_embedded_fw(const char *name, const u8 **data, size_t *size) { struct efi_embedded_fw *iter, *fw = NULL; if (!efi_embedded_fw_checked) { pr_warn("Warning %s called while we did not check for embedded fw\n", __func__); return -ENOENT; } list_for_each_entry(iter, &efi_embedded_fw_list, list) { if (strcmp(name, iter->name) == 0) { fw = iter; break; } } if (!fw) return -ENOENT; *data = fw->data; *size = fw->length; return 0; } EXPORT_SYMBOL_GPL(efi_get_embedded_fw); |