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