18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * efi.c - EFI subsystem 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2001,2003,2004 Dell <Matt_Domsch@dell.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2004 Intel Corporation <matthew.e.tolentino@intel.com> 78c2ecf20Sopenharmony_ci * Copyright (C) 2013 Tom Gundersen <teg@jklm.no> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This code registers /sys/firmware/efi{,/efivars} when EFI is supported, 108c2ecf20Sopenharmony_ci * allowing the efivarfs to be mounted or the efivars module to be loaded. 118c2ecf20Sopenharmony_ci * The existance of /sys/firmware/efi may also be used by userspace to 128c2ecf20Sopenharmony_ci * determine that the system supports EFI. 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/kobject.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/init.h> 208c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 218c2ecf20Sopenharmony_ci#include <linux/device.h> 228c2ecf20Sopenharmony_ci#include <linux/efi.h> 238c2ecf20Sopenharmony_ci#include <linux/of.h> 248c2ecf20Sopenharmony_ci#include <linux/initrd.h> 258c2ecf20Sopenharmony_ci#include <linux/io.h> 268c2ecf20Sopenharmony_ci#include <linux/kexec.h> 278c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 288c2ecf20Sopenharmony_ci#include <linux/random.h> 298c2ecf20Sopenharmony_ci#include <linux/reboot.h> 308c2ecf20Sopenharmony_ci#include <linux/slab.h> 318c2ecf20Sopenharmony_ci#include <linux/acpi.h> 328c2ecf20Sopenharmony_ci#include <linux/ucs2_string.h> 338c2ecf20Sopenharmony_ci#include <linux/memblock.h> 348c2ecf20Sopenharmony_ci#include <linux/security.h> 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#include <asm/early_ioremap.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistruct efi __read_mostly efi = { 398c2ecf20Sopenharmony_ci .runtime_supported_mask = EFI_RT_SUPPORTED_ALL, 408c2ecf20Sopenharmony_ci .acpi = EFI_INVALID_TABLE_ADDR, 418c2ecf20Sopenharmony_ci .acpi20 = EFI_INVALID_TABLE_ADDR, 428c2ecf20Sopenharmony_ci .smbios = EFI_INVALID_TABLE_ADDR, 438c2ecf20Sopenharmony_ci .smbios3 = EFI_INVALID_TABLE_ADDR, 448c2ecf20Sopenharmony_ci .esrt = EFI_INVALID_TABLE_ADDR, 458c2ecf20Sopenharmony_ci .tpm_log = EFI_INVALID_TABLE_ADDR, 468c2ecf20Sopenharmony_ci .tpm_final_log = EFI_INVALID_TABLE_ADDR, 478c2ecf20Sopenharmony_ci#ifdef CONFIG_LOAD_UEFI_KEYS 488c2ecf20Sopenharmony_ci .mokvar_table = EFI_INVALID_TABLE_ADDR, 498c2ecf20Sopenharmony_ci#endif 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(efi); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ciunsigned long __ro_after_init efi_rng_seed = EFI_INVALID_TABLE_ADDR; 548c2ecf20Sopenharmony_cistatic unsigned long __initdata mem_reserve = EFI_INVALID_TABLE_ADDR; 558c2ecf20Sopenharmony_cistatic unsigned long __initdata rt_prop = EFI_INVALID_TABLE_ADDR; 568c2ecf20Sopenharmony_cistatic unsigned long __initdata initrd = EFI_INVALID_TABLE_ADDR; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistruct mm_struct efi_mm = { 598c2ecf20Sopenharmony_ci .mm_rb = RB_ROOT, 608c2ecf20Sopenharmony_ci .mm_users = ATOMIC_INIT(2), 618c2ecf20Sopenharmony_ci .mm_count = ATOMIC_INIT(1), 628c2ecf20Sopenharmony_ci .write_protect_seq = SEQCNT_ZERO(efi_mm.write_protect_seq), 638c2ecf20Sopenharmony_ci MMAP_LOCK_INITIALIZER(efi_mm) 648c2ecf20Sopenharmony_ci .page_table_lock = __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock), 658c2ecf20Sopenharmony_ci .mmlist = LIST_HEAD_INIT(efi_mm.mmlist), 668c2ecf20Sopenharmony_ci .cpu_bitmap = { [BITS_TO_LONGS(NR_CPUS)] = 0}, 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistruct workqueue_struct *efi_rts_wq; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic bool disable_runtime; 728c2ecf20Sopenharmony_cistatic int __init setup_noefi(char *arg) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci disable_runtime = true; 758c2ecf20Sopenharmony_ci return 0; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ciearly_param("noefi", setup_noefi); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cibool efi_runtime_disabled(void) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci return disable_runtime; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cibool __pure __efi_soft_reserve_enabled(void) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci return !efi_enabled(EFI_MEM_NO_SOFT_RESERVE); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic int __init parse_efi_cmdline(char *str) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci if (!str) { 928c2ecf20Sopenharmony_ci pr_warn("need at least one option\n"); 938c2ecf20Sopenharmony_ci return -EINVAL; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (parse_option_str(str, "debug")) 978c2ecf20Sopenharmony_ci set_bit(EFI_DBG, &efi.flags); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (parse_option_str(str, "noruntime")) 1008c2ecf20Sopenharmony_ci disable_runtime = true; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (parse_option_str(str, "nosoftreserve")) 1038c2ecf20Sopenharmony_ci set_bit(EFI_MEM_NO_SOFT_RESERVE, &efi.flags); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci return 0; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ciearly_param("efi", parse_efi_cmdline); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistruct kobject *efi_kobj; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* 1128c2ecf20Sopenharmony_ci * Let's not leave out systab information that snuck into 1138c2ecf20Sopenharmony_ci * the efivars driver 1148c2ecf20Sopenharmony_ci * Note, do not add more fields in systab sysfs file as it breaks sysfs 1158c2ecf20Sopenharmony_ci * one value per file rule! 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_cistatic ssize_t systab_show(struct kobject *kobj, 1188c2ecf20Sopenharmony_ci struct kobj_attribute *attr, char *buf) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci char *str = buf; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (!kobj || !buf) 1238c2ecf20Sopenharmony_ci return -EINVAL; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (efi.acpi20 != EFI_INVALID_TABLE_ADDR) 1268c2ecf20Sopenharmony_ci str += sprintf(str, "ACPI20=0x%lx\n", efi.acpi20); 1278c2ecf20Sopenharmony_ci if (efi.acpi != EFI_INVALID_TABLE_ADDR) 1288c2ecf20Sopenharmony_ci str += sprintf(str, "ACPI=0x%lx\n", efi.acpi); 1298c2ecf20Sopenharmony_ci /* 1308c2ecf20Sopenharmony_ci * If both SMBIOS and SMBIOS3 entry points are implemented, the 1318c2ecf20Sopenharmony_ci * SMBIOS3 entry point shall be preferred, so we list it first to 1328c2ecf20Sopenharmony_ci * let applications stop parsing after the first match. 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_ci if (efi.smbios3 != EFI_INVALID_TABLE_ADDR) 1358c2ecf20Sopenharmony_ci str += sprintf(str, "SMBIOS3=0x%lx\n", efi.smbios3); 1368c2ecf20Sopenharmony_ci if (efi.smbios != EFI_INVALID_TABLE_ADDR) 1378c2ecf20Sopenharmony_ci str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_IA64) || IS_ENABLED(CONFIG_X86)) 1408c2ecf20Sopenharmony_ci str = efi_systab_show_arch(str); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return str - buf; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic struct kobj_attribute efi_attr_systab = __ATTR_RO_MODE(systab, 0400); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic ssize_t fw_platform_size_show(struct kobject *kobj, 1488c2ecf20Sopenharmony_ci struct kobj_attribute *attr, char *buf) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", efi_enabled(EFI_64BIT) ? 64 : 32); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ciextern __weak struct kobj_attribute efi_attr_fw_vendor; 1548c2ecf20Sopenharmony_ciextern __weak struct kobj_attribute efi_attr_runtime; 1558c2ecf20Sopenharmony_ciextern __weak struct kobj_attribute efi_attr_config_table; 1568c2ecf20Sopenharmony_cistatic struct kobj_attribute efi_attr_fw_platform_size = 1578c2ecf20Sopenharmony_ci __ATTR_RO(fw_platform_size); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic struct attribute *efi_subsys_attrs[] = { 1608c2ecf20Sopenharmony_ci &efi_attr_systab.attr, 1618c2ecf20Sopenharmony_ci &efi_attr_fw_platform_size.attr, 1628c2ecf20Sopenharmony_ci &efi_attr_fw_vendor.attr, 1638c2ecf20Sopenharmony_ci &efi_attr_runtime.attr, 1648c2ecf20Sopenharmony_ci &efi_attr_config_table.attr, 1658c2ecf20Sopenharmony_ci NULL, 1668c2ecf20Sopenharmony_ci}; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ciumode_t __weak efi_attr_is_visible(struct kobject *kobj, struct attribute *attr, 1698c2ecf20Sopenharmony_ci int n) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci return attr->mode; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic const struct attribute_group efi_subsys_attr_group = { 1758c2ecf20Sopenharmony_ci .attrs = efi_subsys_attrs, 1768c2ecf20Sopenharmony_ci .is_visible = efi_attr_is_visible, 1778c2ecf20Sopenharmony_ci}; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic struct efivars generic_efivars; 1808c2ecf20Sopenharmony_cistatic struct efivar_operations generic_ops; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic int generic_ops_register(void) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci generic_ops.get_variable = efi.get_variable; 1858c2ecf20Sopenharmony_ci generic_ops.get_next_variable = efi.get_next_variable; 1868c2ecf20Sopenharmony_ci generic_ops.query_variable_store = efi_query_variable_store; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (efi_rt_services_supported(EFI_RT_SUPPORTED_SET_VARIABLE)) { 1898c2ecf20Sopenharmony_ci generic_ops.set_variable = efi.set_variable; 1908c2ecf20Sopenharmony_ci generic_ops.set_variable_nonblocking = efi.set_variable_nonblocking; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci return efivars_register(&generic_efivars, &generic_ops, efi_kobj); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic void generic_ops_unregister(void) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci efivars_unregister(&generic_efivars); 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci#ifdef CONFIG_EFI_CUSTOM_SSDT_OVERLAYS 2018c2ecf20Sopenharmony_ci#define EFIVAR_SSDT_NAME_MAX 16 2028c2ecf20Sopenharmony_cistatic char efivar_ssdt[EFIVAR_SSDT_NAME_MAX] __initdata; 2038c2ecf20Sopenharmony_cistatic int __init efivar_ssdt_setup(char *str) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci int ret = security_locked_down(LOCKDOWN_ACPI_TABLES); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (ret) 2088c2ecf20Sopenharmony_ci return ret; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (strlen(str) < sizeof(efivar_ssdt)) 2118c2ecf20Sopenharmony_ci memcpy(efivar_ssdt, str, strlen(str)); 2128c2ecf20Sopenharmony_ci else 2138c2ecf20Sopenharmony_ci pr_warn("efivar_ssdt: name too long: %s\n", str); 2148c2ecf20Sopenharmony_ci return 1; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci__setup("efivar_ssdt=", efivar_ssdt_setup); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic __init int efivar_ssdt_iter(efi_char16_t *name, efi_guid_t vendor, 2198c2ecf20Sopenharmony_ci unsigned long name_size, void *data) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct efivar_entry *entry; 2228c2ecf20Sopenharmony_ci struct list_head *list = data; 2238c2ecf20Sopenharmony_ci char utf8_name[EFIVAR_SSDT_NAME_MAX]; 2248c2ecf20Sopenharmony_ci int limit = min_t(unsigned long, EFIVAR_SSDT_NAME_MAX, name_size); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci ucs2_as_utf8(utf8_name, name, limit - 1); 2278c2ecf20Sopenharmony_ci if (strncmp(utf8_name, efivar_ssdt, limit) != 0) 2288c2ecf20Sopenharmony_ci return 0; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci entry = kmalloc(sizeof(*entry), GFP_KERNEL); 2318c2ecf20Sopenharmony_ci if (!entry) 2328c2ecf20Sopenharmony_ci return 0; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci memcpy(entry->var.VariableName, name, name_size); 2358c2ecf20Sopenharmony_ci memcpy(&entry->var.VendorGuid, &vendor, sizeof(efi_guid_t)); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci efivar_entry_add(entry, list); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic __init int efivar_ssdt_load(void) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci LIST_HEAD(entries); 2458c2ecf20Sopenharmony_ci struct efivar_entry *entry, *aux; 2468c2ecf20Sopenharmony_ci unsigned long size; 2478c2ecf20Sopenharmony_ci void *data; 2488c2ecf20Sopenharmony_ci int ret; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (!efivar_ssdt[0]) 2518c2ecf20Sopenharmony_ci return 0; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci ret = efivar_init(efivar_ssdt_iter, &entries, true, &entries); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci list_for_each_entry_safe(entry, aux, &entries, list) { 2568c2ecf20Sopenharmony_ci pr_info("loading SSDT from variable %s-%pUl\n", efivar_ssdt, 2578c2ecf20Sopenharmony_ci &entry->var.VendorGuid); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci list_del(&entry->list); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci ret = efivar_entry_size(entry, &size); 2628c2ecf20Sopenharmony_ci if (ret) { 2638c2ecf20Sopenharmony_ci pr_err("failed to get var size\n"); 2648c2ecf20Sopenharmony_ci goto free_entry; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci data = kmalloc(size, GFP_KERNEL); 2688c2ecf20Sopenharmony_ci if (!data) { 2698c2ecf20Sopenharmony_ci ret = -ENOMEM; 2708c2ecf20Sopenharmony_ci goto free_entry; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci ret = efivar_entry_get(entry, NULL, &size, data); 2748c2ecf20Sopenharmony_ci if (ret) { 2758c2ecf20Sopenharmony_ci pr_err("failed to get var data\n"); 2768c2ecf20Sopenharmony_ci goto free_data; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci ret = acpi_load_table(data, NULL); 2808c2ecf20Sopenharmony_ci if (ret) { 2818c2ecf20Sopenharmony_ci pr_err("failed to load table: %d\n", ret); 2828c2ecf20Sopenharmony_ci goto free_data; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci goto free_entry; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cifree_data: 2888c2ecf20Sopenharmony_ci kfree(data); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cifree_entry: 2918c2ecf20Sopenharmony_ci kfree(entry); 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return ret; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci#else 2978c2ecf20Sopenharmony_cistatic inline int efivar_ssdt_load(void) { return 0; } 2988c2ecf20Sopenharmony_ci#endif 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci#define EFI_DEBUGFS_MAX_BLOBS 32 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic struct debugfs_blob_wrapper debugfs_blob[EFI_DEBUGFS_MAX_BLOBS]; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic void __init efi_debugfs_init(void) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct dentry *efi_debugfs; 3098c2ecf20Sopenharmony_ci efi_memory_desc_t *md; 3108c2ecf20Sopenharmony_ci char name[32]; 3118c2ecf20Sopenharmony_ci int type_count[EFI_BOOT_SERVICES_DATA + 1] = {}; 3128c2ecf20Sopenharmony_ci int i = 0; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci efi_debugfs = debugfs_create_dir("efi", NULL); 3158c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(efi_debugfs)) 3168c2ecf20Sopenharmony_ci return; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci for_each_efi_memory_desc(md) { 3198c2ecf20Sopenharmony_ci switch (md->type) { 3208c2ecf20Sopenharmony_ci case EFI_BOOT_SERVICES_CODE: 3218c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "boot_services_code%d", 3228c2ecf20Sopenharmony_ci type_count[md->type]++); 3238c2ecf20Sopenharmony_ci break; 3248c2ecf20Sopenharmony_ci case EFI_BOOT_SERVICES_DATA: 3258c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "boot_services_data%d", 3268c2ecf20Sopenharmony_ci type_count[md->type]++); 3278c2ecf20Sopenharmony_ci break; 3288c2ecf20Sopenharmony_ci default: 3298c2ecf20Sopenharmony_ci continue; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (i >= EFI_DEBUGFS_MAX_BLOBS) { 3338c2ecf20Sopenharmony_ci pr_warn("More then %d EFI boot service segments, only showing first %d in debugfs\n", 3348c2ecf20Sopenharmony_ci EFI_DEBUGFS_MAX_BLOBS, EFI_DEBUGFS_MAX_BLOBS); 3358c2ecf20Sopenharmony_ci break; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci debugfs_blob[i].size = md->num_pages << EFI_PAGE_SHIFT; 3398c2ecf20Sopenharmony_ci debugfs_blob[i].data = memremap(md->phys_addr, 3408c2ecf20Sopenharmony_ci debugfs_blob[i].size, 3418c2ecf20Sopenharmony_ci MEMREMAP_WB); 3428c2ecf20Sopenharmony_ci if (!debugfs_blob[i].data) 3438c2ecf20Sopenharmony_ci continue; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci debugfs_create_blob(name, 0400, efi_debugfs, &debugfs_blob[i]); 3468c2ecf20Sopenharmony_ci i++; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci#else 3508c2ecf20Sopenharmony_cistatic inline void efi_debugfs_init(void) {} 3518c2ecf20Sopenharmony_ci#endif 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci/* 3548c2ecf20Sopenharmony_ci * We register the efi subsystem with the firmware subsystem and the 3558c2ecf20Sopenharmony_ci * efivars subsystem with the efi subsystem, if the system was booted with 3568c2ecf20Sopenharmony_ci * EFI. 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_cistatic int __init efisubsys_init(void) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci int error; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (!efi_enabled(EFI_RUNTIME_SERVICES)) 3638c2ecf20Sopenharmony_ci efi.runtime_supported_mask = 0; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci if (!efi_enabled(EFI_BOOT)) 3668c2ecf20Sopenharmony_ci return 0; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (efi.runtime_supported_mask) { 3698c2ecf20Sopenharmony_ci /* 3708c2ecf20Sopenharmony_ci * Since we process only one efi_runtime_service() at a time, an 3718c2ecf20Sopenharmony_ci * ordered workqueue (which creates only one execution context) 3728c2ecf20Sopenharmony_ci * should suffice for all our needs. 3738c2ecf20Sopenharmony_ci */ 3748c2ecf20Sopenharmony_ci efi_rts_wq = alloc_ordered_workqueue("efi_rts_wq", 0); 3758c2ecf20Sopenharmony_ci if (!efi_rts_wq) { 3768c2ecf20Sopenharmony_ci pr_err("Creating efi_rts_wq failed, EFI runtime services disabled.\n"); 3778c2ecf20Sopenharmony_ci clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); 3788c2ecf20Sopenharmony_ci efi.runtime_supported_mask = 0; 3798c2ecf20Sopenharmony_ci return 0; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (efi_rt_services_supported(EFI_RT_SUPPORTED_TIME_SERVICES)) 3848c2ecf20Sopenharmony_ci platform_device_register_simple("rtc-efi", 0, NULL, 0); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* We register the efi directory at /sys/firmware/efi */ 3878c2ecf20Sopenharmony_ci efi_kobj = kobject_create_and_add("efi", firmware_kobj); 3888c2ecf20Sopenharmony_ci if (!efi_kobj) { 3898c2ecf20Sopenharmony_ci pr_err("efi: Firmware registration failed.\n"); 3908c2ecf20Sopenharmony_ci error = -ENOMEM; 3918c2ecf20Sopenharmony_ci goto err_destroy_wq; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE | 3958c2ecf20Sopenharmony_ci EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME)) { 3968c2ecf20Sopenharmony_ci error = generic_ops_register(); 3978c2ecf20Sopenharmony_ci if (error) 3988c2ecf20Sopenharmony_ci goto err_put; 3998c2ecf20Sopenharmony_ci efivar_ssdt_load(); 4008c2ecf20Sopenharmony_ci platform_device_register_simple("efivars", 0, NULL, 0); 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group); 4048c2ecf20Sopenharmony_ci if (error) { 4058c2ecf20Sopenharmony_ci pr_err("efi: Sysfs attribute export failed with error %d.\n", 4068c2ecf20Sopenharmony_ci error); 4078c2ecf20Sopenharmony_ci goto err_unregister; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci error = efi_runtime_map_init(efi_kobj); 4118c2ecf20Sopenharmony_ci if (error) 4128c2ecf20Sopenharmony_ci goto err_remove_group; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* and the standard mountpoint for efivarfs */ 4158c2ecf20Sopenharmony_ci error = sysfs_create_mount_point(efi_kobj, "efivars"); 4168c2ecf20Sopenharmony_ci if (error) { 4178c2ecf20Sopenharmony_ci pr_err("efivars: Subsystem registration failed.\n"); 4188c2ecf20Sopenharmony_ci goto err_remove_group; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (efi_enabled(EFI_DBG) && efi_enabled(EFI_PRESERVE_BS_REGIONS)) 4228c2ecf20Sopenharmony_ci efi_debugfs_init(); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci return 0; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cierr_remove_group: 4278c2ecf20Sopenharmony_ci sysfs_remove_group(efi_kobj, &efi_subsys_attr_group); 4288c2ecf20Sopenharmony_cierr_unregister: 4298c2ecf20Sopenharmony_ci if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE | 4308c2ecf20Sopenharmony_ci EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME)) 4318c2ecf20Sopenharmony_ci generic_ops_unregister(); 4328c2ecf20Sopenharmony_cierr_put: 4338c2ecf20Sopenharmony_ci kobject_put(efi_kobj); 4348c2ecf20Sopenharmony_cierr_destroy_wq: 4358c2ecf20Sopenharmony_ci if (efi_rts_wq) 4368c2ecf20Sopenharmony_ci destroy_workqueue(efi_rts_wq); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci return error; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cisubsys_initcall(efisubsys_init); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci/* 4448c2ecf20Sopenharmony_ci * Find the efi memory descriptor for a given physical address. Given a 4458c2ecf20Sopenharmony_ci * physical address, determine if it exists within an EFI Memory Map entry, 4468c2ecf20Sopenharmony_ci * and if so, populate the supplied memory descriptor with the appropriate 4478c2ecf20Sopenharmony_ci * data. 4488c2ecf20Sopenharmony_ci */ 4498c2ecf20Sopenharmony_ciint efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci efi_memory_desc_t *md; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (!efi_enabled(EFI_MEMMAP)) { 4548c2ecf20Sopenharmony_ci pr_err_once("EFI_MEMMAP is not enabled.\n"); 4558c2ecf20Sopenharmony_ci return -EINVAL; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (!out_md) { 4598c2ecf20Sopenharmony_ci pr_err_once("out_md is null.\n"); 4608c2ecf20Sopenharmony_ci return -EINVAL; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci for_each_efi_memory_desc(md) { 4648c2ecf20Sopenharmony_ci u64 size; 4658c2ecf20Sopenharmony_ci u64 end; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci size = md->num_pages << EFI_PAGE_SHIFT; 4688c2ecf20Sopenharmony_ci end = md->phys_addr + size; 4698c2ecf20Sopenharmony_ci if (phys_addr >= md->phys_addr && phys_addr < end) { 4708c2ecf20Sopenharmony_ci memcpy(out_md, md, sizeof(*out_md)); 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci return -ENOENT; 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci/* 4788c2ecf20Sopenharmony_ci * Calculate the highest address of an efi memory descriptor. 4798c2ecf20Sopenharmony_ci */ 4808c2ecf20Sopenharmony_ciu64 __init efi_mem_desc_end(efi_memory_desc_t *md) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci u64 size = md->num_pages << EFI_PAGE_SHIFT; 4838c2ecf20Sopenharmony_ci u64 end = md->phys_addr + size; 4848c2ecf20Sopenharmony_ci return end; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_civoid __init __weak efi_arch_mem_reserve(phys_addr_t addr, u64 size) {} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci/** 4908c2ecf20Sopenharmony_ci * efi_mem_reserve - Reserve an EFI memory region 4918c2ecf20Sopenharmony_ci * @addr: Physical address to reserve 4928c2ecf20Sopenharmony_ci * @size: Size of reservation 4938c2ecf20Sopenharmony_ci * 4948c2ecf20Sopenharmony_ci * Mark a region as reserved from general kernel allocation and 4958c2ecf20Sopenharmony_ci * prevent it being released by efi_free_boot_services(). 4968c2ecf20Sopenharmony_ci * 4978c2ecf20Sopenharmony_ci * This function should be called drivers once they've parsed EFI 4988c2ecf20Sopenharmony_ci * configuration tables to figure out where their data lives, e.g. 4998c2ecf20Sopenharmony_ci * efi_esrt_init(). 5008c2ecf20Sopenharmony_ci */ 5018c2ecf20Sopenharmony_civoid __init efi_mem_reserve(phys_addr_t addr, u64 size) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci if (!memblock_is_region_reserved(addr, size)) 5048c2ecf20Sopenharmony_ci memblock_reserve(addr, size); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* 5078c2ecf20Sopenharmony_ci * Some architectures (x86) reserve all boot services ranges 5088c2ecf20Sopenharmony_ci * until efi_free_boot_services() because of buggy firmware 5098c2ecf20Sopenharmony_ci * implementations. This means the above memblock_reserve() is 5108c2ecf20Sopenharmony_ci * superfluous on x86 and instead what it needs to do is 5118c2ecf20Sopenharmony_ci * ensure the @start, @size is not freed. 5128c2ecf20Sopenharmony_ci */ 5138c2ecf20Sopenharmony_ci efi_arch_mem_reserve(addr, size); 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic const efi_config_table_type_t common_tables[] __initconst = { 5178c2ecf20Sopenharmony_ci {ACPI_20_TABLE_GUID, &efi.acpi20, "ACPI 2.0" }, 5188c2ecf20Sopenharmony_ci {ACPI_TABLE_GUID, &efi.acpi, "ACPI" }, 5198c2ecf20Sopenharmony_ci {SMBIOS_TABLE_GUID, &efi.smbios, "SMBIOS" }, 5208c2ecf20Sopenharmony_ci {SMBIOS3_TABLE_GUID, &efi.smbios3, "SMBIOS 3.0" }, 5218c2ecf20Sopenharmony_ci {EFI_SYSTEM_RESOURCE_TABLE_GUID, &efi.esrt, "ESRT" }, 5228c2ecf20Sopenharmony_ci {EFI_MEMORY_ATTRIBUTES_TABLE_GUID, &efi_mem_attr_table, "MEMATTR" }, 5238c2ecf20Sopenharmony_ci {LINUX_EFI_RANDOM_SEED_TABLE_GUID, &efi_rng_seed, "RNG" }, 5248c2ecf20Sopenharmony_ci {LINUX_EFI_TPM_EVENT_LOG_GUID, &efi.tpm_log, "TPMEventLog" }, 5258c2ecf20Sopenharmony_ci {LINUX_EFI_TPM_FINAL_LOG_GUID, &efi.tpm_final_log, "TPMFinalLog" }, 5268c2ecf20Sopenharmony_ci {LINUX_EFI_MEMRESERVE_TABLE_GUID, &mem_reserve, "MEMRESERVE" }, 5278c2ecf20Sopenharmony_ci {LINUX_EFI_INITRD_MEDIA_GUID, &initrd, "INITRD" }, 5288c2ecf20Sopenharmony_ci {EFI_RT_PROPERTIES_TABLE_GUID, &rt_prop, "RTPROP" }, 5298c2ecf20Sopenharmony_ci#ifdef CONFIG_EFI_RCI2_TABLE 5308c2ecf20Sopenharmony_ci {DELLEMC_EFI_RCI2_TABLE_GUID, &rci2_table_phys }, 5318c2ecf20Sopenharmony_ci#endif 5328c2ecf20Sopenharmony_ci#ifdef CONFIG_LOAD_UEFI_KEYS 5338c2ecf20Sopenharmony_ci {LINUX_EFI_MOK_VARIABLE_TABLE_GUID, &efi.mokvar_table, "MOKvar" }, 5348c2ecf20Sopenharmony_ci#endif 5358c2ecf20Sopenharmony_ci {}, 5368c2ecf20Sopenharmony_ci}; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cistatic __init int match_config_table(const efi_guid_t *guid, 5398c2ecf20Sopenharmony_ci unsigned long table, 5408c2ecf20Sopenharmony_ci const efi_config_table_type_t *table_types) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci int i; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci for (i = 0; efi_guidcmp(table_types[i].guid, NULL_GUID); i++) { 5458c2ecf20Sopenharmony_ci if (!efi_guidcmp(*guid, table_types[i].guid)) { 5468c2ecf20Sopenharmony_ci *(table_types[i].ptr) = table; 5478c2ecf20Sopenharmony_ci if (table_types[i].name[0]) 5488c2ecf20Sopenharmony_ci pr_cont("%s=0x%lx ", 5498c2ecf20Sopenharmony_ci table_types[i].name, table); 5508c2ecf20Sopenharmony_ci return 1; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci return 0; 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ciint __init efi_config_parse_tables(const efi_config_table_t *config_tables, 5588c2ecf20Sopenharmony_ci int count, 5598c2ecf20Sopenharmony_ci const efi_config_table_type_t *arch_tables) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci const efi_config_table_64_t *tbl64 = (void *)config_tables; 5628c2ecf20Sopenharmony_ci const efi_config_table_32_t *tbl32 = (void *)config_tables; 5638c2ecf20Sopenharmony_ci const efi_guid_t *guid; 5648c2ecf20Sopenharmony_ci unsigned long table; 5658c2ecf20Sopenharmony_ci int i; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci pr_info(""); 5688c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 5698c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_X86)) { 5708c2ecf20Sopenharmony_ci guid = &config_tables[i].guid; 5718c2ecf20Sopenharmony_ci table = (unsigned long)config_tables[i].table; 5728c2ecf20Sopenharmony_ci } else if (efi_enabled(EFI_64BIT)) { 5738c2ecf20Sopenharmony_ci guid = &tbl64[i].guid; 5748c2ecf20Sopenharmony_ci table = tbl64[i].table; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_X86_32) && 5778c2ecf20Sopenharmony_ci tbl64[i].table > U32_MAX) { 5788c2ecf20Sopenharmony_ci pr_cont("\n"); 5798c2ecf20Sopenharmony_ci pr_err("Table located above 4GB, disabling EFI.\n"); 5808c2ecf20Sopenharmony_ci return -EINVAL; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci } else { 5838c2ecf20Sopenharmony_ci guid = &tbl32[i].guid; 5848c2ecf20Sopenharmony_ci table = tbl32[i].table; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci if (!match_config_table(guid, table, common_tables) && arch_tables) 5888c2ecf20Sopenharmony_ci match_config_table(guid, table, arch_tables); 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci pr_cont("\n"); 5918c2ecf20Sopenharmony_ci set_bit(EFI_CONFIG_TABLES, &efi.flags); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci if (efi_rng_seed != EFI_INVALID_TABLE_ADDR) { 5948c2ecf20Sopenharmony_ci struct linux_efi_random_seed *seed; 5958c2ecf20Sopenharmony_ci u32 size = 0; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci seed = early_memremap(efi_rng_seed, sizeof(*seed)); 5988c2ecf20Sopenharmony_ci if (seed != NULL) { 5998c2ecf20Sopenharmony_ci size = min_t(u32, seed->size, SZ_1K); // sanity check 6008c2ecf20Sopenharmony_ci early_memunmap(seed, sizeof(*seed)); 6018c2ecf20Sopenharmony_ci } else { 6028c2ecf20Sopenharmony_ci pr_err("Could not map UEFI random seed!\n"); 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci if (size > 0) { 6058c2ecf20Sopenharmony_ci seed = early_memremap(efi_rng_seed, 6068c2ecf20Sopenharmony_ci sizeof(*seed) + size); 6078c2ecf20Sopenharmony_ci if (seed != NULL) { 6088c2ecf20Sopenharmony_ci add_bootloader_randomness(seed->bits, size); 6098c2ecf20Sopenharmony_ci memzero_explicit(seed->bits, size); 6108c2ecf20Sopenharmony_ci early_memunmap(seed, sizeof(*seed) + size); 6118c2ecf20Sopenharmony_ci } else { 6128c2ecf20Sopenharmony_ci pr_err("Could not map UEFI random seed!\n"); 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_X86_32) && efi_enabled(EFI_MEMMAP)) 6188c2ecf20Sopenharmony_ci efi_memattr_init(); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci efi_tpm_eventlog_init(); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci if (mem_reserve != EFI_INVALID_TABLE_ADDR) { 6238c2ecf20Sopenharmony_ci unsigned long prsv = mem_reserve; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci while (prsv) { 6268c2ecf20Sopenharmony_ci struct linux_efi_memreserve *rsv; 6278c2ecf20Sopenharmony_ci u8 *p; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* 6308c2ecf20Sopenharmony_ci * Just map a full page: that is what we will get 6318c2ecf20Sopenharmony_ci * anyway, and it permits us to map the entire entry 6328c2ecf20Sopenharmony_ci * before knowing its size. 6338c2ecf20Sopenharmony_ci */ 6348c2ecf20Sopenharmony_ci p = early_memremap(ALIGN_DOWN(prsv, PAGE_SIZE), 6358c2ecf20Sopenharmony_ci PAGE_SIZE); 6368c2ecf20Sopenharmony_ci if (p == NULL) { 6378c2ecf20Sopenharmony_ci pr_err("Could not map UEFI memreserve entry!\n"); 6388c2ecf20Sopenharmony_ci return -ENOMEM; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci rsv = (void *)(p + prsv % PAGE_SIZE); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci /* reserve the entry itself */ 6448c2ecf20Sopenharmony_ci memblock_reserve(prsv, 6458c2ecf20Sopenharmony_ci struct_size(rsv, entry, rsv->size)); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci for (i = 0; i < atomic_read(&rsv->count); i++) { 6488c2ecf20Sopenharmony_ci memblock_reserve(rsv->entry[i].base, 6498c2ecf20Sopenharmony_ci rsv->entry[i].size); 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci prsv = rsv->next; 6538c2ecf20Sopenharmony_ci early_memunmap(p, PAGE_SIZE); 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci if (rt_prop != EFI_INVALID_TABLE_ADDR) { 6588c2ecf20Sopenharmony_ci efi_rt_properties_table_t *tbl; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci tbl = early_memremap(rt_prop, sizeof(*tbl)); 6618c2ecf20Sopenharmony_ci if (tbl) { 6628c2ecf20Sopenharmony_ci efi.runtime_supported_mask &= tbl->runtime_services_supported; 6638c2ecf20Sopenharmony_ci early_memunmap(tbl, sizeof(*tbl)); 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && 6688c2ecf20Sopenharmony_ci initrd != EFI_INVALID_TABLE_ADDR && phys_initrd_size == 0) { 6698c2ecf20Sopenharmony_ci struct linux_efi_initrd *tbl; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci tbl = early_memremap(initrd, sizeof(*tbl)); 6728c2ecf20Sopenharmony_ci if (tbl) { 6738c2ecf20Sopenharmony_ci phys_initrd_start = tbl->base; 6748c2ecf20Sopenharmony_ci phys_initrd_size = tbl->size; 6758c2ecf20Sopenharmony_ci early_memunmap(tbl, sizeof(*tbl)); 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci return 0; 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ciint __init efi_systab_check_header(const efi_table_hdr_t *systab_hdr, 6838c2ecf20Sopenharmony_ci int min_major_version) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci if (systab_hdr->signature != EFI_SYSTEM_TABLE_SIGNATURE) { 6868c2ecf20Sopenharmony_ci pr_err("System table signature incorrect!\n"); 6878c2ecf20Sopenharmony_ci return -EINVAL; 6888c2ecf20Sopenharmony_ci } 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci if ((systab_hdr->revision >> 16) < min_major_version) 6918c2ecf20Sopenharmony_ci pr_err("Warning: System table version %d.%02d, expected %d.00 or greater!\n", 6928c2ecf20Sopenharmony_ci systab_hdr->revision >> 16, 6938c2ecf20Sopenharmony_ci systab_hdr->revision & 0xffff, 6948c2ecf20Sopenharmony_ci min_major_version); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci return 0; 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci#ifndef CONFIG_IA64 7008c2ecf20Sopenharmony_cistatic const efi_char16_t *__init map_fw_vendor(unsigned long fw_vendor, 7018c2ecf20Sopenharmony_ci size_t size) 7028c2ecf20Sopenharmony_ci{ 7038c2ecf20Sopenharmony_ci const efi_char16_t *ret; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci ret = early_memremap_ro(fw_vendor, size); 7068c2ecf20Sopenharmony_ci if (!ret) 7078c2ecf20Sopenharmony_ci pr_err("Could not map the firmware vendor!\n"); 7088c2ecf20Sopenharmony_ci return ret; 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic void __init unmap_fw_vendor(const void *fw_vendor, size_t size) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci early_memunmap((void *)fw_vendor, size); 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci#else 7168c2ecf20Sopenharmony_ci#define map_fw_vendor(p, s) __va(p) 7178c2ecf20Sopenharmony_ci#define unmap_fw_vendor(v, s) 7188c2ecf20Sopenharmony_ci#endif 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_civoid __init efi_systab_report_header(const efi_table_hdr_t *systab_hdr, 7218c2ecf20Sopenharmony_ci unsigned long fw_vendor) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci char vendor[100] = "unknown"; 7248c2ecf20Sopenharmony_ci const efi_char16_t *c16; 7258c2ecf20Sopenharmony_ci size_t i; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci c16 = map_fw_vendor(fw_vendor, sizeof(vendor) * sizeof(efi_char16_t)); 7288c2ecf20Sopenharmony_ci if (c16) { 7298c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(vendor) - 1 && c16[i]; ++i) 7308c2ecf20Sopenharmony_ci vendor[i] = c16[i]; 7318c2ecf20Sopenharmony_ci vendor[i] = '\0'; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci unmap_fw_vendor(c16, sizeof(vendor) * sizeof(efi_char16_t)); 7348c2ecf20Sopenharmony_ci } 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci pr_info("EFI v%u.%.02u by %s\n", 7378c2ecf20Sopenharmony_ci systab_hdr->revision >> 16, 7388c2ecf20Sopenharmony_ci systab_hdr->revision & 0xffff, 7398c2ecf20Sopenharmony_ci vendor); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_X86_64) && 7428c2ecf20Sopenharmony_ci systab_hdr->revision > EFI_1_10_SYSTEM_TABLE_REVISION && 7438c2ecf20Sopenharmony_ci !strcmp(vendor, "Apple")) { 7448c2ecf20Sopenharmony_ci pr_info("Apple Mac detected, using EFI v1.10 runtime services only\n"); 7458c2ecf20Sopenharmony_ci efi.runtime_version = EFI_1_10_SYSTEM_TABLE_REVISION; 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci} 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_cistatic __initdata char memory_type_name[][13] = { 7508c2ecf20Sopenharmony_ci "Reserved", 7518c2ecf20Sopenharmony_ci "Loader Code", 7528c2ecf20Sopenharmony_ci "Loader Data", 7538c2ecf20Sopenharmony_ci "Boot Code", 7548c2ecf20Sopenharmony_ci "Boot Data", 7558c2ecf20Sopenharmony_ci "Runtime Code", 7568c2ecf20Sopenharmony_ci "Runtime Data", 7578c2ecf20Sopenharmony_ci "Conventional", 7588c2ecf20Sopenharmony_ci "Unusable", 7598c2ecf20Sopenharmony_ci "ACPI Reclaim", 7608c2ecf20Sopenharmony_ci "ACPI Mem NVS", 7618c2ecf20Sopenharmony_ci "MMIO", 7628c2ecf20Sopenharmony_ci "MMIO Port", 7638c2ecf20Sopenharmony_ci "PAL Code", 7648c2ecf20Sopenharmony_ci "Persistent", 7658c2ecf20Sopenharmony_ci}; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_cichar * __init efi_md_typeattr_format(char *buf, size_t size, 7688c2ecf20Sopenharmony_ci const efi_memory_desc_t *md) 7698c2ecf20Sopenharmony_ci{ 7708c2ecf20Sopenharmony_ci char *pos; 7718c2ecf20Sopenharmony_ci int type_len; 7728c2ecf20Sopenharmony_ci u64 attr; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci pos = buf; 7758c2ecf20Sopenharmony_ci if (md->type >= ARRAY_SIZE(memory_type_name)) 7768c2ecf20Sopenharmony_ci type_len = snprintf(pos, size, "[type=%u", md->type); 7778c2ecf20Sopenharmony_ci else 7788c2ecf20Sopenharmony_ci type_len = snprintf(pos, size, "[%-*s", 7798c2ecf20Sopenharmony_ci (int)(sizeof(memory_type_name[0]) - 1), 7808c2ecf20Sopenharmony_ci memory_type_name[md->type]); 7818c2ecf20Sopenharmony_ci if (type_len >= size) 7828c2ecf20Sopenharmony_ci return buf; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci pos += type_len; 7858c2ecf20Sopenharmony_ci size -= type_len; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci attr = md->attribute; 7888c2ecf20Sopenharmony_ci if (attr & ~(EFI_MEMORY_UC | EFI_MEMORY_WC | EFI_MEMORY_WT | 7898c2ecf20Sopenharmony_ci EFI_MEMORY_WB | EFI_MEMORY_UCE | EFI_MEMORY_RO | 7908c2ecf20Sopenharmony_ci EFI_MEMORY_WP | EFI_MEMORY_RP | EFI_MEMORY_XP | 7918c2ecf20Sopenharmony_ci EFI_MEMORY_NV | EFI_MEMORY_SP | EFI_MEMORY_CPU_CRYPTO | 7928c2ecf20Sopenharmony_ci EFI_MEMORY_RUNTIME | EFI_MEMORY_MORE_RELIABLE)) 7938c2ecf20Sopenharmony_ci snprintf(pos, size, "|attr=0x%016llx]", 7948c2ecf20Sopenharmony_ci (unsigned long long)attr); 7958c2ecf20Sopenharmony_ci else 7968c2ecf20Sopenharmony_ci snprintf(pos, size, 7978c2ecf20Sopenharmony_ci "|%3s|%2s|%2s|%2s|%2s|%2s|%2s|%2s|%2s|%3s|%2s|%2s|%2s|%2s]", 7988c2ecf20Sopenharmony_ci attr & EFI_MEMORY_RUNTIME ? "RUN" : "", 7998c2ecf20Sopenharmony_ci attr & EFI_MEMORY_MORE_RELIABLE ? "MR" : "", 8008c2ecf20Sopenharmony_ci attr & EFI_MEMORY_CPU_CRYPTO ? "CC" : "", 8018c2ecf20Sopenharmony_ci attr & EFI_MEMORY_SP ? "SP" : "", 8028c2ecf20Sopenharmony_ci attr & EFI_MEMORY_NV ? "NV" : "", 8038c2ecf20Sopenharmony_ci attr & EFI_MEMORY_XP ? "XP" : "", 8048c2ecf20Sopenharmony_ci attr & EFI_MEMORY_RP ? "RP" : "", 8058c2ecf20Sopenharmony_ci attr & EFI_MEMORY_WP ? "WP" : "", 8068c2ecf20Sopenharmony_ci attr & EFI_MEMORY_RO ? "RO" : "", 8078c2ecf20Sopenharmony_ci attr & EFI_MEMORY_UCE ? "UCE" : "", 8088c2ecf20Sopenharmony_ci attr & EFI_MEMORY_WB ? "WB" : "", 8098c2ecf20Sopenharmony_ci attr & EFI_MEMORY_WT ? "WT" : "", 8108c2ecf20Sopenharmony_ci attr & EFI_MEMORY_WC ? "WC" : "", 8118c2ecf20Sopenharmony_ci attr & EFI_MEMORY_UC ? "UC" : ""); 8128c2ecf20Sopenharmony_ci return buf; 8138c2ecf20Sopenharmony_ci} 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci/* 8168c2ecf20Sopenharmony_ci * IA64 has a funky EFI memory map that doesn't work the same way as 8178c2ecf20Sopenharmony_ci * other architectures. 8188c2ecf20Sopenharmony_ci */ 8198c2ecf20Sopenharmony_ci#ifndef CONFIG_IA64 8208c2ecf20Sopenharmony_ci/* 8218c2ecf20Sopenharmony_ci * efi_mem_attributes - lookup memmap attributes for physical address 8228c2ecf20Sopenharmony_ci * @phys_addr: the physical address to lookup 8238c2ecf20Sopenharmony_ci * 8248c2ecf20Sopenharmony_ci * Search in the EFI memory map for the region covering 8258c2ecf20Sopenharmony_ci * @phys_addr. Returns the EFI memory attributes if the region 8268c2ecf20Sopenharmony_ci * was found in the memory map, 0 otherwise. 8278c2ecf20Sopenharmony_ci */ 8288c2ecf20Sopenharmony_ciu64 efi_mem_attributes(unsigned long phys_addr) 8298c2ecf20Sopenharmony_ci{ 8308c2ecf20Sopenharmony_ci efi_memory_desc_t *md; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci if (!efi_enabled(EFI_MEMMAP)) 8338c2ecf20Sopenharmony_ci return 0; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci for_each_efi_memory_desc(md) { 8368c2ecf20Sopenharmony_ci if ((md->phys_addr <= phys_addr) && 8378c2ecf20Sopenharmony_ci (phys_addr < (md->phys_addr + 8388c2ecf20Sopenharmony_ci (md->num_pages << EFI_PAGE_SHIFT)))) 8398c2ecf20Sopenharmony_ci return md->attribute; 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci return 0; 8428c2ecf20Sopenharmony_ci} 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci/* 8458c2ecf20Sopenharmony_ci * efi_mem_type - lookup memmap type for physical address 8468c2ecf20Sopenharmony_ci * @phys_addr: the physical address to lookup 8478c2ecf20Sopenharmony_ci * 8488c2ecf20Sopenharmony_ci * Search in the EFI memory map for the region covering @phys_addr. 8498c2ecf20Sopenharmony_ci * Returns the EFI memory type if the region was found in the memory 8508c2ecf20Sopenharmony_ci * map, -EINVAL otherwise. 8518c2ecf20Sopenharmony_ci */ 8528c2ecf20Sopenharmony_ciint efi_mem_type(unsigned long phys_addr) 8538c2ecf20Sopenharmony_ci{ 8548c2ecf20Sopenharmony_ci const efi_memory_desc_t *md; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci if (!efi_enabled(EFI_MEMMAP)) 8578c2ecf20Sopenharmony_ci return -ENOTSUPP; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci for_each_efi_memory_desc(md) { 8608c2ecf20Sopenharmony_ci if ((md->phys_addr <= phys_addr) && 8618c2ecf20Sopenharmony_ci (phys_addr < (md->phys_addr + 8628c2ecf20Sopenharmony_ci (md->num_pages << EFI_PAGE_SHIFT)))) 8638c2ecf20Sopenharmony_ci return md->type; 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci return -EINVAL; 8668c2ecf20Sopenharmony_ci} 8678c2ecf20Sopenharmony_ci#endif 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ciint efi_status_to_err(efi_status_t status) 8708c2ecf20Sopenharmony_ci{ 8718c2ecf20Sopenharmony_ci int err; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci switch (status) { 8748c2ecf20Sopenharmony_ci case EFI_SUCCESS: 8758c2ecf20Sopenharmony_ci err = 0; 8768c2ecf20Sopenharmony_ci break; 8778c2ecf20Sopenharmony_ci case EFI_INVALID_PARAMETER: 8788c2ecf20Sopenharmony_ci err = -EINVAL; 8798c2ecf20Sopenharmony_ci break; 8808c2ecf20Sopenharmony_ci case EFI_OUT_OF_RESOURCES: 8818c2ecf20Sopenharmony_ci err = -ENOSPC; 8828c2ecf20Sopenharmony_ci break; 8838c2ecf20Sopenharmony_ci case EFI_DEVICE_ERROR: 8848c2ecf20Sopenharmony_ci err = -EIO; 8858c2ecf20Sopenharmony_ci break; 8868c2ecf20Sopenharmony_ci case EFI_WRITE_PROTECTED: 8878c2ecf20Sopenharmony_ci err = -EROFS; 8888c2ecf20Sopenharmony_ci break; 8898c2ecf20Sopenharmony_ci case EFI_SECURITY_VIOLATION: 8908c2ecf20Sopenharmony_ci err = -EACCES; 8918c2ecf20Sopenharmony_ci break; 8928c2ecf20Sopenharmony_ci case EFI_NOT_FOUND: 8938c2ecf20Sopenharmony_ci err = -ENOENT; 8948c2ecf20Sopenharmony_ci break; 8958c2ecf20Sopenharmony_ci case EFI_ABORTED: 8968c2ecf20Sopenharmony_ci err = -EINTR; 8978c2ecf20Sopenharmony_ci break; 8988c2ecf20Sopenharmony_ci default: 8998c2ecf20Sopenharmony_ci err = -EINVAL; 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci return err; 9038c2ecf20Sopenharmony_ci} 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(efi_mem_reserve_persistent_lock); 9068c2ecf20Sopenharmony_cistatic struct linux_efi_memreserve *efi_memreserve_root __ro_after_init; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_cistatic int __init efi_memreserve_map_root(void) 9098c2ecf20Sopenharmony_ci{ 9108c2ecf20Sopenharmony_ci if (mem_reserve == EFI_INVALID_TABLE_ADDR) 9118c2ecf20Sopenharmony_ci return -ENODEV; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci efi_memreserve_root = memremap(mem_reserve, 9148c2ecf20Sopenharmony_ci sizeof(*efi_memreserve_root), 9158c2ecf20Sopenharmony_ci MEMREMAP_WB); 9168c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!efi_memreserve_root)) 9178c2ecf20Sopenharmony_ci return -ENOMEM; 9188c2ecf20Sopenharmony_ci return 0; 9198c2ecf20Sopenharmony_ci} 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_cistatic int efi_mem_reserve_iomem(phys_addr_t addr, u64 size) 9228c2ecf20Sopenharmony_ci{ 9238c2ecf20Sopenharmony_ci struct resource *res, *parent; 9248c2ecf20Sopenharmony_ci int ret; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci res = kzalloc(sizeof(struct resource), GFP_ATOMIC); 9278c2ecf20Sopenharmony_ci if (!res) 9288c2ecf20Sopenharmony_ci return -ENOMEM; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci res->name = "reserved"; 9318c2ecf20Sopenharmony_ci res->flags = IORESOURCE_MEM; 9328c2ecf20Sopenharmony_ci res->start = addr; 9338c2ecf20Sopenharmony_ci res->end = addr + size - 1; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci /* we expect a conflict with a 'System RAM' region */ 9368c2ecf20Sopenharmony_ci parent = request_resource_conflict(&iomem_resource, res); 9378c2ecf20Sopenharmony_ci ret = parent ? request_resource(parent, res) : 0; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci /* 9408c2ecf20Sopenharmony_ci * Given that efi_mem_reserve_iomem() can be called at any 9418c2ecf20Sopenharmony_ci * time, only call memblock_reserve() if the architecture 9428c2ecf20Sopenharmony_ci * keeps the infrastructure around. 9438c2ecf20Sopenharmony_ci */ 9448c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ARCH_KEEP_MEMBLOCK) && !ret) 9458c2ecf20Sopenharmony_ci memblock_reserve(addr, size); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci return ret; 9488c2ecf20Sopenharmony_ci} 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ciint __ref efi_mem_reserve_persistent(phys_addr_t addr, u64 size) 9518c2ecf20Sopenharmony_ci{ 9528c2ecf20Sopenharmony_ci struct linux_efi_memreserve *rsv; 9538c2ecf20Sopenharmony_ci unsigned long prsv; 9548c2ecf20Sopenharmony_ci int rc, index; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci if (efi_memreserve_root == (void *)ULONG_MAX) 9578c2ecf20Sopenharmony_ci return -ENODEV; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci if (!efi_memreserve_root) { 9608c2ecf20Sopenharmony_ci rc = efi_memreserve_map_root(); 9618c2ecf20Sopenharmony_ci if (rc) 9628c2ecf20Sopenharmony_ci return rc; 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci /* first try to find a slot in an existing linked list entry */ 9668c2ecf20Sopenharmony_ci for (prsv = efi_memreserve_root->next; prsv; ) { 9678c2ecf20Sopenharmony_ci rsv = memremap(prsv, sizeof(*rsv), MEMREMAP_WB); 9688c2ecf20Sopenharmony_ci if (!rsv) 9698c2ecf20Sopenharmony_ci return -ENOMEM; 9708c2ecf20Sopenharmony_ci index = atomic_fetch_add_unless(&rsv->count, 1, rsv->size); 9718c2ecf20Sopenharmony_ci if (index < rsv->size) { 9728c2ecf20Sopenharmony_ci rsv->entry[index].base = addr; 9738c2ecf20Sopenharmony_ci rsv->entry[index].size = size; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci memunmap(rsv); 9768c2ecf20Sopenharmony_ci return efi_mem_reserve_iomem(addr, size); 9778c2ecf20Sopenharmony_ci } 9788c2ecf20Sopenharmony_ci prsv = rsv->next; 9798c2ecf20Sopenharmony_ci memunmap(rsv); 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci /* no slot found - allocate a new linked list entry */ 9838c2ecf20Sopenharmony_ci rsv = (struct linux_efi_memreserve *)__get_free_page(GFP_ATOMIC); 9848c2ecf20Sopenharmony_ci if (!rsv) 9858c2ecf20Sopenharmony_ci return -ENOMEM; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci rc = efi_mem_reserve_iomem(__pa(rsv), SZ_4K); 9888c2ecf20Sopenharmony_ci if (rc) { 9898c2ecf20Sopenharmony_ci free_page((unsigned long)rsv); 9908c2ecf20Sopenharmony_ci return rc; 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci /* 9948c2ecf20Sopenharmony_ci * The memremap() call above assumes that a linux_efi_memreserve entry 9958c2ecf20Sopenharmony_ci * never crosses a page boundary, so let's ensure that this remains true 9968c2ecf20Sopenharmony_ci * even when kexec'ing a 4k pages kernel from a >4k pages kernel, by 9978c2ecf20Sopenharmony_ci * using SZ_4K explicitly in the size calculation below. 9988c2ecf20Sopenharmony_ci */ 9998c2ecf20Sopenharmony_ci rsv->size = EFI_MEMRESERVE_COUNT(SZ_4K); 10008c2ecf20Sopenharmony_ci atomic_set(&rsv->count, 1); 10018c2ecf20Sopenharmony_ci rsv->entry[0].base = addr; 10028c2ecf20Sopenharmony_ci rsv->entry[0].size = size; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci spin_lock(&efi_mem_reserve_persistent_lock); 10058c2ecf20Sopenharmony_ci rsv->next = efi_memreserve_root->next; 10068c2ecf20Sopenharmony_ci efi_memreserve_root->next = __pa(rsv); 10078c2ecf20Sopenharmony_ci spin_unlock(&efi_mem_reserve_persistent_lock); 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci return efi_mem_reserve_iomem(addr, size); 10108c2ecf20Sopenharmony_ci} 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_cistatic int __init efi_memreserve_root_init(void) 10138c2ecf20Sopenharmony_ci{ 10148c2ecf20Sopenharmony_ci if (efi_memreserve_root) 10158c2ecf20Sopenharmony_ci return 0; 10168c2ecf20Sopenharmony_ci if (efi_memreserve_map_root()) 10178c2ecf20Sopenharmony_ci efi_memreserve_root = (void *)ULONG_MAX; 10188c2ecf20Sopenharmony_ci return 0; 10198c2ecf20Sopenharmony_ci} 10208c2ecf20Sopenharmony_ciearly_initcall(efi_memreserve_root_init); 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci#ifdef CONFIG_KEXEC 10238c2ecf20Sopenharmony_cistatic int update_efi_random_seed(struct notifier_block *nb, 10248c2ecf20Sopenharmony_ci unsigned long code, void *unused) 10258c2ecf20Sopenharmony_ci{ 10268c2ecf20Sopenharmony_ci struct linux_efi_random_seed *seed; 10278c2ecf20Sopenharmony_ci u32 size = 0; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci if (!kexec_in_progress) 10308c2ecf20Sopenharmony_ci return NOTIFY_DONE; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci seed = memremap(efi_rng_seed, sizeof(*seed), MEMREMAP_WB); 10338c2ecf20Sopenharmony_ci if (seed != NULL) { 10348c2ecf20Sopenharmony_ci size = min(seed->size, EFI_RANDOM_SEED_SIZE); 10358c2ecf20Sopenharmony_ci memunmap(seed); 10368c2ecf20Sopenharmony_ci } else { 10378c2ecf20Sopenharmony_ci pr_err("Could not map UEFI random seed!\n"); 10388c2ecf20Sopenharmony_ci } 10398c2ecf20Sopenharmony_ci if (size > 0) { 10408c2ecf20Sopenharmony_ci seed = memremap(efi_rng_seed, sizeof(*seed) + size, 10418c2ecf20Sopenharmony_ci MEMREMAP_WB); 10428c2ecf20Sopenharmony_ci if (seed != NULL) { 10438c2ecf20Sopenharmony_ci seed->size = size; 10448c2ecf20Sopenharmony_ci get_random_bytes(seed->bits, seed->size); 10458c2ecf20Sopenharmony_ci memunmap(seed); 10468c2ecf20Sopenharmony_ci } else { 10478c2ecf20Sopenharmony_ci pr_err("Could not map UEFI random seed!\n"); 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci } 10508c2ecf20Sopenharmony_ci return NOTIFY_DONE; 10518c2ecf20Sopenharmony_ci} 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_cistatic struct notifier_block efi_random_seed_nb = { 10548c2ecf20Sopenharmony_ci .notifier_call = update_efi_random_seed, 10558c2ecf20Sopenharmony_ci}; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_cistatic int __init register_update_efi_random_seed(void) 10588c2ecf20Sopenharmony_ci{ 10598c2ecf20Sopenharmony_ci if (efi_rng_seed == EFI_INVALID_TABLE_ADDR) 10608c2ecf20Sopenharmony_ci return 0; 10618c2ecf20Sopenharmony_ci return register_reboot_notifier(&efi_random_seed_nb); 10628c2ecf20Sopenharmony_ci} 10638c2ecf20Sopenharmony_cilate_initcall(register_update_efi_random_seed); 10648c2ecf20Sopenharmony_ci#endif 1065