diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-10-12 23:26:49 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-10-12 23:26:49 +0300 |
commit | e6412f9833db23740ee848ab3d6e7af18dff82a6 (patch) | |
tree | c718a1ed9a631e01c226a25910a6939342a387b2 /drivers/firmware | |
parent | ed016af52ee3035b4799ebd7d53f9ae59d5782c4 (diff) | |
parent | 4d0a4388ccdd9482fef6b26f879d0f6099143f80 (diff) | |
download | linux-e6412f9833db23740ee848ab3d6e7af18dff82a6.tar.xz |
Merge tag 'efi-core-2020-10-12' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull EFI changes from Ingo Molnar:
- Preliminary RISC-V enablement - the bulk of it will arrive via the
RISCV tree.
- Relax decompressed image placement rules for 32-bit ARM
- Add support for passing MOK certificate table contents via a config
table rather than a EFI variable.
- Add support for 18 bit DIMM row IDs in the CPER records.
- Work around broken Dell firmware that passes the entire Boot####
variable contents as the command line
- Add definition of the EFI_MEMORY_CPU_CRYPTO memory attribute so we
can identify it in the memory map listings.
- Don't abort the boot on arm64 if the EFI RNG protocol is available
but returns with an error
- Replace slashes with exclamation marks in efivarfs file names
- Split efi-pstore from the deprecated efivars sysfs code, so we can
disable the latter on !x86.
- Misc fixes, cleanups and updates.
* tag 'efi-core-2020-10-12' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (26 commits)
efi: mokvar: add missing include of asm/early_ioremap.h
efi: efivars: limit availability to X86 builds
efi: remove some false dependencies on CONFIG_EFI_VARS
efi: gsmi: fix false dependency on CONFIG_EFI_VARS
efi: efivars: un-export efivars_sysfs_init()
efi: pstore: move workqueue handling out of efivars
efi: pstore: disentangle from deprecated efivars module
efi: mokvar-table: fix some issues in new code
efi/arm64: libstub: Deal gracefully with EFI_RNG_PROTOCOL failure
efivarfs: Replace invalid slashes with exclamation marks in dentries.
efi: Delete deprecated parameter comments
efi/libstub: Fix missing-prototypes in string.c
efi: Add definition of EFI_MEMORY_CPU_CRYPTO and ability to report it
cper,edac,efi: Memory Error Record: bank group/address and chip id
edac,ghes,cper: Add Row Extension to Memory Error Record
efi/x86: Add a quirk to support command line arguments on Dell EFI firmware
efi/libstub: Add efi_warn and *_once logging helpers
integrity: Load certs from the EFI MOK config table
integrity: Move import of MokListRT certs to a separate routine
efi: Support for MOK variable config table
...
Diffstat (limited to 'drivers/firmware')
21 files changed, 709 insertions, 316 deletions
diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 3939699e62fe..da1887f72a51 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -4,20 +4,15 @@ menu "EFI (Extensible Firmware Interface) Support" config EFI_VARS tristate "EFI Variable Support via sysfs" - depends on EFI + depends on EFI && (X86 || IA64) default n help If you say Y here, you are able to get EFI (Extensible Firmware Interface) variable information via sysfs. You may read, write, create, and destroy EFI variables through this interface. - - Note that using this driver in concert with efibootmgr requires - at least test release version 0.5.0-test3 or later, which is - available from: - <http://linux.dell.com/efibootmgr/testing/efibootmgr-0.5.0-test3.tar.gz> - - Subsequent efibootmgr releases may be found at: - <http://github.com/vathpela/efibootmgr> + Note that this driver is only retained for compatibility with + legacy users: new users should use the efivarfs filesystem + instead. config EFI_ESRT bool @@ -26,7 +21,7 @@ config EFI_ESRT config EFI_VARS_PSTORE tristate "Register efivars backend for pstore" - depends on EFI_VARS && PSTORE + depends on PSTORE default y help Say Y here to enable use efivars as a backend to pstore. This @@ -137,7 +132,6 @@ config EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER config EFI_BOOTLOADER_CONTROL tristate "EFI Bootloader Control" - depends on EFI_VARS default n help This module installs a reboot hook, such that if reboot() is @@ -281,7 +275,7 @@ config EFI_EARLYCON config EFI_CUSTOM_SSDT_OVERLAYS bool "Load custom ACPI SSDT overlay from an EFI variable" - depends on EFI_VARS && ACPI + depends on EFI && ACPI default ACPI_TABLE_UPGRADE help Allow loading of an ACPI SSDT overlay from an EFI variable specified diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index 7a216984552b..e8da782280b6 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -28,11 +28,12 @@ obj-$(CONFIG_EFI_DEV_PATH_PARSER) += dev-path-parser.o obj-$(CONFIG_APPLE_PROPERTIES) += apple-properties.o obj-$(CONFIG_EFI_RCI2_TABLE) += rci2-table.o obj-$(CONFIG_EFI_EMBEDDED_FIRMWARE) += embedded-firmware.o +obj-$(CONFIG_LOAD_UEFI_KEYS) += mokvar-table.o fake_map-y += fake_mem.o fake_map-$(CONFIG_X86) += x86_fake_mem.o -arm-obj-$(CONFIG_EFI) := arm-init.o arm-runtime.o +arm-obj-$(CONFIG_EFI) := efi-init.o arm-runtime.o obj-$(CONFIG_ARM) += $(arm-obj-y) obj-$(CONFIG_ARM64) += $(arm-obj-y) obj-$(CONFIG_EFI_CAPSULE_LOADER) += capsule-loader.o diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index f564e15fbc7e..e15d484b6a5a 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -232,10 +232,20 @@ static int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg) n += scnprintf(msg + n, len - n, "rank: %d ", mem->rank); if (mem->validation_bits & CPER_MEM_VALID_BANK) n += scnprintf(msg + n, len - n, "bank: %d ", mem->bank); + if (mem->validation_bits & CPER_MEM_VALID_BANK_GROUP) + n += scnprintf(msg + n, len - n, "bank_group: %d ", + mem->bank >> CPER_MEM_BANK_GROUP_SHIFT); + if (mem->validation_bits & CPER_MEM_VALID_BANK_ADDRESS) + n += scnprintf(msg + n, len - n, "bank_address: %d ", + mem->bank & CPER_MEM_BANK_ADDRESS_MASK); if (mem->validation_bits & CPER_MEM_VALID_DEVICE) n += scnprintf(msg + n, len - n, "device: %d ", mem->device); - if (mem->validation_bits & CPER_MEM_VALID_ROW) - n += scnprintf(msg + n, len - n, "row: %d ", mem->row); + if (mem->validation_bits & (CPER_MEM_VALID_ROW | CPER_MEM_VALID_ROW_EXT)) { + u32 row = mem->row; + + row |= cper_get_mem_extension(mem->validation_bits, mem->extended); + n += scnprintf(msg + n, len - n, "row: %d ", row); + } if (mem->validation_bits & CPER_MEM_VALID_COLUMN) n += scnprintf(msg + n, len - n, "column: %d ", mem->column); if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION) @@ -250,6 +260,9 @@ static int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg) if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID) scnprintf(msg + n, len - n, "target_id: 0x%016llx ", mem->target_id); + if (mem->validation_bits & CPER_MEM_VALID_CHIP_ID) + scnprintf(msg + n, len - n, "chip_id: %d ", + mem->extended >> CPER_MEM_CHIP_ID_SHIFT); msg[n] = '\0'; return n; @@ -292,6 +305,7 @@ void cper_mem_err_pack(const struct cper_sec_mem_err *mem, cmem->requestor_id = mem->requestor_id; cmem->responder_id = mem->responder_id; cmem->target_id = mem->target_id; + cmem->extended = mem->extended; cmem->rank = mem->rank; cmem->mem_array_handle = mem->mem_array_handle; cmem->mem_dev_handle = mem->mem_dev_handle; diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/efi-init.c index 71c445d20258..f55a92ff12c0 100644 --- a/drivers/firmware/efi/arm-init.c +++ b/drivers/firmware/efi/efi-init.c @@ -236,6 +236,7 @@ void __init efi_init(void) reserve_regions(); efi_esrt_init(); + efi_mokvar_table_init(); memblock_reserve(data.phys_map & PAGE_MASK, PAGE_ALIGN(data.size + (data.phys_map & ~PAGE_MASK))); diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index feb7fe6f2da7..0ef086e43090 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c @@ -8,6 +8,8 @@ #define DUMP_NAME_LEN 66 +#define EFIVARS_DATA_SIZE_MAX 1024 + static bool efivars_pstore_disable = IS_ENABLED(CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE); @@ -18,6 +20,9 @@ module_param_named(pstore_disable, efivars_pstore_disable, bool, 0644); EFI_VARIABLE_BOOTSERVICE_ACCESS | \ EFI_VARIABLE_RUNTIME_ACCESS) +static LIST_HEAD(efi_pstore_list); +static DECLARE_WORK(efivar_work, NULL); + static int efi_pstore_open(struct pstore_info *psi) { psi->data = NULL; @@ -126,7 +131,7 @@ static inline int __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry, if (entry->deleting) { list_del(&entry->list); efivar_entry_iter_end(); - efivar_unregister(entry); + kfree(entry); if (efivar_entry_iter_begin()) return -EINTR; } else if (turn_off_scanning) @@ -169,7 +174,7 @@ static int efi_pstore_sysfs_entry_iter(struct pstore_record *record) { struct efivar_entry **pos = (struct efivar_entry **)&record->psi->data; struct efivar_entry *entry, *n; - struct list_head *head = &efivar_sysfs_list; + struct list_head *head = &efi_pstore_list; int size = 0; int ret; @@ -263,8 +268,9 @@ static int efi_pstore_write(struct pstore_record *record) ret = efivar_entry_set_safe(efi_name, vendor, PSTORE_EFI_ATTRIBUTES, preemptible(), record->size, record->psi->buf); - if (record->reason == KMSG_DUMP_OOPS) - efivar_run_worker(); + if (record->reason == KMSG_DUMP_OOPS && try_module_get(THIS_MODULE)) + if (!schedule_work(&efivar_work)) + module_put(THIS_MODULE); return ret; }; @@ -314,12 +320,12 @@ static int efi_pstore_erase_name(const char *name) if (efivar_entry_iter_begin()) return -EINTR; - found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, + found = __efivar_entry_iter(efi_pstore_erase_func, &efi_pstore_list, efi_name, &entry); efivar_entry_iter_end(); if (found && !entry->scanning) - efivar_unregister(entry); + kfree(entry); return found ? 0 : -ENOENT; } @@ -354,14 +360,77 @@ static struct pstore_info efi_pstore_info = { .erase = efi_pstore_erase, }; +static int efi_pstore_callback(efi_char16_t *name, efi_guid_t vendor, + unsigned long name_size, void *data) +{ + struct efivar_entry *entry; + int ret; + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + memcpy(entry->var.VariableName, name, name_size); + entry->var.VendorGuid = vendor; + + ret = efivar_entry_add(entry, &efi_pstore_list); + if (ret) + kfree(entry); + + return ret; +} + +static int efi_pstore_update_entry(efi_char16_t *name, efi_guid_t vendor, + unsigned long name_size, void *data) +{ + struct efivar_entry *entry = data; + + if (efivar_entry_find(name, vendor, &efi_pstore_list, false)) + return 0; + + memcpy(entry->var.VariableName, name, name_size); + memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t)); + + return 1; +} + +static void efi_pstore_update_entries(struct work_struct *work) +{ + struct efivar_entry *entry; + int err; + + /* Add new sysfs entries */ + while (1) { + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return; + + err = efivar_init(efi_pstore_update_entry, entry, + false, &efi_pstore_list); + if (!err) + break; + + efivar_entry_add(entry, &efi_pstore_list); + } + + kfree(entry); + module_put(THIS_MODULE); +} + static __init int efivars_pstore_init(void) { + int ret; + if (!efivars_kobject() || !efivar_supports_writes()) return 0; if (efivars_pstore_disable) return 0; + ret = efivar_init(efi_pstore_callback, NULL, true, &efi_pstore_list); + if (ret) + return ret; + efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL); if (!efi_pstore_info.buf) return -ENOMEM; @@ -374,6 +443,8 @@ static __init int efivars_pstore_init(void) efi_pstore_info.bufsize = 0; } + INIT_WORK(&efivar_work, efi_pstore_update_entries); + return 0; } diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 3aa07c3b5136..5e5480a0a32d 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -43,6 +43,9 @@ struct efi __read_mostly efi = { .esrt = EFI_INVALID_TABLE_ADDR, .tpm_log = EFI_INVALID_TABLE_ADDR, .tpm_final_log = EFI_INVALID_TABLE_ADDR, +#ifdef CONFIG_LOAD_UEFI_KEYS + .mokvar_table = EFI_INVALID_TABLE_ADDR, +#endif }; EXPORT_SYMBOL(efi); @@ -519,6 +522,9 @@ static const efi_config_table_type_t common_tables[] __initconst = { #ifdef CONFIG_EFI_RCI2_TABLE {DELLEMC_EFI_RCI2_TABLE_GUID, &rci2_table_phys }, #endif +#ifdef CONFIG_LOAD_UEFI_KEYS + {LINUX_EFI_MOK_VARIABLE_TABLE_GUID, &efi.mokvar_table, "MOKvar" }, +#endif {}, }; @@ -714,7 +720,7 @@ void __init efi_systab_report_header(const efi_table_hdr_t *systab_hdr, vendor); } -static __initdata char memory_type_name[][20] = { +static __initdata char memory_type_name[][13] = { "Reserved", "Loader Code", "Loader Data", @@ -722,14 +728,14 @@ static __initdata char memory_type_name[][20] = { "Boot Data", "Runtime Code", "Runtime Data", - "Conventional Memory", - "Unusable Memory", - "ACPI Reclaim Memory", - "ACPI Memory NVS", - "Memory Mapped I/O", - "MMIO Port Space", + "Conventional", + "Unusable", + "ACPI Reclaim", + "ACPI Mem NVS", + "MMIO", + "MMIO Port", "PAL Code", - "Persistent Memory", + "Persistent", }; char * __init efi_md_typeattr_format(char *buf, size_t size, @@ -756,26 +762,27 @@ char * __init efi_md_typeattr_format(char *buf, size_t size, if (attr & ~(EFI_MEMORY_UC | EFI_MEMORY_WC | EFI_MEMORY_WT | EFI_MEMORY_WB | EFI_MEMORY_UCE | EFI_MEMORY_RO | EFI_MEMORY_WP | EFI_MEMORY_RP | EFI_MEMORY_XP | - EFI_MEMORY_NV | EFI_MEMORY_SP | + EFI_MEMORY_NV | EFI_MEMORY_SP | EFI_MEMORY_CPU_CRYPTO | EFI_MEMORY_RUNTIME | EFI_MEMORY_MORE_RELIABLE)) snprintf(pos, size, "|attr=0x%016llx]", (unsigned long long)attr); else snprintf(pos, size, - "|%3s|%2s|%2s|%2s|%2s|%2s|%2s|%2s|%3s|%2s|%2s|%2s|%2s]", - attr & EFI_MEMORY_RUNTIME ? "RUN" : "", - attr & EFI_MEMORY_MORE_RELIABLE ? "MR" : "", - attr & EFI_MEMORY_SP ? "SP" : "", - attr & EFI_MEMORY_NV ? "NV" : "", - attr & EFI_MEMORY_XP ? "XP" : "", - attr & EFI_MEMORY_RP ? "RP" : "", - attr & EFI_MEMORY_WP ? "WP" : "", - attr & EFI_MEMORY_RO ? "RO" : "", - attr & EFI_MEMORY_UCE ? "UCE" : "", - attr & EFI_MEMORY_WB ? "WB" : "", - attr & EFI_MEMORY_WT ? "WT" : "", - attr & EFI_MEMORY_WC ? "WC" : "", - attr & EFI_MEMORY_UC ? "UC" : ""); + "|%3s|%2s|%2s|%2s|%2s|%2s|%2s|%2s|%2s|%3s|%2s|%2s|%2s|%2s]", + attr & EFI_MEMORY_RUNTIME ? "RUN" : "", + attr & EFI_MEMORY_MORE_RELIABLE ? "MR" : "", + attr & EFI_MEMORY_CPU_CRYPTO ? "CC" : "", + attr & EFI_MEMORY_SP ? "SP" : "", + attr & EFI_MEMORY_NV ? "NV" : "", + attr & EFI_MEMORY_XP ? "XP" : "", + attr & EFI_MEMORY_RP ? "RP" : "", + attr & EFI_MEMORY_WP ? "WP" : "", + attr & EFI_MEMORY_RO ? "RO" : "", + attr & EFI_MEMORY_UCE ? "UCE" : "", + attr & EFI_MEMORY_WB ? "WB" : "", + attr & EFI_MEMORY_WT ? "WT" : "", + attr & EFI_MEMORY_WC ? "WC" : "", + attr & EFI_MEMORY_UC ? "UC" : ""); return buf; } diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c index dcea137142b3..e6b16b3a17a8 100644 --- a/drivers/firmware/efi/efivars.c +++ b/drivers/firmware/efi/efivars.c @@ -22,10 +22,8 @@ MODULE_AUTHOR("Matt Domsch <Matt_Domsch@Dell.com>"); MODULE_DESCRIPTION("sysfs interface to EFI Variables"); MODULE_LICENSE("GPL"); MODULE_VERSION(EFIVARS_VERSION); -MODULE_ALIAS("platform:efivars"); -LIST_HEAD(efivar_sysfs_list); -EXPORT_SYMBOL_GPL(efivar_sysfs_list); +static LIST_HEAD(efivar_sysfs_list); static struct kset *efivars_kset; @@ -591,42 +589,6 @@ out_free: return error; } -static int efivar_update_sysfs_entry(efi_char16_t *name, efi_guid_t vendor, - unsigned long name_size, void *data) -{ - struct efivar_entry *entry = data; - - if (efivar_entry_find(name, vendor, &efivar_sysfs_list, false)) - return 0; - - memcpy(entry->var.VariableName, name, name_size); - memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t)); - - return 1; -} - -static void efivar_update_sysfs_entries(struct work_struct *work) -{ - struct efivar_entry *entry; - int err; - - /* Add new sysfs entries */ - while (1) { - entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) - return; - - err = efivar_init(efivar_update_sysfs_entry, entry, - false, &efivar_sysfs_list); - if (!err) - break; - - efivar_create_sysfs_entry(entry); - } - - kfree(entry); -} - static int efivars_sysfs_callback(efi_char16_t *name, efi_guid_t vendor, unsigned long name_size, void *data) { @@ -675,7 +637,7 @@ static void efivars_sysfs_exit(void) kset_unregister(efivars_kset); } -int efivars_sysfs_init(void) +static int efivars_sysfs_init(void) { struct kobject *parent_kobj = efivars_kobject(); int error = 0; @@ -701,11 +663,8 @@ int efivars_sysfs_init(void) return error; } - INIT_WORK(&efivar_work, efivar_update_sysfs_entries); - return 0; } -EXPORT_SYMBOL_GPL(efivars_sysfs_init); module_init(efivars_sysfs_init); module_exit(efivars_sysfs_exit); diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c index d08e5d55838c..4b5b2403b3a0 100644 --- a/drivers/firmware/efi/libstub/arm32-stub.c +++ b/drivers/firmware/efi/libstub/arm32-stub.c @@ -113,162 +113,58 @@ void free_screen_info(struct screen_info *si) efi_bs_call(free_pool, si); } -static efi_status_t reserve_kernel_base(unsigned long dram_base, - unsigned long *reserve_addr, - unsigned long *reserve_size) -{ - efi_physical_addr_t alloc_addr; - efi_memory_desc_t *memory_map; - unsigned long nr_pages, map_size, desc_size, buff_size; - efi_status_t status; - unsigned long l; - - struct efi_boot_memmap map = { - .map = &memory_map, - .map_size = &map_size, - .desc_size = &desc_size, - .desc_ver = NULL, - .key_ptr = NULL, - .buff_size = &buff_size, - }; - - /* - * Reserve memory for the uncompressed kernel image. This is - * all that prevents any future allocations from conflicting - * with the kernel. Since we can't tell from the compressed - * image how much DRAM the kernel actually uses (due to BSS - * size uncertainty) we allocate the maximum possible size. - * Do this very early, as prints can cause memory allocations - * that may conflict with this. - */ - alloc_addr = dram_base + MAX_UNCOMP_KERNEL_SIZE; - nr_pages = MAX_UNCOMP_KERNEL_SIZE / EFI_PAGE_SIZE; - status = efi_bs_call(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS, - EFI_BOOT_SERVICES_DATA, nr_pages, &alloc_addr); - if (status == EFI_SUCCESS) { - if (alloc_addr == dram_base) { - *reserve_addr = alloc_addr; - *reserve_size = MAX_UNCOMP_KERNEL_SIZE; - return EFI_SUCCESS; - } - /* - * If we end up here, the allocation succeeded but starts below - * dram_base. This can only occur if the real base of DRAM is - * not a multiple of 128 MB, in which case dram_base will have - * been rounded up. Since this implies that a part of the region - * was already occupied, we need to fall through to the code - * below to ensure that the existing allocations don't conflict. - * For this reason, we use EFI_BOOT_SERVICES_DATA above and not - * EFI_LOADER_DATA, which we wouldn't able to distinguish from - * allocations that we want to disallow. - */ - } - - /* - * If the allocation above failed, we may still be able to proceed: - * if the only allocations in the region are of types that will be - * released to the OS after ExitBootServices(), the decompressor can - * safely overwrite them. - */ - status = efi_get_memory_map(&map); - if (status != EFI_SUCCESS) { - efi_err("reserve_kernel_base(): Unable to retrieve memory map.\n"); - return status; - } - - for (l = 0; l < map_size; l += desc_size) { - efi_memory_desc_t *desc; - u64 start, end; - - desc = (void *)memory_map + l; - start = desc->phys_addr; - end = start + desc->num_pages * EFI_PAGE_SIZE; - - /* Skip if entry does not intersect with region */ - if (start >= dram_base + MAX_UNCOMP_KERNEL_SIZE || - end <= dram_base) - continue; - - switch (desc->type) { - case EFI_BOOT_SERVICES_CODE: - case EFI_BOOT_SERVICES_DATA: - /* Ignore types that are released to the OS anyway */ - continue; - - case EFI_CONVENTIONAL_MEMORY: - /* Skip soft reserved conventional memory */ - if (efi_soft_reserve_enabled() && - (desc->attribute & EFI_MEMORY_SP)) - continue; - - /* - * Reserve the intersection between this entry and the - * region. - */ - start = max(start, (u64)dram_base); - end = min(end, (u64)dram_base + MAX_UNCOMP_KERNEL_SIZE); - - status = efi_bs_call(allocate_pages, - EFI_ALLOCATE_ADDRESS, - EFI_LOADER_DATA, - (end - start) / EFI_PAGE_SIZE, - &start); - if (status != EFI_SUCCESS) { - efi_err("reserve_kernel_base(): alloc failed.\n"); - goto out; - } - break; - - case EFI_LOADER_CODE: - case EFI_LOADER_DATA: - /* - * These regions may be released and reallocated for - * another purpose (including EFI_RUNTIME_SERVICE_DATA) - * at any time during the execution of the OS loader, - * so we cannot consider them as safe. - */ - default: - /* - * Treat any other allocation in the region as unsafe */ - status = EFI_OUT_OF_RESOURCES; - goto out; - } - } - - status = EFI_SUCCESS; -out: - efi_bs_call(free_pool, memory_map); - return status; -} - efi_status_t handle_kernel_image(unsigned long *image_addr, unsigned long *image_size, unsigned long *reserve_addr, unsigned long *reserve_size, - unsigned long dram_base, efi_loaded_image_t *image) { - unsigned long kernel_base; + const int slack = TEXT_OFFSET - 5 * PAGE_SIZE; + int alloc_size = MAX_UNCOMP_KERNEL_SIZE + EFI_PHYS_ALIGN; + unsigned long alloc_base, kernel_base; efi_status_t status; - /* use a 16 MiB aligned base for the decompressed kernel */ - kernel_base = round_up(dram_base, SZ_16M) + TEXT_OFFSET; - /* - * Note that some platforms (notably, the Raspberry Pi 2) put - * spin-tables and other pieces of firmware at the base of RAM, - * abusing the fact that the window of TEXT_OFFSET bytes at the - * base of the kernel image is only partially used at the moment. - * (Up to 5 pages are used for the swapper page tables) + * Allocate space for the decompressed kernel as low as possible. + * The region should be 16 MiB aligned, but the first 'slack' bytes + * are not used by Linux, so we allow those to be occupied by the + * firmware. */ - status = reserve_kernel_base(kernel_base - 5 * PAGE_SIZE, reserve_addr, - reserve_size); + status = efi_low_alloc_above(alloc_size, EFI_PAGE_SIZE, &alloc_base, 0x0); if (status != EFI_SUCCESS) { efi_err("Unable to allocate memory for uncompressed kernel.\n"); return status; } - *image_addr = kernel_base; + if ((alloc_base % EFI_PHYS_ALIGN) > slack) { + /* + * More than 'slack' bytes are already occupied at the base of + * the allocation, so we need to advance to the next 16 MiB block. + */ + kernel_base = round_up(alloc_base, EFI_PHYS_ALIGN); + efi_info("Free memory starts at 0x%lx, setting kernel_base to 0x%lx\n", + alloc_base, kernel_base); + } else { + kernel_base = round_down(alloc_base, EFI_PHYS_ALIGN); + } + + *reserve_addr = kernel_base + slack; + *reserve_size = MAX_UNCOMP_KERNEL_SIZE; + + /* now free the parts that we will not use */ + if (*reserve_addr > alloc_base) { + efi_bs_call(free_pages, alloc_base, + (*reserve_addr - alloc_base) / EFI_PAGE_SIZE); + alloc_size -= *reserve_addr - alloc_base; + } + efi_bs_call(free_pages, *reserve_addr + MAX_UNCOMP_KERNEL_SIZE, + (alloc_size - MAX_UNCOMP_KERNEL_SIZE) / EFI_PAGE_SIZE); + + *image_addr = kernel_base + TEXT_OFFSET; *image_size = 0; + + efi_debug("image addr == 0x%lx, reserve_addr == 0x%lx\n", + *image_addr, *reserve_addr); + return EFI_SUCCESS; } diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index fc178398f1ac..22ece1ad68a8 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -50,7 +50,6 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, unsigned long *image_size, unsigned long *reserve_addr, unsigned long *reserve_size, - unsigned long dram_base, efi_loaded_image_t *image) { efi_status_t status; @@ -62,10 +61,12 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, status = efi_get_random_bytes(sizeof(phys_seed), (u8 *)&phys_seed); if (status == EFI_NOT_FOUND) { - efi_info("EFI_RNG_PROTOCOL unavailable, no randomness supplied\n"); + efi_info("EFI_RNG_PROTOCOL unavailable, KASLR will be disabled\n"); + efi_nokaslr = true; } else if (status != EFI_SUCCESS) { - efi_err("efi_get_random_bytes() failed\n"); - return status; + efi_err("efi_get_random_bytes() failed (0x%lx), KASLR will be disabled\n", + status); + efi_nokaslr = true; } } else { efi_info("KASLR disabled on kernel command line\n"); diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index f735db55adc0..aa8da0a49829 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -239,6 +239,102 @@ efi_status_t efi_parse_options(char const *cmdline) } /* + * The EFI_LOAD_OPTION descriptor has the following layout: + * u32 Attributes; + * u16 FilePathListLength; + * u16 Description[]; + * efi_device_path_protocol_t FilePathList[]; + * u8 OptionalData[]; + * + * This function validates and unpacks the variable-size data fields. + */ +static +bool efi_load_option_unpack(efi_load_option_unpacked_t *dest, + const efi_load_option_t *src, size_t size) +{ + const void *pos; + u16 c; + efi_device_path_protocol_t header; + const efi_char16_t *description; + const efi_device_path_protocol_t *file_path_list; + + if (size < offsetof(efi_load_option_t, variable_data)) + return false; + pos = src->variable_data; + size -= offsetof(efi_load_option_t, variable_data); + + if ((src->attributes & ~EFI_LOAD_OPTION_MASK) != 0) + return false; + + /* Scan description. */ + description = pos; + do { + if (size < sizeof(c)) + return false; + c = *(const u16 *)pos; + pos += sizeof(c); + size -= sizeof(c); + } while (c != L'\0'); + + /* Scan file_path_list. */ + file_path_list = pos; + do { + if (size < sizeof(header)) + return false; + header = *(const efi_device_path_protocol_t *)pos; + if (header.length < sizeof(header)) + return false; + if (size < header.length) + return false; + pos += header.length; + size -= header.length; + } while ((header.type != EFI_DEV_END_PATH && header.type != EFI_DEV_END_PATH2) || + (header.sub_type != EFI_DEV_END_ENTIRE)); + if (pos != (const void *)file_path_list + src->file_path_list_length) + return false; + + dest->attributes = src->attributes; + dest->file_path_list_length = src->file_path_list_length; + dest->description = description; + dest->file_path_list = file_path_list; + dest->optional_data_size = size; + dest->optional_data = size ? pos : NULL; + + return true; +} + +/* + * At least some versions of Dell firmware pass the entire contents of the + * Boot#### variable, i.e. the EFI_LOAD_OPTION descriptor, rather than just the + * OptionalData field. + * + * Detect this case and extract OptionalData. + */ +void efi_apply_loadoptions_quirk(const void **load_options, int *load_options_size) +{ + const efi_load_option_t *load_option = *load_options; + efi_load_option_unpacked_t load_option_unpacked; + + if (!IS_ENABLED(CONFIG_X86)) + return; + if (!load_option) + return; + if (*load_options_size < sizeof(*load_option)) + return; + if ((load_option->attributes & ~EFI_LOAD_OPTION_BOOT_MASK) != 0) + return; + + if (!efi_load_option_unpack(&load_option_unpacked, load_option, *load_options_size)) + return; + + efi_warn_once(FW_BUG "LoadOptions is an EFI_LOAD_OPTION descriptor\n"); + efi_warn_once(FW_BUG "Using OptionalData as a workaround\n"); + + *load_options = load_option_unpacked.optional_data; + *load_options_size = load_option_unpacked.optional_data_size; +} + +/* * Convert the unicode UEFI command line to ASCII to pass to kernel. * Size of memory allocated return in *cmd_line_len. * Returns NULL on error. @@ -247,12 +343,15 @@ char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len) { const u16 *s2; unsigned long cmdline_addr = 0; - int options_chars = efi_table_attr(image, load_options_size) / 2; + int options_chars = efi_table_attr(image, load_options_size); const u16 *options = efi_table_attr(image, load_options); int options_bytes = 0, safe_options_bytes = 0; /* UTF-8 bytes */ bool in_quote = false; efi_status_t status; + efi_apply_loadoptions_quirk((const void **)&options, &options_chars); + options_chars /= sizeof(*options); + if (options) { s2 = options; while (options_bytes < COMMAND_LINE_SIZE && options_chars--) { diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c index a5a405d8ab44..311a16802dd6 100644 --- a/drivers/firmware/efi/libstub/efi-stub.c +++ b/drivers/firmware/efi/libstub/efi-stub.c @@ -87,40 +87,6 @@ static void install_memreserve_table(void) efi_err("Failed to install memreserve config table!\n"); } -static unsigned long get_dram_base(void) -{ - efi_status_t status; - unsigned long map_size, buff_size; - unsigned long membase = EFI_ERROR; - struct efi_memory_map map; - efi_memory_desc_t *md; - struct efi_boot_memmap boot_map; - - boot_map.map = (efi_memory_desc_t **)&map.map; - boot_map.map_size = &map_size; - boot_map.desc_size = &map.desc_size; - boot_map.desc_ver = NULL; - boot_map.key_ptr = NULL; - boot_map.buff_size = &buff_size; - - status = efi_get_memory_map(&boot_map); - if (status != EFI_SUCCESS) - return membase; - - map.map_end = map.map + map_size; - - for_each_efi_memory_desc_in_map(&map, md) { - if (md->attribute & EFI_MEMORY_WB) { - if (membase > md->phys_addr) - membase = md->phys_addr; - } - } - - efi_bs_call(free_pool, map.map); - - return membase; -} - /* * EFI entry point for the arm/arm64 EFI stubs. This is the entrypoint * that is described in the PE/COFF header. Most of the code is the same @@ -134,7 +100,6 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, efi_status_t status; unsigned long image_addr; unsigned long image_size = 0; - unsigned long dram_base; /* addr/point and size pairs for memory management*/ unsigned long initrd_addr = 0; unsigned long initrd_size = 0; @@ -174,13 +139,6 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, goto fail; } - dram_base = get_dram_base(); - if (dram_base == EFI_ERROR) { - efi_err("Failed to find DRAM base\n"); - status = EFI_LOAD_ERROR; - goto fail; - } - /* * Get the command line from EFI, using the LOADED_IMAGE * protocol. We are going to copy the command line into the @@ -218,7 +176,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, status = handle_kernel_image(&image_addr, &image_size, &reserve_addr, &reserve_size, - dram_base, image); + image); if (status != EFI_SUCCESS) { efi_err("Failed to relocate kernel\n"); goto fail_free_screeninfo; @@ -262,7 +220,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, efi_info("Generating empty DTB\n"); if (!efi_noinitrd) { - max_addr = efi_get_max_initrd_addr(dram_base, image_addr); + max_addr = efi_get_max_initrd_addr(image_addr); status = efi_load_initrd(image, &initrd_addr, &initrd_size, ULONG_MAX, max_addr); if (status != EFI_SUCCESS) @@ -306,7 +264,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, install_memreserve_table(); status = allocate_new_fdt_and_exit_boot(handle, &fdt_addr, - efi_get_max_fdt_addr(dram_base), + efi_get_max_fdt_addr(image_addr), initrd_addr, initrd_size, cmdline_ptr, fdt_addr, fdt_size); if (status != EFI_SUCCESS) diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 85050f5a1b28..2d7abcd99de9 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -10,9 +10,6 @@ #include <linux/types.h> #include <asm/efi.h> -/* error code which can't be mistaken for valid address */ -#define EFI_ERROR (~0UL) - /* * __init annotations should not be used in the EFI stub, since the code is * either included in the decompressor (x86, ARM) where they have no effect, @@ -55,11 +52,34 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, #define efi_info(fmt, ...) \ efi_printk(KERN_INFO fmt, ##__VA_ARGS__) +#define efi_warn(fmt, ...) \ + efi_printk(KERN_WARNING "WARNING: " fmt, ##__VA_ARGS__) #define efi_err(fmt, ...) \ efi_printk(KERN_ERR "ERROR: " fmt, ##__VA_ARGS__) #define efi_debug(fmt, ...) \ efi_printk(KERN_DEBUG "DEBUG: " fmt, ##__VA_ARGS__) +#define efi_printk_once(fmt, ...) \ +({ \ + static bool __print_once; \ + bool __ret_print_once = !__print_once; \ + \ + if (!__print_once) { \ + __print_once = true; \ + efi_printk(fmt, ##__VA_ARGS__); \ + } \ + __ret_print_once; \ +}) + +#define efi_info_once(fmt, ...) \ + efi_printk_once(KERN_INFO fmt, ##__VA_ARGS__) +#define efi_warn_once(fmt, ...) \ + efi_printk_once(KERN_WARNING "WARNING: " fmt, ##__VA_ARGS__) +#define efi_err_once(fmt, ...) \ + efi_printk_once(KERN_ERR "ERROR: " fmt, ##__VA_ARGS__) +#define efi_debug_once(fmt, ...) \ + efi_printk_once(KERN_DEBUG "DEBUG: " fmt, ##__VA_ARGS__) + /* Helper macros for the usual case of using simple C variables: */ #ifndef fdt_setprop_inplace_var #define fdt_setprop_inplace_var(fdt, node_offset, name, var) \ @@ -688,6 +708,35 @@ union efi_load_file_protocol { } mixed_mode; }; +typedef struct { + u32 attributes; + u16 file_path_list_length; + u8 variable_data[]; + // efi_char16_t description[]; + // efi_device_path_protocol_t file_path_list[]; + // u8 optional_data[]; +} __packed efi_load_option_t; + +#define EFI_LOAD_OPTION_ACTIVE 0x0001U +#define EFI_LOAD_OPTION_FORCE_RECONNECT 0x0002U +#define EFI_LOAD_OPTION_HIDDEN 0x0008U +#define EFI_LOAD_OPTION_CATEGORY 0x1f00U +#define EFI_LOAD_OPTION_CATEGORY_BOOT 0x0000U +#define EFI_LOAD_OPTION_CATEGORY_APP 0x0100U + +#define EFI_LOAD_OPTION_BOOT_MASK \ + (EFI_LOAD_OPTION_ACTIVE|EFI_LOAD_OPTION_HIDDEN|EFI_LOAD_OPTION_CATEGORY) +#define EFI_LOAD_OPTION_MASK (EFI_LOAD_OPTION_FORCE_RECONNECT|EFI_LOAD_OPTION_BOOT_MASK) + +typedef struct { + u32 attributes; + u16 file_path_list_length; + const efi_char16_t *description; + const efi_device_path_protocol_t *file_path_list; + size_t optional_data_size; + const void *optional_data; +} efi_load_option_unpacked_t; + void efi_pci_disable_bridge_busmaster(void); typedef efi_status_t (*efi_exit_boot_map_processing)( @@ -730,6 +779,8 @@ __printf(1, 2) int efi_printk(char const *fmt, ...); void efi_free(unsigned long size, unsigned long addr); +void efi_apply_loadoptions_quirk(const void **load_options, int *load_options_size); + char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len); efi_status_t efi_get_memory_map(struct efi_boot_memmap *map); @@ -740,6 +791,9 @@ efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr, efi_status_t efi_allocate_pages_aligned(unsigned long size, unsigned long *addr, unsigned long max, unsigned long align); +efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, + unsigned long *addr, unsigned long min); + efi_status_t efi_relocate_kernel(unsigned long *image_addr, unsigned long image_size, unsigned long alloc_size, @@ -786,7 +840,6 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, unsigned long *image_size, unsigned long *reserve_addr, unsigned long *reserve_size, - unsigned long dram_base, efi_loaded_image_t *image); asmlinkage void __noreturn efi_enter_kernel(unsigned long entrypoint, diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c index 11ecf3c4640e..368cd60000ee 100644 --- a/drivers/firmware/efi/libstub/fdt.c +++ b/drivers/firmware/efi/libstub/fdt.c @@ -136,7 +136,7 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size, if (status) goto fdt_set_fail; - if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) { + if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && !efi_nokaslr) { efi_status_t efi_status; efi_status = efi_get_random_bytes(sizeof(fdt_val64), @@ -145,8 +145,6 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size, status = fdt_setprop_var(fdt, node, "kaslr-seed", fdt_val64); if (status) goto fdt_set_fail; - } else if (efi_status != EFI_NOT_FOUND) { - return efi_status; } } diff --git a/drivers/firmware/efi/libstub/file.c b/drivers/firmware/efi/libstub/file.c index 630caa6b1f4c..4e81c6077188 100644 --- a/drivers/firmware/efi/libstub/file.c +++ b/drivers/firmware/efi/libstub/file.c @@ -136,7 +136,7 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image, unsigned long *load_size) { const efi_char16_t *cmdline = image->load_options; - int cmdline_len = image->load_options_size / 2; + int cmdline_len = image->load_options_size; unsigned long efi_chunk_size = ULONG_MAX; efi_file_protocol_t *volume = NULL; efi_file_protocol_t *file; @@ -148,6 +148,9 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image, if (!load_addr || !load_size) return EFI_INVALID_PARAMETER; + efi_apply_loadoptions_quirk((const void **)&cmdline, &cmdline_len); + cmdline_len /= sizeof(*cmdline); + if (IS_ENABLED(CONFIG_X86) && !efi_nochunk) efi_chunk_size = EFI_READ_CHUNK_SIZE; diff --git a/drivers/firmware/efi/libstub/relocate.c b/drivers/firmware/efi/libstub/relocate.c index 9b1aaf8b123f..8ee9eb2b9039 100644 --- a/drivers/firmware/efi/libstub/relocate.c +++ b/drivers/firmware/efi/libstub/relocate.c @@ -20,8 +20,8 @@ * * Return: status code */ -static efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, - unsigned long *addr, unsigned long min) +efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, + unsigned long *addr, unsigned long min) { unsigned long map_size, desc_size, buff_size; efi_memory_desc_t *map; diff --git a/drivers/firmware/efi/libstub/string.c b/drivers/firmware/efi/libstub/string.c index 1ac2f8764715..5d13e43869ee 100644 --- a/drivers/firmware/efi/libstub/string.c +++ b/drivers/firmware/efi/libstub/string.c @@ -7,6 +7,7 @@ */ #include <linux/ctype.h> +#include <linux/kernel.h> #include <linux/types.h> #include <linux/string.h> diff --git a/drivers/firmware/efi/libstub/vsprintf.c b/drivers/firmware/efi/libstub/vsprintf.c index e65ef49a54cd..1088e288c04d 100644 --- a/drivers/firmware/efi/libstub/vsprintf.c +++ b/drivers/firmware/efi/libstub/vsprintf.c @@ -135,7 +135,7 @@ char *number(char *end, unsigned long long num, int base, char locase) break; default: unreachable(); - }; + } return end; } diff --git a/drivers/firmware/efi/mokvar-table.c b/drivers/firmware/efi/mokvar-table.c new file mode 100644 index 000000000000..d8bc01340686 --- /dev/null +++ b/drivers/firmware/efi/mokvar-table.c @@ -0,0 +1,359 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mokvar-table.c + * + * Copyright (c) 2020 Red Hat + * Author: Lenny Szubowicz <lszubowi@redhat.com> + * + * This module contains the kernel support for the Linux EFI Machine + * Owner Key (MOK) variable configuration table, which is identified by + * the LINUX_EFI_MOK_VARIABLE_TABLE_GUID. + * + * This EFI configuration table provides a more robust alternative to + * EFI volatile variables by which an EFI boot loader can pass the + * contents of the Machine Owner Key (MOK) certificate stores to the + * kernel during boot. If both the EFI MOK config table and corresponding + * EFI MOK variables are present, the table should be considered as + * more authoritative. + * + * This module includes code that validates and maps the EFI MOK table, + * if it's presence was detected very early in boot. + * + * Kernel interface routines are provided to walk through all the + * entries in the MOK config table or to search for a specific named + * entry. + * + * The contents of the individual named MOK config table entries are + * made available to user space via read-only sysfs binary files under: + * + * /sys/firmware/efi/mok-variables/ + * + */ +#define pr_fmt(fmt) "mokvar: " fmt + +#include <linux/capability.h> +#include <linux/efi.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/kobject.h> +#include <linux/list.h> +#include <linux/slab.h> + +#include <asm/early_ioremap.h> + +/* + * The LINUX_EFI_MOK_VARIABLE_TABLE_GUID config table is a packed + * sequence of struct efi_mokvar_table_entry, one for each named + * MOK variable. The sequence is terminated by an entry with a + * completely NULL name and 0 data size. + * + * efi_mokvar_table_size is set to the computed size of the + * MOK config table by efi_mokvar_table_init(). This will be + * non-zero if and only if the table if present and has been + * validated by efi_mokvar_table_init(). + */ +static size_t efi_mokvar_table_size; + +/* + * efi_mokvar_table_va is the kernel virtual address at which the + * EFI MOK config table has been mapped by efi_mokvar_sysfs_init(). + */ +static struct efi_mokvar_table_entry *efi_mokvar_table_va; + +/* + * Each /sys/firmware/efi/mok-variables/ sysfs file is represented by + * an instance of struct efi_mokvar_sysfs_attr on efi_mokvar_sysfs_list. + * bin_attr.private points to the associated EFI MOK config table entry. + * + * This list is created during boot and then remains unchanged. + * So no synchronization is currently required to walk the list. + */ +struct efi_mokvar_sysfs_attr { + struct bin_attribute bin_attr; + struct list_head node; +}; + +static LIST_HEAD(efi_mokvar_sysfs_list); +static struct kobject *mokvar_kobj; + +/* + * efi_mokvar_table_init() - Early boot validation of EFI MOK config table + * + * If present, validate and compute the size of the EFI MOK variable + * configuration table. This table may be provided by an EFI boot loader + * as an alternative to ordinary EFI variables, due to platform-dependent + * limitations. The memory occupied by this table is marked as reserved. + * + * This routine must be called before efi_free_boot_services() in order + * to guarantee that it can mark the table as reserved. + * + * Implicit inputs: + * efi.mokvar_table: Physical address of EFI MOK variable config table + * or special value that indicates no such table. + * + * Implicit outputs: + * efi_mokvar_table_size: Computed size of EFI MOK variable config table. + * The table is considered present and valid if this + * is non-zero. + */ +void __init efi_mokvar_table_init(void) +{ + efi_memory_desc_t md; + void *va = NULL; + unsigned long cur_offset = 0; + unsigned long offset_limit; + unsigned long map_size = 0; + unsigned long map_size_needed = 0; + unsigned long size; + struct efi_mokvar_table_entry *mokvar_entry; + int err; + + if (!efi_enabled(EFI_MEMMAP)) + return; + + if (efi.mokvar_table == EFI_INVALID_TABLE_ADDR) + return; + /* + * The EFI MOK config table must fit within a single EFI memory + * descriptor range. + */ + err = efi_mem_desc_lookup(efi.mokvar_table, &md); + if (err) { + pr_warn("EFI MOKvar config table is not within the EFI memory map\n"); + return; + } + + offset_limit = efi_mem_desc_end(&md) - efi.mokvar_table; + + /* + * Validate the MOK config table. Since there is no table header + * from which we could get the total size of the MOK config table, + * we compute the total size as we validate each variably sized + * entry, remapping as necessary. + */ + err = -EINVAL; + while (cur_offset + sizeof(*mokvar_entry) <= offset_limit) { + mokvar_entry = va + cur_offset; + map_size_needed = cur_offset + sizeof(*mokvar_entry); + if (map_size_needed > map_size) { + if (va) + early_memunmap(va, map_size); + /* + * Map a little more than the fixed size entry + * header, anticipating some data. It's safe to + * do so as long as we stay within current memory + * descriptor. + */ + map_size = min(map_size_needed + 2*EFI_PAGE_SIZE, + offset_limit); + va = early_memremap(efi.mokvar_table, map_size); + if (!va) { + pr_err("Failed to map EFI MOKvar config table pa=0x%lx, size=%lu.\n", + efi.mokvar_table, map_size); + return; + } + mokvar_entry = va + cur_offset; + } + + /* Check for last sentinel entry */ + if (mokvar_entry->name[0] == '\0') { + if (mokvar_entry->data_size != 0) + break; + err = 0; + break; + } + + /* Sanity check that the name is null terminated */ + size = strnlen(mokvar_entry->name, + sizeof(mokvar_entry->name)); + if (size >= sizeof(mokvar_entry->name)) + break; + + /* Advance to the next entry */ + cur_offset = map_size_needed + mokvar_entry->data_size; + } + + if (va) + early_memunmap(va, map_size); + if (err) { + pr_err("EFI MOKvar config table is not valid\n"); + return; + } + efi_mem_reserve(efi.mokvar_table, map_size_needed); + efi_mokvar_table_size = map_size_needed; +} + +/* + * efi_mokvar_entry_next() - Get next entry in the EFI MOK config table + * + * mokvar_entry: Pointer to current EFI MOK config table entry + * or null. Null indicates get first entry. + * Passed by reference. This is updated to the + * same value as the return value. + * + * Returns: Pointer to next EFI MOK config table entry + * or null, if there are no more entries. + * Same value is returned in the mokvar_entry + * parameter. + * + * This routine depends on the EFI MOK config table being entirely + * mapped with it's starting virtual address in efi_mokvar_table_va. + */ +struct efi_mokvar_table_entry *efi_mokvar_entry_next( + struct efi_mokvar_table_entry **mokvar_entry) +{ + struct efi_mokvar_table_entry *mokvar_cur; + struct efi_mokvar_table_entry *mokvar_next; + size_t size_cur; + + mokvar_cur = *mokvar_entry; + *mokvar_entry = NULL; + + if (efi_mokvar_table_va == NULL) + return NULL; + + if (mokvar_cur == NULL) { + mokvar_next = efi_mokvar_table_va; + } else { + if (mokvar_cur->name[0] == '\0') + return NULL; + size_cur = sizeof(*mokvar_cur) + mokvar_cur->data_size; + mokvar_next = (void *)mokvar_cur + size_cur; + } + + if (mokvar_next->name[0] == '\0') + return NULL; + + *mokvar_entry = mokvar_next; + return mokvar_next; +} + +/* + * efi_mokvar_entry_find() - Find EFI MOK config entry by name + * + * name: Name of the entry to look for. + * + * Returns: Pointer to EFI MOK config table entry if found; + * null otherwise. + * + * This routine depends on the EFI MOK config table being entirely + * mapped with it's starting virtual address in efi_mokvar_table_va. + */ +struct efi_mokvar_table_entry *efi_mokvar_entry_find(const char *name) +{ + struct efi_mokvar_table_entry *mokvar_entry = NULL; + + while (efi_mokvar_entry_next(&mokvar_entry)) { + if (!strncmp(name, mokvar_entry->name, + sizeof(mokvar_entry->name))) + return mokvar_entry; + } + return NULL; +} + +/* + * efi_mokvar_sysfs_read() - sysfs binary file read routine + * + * Returns: Count of bytes read. + * + * Copy EFI MOK config table entry data for this mokvar sysfs binary file + * to the supplied buffer, starting at the specified offset into mokvar table + * entry data, for the specified count bytes. The copy is limited by the + * amount of data in this mokvar config table entry. + */ +static ssize_t efi_mokvar_sysfs_read(struct file *file, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct efi_mokvar_table_entry *mokvar_entry = bin_attr->private; + + if (!capable(CAP_SYS_ADMIN)) + return 0; + + if (off >= mokvar_entry->data_size) + return 0; + if (count > mokvar_entry->data_size - off) + count = mokvar_entry->data_size - off; + + memcpy(buf, mokvar_entry->data + off, count); + return count; +} + +/* + * efi_mokvar_sysfs_init() - Map EFI MOK config table and create sysfs + * + * Map the EFI MOK variable config table for run-time use by the kernel + * and create the sysfs entries in /sys/firmware/efi/mok-variables/ + * + * This routine just returns if a valid EFI MOK variable config table + * was not found earlier during boot. + * + * This routine must be called during a "middle" initcall phase, i.e. + * after efi_mokvar_table_init() but before UEFI certs are loaded + * during late init. + * + * Implicit inputs: + * efi.mokvar_table: Physical address of EFI MOK variable config table + * or special value that indicates no such table. + * + * efi_mokvar_table_size: Computed size of EFI MOK variable config table. + * The table is considered present and valid if this + * is non-zero. + * + * Implicit outputs: + * efi_mokvar_table_va: Start virtual address of the EFI MOK config table. + */ +static int __init efi_mokvar_sysfs_init(void) +{ + void *config_va; + struct efi_mokvar_table_entry *mokvar_entry = NULL; + struct efi_mokvar_sysfs_attr *mokvar_sysfs = NULL; + int err = 0; + + if (efi_mokvar_table_size == 0) + return -ENOENT; + + config_va = memremap(efi.mokvar_table, efi_mokvar_table_size, + MEMREMAP_WB); + if (!config_va) { + pr_err("Failed to map EFI MOKvar config table\n"); + return -ENOMEM; + } + efi_mokvar_table_va = config_va; + + mokvar_kobj = kobject_create_and_add("mok-variables", efi_kobj); + if (!mokvar_kobj) { + pr_err("Failed to create EFI mok-variables sysfs entry\n"); + return -ENOMEM; + } + + while (efi_mokvar_entry_next(&mokvar_entry)) { + mokvar_sysfs = kzalloc(sizeof(*mokvar_sysfs), GFP_KERNEL); + if (!mokvar_sysfs) { + err = -ENOMEM; + break; + } + + sysfs_bin_attr_init(&mokvar_sysfs->bin_attr); + mokvar_sysfs->bin_attr.private = mokvar_entry; + mokvar_sysfs->bin_attr.attr.name = mokvar_entry->name; + mokvar_sysfs->bin_attr.attr.mode = 0400; + mokvar_sysfs->bin_attr.size = mokvar_entry->data_size; + mokvar_sysfs->bin_attr.read = efi_mokvar_sysfs_read; + + err = sysfs_create_bin_file(mokvar_kobj, + &mokvar_sysfs->bin_attr); + if (err) + break; + + list_add_tail(&mokvar_sysfs->node, &efi_mokvar_sysfs_list); + } + + if (err) { + pr_err("Failed to create some EFI mok-variables sysfs entries\n"); + kfree(mokvar_sysfs); + } + return err; +} +device_initcall(efi_mokvar_sysfs_init); diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c index 973eef234b36..41c1d00bf933 100644 --- a/drivers/firmware/efi/vars.c +++ b/drivers/firmware/efi/vars.c @@ -32,10 +32,6 @@ static struct efivars *__efivars; */ static DEFINE_SEMAPHORE(efivars_lock); -static bool efivar_wq_enabled = true; -DECLARE_WORK(efivar_work, NULL); -EXPORT_SYMBOL_GPL(efivar_work); - static bool validate_device_path(efi_char16_t *var_name, int match, u8 *buffer, unsigned long len) @@ -391,13 +387,6 @@ static void dup_variable_bug(efi_char16_t *str16, efi_guid_t *vendor_guid, size_t i, len8 = len16 / sizeof(efi_char16_t); char *str8; - /* - * Disable the workqueue since the algorithm it uses for - * detecting new variables won't work with this buggy - * implementation of GetNextVariableName(). - */ - efivar_wq_enabled = false; - str8 = kzalloc(len8, GFP_KERNEL); if (!str8) return; @@ -414,7 +403,6 @@ static void dup_variable_bug(efi_char16_t *str16, efi_guid_t *vendor_guid, * efivar_init - build the initial list of EFI variables * @func: callback function to invoke for every variable * @data: function-specific data to pass to @func - * @atomic: do we need to execute the @func-loop atomically? * @duplicates: error if we encounter duplicates on @head? * @head: initialised head of variable list * @@ -1158,16 +1146,6 @@ struct kobject *efivars_kobject(void) EXPORT_SYMBOL_GPL(efivars_kobject); /** - * efivar_run_worker - schedule the efivar worker thread - */ -void efivar_run_worker(void) -{ - if (efivar_wq_enabled) - schedule_work(&efivar_work); -} -EXPORT_SYMBOL_GPL(efivar_run_worker); - -/** * efivars_register - register an efivars * @efivars: efivars to register * @ops: efivars operations diff --git a/drivers/firmware/google/Kconfig b/drivers/firmware/google/Kconfig index a3a6ca659ffa..97968aece54f 100644 --- a/drivers/firmware/google/Kconfig +++ b/drivers/firmware/google/Kconfig @@ -15,7 +15,7 @@ config GOOGLE_SMI help Say Y here if you want to enable SMI callbacks for Google platforms. This provides an interface for writing to and - clearing the event log. If EFI_VARS is also enabled this + clearing the event log. If CONFIG_EFI is also enabled this driver provides an interface for reading and writing NVRAM variables. diff --git a/drivers/firmware/google/gsmi.c b/drivers/firmware/google/gsmi.c index 5b2011ebbe26..7d9367b22010 100644 --- a/drivers/firmware/google/gsmi.c +++ b/drivers/firmware/google/gsmi.c @@ -302,7 +302,7 @@ static int gsmi_exec(u8 func, u8 sub) return rc; } -#ifdef CONFIG_EFI_VARS +#ifdef CONFIG_EFI static struct efivars efivars; @@ -483,7 +483,7 @@ static const struct efivar_operations efivar_ops = { .get_next_variable = gsmi_get_next_variable, }; -#endif /* CONFIG_EFI_VARS */ +#endif /* CONFIG_EFI */ static ssize_t eventlog_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, @@ -1007,7 +1007,7 @@ static __init int gsmi_init(void) goto out_remove_bin_file; } -#ifdef CONFIG_EFI_VARS +#ifdef CONFIG_EFI ret = efivars_register(&efivars, &efivar_ops, gsmi_kobj); if (ret) { printk(KERN_INFO "gsmi: Failed to register efivars\n"); @@ -1047,7 +1047,7 @@ static void __exit gsmi_exit(void) unregister_die_notifier(&gsmi_die_notifier); atomic_notifier_chain_unregister(&panic_notifier_list, &gsmi_panic_notifier); -#ifdef CONFIG_EFI_VARS +#ifdef CONFIG_EFI efivars_unregister(&efivars); #endif |