diff options
Diffstat (limited to 'drivers/firmware/efi/efi.c')
| -rw-r--r-- | drivers/firmware/efi/efi.c | 408 |
1 files changed, 300 insertions, 108 deletions
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 4b7ee3fa9224..a9070d00b833 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -21,6 +21,7 @@ #include <linux/device.h> #include <linux/efi.h> #include <linux/of.h> +#include <linux/initrd.h> #include <linux/io.h> #include <linux/kexec.h> #include <linux/platform_device.h> @@ -31,6 +32,7 @@ #include <linux/ucs2_string.h> #include <linux/memblock.h> #include <linux/security.h> +#include <linux/notifier.h> #include <asm/early_ioremap.h> @@ -43,18 +45,28 @@ struct efi __read_mostly efi = { .esrt = EFI_INVALID_TABLE_ADDR, .tpm_log = EFI_INVALID_TABLE_ADDR, .tpm_final_log = EFI_INVALID_TABLE_ADDR, + .ovmf_debug_log = EFI_INVALID_TABLE_ADDR, #ifdef CONFIG_LOAD_UEFI_KEYS .mokvar_table = EFI_INVALID_TABLE_ADDR, #endif +#ifdef CONFIG_EFI_COCO_SECRET + .coco_secret = EFI_INVALID_TABLE_ADDR, +#endif +#ifdef CONFIG_UNACCEPTED_MEMORY + .unaccepted = EFI_INVALID_TABLE_ADDR, +#endif }; EXPORT_SYMBOL(efi); unsigned long __ro_after_init efi_rng_seed = EFI_INVALID_TABLE_ADDR; static unsigned long __initdata mem_reserve = EFI_INVALID_TABLE_ADDR; static unsigned long __initdata rt_prop = EFI_INVALID_TABLE_ADDR; +static unsigned long __initdata initrd = EFI_INVALID_TABLE_ADDR; + +extern unsigned long screen_info_table; struct mm_struct efi_mm = { - .mm_rb = RB_ROOT, + .mm_mt = MTREE_INIT_EXT(mm_mt, MM_MT_FLAGS, efi_mm.mmap_lock), .mm_users = ATOMIC_INIT(2), .mm_count = ATOMIC_INIT(1), .write_protect_seq = SEQCNT_ZERO(efi_mm.write_protect_seq), @@ -62,11 +74,14 @@ struct mm_struct efi_mm = { .page_table_lock = __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock), .mmlist = LIST_HEAD_INIT(efi_mm.mmlist), .cpu_bitmap = { [BITS_TO_LONGS(NR_CPUS)] = 0}, +#ifdef CONFIG_SCHED_MM_CID + .mm_cid.lock = __RAW_SPIN_LOCK_UNLOCKED(efi_mm.mm_cid.lock), +#endif }; struct workqueue_struct *efi_rts_wq; -static bool disable_runtime; +static bool disable_runtime = IS_ENABLED(CONFIG_EFI_DISABLE_RUNTIME); static int __init setup_noefi(char *arg) { disable_runtime = true; @@ -97,6 +112,9 @@ static int __init parse_efi_cmdline(char *str) if (parse_option_str(str, "noruntime")) disable_runtime = true; + if (parse_option_str(str, "runtime")) + disable_runtime = false; + if (parse_option_str(str, "nosoftreserve")) set_bit(EFI_MEM_NO_SOFT_RESERVE, &efi.flags); @@ -134,9 +152,6 @@ static ssize_t systab_show(struct kobject *kobj, if (efi.smbios != EFI_INVALID_TABLE_ADDR) str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios); - if (IS_ENABLED(CONFIG_IA64) || IS_ENABLED(CONFIG_X86)) - str = efi_systab_show_arch(str); - return str - buf; } @@ -174,29 +189,69 @@ static const struct attribute_group efi_subsys_attr_group = { .is_visible = efi_attr_is_visible, }; +struct blocking_notifier_head efivar_ops_nh; +EXPORT_SYMBOL_GPL(efivar_ops_nh); + static struct efivars generic_efivars; static struct efivar_operations generic_ops; +static bool generic_ops_supported(void) +{ + unsigned long name_size; + efi_status_t status; + efi_char16_t name; + efi_guid_t guid; + + name_size = sizeof(name); + + if (!efi.get_next_variable) + return false; + status = efi.get_next_variable(&name_size, &name, &guid); + if (status == EFI_UNSUPPORTED) + return false; + + return true; +} + static int generic_ops_register(void) { + if (!generic_ops_supported()) + return 0; + generic_ops.get_variable = efi.get_variable; generic_ops.get_next_variable = efi.get_next_variable; generic_ops.query_variable_store = efi_query_variable_store; + generic_ops.query_variable_info = efi.query_variable_info; if (efi_rt_services_supported(EFI_RT_SUPPORTED_SET_VARIABLE)) { generic_ops.set_variable = efi.set_variable; generic_ops.set_variable_nonblocking = efi.set_variable_nonblocking; } - return efivars_register(&generic_efivars, &generic_ops, efi_kobj); + return efivars_register(&generic_efivars, &generic_ops); } static void generic_ops_unregister(void) { + if (!generic_ops.get_variable) + return; + efivars_unregister(&generic_efivars); } +void efivars_generic_ops_register(void) +{ + generic_ops_register(); +} +EXPORT_SYMBOL_GPL(efivars_generic_ops_register); + +void efivars_generic_ops_unregister(void) +{ + generic_ops_unregister(); +} +EXPORT_SYMBOL_GPL(efivars_generic_ops_unregister); + #ifdef CONFIG_EFI_CUSTOM_SSDT_OVERLAYS -#define EFIVAR_SSDT_NAME_MAX 16 +#define EFIVAR_SSDT_NAME_MAX 16UL static char efivar_ssdt[EFIVAR_SSDT_NAME_MAX] __initdata; static int __init efivar_ssdt_setup(char *str) { @@ -209,86 +264,84 @@ static int __init efivar_ssdt_setup(char *str) memcpy(efivar_ssdt, str, strlen(str)); else pr_warn("efivar_ssdt: name too long: %s\n", str); - return 0; + return 1; } __setup("efivar_ssdt=", efivar_ssdt_setup); -static __init int efivar_ssdt_iter(efi_char16_t *name, efi_guid_t vendor, - unsigned long name_size, void *data) -{ - struct efivar_entry *entry; - struct list_head *list = data; - char utf8_name[EFIVAR_SSDT_NAME_MAX]; - int limit = min_t(unsigned long, EFIVAR_SSDT_NAME_MAX, name_size); - - ucs2_as_utf8(utf8_name, name, limit - 1); - if (strncmp(utf8_name, efivar_ssdt, limit) != 0) - return 0; - - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) - return 0; - - memcpy(entry->var.VariableName, name, name_size); - memcpy(&entry->var.VendorGuid, &vendor, sizeof(efi_guid_t)); - - efivar_entry_add(entry, list); - - return 0; -} - static __init int efivar_ssdt_load(void) { - LIST_HEAD(entries); - struct efivar_entry *entry, *aux; - unsigned long size; - void *data; - int ret; + unsigned long name_size = 256; + efi_char16_t *name = NULL; + efi_status_t status; + efi_guid_t guid; + int ret = 0; if (!efivar_ssdt[0]) return 0; - ret = efivar_init(efivar_ssdt_iter, &entries, true, &entries); + name = kzalloc(name_size, GFP_KERNEL); + if (!name) + return -ENOMEM; + + for (;;) { + char utf8_name[EFIVAR_SSDT_NAME_MAX]; + unsigned long data_size = 0; + void *data; + int limit; + + status = efi.get_next_variable(&name_size, name, &guid); + if (status == EFI_NOT_FOUND) { + break; + } else if (status == EFI_BUFFER_TOO_SMALL) { + efi_char16_t *name_tmp = + krealloc(name, name_size, GFP_KERNEL); + if (!name_tmp) { + ret = -ENOMEM; + goto out; + } + name = name_tmp; + continue; + } - list_for_each_entry_safe(entry, aux, &entries, list) { - pr_info("loading SSDT from variable %s-%pUl\n", efivar_ssdt, - &entry->var.VendorGuid); + limit = min(EFIVAR_SSDT_NAME_MAX, name_size); + ucs2_as_utf8(utf8_name, name, limit - 1); + if (strncmp(utf8_name, efivar_ssdt, limit) != 0) + continue; - list_del(&entry->list); + pr_info("loading SSDT from variable %s-%pUl\n", efivar_ssdt, &guid); - ret = efivar_entry_size(entry, &size); - if (ret) { - pr_err("failed to get var size\n"); - goto free_entry; + status = efi.get_variable(name, &guid, NULL, &data_size, NULL); + if (status != EFI_BUFFER_TOO_SMALL || !data_size) { + ret = -EIO; + goto out; } - data = kmalloc(size, GFP_KERNEL); + data = kmalloc(data_size, GFP_KERNEL); if (!data) { ret = -ENOMEM; - goto free_entry; - } - - ret = efivar_entry_get(entry, NULL, &size, data); - if (ret) { - pr_err("failed to get var data\n"); - goto free_data; + goto out; } - ret = acpi_load_table(data, NULL); - if (ret) { - pr_err("failed to load table: %d\n", ret); - goto free_data; + status = efi.get_variable(name, &guid, NULL, &data_size, data); + if (status == EFI_SUCCESS) { + acpi_status acpi_ret = acpi_load_table(data, NULL); + if (ACPI_FAILURE(acpi_ret)) { + pr_err("efivar_ssdt: failed to load table: %u\n", + acpi_ret); + } else { + /* + * The @data will be in use by ACPI engine, + * do not free it! + */ + continue; + } + } else { + pr_err("efivar_ssdt: failed to get var data: 0x%lx\n", status); } - - goto free_entry; - -free_data: kfree(data); - -free_entry: - kfree(entry); } - +out: + kfree(name); return ret; } #else @@ -310,7 +363,7 @@ static void __init efi_debugfs_init(void) int i = 0; efi_debugfs = debugfs_create_dir("efi", NULL); - if (IS_ERR_OR_NULL(efi_debugfs)) + if (IS_ERR(efi_debugfs)) return; for_each_efi_memory_desc(md) { @@ -385,8 +438,8 @@ static int __init efisubsys_init(void) efi_kobj = kobject_create_and_add("efi", firmware_kobj); if (!efi_kobj) { pr_err("efi: Firmware registration failed.\n"); - destroy_workqueue(efi_rts_wq); - return -ENOMEM; + error = -ENOMEM; + goto err_destroy_wq; } if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE | @@ -394,10 +447,14 @@ static int __init efisubsys_init(void) error = generic_ops_register(); if (error) goto err_put; - efivar_ssdt_load(); + error = efivar_ssdt_load(); + if (error) + pr_err("efi: failed to load SSDT, error %d.\n", error); platform_device_register_simple("efivars", 0, NULL, 0); } + BLOCKING_INIT_NOTIFIER_HEAD(&efivar_ops_nh); + error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group); if (error) { pr_err("efi: Sysfs attribute export failed with error %d.\n", @@ -405,10 +462,6 @@ static int __init efisubsys_init(void) goto err_unregister; } - error = efi_runtime_map_init(efi_kobj); - if (error) - goto err_remove_group; - /* and the standard mountpoint for efivarfs */ error = sysfs_create_mount_point(efi_kobj, "efivars"); if (error) { @@ -419,6 +472,15 @@ static int __init efisubsys_init(void) if (efi_enabled(EFI_DBG) && efi_enabled(EFI_PRESERVE_BS_REGIONS)) efi_debugfs_init(); +#ifdef CONFIG_EFI_COCO_SECRET + if (efi.coco_secret != EFI_INVALID_TABLE_ADDR) + platform_device_register_simple("efi_secret", 0, NULL, 0); +#endif + + if (IS_ENABLED(CONFIG_OVMF_DEBUG_LOG) && + efi.ovmf_debug_log != EFI_INVALID_TABLE_ADDR) + ovmf_log_probe(efi.ovmf_debug_log); + return 0; err_remove_group: @@ -429,19 +491,46 @@ err_unregister: generic_ops_unregister(); err_put: kobject_put(efi_kobj); - destroy_workqueue(efi_rts_wq); + efi_kobj = NULL; +err_destroy_wq: + if (efi_rts_wq) + destroy_workqueue(efi_rts_wq); + return error; } subsys_initcall(efisubsys_init); +void __init efi_find_mirror(void) +{ + efi_memory_desc_t *md; + u64 mirror_size = 0, total_size = 0; + + if (!efi_enabled(EFI_MEMMAP)) + return; + + for_each_efi_memory_desc(md) { + unsigned long long start = md->phys_addr; + unsigned long long size = md->num_pages << EFI_PAGE_SHIFT; + + total_size += size; + if (md->attribute & EFI_MEMORY_MORE_RELIABLE) { + memblock_mark_mirror(start, size); + mirror_size += size; + } + } + if (mirror_size) + pr_info("Memory: %lldM/%lldM mirrored memory\n", + mirror_size>>20, total_size>>20); +} + /* * Find the efi memory descriptor for a given physical address. Given a * physical address, determine if it exists within an EFI Memory Map entry, * and if so, populate the supplied memory descriptor with the appropriate * data. */ -int efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md) +int __efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md) { efi_memory_desc_t *md; @@ -459,6 +548,12 @@ int efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md) u64 size; u64 end; + /* skip bogus entries (including empty ones) */ + if ((md->phys_addr & (EFI_PAGE_SIZE - 1)) || + (md->num_pages <= 0) || + (md->num_pages > (U64_MAX - md->phys_addr) >> EFI_PAGE_SHIFT)) + continue; + size = md->num_pages << EFI_PAGE_SHIFT; end = md->phys_addr + size; if (phys_addr >= md->phys_addr && phys_addr < end) { @@ -469,6 +564,10 @@ int efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md) return -ENOENT; } +extern int efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md) + __weak __alias(__efi_mem_desc_lookup); +EXPORT_SYMBOL_GPL(efi_mem_desc_lookup); + /* * Calculate the highest address of an efi memory descriptor. */ @@ -495,6 +594,10 @@ void __init __weak efi_arch_mem_reserve(phys_addr_t addr, u64 size) {} */ void __init efi_mem_reserve(phys_addr_t addr, u64 size) { + /* efi_mem_reserve() does not work under Xen */ + if (WARN_ON_ONCE(efi_enabled(EFI_PARAVIRT))) + return; + if (!memblock_is_region_reserved(addr, size)) memblock_reserve(addr, size); @@ -517,15 +620,29 @@ static const efi_config_table_type_t common_tables[] __initconst = { {EFI_MEMORY_ATTRIBUTES_TABLE_GUID, &efi_mem_attr_table, "MEMATTR" }, {LINUX_EFI_RANDOM_SEED_TABLE_GUID, &efi_rng_seed, "RNG" }, {LINUX_EFI_TPM_EVENT_LOG_GUID, &efi.tpm_log, "TPMEventLog" }, - {LINUX_EFI_TPM_FINAL_LOG_GUID, &efi.tpm_final_log, "TPMFinalLog" }, + {EFI_TCG2_FINAL_EVENTS_TABLE_GUID, &efi.tpm_final_log, "TPMFinalLog" }, + {EFI_CC_FINAL_EVENTS_TABLE_GUID, &efi.tpm_final_log, "CCFinalLog" }, {LINUX_EFI_MEMRESERVE_TABLE_GUID, &mem_reserve, "MEMRESERVE" }, + {LINUX_EFI_INITRD_MEDIA_GUID, &initrd, "INITRD" }, {EFI_RT_PROPERTIES_TABLE_GUID, &rt_prop, "RTPROP" }, +#ifdef CONFIG_OVMF_DEBUG_LOG + {OVMF_MEMORY_LOG_TABLE_GUID, &efi.ovmf_debug_log, "OvmfDebugLog" }, +#endif #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 +#ifdef CONFIG_EFI_COCO_SECRET + {LINUX_EFI_COCO_SECRET_AREA_GUID, &efi.coco_secret, "CocoSecret" }, +#endif +#ifdef CONFIG_UNACCEPTED_MEMORY + {LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID, &efi.unaccepted, "Unaccepted" }, +#endif +#ifdef CONFIG_EFI_GENERIC_STUB + {LINUX_EFI_SCREEN_INFO_TABLE_GUID, &screen_info_table }, +#endif {}, }; @@ -536,18 +653,53 @@ static __init int match_config_table(const efi_guid_t *guid, int i; for (i = 0; efi_guidcmp(table_types[i].guid, NULL_GUID); i++) { - if (!efi_guidcmp(*guid, table_types[i].guid)) { - *(table_types[i].ptr) = table; + if (efi_guidcmp(*guid, table_types[i].guid)) + continue; + + if (!efi_config_table_is_usable(guid, table)) { if (table_types[i].name[0]) - pr_cont("%s=0x%lx ", + pr_cont("(%s=0x%lx unusable) ", table_types[i].name, table); return 1; } + + *(table_types[i].ptr) = table; + if (table_types[i].name[0]) + pr_cont("%s=0x%lx ", table_types[i].name, table); + return 1; } return 0; } +/** + * reserve_unaccepted - Map and reserve unaccepted configuration table + * @unaccepted: Pointer to unaccepted memory table + * + * memblock_add() makes sure that the table is mapped in direct mapping. During + * normal boot it happens automatically because the table is allocated from + * usable memory. But during crashkernel boot only memory specifically reserved + * for crash scenario is mapped. memblock_add() forces the table to be mapped + * in crashkernel case. + * + * Align the range to the nearest page borders. Ranges smaller than page size + * are not going to be mapped. + * + * memblock_reserve() makes sure that future allocations will not touch the + * table. + */ + +static __init void reserve_unaccepted(struct efi_unaccepted_memory *unaccepted) +{ + phys_addr_t start, size; + + start = PAGE_ALIGN_DOWN(efi.unaccepted); + size = PAGE_ALIGN(sizeof(*unaccepted) + unaccepted->size); + + memblock_add(start, size); + memblock_reserve(start, size); +} + int __init efi_config_parse_tables(const efi_config_table_t *config_tables, int count, const efi_config_table_type_t *arch_tables) @@ -590,7 +742,7 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables, seed = early_memremap(efi_rng_seed, sizeof(*seed)); if (seed != NULL) { - size = READ_ONCE(seed->size); + size = min_t(u32, seed->size, SZ_1K); // sanity check early_memunmap(seed, sizeof(*seed)); } else { pr_err("Could not map UEFI random seed!\n"); @@ -599,8 +751,8 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables, seed = early_memremap(efi_rng_seed, sizeof(*seed) + size); if (seed != NULL) { - pr_notice("seeding entropy pool\n"); add_bootloader_randomness(seed->bits, size); + memzero_explicit(seed->bits, size); early_memunmap(seed, sizeof(*seed) + size); } else { pr_err("Could not map UEFI random seed!\n"); @@ -658,27 +810,48 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables, } } + if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && + initrd != EFI_INVALID_TABLE_ADDR && phys_initrd_size == 0) { + struct linux_efi_initrd *tbl; + + tbl = early_memremap(initrd, sizeof(*tbl)); + if (tbl) { + phys_initrd_start = tbl->base; + phys_initrd_size = tbl->size; + early_memunmap(tbl, sizeof(*tbl)); + } + } + + if (IS_ENABLED(CONFIG_UNACCEPTED_MEMORY) && + efi.unaccepted != EFI_INVALID_TABLE_ADDR) { + struct efi_unaccepted_memory *unaccepted; + + unaccepted = early_memremap(efi.unaccepted, sizeof(*unaccepted)); + if (unaccepted) { + + if (unaccepted->version == 1) { + reserve_unaccepted(unaccepted); + } else { + efi.unaccepted = EFI_INVALID_TABLE_ADDR; + } + + early_memunmap(unaccepted, sizeof(*unaccepted)); + } + } + return 0; } -int __init efi_systab_check_header(const efi_table_hdr_t *systab_hdr, - int min_major_version) +int __init efi_systab_check_header(const efi_table_hdr_t *systab_hdr) { if (systab_hdr->signature != EFI_SYSTEM_TABLE_SIGNATURE) { pr_err("System table signature incorrect!\n"); return -EINVAL; } - if ((systab_hdr->revision >> 16) < min_major_version) - pr_err("Warning: System table version %d.%02d, expected %d.00 or greater!\n", - systab_hdr->revision >> 16, - systab_hdr->revision & 0xffff, - min_major_version); - return 0; } -#ifndef CONFIG_IA64 static const efi_char16_t *__init map_fw_vendor(unsigned long fw_vendor, size_t size) { @@ -694,10 +867,6 @@ static void __init unmap_fw_vendor(const void *fw_vendor, size_t size) { early_memunmap((void *)fw_vendor, size); } -#else -#define map_fw_vendor(p, s) __va(p) -#define unmap_fw_vendor(v, s) -#endif void __init efi_systab_report_header(const efi_table_hdr_t *systab_hdr, unsigned long fw_vendor) @@ -705,6 +874,7 @@ void __init efi_systab_report_header(const efi_table_hdr_t *systab_hdr, char vendor[100] = "unknown"; const efi_char16_t *c16; size_t i; + u16 rev; c16 = map_fw_vendor(fw_vendor, sizeof(vendor) * sizeof(efi_char16_t)); if (c16) { @@ -715,10 +885,21 @@ void __init efi_systab_report_header(const efi_table_hdr_t *systab_hdr, unmap_fw_vendor(c16, sizeof(vendor) * sizeof(efi_char16_t)); } - pr_info("EFI v%u.%.02u by %s\n", - systab_hdr->revision >> 16, - systab_hdr->revision & 0xffff, - vendor); + rev = (u16)systab_hdr->revision; + pr_info("EFI v%u.%u", systab_hdr->revision >> 16, rev / 10); + + rev %= 10; + if (rev) + pr_cont(".%u", rev); + + pr_cont(" by %s\n", vendor); + + if (IS_ENABLED(CONFIG_X86_64) && + systab_hdr->revision > EFI_1_10_SYSTEM_TABLE_REVISION && + !strcmp(vendor, "Apple")) { + pr_info("Apple Mac detected, using EFI v1.10 runtime services only\n"); + efi.runtime_version = EFI_1_10_SYSTEM_TABLE_REVISION; + } } static __initdata char memory_type_name[][13] = { @@ -737,6 +918,7 @@ static __initdata char memory_type_name[][13] = { "MMIO Port", "PAL Code", "Persistent", + "Unaccepted", }; char * __init efi_md_typeattr_format(char *buf, size_t size, @@ -764,13 +946,15 @@ char * __init efi_md_typeattr_format(char *buf, size_t size, 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_CPU_CRYPTO | - EFI_MEMORY_RUNTIME | EFI_MEMORY_MORE_RELIABLE)) + EFI_MEMORY_MORE_RELIABLE | EFI_MEMORY_HOT_PLUGGABLE | + EFI_MEMORY_RUNTIME)) snprintf(pos, size, "|attr=0x%016llx]", (unsigned long long)attr); else snprintf(pos, size, - "|%3s|%2s|%2s|%2s|%2s|%2s|%2s|%2s|%2s|%3s|%2s|%2s|%2s|%2s]", + "|%3s|%2s|%2s|%2s|%2s|%2s|%2s|%2s|%2s|%2s|%3s|%2s|%2s|%2s|%2s]", attr & EFI_MEMORY_RUNTIME ? "RUN" : "", + attr & EFI_MEMORY_HOT_PLUGGABLE ? "HP" : "", attr & EFI_MEMORY_MORE_RELIABLE ? "MR" : "", attr & EFI_MEMORY_CPU_CRYPTO ? "CC" : "", attr & EFI_MEMORY_SP ? "SP" : "", @@ -788,11 +972,6 @@ char * __init efi_md_typeattr_format(char *buf, size_t size, } /* - * IA64 has a funky EFI memory map that doesn't work the same way as - * other architectures. - */ -#ifndef CONFIG_IA64 -/* * efi_mem_attributes - lookup memmap attributes for physical address * @phys_addr: the physical address to lookup * @@ -839,7 +1018,6 @@ int efi_mem_type(unsigned long phys_addr) } return -EINVAL; } -#endif int efi_status_to_err(efi_status_t status) { @@ -876,6 +1054,7 @@ int efi_status_to_err(efi_status_t status) return err; } +EXPORT_SYMBOL_GPL(efi_status_to_err); static DEFINE_SPINLOCK(efi_mem_reserve_persistent_lock); static struct linux_efi_memreserve *efi_memreserve_root __ro_after_init; @@ -896,6 +1075,7 @@ static int __init efi_memreserve_map_root(void) static int efi_mem_reserve_iomem(phys_addr_t addr, u64 size) { struct resource *res, *parent; + int ret; res = kzalloc(sizeof(struct resource), GFP_ATOMIC); if (!res) @@ -908,7 +1088,17 @@ static int efi_mem_reserve_iomem(phys_addr_t addr, u64 size) /* we expect a conflict with a 'System RAM' region */ parent = request_resource_conflict(&iomem_resource, res); - return parent ? request_resource(parent, res) : 0; + ret = parent ? request_resource(parent, res) : 0; + + /* + * Given that efi_mem_reserve_iomem() can be called at any + * time, only call memblock_reserve() if the architecture + * keeps the infrastructure around. + */ + if (IS_ENABLED(CONFIG_ARCH_KEEP_MEMBLOCK) && !ret) + memblock_reserve(addr, size); + + return ret; } int __ref efi_mem_reserve_persistent(phys_addr_t addr, u64 size) @@ -929,6 +1119,8 @@ int __ref efi_mem_reserve_persistent(phys_addr_t addr, u64 size) /* first try to find a slot in an existing linked list entry */ for (prsv = efi_memreserve_root->next; prsv; ) { rsv = memremap(prsv, sizeof(*rsv), MEMREMAP_WB); + if (!rsv) + return -ENOMEM; index = atomic_fetch_add_unless(&rsv->count, 1, rsv->size); if (index < rsv->size) { rsv->entry[index].base = addr; |
