xref: /openbmc/linux/drivers/firmware/efi/efi.c (revision fd5e9fccbd504c5179ab57ff695c610bca8809d6)
155716d26SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2a9499fa7STom Gundersen /*
3a9499fa7STom Gundersen  * efi.c - EFI subsystem
4a9499fa7STom Gundersen  *
5a9499fa7STom Gundersen  * Copyright (C) 2001,2003,2004 Dell <Matt_Domsch@dell.com>
6a9499fa7STom Gundersen  * Copyright (C) 2004 Intel Corporation <matthew.e.tolentino@intel.com>
7a9499fa7STom Gundersen  * Copyright (C) 2013 Tom Gundersen <teg@jklm.no>
8a9499fa7STom Gundersen  *
9a9499fa7STom Gundersen  * This code registers /sys/firmware/efi{,/efivars} when EFI is supported,
10a9499fa7STom Gundersen  * allowing the efivarfs to be mounted or the efivars module to be loaded.
11a9499fa7STom Gundersen  * The existance of /sys/firmware/efi may also be used by userspace to
12a9499fa7STom Gundersen  * determine that the system supports EFI.
13a9499fa7STom Gundersen  */
14a9499fa7STom Gundersen 
15272686bfSLeif Lindholm #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
16272686bfSLeif Lindholm 
17a9499fa7STom Gundersen #include <linux/kobject.h>
18a9499fa7STom Gundersen #include <linux/module.h>
19a9499fa7STom Gundersen #include <linux/init.h>
200e72a6a3SHans de Goede #include <linux/debugfs.h>
21a9499fa7STom Gundersen #include <linux/device.h>
22a9499fa7STom Gundersen #include <linux/efi.h>
230302f71cSMark Salter #include <linux/of.h>
24f4dc7fffSArd Biesheuvel #include <linux/initrd.h>
25272686bfSLeif Lindholm #include <linux/io.h>
2663625988SArd Biesheuvel #include <linux/kexec.h>
2728d54022SLee, Chun-Yi #include <linux/platform_device.h>
2863625988SArd Biesheuvel #include <linux/random.h>
2963625988SArd Biesheuvel #include <linux/reboot.h>
30475fb4e8SOctavian Purdila #include <linux/slab.h>
31475fb4e8SOctavian Purdila #include <linux/acpi.h>
32475fb4e8SOctavian Purdila #include <linux/ucs2_string.h>
33816e7612SMatt Fleming #include <linux/memblock.h>
341957a85bSMatthew Garrett #include <linux/security.h>
35272686bfSLeif Lindholm 
360f7f2f0cSArd Biesheuvel #include <asm/early_ioremap.h>
37f7d92489SArd Biesheuvel 
38272686bfSLeif Lindholm struct efi __read_mostly efi = {
3996a3dd3dSArd Biesheuvel 	.runtime_supported_mask = EFI_RT_SUPPORTED_ALL,
40272686bfSLeif Lindholm 	.acpi			= EFI_INVALID_TABLE_ADDR,
41272686bfSLeif Lindholm 	.acpi20			= EFI_INVALID_TABLE_ADDR,
42272686bfSLeif Lindholm 	.smbios			= EFI_INVALID_TABLE_ADDR,
43e1ccbbc9SArd Biesheuvel 	.smbios3		= EFI_INVALID_TABLE_ADDR,
440bb54905SPeter Jones 	.esrt			= EFI_INVALID_TABLE_ADDR,
4571e0940dSArd Biesheuvel 	.tpm_log		= EFI_INVALID_TABLE_ADDR,
46c46f3405SMatthew Garrett 	.tpm_final_log		= EFI_INVALID_TABLE_ADDR,
4758c90902SLenny Szubowicz #ifdef CONFIG_LOAD_UEFI_KEYS
4858c90902SLenny Szubowicz 	.mokvar_table		= EFI_INVALID_TABLE_ADDR,
4958c90902SLenny Szubowicz #endif
5012274189SDov Murik #ifdef CONFIG_EFI_COCO_SECRET
5112274189SDov Murik 	.coco_secret		= EFI_INVALID_TABLE_ADDR,
5212274189SDov Murik #endif
532053bc57SKirill A. Shutemov #ifdef CONFIG_UNACCEPTED_MEMORY
542053bc57SKirill A. Shutemov 	.unaccepted		= EFI_INVALID_TABLE_ADDR,
552053bc57SKirill A. Shutemov #endif
56272686bfSLeif Lindholm };
57272686bfSLeif Lindholm EXPORT_SYMBOL(efi);
58a9499fa7STom Gundersen 
59badc6198STom Lendacky unsigned long __ro_after_init efi_rng_seed = EFI_INVALID_TABLE_ADDR;
60b7846e6bSArd Biesheuvel static unsigned long __initdata mem_reserve = EFI_INVALID_TABLE_ADDR;
61fe4db90aSArd Biesheuvel static unsigned long __initdata rt_prop = EFI_INVALID_TABLE_ADDR;
62f4dc7fffSArd Biesheuvel static unsigned long __initdata initrd = EFI_INVALID_TABLE_ADDR;
635d288dbdSArd Biesheuvel 
64732ea9dbSArd Biesheuvel extern unsigned long screen_info_table;
65732ea9dbSArd Biesheuvel 
667e904a91SSai Praneeth struct mm_struct efi_mm = {
67d4af56c5SLiam R. Howlett 	.mm_mt			= MTREE_INIT_EXT(mm_mt, MM_MT_FLAGS, efi_mm.mmap_lock),
687e904a91SSai Praneeth 	.mm_users		= ATOMIC_INIT(2),
697e904a91SSai Praneeth 	.mm_count		= ATOMIC_INIT(1),
7057efa1feSJason Gunthorpe 	.write_protect_seq      = SEQCNT_ZERO(efi_mm.write_protect_seq),
7114c3656bSMichel Lespinasse 	MMAP_LOCK_INITIALIZER(efi_mm)
727e904a91SSai Praneeth 	.page_table_lock	= __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock),
737e904a91SSai Praneeth 	.mmlist			= LIST_HEAD_INIT(efi_mm.mmlist),
74c1a2f7f0SRik van Riel 	.cpu_bitmap		= { [BITS_TO_LONGS(NR_CPUS)] = 0},
757e904a91SSai Praneeth };
767e904a91SSai Praneeth 
773eb420e7SSai Praneeth struct workqueue_struct *efi_rts_wq;
783eb420e7SSai Praneeth 
79a031651fSJavier Martinez Canillas static bool disable_runtime = IS_ENABLED(CONFIG_EFI_DISABLE_RUNTIME);
setup_noefi(char * arg)80b2e0a54aSDave Young static int __init setup_noefi(char *arg)
81b2e0a54aSDave Young {
82b2e0a54aSDave Young 	disable_runtime = true;
83b2e0a54aSDave Young 	return 0;
84b2e0a54aSDave Young }
85b2e0a54aSDave Young early_param("noefi", setup_noefi);
86b2e0a54aSDave Young 
efi_runtime_disabled(void)87b2e0a54aSDave Young bool efi_runtime_disabled(void)
88b2e0a54aSDave Young {
89b2e0a54aSDave Young 	return disable_runtime;
90b2e0a54aSDave Young }
91b2e0a54aSDave Young 
__efi_soft_reserve_enabled(void)92b617c526SDan Williams bool __pure __efi_soft_reserve_enabled(void)
93b617c526SDan Williams {
94b617c526SDan Williams 	return !efi_enabled(EFI_MEM_NO_SOFT_RESERVE);
95b617c526SDan Williams }
96b617c526SDan Williams 
parse_efi_cmdline(char * str)975ae3683cSDave Young static int __init parse_efi_cmdline(char *str)
985ae3683cSDave Young {
999115c758SRicardo Neri 	if (!str) {
1009115c758SRicardo Neri 		pr_warn("need at least one option\n");
1019115c758SRicardo Neri 		return -EINVAL;
1029115c758SRicardo Neri 	}
1039115c758SRicardo Neri 
10412dd00e8SLeif Lindholm 	if (parse_option_str(str, "debug"))
10512dd00e8SLeif Lindholm 		set_bit(EFI_DBG, &efi.flags);
10612dd00e8SLeif Lindholm 
1075ae3683cSDave Young 	if (parse_option_str(str, "noruntime"))
1085ae3683cSDave Young 		disable_runtime = true;
1095ae3683cSDave Young 
110720dff78SSebastian Andrzej Siewior 	if (parse_option_str(str, "runtime"))
111720dff78SSebastian Andrzej Siewior 		disable_runtime = false;
112720dff78SSebastian Andrzej Siewior 
113b617c526SDan Williams 	if (parse_option_str(str, "nosoftreserve"))
114b617c526SDan Williams 		set_bit(EFI_MEM_NO_SOFT_RESERVE, &efi.flags);
115b617c526SDan Williams 
1165ae3683cSDave Young 	return 0;
1175ae3683cSDave Young }
1185ae3683cSDave Young early_param("efi", parse_efi_cmdline);
1195ae3683cSDave Young 
1200bb54905SPeter Jones struct kobject *efi_kobj;
121a9499fa7STom Gundersen 
122a9499fa7STom Gundersen /*
123a9499fa7STom Gundersen  * Let's not leave out systab information that snuck into
124a9499fa7STom Gundersen  * the efivars driver
1250b02e448SDave Young  * Note, do not add more fields in systab sysfs file as it breaks sysfs
1260b02e448SDave Young  * one value per file rule!
127a9499fa7STom Gundersen  */
systab_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)128a9499fa7STom Gundersen static ssize_t systab_show(struct kobject *kobj,
129a9499fa7STom Gundersen 			   struct kobj_attribute *attr, char *buf)
130a9499fa7STom Gundersen {
131a9499fa7STom Gundersen 	char *str = buf;
132a9499fa7STom Gundersen 
133a9499fa7STom Gundersen 	if (!kobj || !buf)
134a9499fa7STom Gundersen 		return -EINVAL;
135a9499fa7STom Gundersen 
136a9499fa7STom Gundersen 	if (efi.acpi20 != EFI_INVALID_TABLE_ADDR)
137a9499fa7STom Gundersen 		str += sprintf(str, "ACPI20=0x%lx\n", efi.acpi20);
138a9499fa7STom Gundersen 	if (efi.acpi != EFI_INVALID_TABLE_ADDR)
139a9499fa7STom Gundersen 		str += sprintf(str, "ACPI=0x%lx\n", efi.acpi);
140b119fe08SJean Delvare 	/*
141b119fe08SJean Delvare 	 * If both SMBIOS and SMBIOS3 entry points are implemented, the
142b119fe08SJean Delvare 	 * SMBIOS3 entry point shall be preferred, so we list it first to
143b119fe08SJean Delvare 	 * let applications stop parsing after the first match.
144b119fe08SJean Delvare 	 */
145e1ccbbc9SArd Biesheuvel 	if (efi.smbios3 != EFI_INVALID_TABLE_ADDR)
146e1ccbbc9SArd Biesheuvel 		str += sprintf(str, "SMBIOS3=0x%lx\n", efi.smbios3);
147b119fe08SJean Delvare 	if (efi.smbios != EFI_INVALID_TABLE_ADDR)
148b119fe08SJean Delvare 		str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios);
149a9499fa7STom Gundersen 
150e8da08a0SBenjamin Thiel 	if (IS_ENABLED(CONFIG_IA64) || IS_ENABLED(CONFIG_X86))
151120540f2SArd Biesheuvel 		str = efi_systab_show_arch(str);
152120540f2SArd Biesheuvel 
153a9499fa7STom Gundersen 	return str - buf;
154a9499fa7STom Gundersen }
155a9499fa7STom Gundersen 
156af97a77bSGreg Kroah-Hartman static struct kobj_attribute efi_attr_systab = __ATTR_RO_MODE(systab, 0400);
157a9499fa7STom Gundersen 
fw_platform_size_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)1582859dff9SSteve McIntyre static ssize_t fw_platform_size_show(struct kobject *kobj,
1592859dff9SSteve McIntyre 				     struct kobj_attribute *attr, char *buf)
1602859dff9SSteve McIntyre {
1612859dff9SSteve McIntyre 	return sprintf(buf, "%d\n", efi_enabled(EFI_64BIT) ? 64 : 32);
1622859dff9SSteve McIntyre }
1632859dff9SSteve McIntyre 
1649cd437acSArd Biesheuvel extern __weak struct kobj_attribute efi_attr_fw_vendor;
1659cd437acSArd Biesheuvel extern __weak struct kobj_attribute efi_attr_runtime;
1669cd437acSArd Biesheuvel extern __weak struct kobj_attribute efi_attr_config_table;
1672859dff9SSteve McIntyre static struct kobj_attribute efi_attr_fw_platform_size =
1682859dff9SSteve McIntyre 	__ATTR_RO(fw_platform_size);
169a0998eb1SDave Young 
170a9499fa7STom Gundersen static struct attribute *efi_subsys_attrs[] = {
171a9499fa7STom Gundersen 	&efi_attr_systab.attr,
1729cd437acSArd Biesheuvel 	&efi_attr_fw_platform_size.attr,
173a0998eb1SDave Young 	&efi_attr_fw_vendor.attr,
174a0998eb1SDave Young 	&efi_attr_runtime.attr,
175a0998eb1SDave Young 	&efi_attr_config_table.attr,
176a0998eb1SDave Young 	NULL,
177a9499fa7STom Gundersen };
178a9499fa7STom Gundersen 
efi_attr_is_visible(struct kobject * kobj,struct attribute * attr,int n)1799cd437acSArd Biesheuvel umode_t __weak efi_attr_is_visible(struct kobject *kobj, struct attribute *attr,
1809cd437acSArd Biesheuvel 				   int n)
181a0998eb1SDave Young {
1829f27bc54SDaniel Kiper 	return attr->mode;
183a0998eb1SDave Young }
184a0998eb1SDave Young 
1853ad6bd7cSArvind Yadav static const struct attribute_group efi_subsys_attr_group = {
186a9499fa7STom Gundersen 	.attrs = efi_subsys_attrs,
187a0998eb1SDave Young 	.is_visible = efi_attr_is_visible,
188a9499fa7STom Gundersen };
189a9499fa7STom Gundersen 
190a9499fa7STom Gundersen static struct efivars generic_efivars;
191a9499fa7STom Gundersen static struct efivar_operations generic_ops;
192a9499fa7STom Gundersen 
generic_ops_supported(void)193bad267f9SJohan Hovold static bool generic_ops_supported(void)
194bad267f9SJohan Hovold {
195bad267f9SJohan Hovold 	unsigned long name_size;
196bad267f9SJohan Hovold 	efi_status_t status;
197bad267f9SJohan Hovold 	efi_char16_t name;
198bad267f9SJohan Hovold 	efi_guid_t guid;
199bad267f9SJohan Hovold 
200bad267f9SJohan Hovold 	name_size = sizeof(name);
201bad267f9SJohan Hovold 
2029114ba99SOleksandr Tymoshenko 	if (!efi.get_next_variable)
2039114ba99SOleksandr Tymoshenko 		return false;
204bad267f9SJohan Hovold 	status = efi.get_next_variable(&name_size, &name, &guid);
205bad267f9SJohan Hovold 	if (status == EFI_UNSUPPORTED)
206bad267f9SJohan Hovold 		return false;
207bad267f9SJohan Hovold 
208bad267f9SJohan Hovold 	return true;
209bad267f9SJohan Hovold }
210bad267f9SJohan Hovold 
generic_ops_register(void)211a9499fa7STom Gundersen static int generic_ops_register(void)
212a9499fa7STom Gundersen {
213bad267f9SJohan Hovold 	if (!generic_ops_supported())
214bad267f9SJohan Hovold 		return 0;
215bad267f9SJohan Hovold 
216a9499fa7STom Gundersen 	generic_ops.get_variable = efi.get_variable;
217a9499fa7STom Gundersen 	generic_ops.get_next_variable = efi.get_next_variable;
218a614e192SMatt Fleming 	generic_ops.query_variable_store = efi_query_variable_store;
219d86ff333SAnisse Astier 	generic_ops.query_variable_info = efi.query_variable_info;
220a9499fa7STom Gundersen 
221f88814ccSArd Biesheuvel 	if (efi_rt_services_supported(EFI_RT_SUPPORTED_SET_VARIABLE)) {
222f88814ccSArd Biesheuvel 		generic_ops.set_variable = efi.set_variable;
223f88814ccSArd Biesheuvel 		generic_ops.set_variable_nonblocking = efi.set_variable_nonblocking;
224f88814ccSArd Biesheuvel 	}
225ade7fd90SJohan Hovold 	return efivars_register(&generic_efivars, &generic_ops);
226a9499fa7STom Gundersen }
227a9499fa7STom Gundersen 
generic_ops_unregister(void)228a9499fa7STom Gundersen static void generic_ops_unregister(void)
229a9499fa7STom Gundersen {
230bad267f9SJohan Hovold 	if (!generic_ops.get_variable)
231bad267f9SJohan Hovold 		return;
232bad267f9SJohan Hovold 
233a9499fa7STom Gundersen 	efivars_unregister(&generic_efivars);
234a9499fa7STom Gundersen }
235a9499fa7STom Gundersen 
236435d1a47SPeter Jones #ifdef CONFIG_EFI_CUSTOM_SSDT_OVERLAYS
2373881ee0bSArd Biesheuvel #define EFIVAR_SSDT_NAME_MAX	16UL
238475fb4e8SOctavian Purdila static char efivar_ssdt[EFIVAR_SSDT_NAME_MAX] __initdata;
efivar_ssdt_setup(char * str)239475fb4e8SOctavian Purdila static int __init efivar_ssdt_setup(char *str)
240475fb4e8SOctavian Purdila {
2411957a85bSMatthew Garrett 	int ret = security_locked_down(LOCKDOWN_ACPI_TABLES);
2421957a85bSMatthew Garrett 
2431957a85bSMatthew Garrett 	if (ret)
2441957a85bSMatthew Garrett 		return ret;
2451957a85bSMatthew Garrett 
246475fb4e8SOctavian Purdila 	if (strlen(str) < sizeof(efivar_ssdt))
247475fb4e8SOctavian Purdila 		memcpy(efivar_ssdt, str, strlen(str));
248475fb4e8SOctavian Purdila 	else
249475fb4e8SOctavian Purdila 		pr_warn("efivar_ssdt: name too long: %s\n", str);
2509feaf8b3SRandy Dunlap 	return 1;
251475fb4e8SOctavian Purdila }
252475fb4e8SOctavian Purdila __setup("efivar_ssdt=", efivar_ssdt_setup);
253475fb4e8SOctavian Purdila 
efivar_ssdt_load(void)254475fb4e8SOctavian Purdila static __init int efivar_ssdt_load(void)
255475fb4e8SOctavian Purdila {
2563881ee0bSArd Biesheuvel 	unsigned long name_size = 256;
2573881ee0bSArd Biesheuvel 	efi_char16_t *name = NULL;
2583881ee0bSArd Biesheuvel 	efi_status_t status;
2593881ee0bSArd Biesheuvel 	efi_guid_t guid;
260475fb4e8SOctavian Purdila 
261c05f8f92SArd Biesheuvel 	if (!efivar_ssdt[0])
262c05f8f92SArd Biesheuvel 		return 0;
263c05f8f92SArd Biesheuvel 
2643881ee0bSArd Biesheuvel 	name = kzalloc(name_size, GFP_KERNEL);
2653881ee0bSArd Biesheuvel 	if (!name)
2663881ee0bSArd Biesheuvel 		return -ENOMEM;
267475fb4e8SOctavian Purdila 
2683881ee0bSArd Biesheuvel 	for (;;) {
2693881ee0bSArd Biesheuvel 		char utf8_name[EFIVAR_SSDT_NAME_MAX];
2703881ee0bSArd Biesheuvel 		unsigned long data_size = 0;
2713881ee0bSArd Biesheuvel 		void *data;
2723881ee0bSArd Biesheuvel 		int limit;
273475fb4e8SOctavian Purdila 
2743881ee0bSArd Biesheuvel 		status = efi.get_next_variable(&name_size, name, &guid);
2753881ee0bSArd Biesheuvel 		if (status == EFI_NOT_FOUND) {
2763881ee0bSArd Biesheuvel 			break;
2773881ee0bSArd Biesheuvel 		} else if (status == EFI_BUFFER_TOO_SMALL) {
2780d3ad191SKuan-Wei Chiu 			efi_char16_t *name_tmp =
2790d3ad191SKuan-Wei Chiu 				krealloc(name, name_size, GFP_KERNEL);
2800d3ad191SKuan-Wei Chiu 			if (!name_tmp) {
2810d3ad191SKuan-Wei Chiu 				kfree(name);
2823881ee0bSArd Biesheuvel 				return -ENOMEM;
2830d3ad191SKuan-Wei Chiu 			}
2840d3ad191SKuan-Wei Chiu 			name = name_tmp;
2853881ee0bSArd Biesheuvel 			continue;
286475fb4e8SOctavian Purdila 		}
287475fb4e8SOctavian Purdila 
2883881ee0bSArd Biesheuvel 		limit = min(EFIVAR_SSDT_NAME_MAX, name_size);
2893881ee0bSArd Biesheuvel 		ucs2_as_utf8(utf8_name, name, limit - 1);
2903881ee0bSArd Biesheuvel 		if (strncmp(utf8_name, efivar_ssdt, limit) != 0)
2913881ee0bSArd Biesheuvel 			continue;
2923881ee0bSArd Biesheuvel 
2933881ee0bSArd Biesheuvel 		pr_info("loading SSDT from variable %s-%pUl\n", efivar_ssdt, &guid);
2943881ee0bSArd Biesheuvel 
2953881ee0bSArd Biesheuvel 		status = efi.get_variable(name, &guid, NULL, &data_size, NULL);
2963881ee0bSArd Biesheuvel 		if (status != EFI_BUFFER_TOO_SMALL || !data_size)
2973881ee0bSArd Biesheuvel 			return -EIO;
2983881ee0bSArd Biesheuvel 
2993881ee0bSArd Biesheuvel 		data = kmalloc(data_size, GFP_KERNEL);
3003881ee0bSArd Biesheuvel 		if (!data)
3013881ee0bSArd Biesheuvel 			return -ENOMEM;
3023881ee0bSArd Biesheuvel 
3033881ee0bSArd Biesheuvel 		status = efi.get_variable(name, &guid, NULL, &data_size, data);
3043881ee0bSArd Biesheuvel 		if (status == EFI_SUCCESS) {
3053881ee0bSArd Biesheuvel 			acpi_status ret = acpi_load_table(data, NULL);
3063881ee0bSArd Biesheuvel 			if (ret)
3073881ee0bSArd Biesheuvel 				pr_err("failed to load table: %u\n", ret);
3084b017e59SArd Biesheuvel 			else
3094b017e59SArd Biesheuvel 				continue;
3103881ee0bSArd Biesheuvel 		} else {
3113881ee0bSArd Biesheuvel 			pr_err("failed to get var data: 0x%lx\n", status);
312a75dcb58SDan Carpenter 		}
313475fb4e8SOctavian Purdila 		kfree(data);
314475fb4e8SOctavian Purdila 	}
3153881ee0bSArd Biesheuvel 	return 0;
316475fb4e8SOctavian Purdila }
317475fb4e8SOctavian Purdila #else
efivar_ssdt_load(void)318475fb4e8SOctavian Purdila static inline int efivar_ssdt_load(void) { return 0; }
319475fb4e8SOctavian Purdila #endif
320475fb4e8SOctavian Purdila 
3210e72a6a3SHans de Goede #ifdef CONFIG_DEBUG_FS
3220e72a6a3SHans de Goede 
3230e72a6a3SHans de Goede #define EFI_DEBUGFS_MAX_BLOBS 32
3240e72a6a3SHans de Goede 
3250e72a6a3SHans de Goede static struct debugfs_blob_wrapper debugfs_blob[EFI_DEBUGFS_MAX_BLOBS];
3260e72a6a3SHans de Goede 
efi_debugfs_init(void)3270e72a6a3SHans de Goede static void __init efi_debugfs_init(void)
3280e72a6a3SHans de Goede {
3290e72a6a3SHans de Goede 	struct dentry *efi_debugfs;
3300e72a6a3SHans de Goede 	efi_memory_desc_t *md;
3310e72a6a3SHans de Goede 	char name[32];
3320e72a6a3SHans de Goede 	int type_count[EFI_BOOT_SERVICES_DATA + 1] = {};
3330e72a6a3SHans de Goede 	int i = 0;
3340e72a6a3SHans de Goede 
3350e72a6a3SHans de Goede 	efi_debugfs = debugfs_create_dir("efi", NULL);
3360e72a6a3SHans de Goede 	if (IS_ERR_OR_NULL(efi_debugfs))
3370e72a6a3SHans de Goede 		return;
3380e72a6a3SHans de Goede 
3390e72a6a3SHans de Goede 	for_each_efi_memory_desc(md) {
3400e72a6a3SHans de Goede 		switch (md->type) {
3410e72a6a3SHans de Goede 		case EFI_BOOT_SERVICES_CODE:
3420e72a6a3SHans de Goede 			snprintf(name, sizeof(name), "boot_services_code%d",
3430e72a6a3SHans de Goede 				 type_count[md->type]++);
3440e72a6a3SHans de Goede 			break;
3450e72a6a3SHans de Goede 		case EFI_BOOT_SERVICES_DATA:
3460e72a6a3SHans de Goede 			snprintf(name, sizeof(name), "boot_services_data%d",
3470e72a6a3SHans de Goede 				 type_count[md->type]++);
3480e72a6a3SHans de Goede 			break;
3490e72a6a3SHans de Goede 		default:
3500e72a6a3SHans de Goede 			continue;
3510e72a6a3SHans de Goede 		}
3520e72a6a3SHans de Goede 
3530e72a6a3SHans de Goede 		if (i >= EFI_DEBUGFS_MAX_BLOBS) {
3540e72a6a3SHans de Goede 			pr_warn("More then %d EFI boot service segments, only showing first %d in debugfs\n",
3550e72a6a3SHans de Goede 				EFI_DEBUGFS_MAX_BLOBS, EFI_DEBUGFS_MAX_BLOBS);
3560e72a6a3SHans de Goede 			break;
3570e72a6a3SHans de Goede 		}
3580e72a6a3SHans de Goede 
3590e72a6a3SHans de Goede 		debugfs_blob[i].size = md->num_pages << EFI_PAGE_SHIFT;
3600e72a6a3SHans de Goede 		debugfs_blob[i].data = memremap(md->phys_addr,
3610e72a6a3SHans de Goede 						debugfs_blob[i].size,
3620e72a6a3SHans de Goede 						MEMREMAP_WB);
3630e72a6a3SHans de Goede 		if (!debugfs_blob[i].data)
3640e72a6a3SHans de Goede 			continue;
3650e72a6a3SHans de Goede 
3660e72a6a3SHans de Goede 		debugfs_create_blob(name, 0400, efi_debugfs, &debugfs_blob[i]);
3670e72a6a3SHans de Goede 		i++;
3680e72a6a3SHans de Goede 	}
3690e72a6a3SHans de Goede }
3700e72a6a3SHans de Goede #else
efi_debugfs_init(void)3710e72a6a3SHans de Goede static inline void efi_debugfs_init(void) {}
3720e72a6a3SHans de Goede #endif
3730e72a6a3SHans de Goede 
374a9499fa7STom Gundersen /*
375a9499fa7STom Gundersen  * We register the efi subsystem with the firmware subsystem and the
376a9499fa7STom Gundersen  * efivars subsystem with the efi subsystem, if the system was booted with
377a9499fa7STom Gundersen  * EFI.
378a9499fa7STom Gundersen  */
efisubsys_init(void)379a9499fa7STom Gundersen static int __init efisubsys_init(void)
380a9499fa7STom Gundersen {
381a9499fa7STom Gundersen 	int error;
382a9499fa7STom Gundersen 
38396a3dd3dSArd Biesheuvel 	if (!efi_enabled(EFI_RUNTIME_SERVICES))
38496a3dd3dSArd Biesheuvel 		efi.runtime_supported_mask = 0;
38596a3dd3dSArd Biesheuvel 
3863e03dca5SArd Biesheuvel 	if (!efi_enabled(EFI_BOOT))
3873e03dca5SArd Biesheuvel 		return 0;
3883e03dca5SArd Biesheuvel 
38996a3dd3dSArd Biesheuvel 	if (efi.runtime_supported_mask) {
3903eb420e7SSai Praneeth 		/*
3913eb420e7SSai Praneeth 		 * Since we process only one efi_runtime_service() at a time, an
3923eb420e7SSai Praneeth 		 * ordered workqueue (which creates only one execution context)
39396a3dd3dSArd Biesheuvel 		 * should suffice for all our needs.
3943eb420e7SSai Praneeth 		 */
3953eb420e7SSai Praneeth 		efi_rts_wq = alloc_ordered_workqueue("efi_rts_wq", 0);
3963eb420e7SSai Praneeth 		if (!efi_rts_wq) {
3973eb420e7SSai Praneeth 			pr_err("Creating efi_rts_wq failed, EFI runtime services disabled.\n");
3983eb420e7SSai Praneeth 			clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
39996a3dd3dSArd Biesheuvel 			efi.runtime_supported_mask = 0;
4003eb420e7SSai Praneeth 			return 0;
4013eb420e7SSai Praneeth 		}
40296a3dd3dSArd Biesheuvel 	}
4033eb420e7SSai Praneeth 
404e5c3b1ccSArd Biesheuvel 	if (efi_rt_services_supported(EFI_RT_SUPPORTED_TIME_SERVICES))
405e5c3b1ccSArd Biesheuvel 		platform_device_register_simple("rtc-efi", 0, NULL, 0);
406e5c3b1ccSArd Biesheuvel 
407a9499fa7STom Gundersen 	/* We register the efi directory at /sys/firmware/efi */
408a9499fa7STom Gundersen 	efi_kobj = kobject_create_and_add("efi", firmware_kobj);
409a9499fa7STom Gundersen 	if (!efi_kobj) {
410a9499fa7STom Gundersen 		pr_err("efi: Firmware registration failed.\n");
411703c13feSJohan Hovold 		error = -ENOMEM;
412703c13feSJohan Hovold 		goto err_destroy_wq;
413a9499fa7STom Gundersen 	}
414a9499fa7STom Gundersen 
415f88814ccSArd Biesheuvel 	if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE |
416f88814ccSArd Biesheuvel 				      EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME)) {
417a9499fa7STom Gundersen 		error = generic_ops_register();
418a9499fa7STom Gundersen 		if (error)
419a9499fa7STom Gundersen 			goto err_put;
42050bdcf04SAmadeusz Sławiński 		efivar_ssdt_load();
421bf67fad1SArd Biesheuvel 		platform_device_register_simple("efivars", 0, NULL, 0);
422bf67fad1SArd Biesheuvel 	}
423475fb4e8SOctavian Purdila 
424a9499fa7STom Gundersen 	error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group);
425a9499fa7STom Gundersen 	if (error) {
426a9499fa7STom Gundersen 		pr_err("efi: Sysfs attribute export failed with error %d.\n",
427a9499fa7STom Gundersen 		       error);
428a9499fa7STom Gundersen 		goto err_unregister;
429a9499fa7STom Gundersen 	}
430a9499fa7STom Gundersen 
431a9499fa7STom Gundersen 	/* and the standard mountpoint for efivarfs */
432f9bb4882SEric W. Biederman 	error = sysfs_create_mount_point(efi_kobj, "efivars");
433f9bb4882SEric W. Biederman 	if (error) {
434a9499fa7STom Gundersen 		pr_err("efivars: Subsystem registration failed.\n");
435a9499fa7STom Gundersen 		goto err_remove_group;
436a9499fa7STom Gundersen 	}
437a9499fa7STom Gundersen 
4380e72a6a3SHans de Goede 	if (efi_enabled(EFI_DBG) && efi_enabled(EFI_PRESERVE_BS_REGIONS))
4390e72a6a3SHans de Goede 		efi_debugfs_init();
4400e72a6a3SHans de Goede 
44120ffd920SDov Murik #ifdef CONFIG_EFI_COCO_SECRET
44220ffd920SDov Murik 	if (efi.coco_secret != EFI_INVALID_TABLE_ADDR)
44320ffd920SDov Murik 		platform_device_register_simple("efi_secret", 0, NULL, 0);
44420ffd920SDov Murik #endif
44520ffd920SDov Murik 
446a9499fa7STom Gundersen 	return 0;
447a9499fa7STom Gundersen 
448a9499fa7STom Gundersen err_remove_group:
449a9499fa7STom Gundersen 	sysfs_remove_group(efi_kobj, &efi_subsys_attr_group);
450a9499fa7STom Gundersen err_unregister:
451f88814ccSArd Biesheuvel 	if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE |
452f88814ccSArd Biesheuvel 				      EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME))
453a9499fa7STom Gundersen 		generic_ops_unregister();
454a9499fa7STom Gundersen err_put:
455a9499fa7STom Gundersen 	kobject_put(efi_kobj);
4561fff234dSArd Biesheuvel 	efi_kobj = NULL;
457703c13feSJohan Hovold err_destroy_wq:
458703c13feSJohan Hovold 	if (efi_rts_wq)
45998086df8SLi Heng 		destroy_workqueue(efi_rts_wq);
460703c13feSJohan Hovold 
461a9499fa7STom Gundersen 	return error;
462a9499fa7STom Gundersen }
463a9499fa7STom Gundersen 
464a9499fa7STom Gundersen subsys_initcall(efisubsys_init);
465272686bfSLeif Lindholm 
efi_find_mirror(void)4666365a193SMa Wupeng void __init efi_find_mirror(void)
4676365a193SMa Wupeng {
4686365a193SMa Wupeng 	efi_memory_desc_t *md;
4696365a193SMa Wupeng 	u64 mirror_size = 0, total_size = 0;
4706365a193SMa Wupeng 
4716365a193SMa Wupeng 	if (!efi_enabled(EFI_MEMMAP))
4726365a193SMa Wupeng 		return;
4736365a193SMa Wupeng 
4746365a193SMa Wupeng 	for_each_efi_memory_desc(md) {
4756365a193SMa Wupeng 		unsigned long long start = md->phys_addr;
4766365a193SMa Wupeng 		unsigned long long size = md->num_pages << EFI_PAGE_SHIFT;
4776365a193SMa Wupeng 
4786365a193SMa Wupeng 		total_size += size;
4796365a193SMa Wupeng 		if (md->attribute & EFI_MEMORY_MORE_RELIABLE) {
4806365a193SMa Wupeng 			memblock_mark_mirror(start, size);
4816365a193SMa Wupeng 			mirror_size += size;
4826365a193SMa Wupeng 		}
4836365a193SMa Wupeng 	}
4846365a193SMa Wupeng 	if (mirror_size)
4856365a193SMa Wupeng 		pr_info("Memory: %lldM/%lldM mirrored memory\n",
4866365a193SMa Wupeng 			mirror_size>>20, total_size>>20);
4876365a193SMa Wupeng }
4886365a193SMa Wupeng 
4890bb54905SPeter Jones /*
4900bb54905SPeter Jones  * Find the efi memory descriptor for a given physical address.  Given a
491dca0f971SMatt Fleming  * physical address, determine if it exists within an EFI Memory Map entry,
4920bb54905SPeter Jones  * and if so, populate the supplied memory descriptor with the appropriate
4930bb54905SPeter Jones  * data.
4940bb54905SPeter Jones  */
__efi_mem_desc_lookup(u64 phys_addr,efi_memory_desc_t * out_md)495aca1d27aSDemi Marie Obenour int __efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
4960bb54905SPeter Jones {
497dca0f971SMatt Fleming 	efi_memory_desc_t *md;
4980bb54905SPeter Jones 
4990bb54905SPeter Jones 	if (!efi_enabled(EFI_MEMMAP)) {
5000bb54905SPeter Jones 		pr_err_once("EFI_MEMMAP is not enabled.\n");
5010bb54905SPeter Jones 		return -EINVAL;
5020bb54905SPeter Jones 	}
5030bb54905SPeter Jones 
5040bb54905SPeter Jones 	if (!out_md) {
5050bb54905SPeter Jones 		pr_err_once("out_md is null.\n");
5060bb54905SPeter Jones 		return -EINVAL;
5070bb54905SPeter Jones         }
5080bb54905SPeter Jones 
509dca0f971SMatt Fleming 	for_each_efi_memory_desc(md) {
5100bb54905SPeter Jones 		u64 size;
5110bb54905SPeter Jones 		u64 end;
5120bb54905SPeter Jones 
513ab03e91eSDemi Marie Obenour 		/* skip bogus entries (including empty ones) */
514ab03e91eSDemi Marie Obenour 		if ((md->phys_addr & (EFI_PAGE_SIZE - 1)) ||
515ab03e91eSDemi Marie Obenour 		    (md->num_pages <= 0) ||
516ab03e91eSDemi Marie Obenour 		    (md->num_pages > (U64_MAX - md->phys_addr) >> EFI_PAGE_SHIFT))
517ab03e91eSDemi Marie Obenour 			continue;
518ab03e91eSDemi Marie Obenour 
5190bb54905SPeter Jones 		size = md->num_pages << EFI_PAGE_SHIFT;
5200bb54905SPeter Jones 		end = md->phys_addr + size;
5210bb54905SPeter Jones 		if (phys_addr >= md->phys_addr && phys_addr < end) {
5220bb54905SPeter Jones 			memcpy(out_md, md, sizeof(*out_md));
5230bb54905SPeter Jones 			return 0;
5240bb54905SPeter Jones 		}
5250bb54905SPeter Jones 	}
5260bb54905SPeter Jones 	return -ENOENT;
5270bb54905SPeter Jones }
5280bb54905SPeter Jones 
529aca1d27aSDemi Marie Obenour extern int efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
530aca1d27aSDemi Marie Obenour 	__weak __alias(__efi_mem_desc_lookup);
531aca1d27aSDemi Marie Obenour 
5320bb54905SPeter Jones /*
5330bb54905SPeter Jones  * Calculate the highest address of an efi memory descriptor.
5340bb54905SPeter Jones  */
efi_mem_desc_end(efi_memory_desc_t * md)5350bb54905SPeter Jones u64 __init efi_mem_desc_end(efi_memory_desc_t *md)
5360bb54905SPeter Jones {
5370bb54905SPeter Jones 	u64 size = md->num_pages << EFI_PAGE_SHIFT;
5380bb54905SPeter Jones 	u64 end = md->phys_addr + size;
5390bb54905SPeter Jones 	return end;
5400bb54905SPeter Jones }
541272686bfSLeif Lindholm 
efi_arch_mem_reserve(phys_addr_t addr,u64 size)542816e7612SMatt Fleming void __init __weak efi_arch_mem_reserve(phys_addr_t addr, u64 size) {}
543816e7612SMatt Fleming 
544816e7612SMatt Fleming /**
545816e7612SMatt Fleming  * efi_mem_reserve - Reserve an EFI memory region
546816e7612SMatt Fleming  * @addr: Physical address to reserve
547816e7612SMatt Fleming  * @size: Size of reservation
548816e7612SMatt Fleming  *
549816e7612SMatt Fleming  * Mark a region as reserved from general kernel allocation and
550816e7612SMatt Fleming  * prevent it being released by efi_free_boot_services().
551816e7612SMatt Fleming  *
552816e7612SMatt Fleming  * This function should be called drivers once they've parsed EFI
553816e7612SMatt Fleming  * configuration tables to figure out where their data lives, e.g.
554816e7612SMatt Fleming  * efi_esrt_init().
555816e7612SMatt Fleming  */
efi_mem_reserve(phys_addr_t addr,u64 size)556816e7612SMatt Fleming void __init efi_mem_reserve(phys_addr_t addr, u64 size)
557816e7612SMatt Fleming {
558fa7bee86SDemi Marie Obenour 	/* efi_mem_reserve() does not work under Xen */
559fa7bee86SDemi Marie Obenour 	if (WARN_ON_ONCE(efi_enabled(EFI_PARAVIRT)))
560fa7bee86SDemi Marie Obenour 		return;
561fa7bee86SDemi Marie Obenour 
562816e7612SMatt Fleming 	if (!memblock_is_region_reserved(addr, size))
563816e7612SMatt Fleming 		memblock_reserve(addr, size);
564816e7612SMatt Fleming 
565816e7612SMatt Fleming 	/*
566816e7612SMatt Fleming 	 * Some architectures (x86) reserve all boot services ranges
567816e7612SMatt Fleming 	 * until efi_free_boot_services() because of buggy firmware
568816e7612SMatt Fleming 	 * implementations. This means the above memblock_reserve() is
569816e7612SMatt Fleming 	 * superfluous on x86 and instead what it needs to do is
570816e7612SMatt Fleming 	 * ensure the @start, @size is not freed.
571816e7612SMatt Fleming 	 */
572816e7612SMatt Fleming 	efi_arch_mem_reserve(addr, size);
573816e7612SMatt Fleming }
574816e7612SMatt Fleming 
57506c0bd93SArd Biesheuvel static const efi_config_table_type_t common_tables[] __initconst = {
5764e9a0f73SArd Biesheuvel 	{ACPI_20_TABLE_GUID,			&efi.acpi20,		"ACPI 2.0"	},
5774e9a0f73SArd Biesheuvel 	{ACPI_TABLE_GUID,			&efi.acpi,		"ACPI"		},
5784e9a0f73SArd Biesheuvel 	{SMBIOS_TABLE_GUID,			&efi.smbios,		"SMBIOS"	},
5794e9a0f73SArd Biesheuvel 	{SMBIOS3_TABLE_GUID,			&efi.smbios3,		"SMBIOS 3.0"	},
5804e9a0f73SArd Biesheuvel 	{EFI_SYSTEM_RESOURCE_TABLE_GUID,	&efi.esrt,		"ESRT"		},
5814e9a0f73SArd Biesheuvel 	{EFI_MEMORY_ATTRIBUTES_TABLE_GUID,	&efi_mem_attr_table,	"MEMATTR"	},
5824e9a0f73SArd Biesheuvel 	{LINUX_EFI_RANDOM_SEED_TABLE_GUID,	&efi_rng_seed,		"RNG"		},
5834e9a0f73SArd Biesheuvel 	{LINUX_EFI_TPM_EVENT_LOG_GUID,		&efi.tpm_log,		"TPMEventLog"	},
5844e9a0f73SArd Biesheuvel 	{LINUX_EFI_TPM_FINAL_LOG_GUID,		&efi.tpm_final_log,	"TPMFinalLog"	},
5854e9a0f73SArd Biesheuvel 	{LINUX_EFI_MEMRESERVE_TABLE_GUID,	&mem_reserve,		"MEMRESERVE"	},
586f4dc7fffSArd Biesheuvel 	{LINUX_EFI_INITRD_MEDIA_GUID,		&initrd,		"INITRD"	},
5874e9a0f73SArd Biesheuvel 	{EFI_RT_PROPERTIES_TABLE_GUID,		&rt_prop,		"RTPROP"	},
5881c5fecb6SNarendra K #ifdef CONFIG_EFI_RCI2_TABLE
5894e9a0f73SArd Biesheuvel 	{DELLEMC_EFI_RCI2_TABLE_GUID,		&rci2_table_phys			},
5901c5fecb6SNarendra K #endif
59158c90902SLenny Szubowicz #ifdef CONFIG_LOAD_UEFI_KEYS
59258c90902SLenny Szubowicz 	{LINUX_EFI_MOK_VARIABLE_TABLE_GUID,	&efi.mokvar_table,	"MOKvar"	},
59358c90902SLenny Szubowicz #endif
59412274189SDov Murik #ifdef CONFIG_EFI_COCO_SECRET
59512274189SDov Murik 	{LINUX_EFI_COCO_SECRET_AREA_GUID,	&efi.coco_secret,	"CocoSecret"	},
59612274189SDov Murik #endif
5972053bc57SKirill A. Shutemov #ifdef CONFIG_UNACCEPTED_MEMORY
5982053bc57SKirill A. Shutemov 	{LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID,	&efi.unaccepted,	"Unaccepted"	},
5992053bc57SKirill A. Shutemov #endif
600732ea9dbSArd Biesheuvel #ifdef CONFIG_EFI_GENERIC_STUB
601732ea9dbSArd Biesheuvel 	{LINUX_EFI_SCREEN_INFO_TABLE_GUID,	&screen_info_table			},
602732ea9dbSArd Biesheuvel #endif
6034e9a0f73SArd Biesheuvel 	{},
604272686bfSLeif Lindholm };
605272686bfSLeif Lindholm 
match_config_table(const efi_guid_t * guid,unsigned long table,const efi_config_table_type_t * table_types)60606c0bd93SArd Biesheuvel static __init int match_config_table(const efi_guid_t *guid,
607272686bfSLeif Lindholm 				     unsigned long table,
60806c0bd93SArd Biesheuvel 				     const efi_config_table_type_t *table_types)
609272686bfSLeif Lindholm {
610272686bfSLeif Lindholm 	int i;
611272686bfSLeif Lindholm 
612272686bfSLeif Lindholm 	for (i = 0; efi_guidcmp(table_types[i].guid, NULL_GUID); i++) {
613c0fecaa4SDemi Marie Obenour 		if (efi_guidcmp(*guid, table_types[i].guid))
614c0fecaa4SDemi Marie Obenour 			continue;
615c0fecaa4SDemi Marie Obenour 
616c0fecaa4SDemi Marie Obenour 		if (!efi_config_table_is_usable(guid, table)) {
6174e9a0f73SArd Biesheuvel 			if (table_types[i].name[0])
618c0fecaa4SDemi Marie Obenour 				pr_cont("(%s=0x%lx unusable) ",
619272686bfSLeif Lindholm 					table_types[i].name, table);
620272686bfSLeif Lindholm 			return 1;
621272686bfSLeif Lindholm 		}
622c0fecaa4SDemi Marie Obenour 
623c0fecaa4SDemi Marie Obenour 		*(table_types[i].ptr) = table;
624c0fecaa4SDemi Marie Obenour 		if (table_types[i].name[0])
625c0fecaa4SDemi Marie Obenour 			pr_cont("%s=0x%lx ", table_types[i].name, table);
626c0fecaa4SDemi Marie Obenour 		return 1;
627272686bfSLeif Lindholm 	}
628272686bfSLeif Lindholm 
629272686bfSLeif Lindholm 	return 0;
630272686bfSLeif Lindholm }
631272686bfSLeif Lindholm 
6328dbe3395SKirill A. Shutemov /**
6338dbe3395SKirill A. Shutemov  * reserve_unaccepted - Map and reserve unaccepted configuration table
6348dbe3395SKirill A. Shutemov  * @unaccepted: Pointer to unaccepted memory table
6358dbe3395SKirill A. Shutemov  *
6368dbe3395SKirill A. Shutemov  * memblock_add() makes sure that the table is mapped in direct mapping. During
6378dbe3395SKirill A. Shutemov  * normal boot it happens automatically because the table is allocated from
6388dbe3395SKirill A. Shutemov  * usable memory. But during crashkernel boot only memory specifically reserved
6398dbe3395SKirill A. Shutemov  * for crash scenario is mapped. memblock_add() forces the table to be mapped
6408dbe3395SKirill A. Shutemov  * in crashkernel case.
6418dbe3395SKirill A. Shutemov  *
6428dbe3395SKirill A. Shutemov  * Align the range to the nearest page borders. Ranges smaller than page size
6438dbe3395SKirill A. Shutemov  * are not going to be mapped.
6448dbe3395SKirill A. Shutemov  *
6458dbe3395SKirill A. Shutemov  * memblock_reserve() makes sure that future allocations will not touch the
6468dbe3395SKirill A. Shutemov  * table.
6478dbe3395SKirill A. Shutemov  */
6488dbe3395SKirill A. Shutemov 
reserve_unaccepted(struct efi_unaccepted_memory * unaccepted)6498dbe3395SKirill A. Shutemov static __init void reserve_unaccepted(struct efi_unaccepted_memory *unaccepted)
6508dbe3395SKirill A. Shutemov {
6518dbe3395SKirill A. Shutemov 	phys_addr_t start, size;
6528dbe3395SKirill A. Shutemov 
6538dbe3395SKirill A. Shutemov 	start = PAGE_ALIGN_DOWN(efi.unaccepted);
6548dbe3395SKirill A. Shutemov 	size = PAGE_ALIGN(sizeof(*unaccepted) + unaccepted->size);
6558dbe3395SKirill A. Shutemov 
6568dbe3395SKirill A. Shutemov 	memblock_add(start, size);
6578dbe3395SKirill A. Shutemov 	memblock_reserve(start, size);
6588dbe3395SKirill A. Shutemov }
6598dbe3395SKirill A. Shutemov 
efi_config_parse_tables(const efi_config_table_t * config_tables,int count,const efi_config_table_type_t * arch_tables)66006c0bd93SArd Biesheuvel int __init efi_config_parse_tables(const efi_config_table_t *config_tables,
66106c0bd93SArd Biesheuvel 				   int count,
66206c0bd93SArd Biesheuvel 				   const efi_config_table_type_t *arch_tables)
6637bb68410SArd Biesheuvel {
66406c0bd93SArd Biesheuvel 	const efi_config_table_64_t *tbl64 = (void *)config_tables;
66506c0bd93SArd Biesheuvel 	const efi_config_table_32_t *tbl32 = (void *)config_tables;
66606c0bd93SArd Biesheuvel 	const efi_guid_t *guid;
66706c0bd93SArd Biesheuvel 	unsigned long table;
6687bb68410SArd Biesheuvel 	int i;
6697bb68410SArd Biesheuvel 
6707bb68410SArd Biesheuvel 	pr_info("");
6717bb68410SArd Biesheuvel 	for (i = 0; i < count; i++) {
67206c0bd93SArd Biesheuvel 		if (!IS_ENABLED(CONFIG_X86)) {
67306c0bd93SArd Biesheuvel 			guid = &config_tables[i].guid;
67406c0bd93SArd Biesheuvel 			table = (unsigned long)config_tables[i].table;
67506c0bd93SArd Biesheuvel 		} else if (efi_enabled(EFI_64BIT)) {
67606c0bd93SArd Biesheuvel 			guid = &tbl64[i].guid;
67706c0bd93SArd Biesheuvel 			table = tbl64[i].table;
6787bb68410SArd Biesheuvel 
67906c0bd93SArd Biesheuvel 			if (IS_ENABLED(CONFIG_X86_32) &&
68006c0bd93SArd Biesheuvel 			    tbl64[i].table > U32_MAX) {
6817bb68410SArd Biesheuvel 				pr_cont("\n");
6827bb68410SArd Biesheuvel 				pr_err("Table located above 4GB, disabling EFI.\n");
6837bb68410SArd Biesheuvel 				return -EINVAL;
6847bb68410SArd Biesheuvel 			}
6857bb68410SArd Biesheuvel 		} else {
68606c0bd93SArd Biesheuvel 			guid = &tbl32[i].guid;
68706c0bd93SArd Biesheuvel 			table = tbl32[i].table;
6887bb68410SArd Biesheuvel 		}
6897bb68410SArd Biesheuvel 
6904eb8320bSArd Biesheuvel 		if (!match_config_table(guid, table, common_tables) && arch_tables)
69106c0bd93SArd Biesheuvel 			match_config_table(guid, table, arch_tables);
6927bb68410SArd Biesheuvel 	}
6937bb68410SArd Biesheuvel 	pr_cont("\n");
6947bb68410SArd Biesheuvel 	set_bit(EFI_CONFIG_TABLES, &efi.flags);
695a1041713SArd Biesheuvel 
696badc6198STom Lendacky 	if (efi_rng_seed != EFI_INVALID_TABLE_ADDR) {
69763625988SArd Biesheuvel 		struct linux_efi_random_seed *seed;
69863625988SArd Biesheuvel 		u32 size = 0;
69963625988SArd Biesheuvel 
700badc6198STom Lendacky 		seed = early_memremap(efi_rng_seed, sizeof(*seed));
70163625988SArd Biesheuvel 		if (seed != NULL) {
702196dff27SArd Biesheuvel 			size = min_t(u32, seed->size, SZ_1K); // sanity check
70363625988SArd Biesheuvel 			early_memunmap(seed, sizeof(*seed));
70463625988SArd Biesheuvel 		} else {
70563625988SArd Biesheuvel 			pr_err("Could not map UEFI random seed!\n");
70663625988SArd Biesheuvel 		}
70763625988SArd Biesheuvel 		if (size > 0) {
708badc6198STom Lendacky 			seed = early_memremap(efi_rng_seed,
709badc6198STom Lendacky 					      sizeof(*seed) + size);
71063625988SArd Biesheuvel 			if (seed != NULL) {
711be36f9e7SJason A. Donenfeld 				add_bootloader_randomness(seed->bits, size);
712196dff27SArd Biesheuvel 				memzero_explicit(seed->bits, size);
71363625988SArd Biesheuvel 				early_memunmap(seed, sizeof(*seed) + size);
71463625988SArd Biesheuvel 			} else {
71563625988SArd Biesheuvel 				pr_err("Could not map UEFI random seed!\n");
71663625988SArd Biesheuvel 			}
71763625988SArd Biesheuvel 		}
71863625988SArd Biesheuvel 	}
71963625988SArd Biesheuvel 
720dd09fad9SArd Biesheuvel 	if (!IS_ENABLED(CONFIG_X86_32) && efi_enabled(EFI_MEMMAP))
7213a6b6c6fSSai Praneeth 		efi_memattr_init();
7223a6b6c6fSSai Praneeth 
72333b6d034SThiebaud Weksteen 	efi_tpm_eventlog_init();
72433b6d034SThiebaud Weksteen 
725b7846e6bSArd Biesheuvel 	if (mem_reserve != EFI_INVALID_TABLE_ADDR) {
726b7846e6bSArd Biesheuvel 		unsigned long prsv = mem_reserve;
72771e0940dSArd Biesheuvel 
72871e0940dSArd Biesheuvel 		while (prsv) {
72971e0940dSArd Biesheuvel 			struct linux_efi_memreserve *rsv;
7305f0b0ecfSArd Biesheuvel 			u8 *p;
73171e0940dSArd Biesheuvel 
7325f0b0ecfSArd Biesheuvel 			/*
7335f0b0ecfSArd Biesheuvel 			 * Just map a full page: that is what we will get
7345f0b0ecfSArd Biesheuvel 			 * anyway, and it permits us to map the entire entry
7355f0b0ecfSArd Biesheuvel 			 * before knowing its size.
7365f0b0ecfSArd Biesheuvel 			 */
7375f0b0ecfSArd Biesheuvel 			p = early_memremap(ALIGN_DOWN(prsv, PAGE_SIZE),
7385f0b0ecfSArd Biesheuvel 					   PAGE_SIZE);
7395f0b0ecfSArd Biesheuvel 			if (p == NULL) {
74071e0940dSArd Biesheuvel 				pr_err("Could not map UEFI memreserve entry!\n");
74171e0940dSArd Biesheuvel 				return -ENOMEM;
74271e0940dSArd Biesheuvel 			}
74371e0940dSArd Biesheuvel 
7445f0b0ecfSArd Biesheuvel 			rsv = (void *)(p + prsv % PAGE_SIZE);
7455f0b0ecfSArd Biesheuvel 
7465f0b0ecfSArd Biesheuvel 			/* reserve the entry itself */
74729637951SGustavo A. R. Silva 			memblock_reserve(prsv,
74829637951SGustavo A. R. Silva 					 struct_size(rsv, entry, rsv->size));
7495f0b0ecfSArd Biesheuvel 
7505f0b0ecfSArd Biesheuvel 			for (i = 0; i < atomic_read(&rsv->count); i++) {
7515f0b0ecfSArd Biesheuvel 				memblock_reserve(rsv->entry[i].base,
7525f0b0ecfSArd Biesheuvel 						 rsv->entry[i].size);
7535f0b0ecfSArd Biesheuvel 			}
75471e0940dSArd Biesheuvel 
75571e0940dSArd Biesheuvel 			prsv = rsv->next;
7565f0b0ecfSArd Biesheuvel 			early_memunmap(p, PAGE_SIZE);
75771e0940dSArd Biesheuvel 		}
75871e0940dSArd Biesheuvel 	}
75971e0940dSArd Biesheuvel 
760fe4db90aSArd Biesheuvel 	if (rt_prop != EFI_INVALID_TABLE_ADDR) {
761fe4db90aSArd Biesheuvel 		efi_rt_properties_table_t *tbl;
762fe4db90aSArd Biesheuvel 
763fe4db90aSArd Biesheuvel 		tbl = early_memremap(rt_prop, sizeof(*tbl));
764fe4db90aSArd Biesheuvel 		if (tbl) {
765fe4db90aSArd Biesheuvel 			efi.runtime_supported_mask &= tbl->runtime_services_supported;
766fe4db90aSArd Biesheuvel 			early_memunmap(tbl, sizeof(*tbl));
767fe4db90aSArd Biesheuvel 		}
768fe4db90aSArd Biesheuvel 	}
769fe4db90aSArd Biesheuvel 
770f4dc7fffSArd Biesheuvel 	if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) &&
771f4dc7fffSArd Biesheuvel 	    initrd != EFI_INVALID_TABLE_ADDR && phys_initrd_size == 0) {
772f4dc7fffSArd Biesheuvel 		struct linux_efi_initrd *tbl;
773f4dc7fffSArd Biesheuvel 
774f4dc7fffSArd Biesheuvel 		tbl = early_memremap(initrd, sizeof(*tbl));
775f4dc7fffSArd Biesheuvel 		if (tbl) {
776f4dc7fffSArd Biesheuvel 			phys_initrd_start = tbl->base;
777f4dc7fffSArd Biesheuvel 			phys_initrd_size = tbl->size;
778f4dc7fffSArd Biesheuvel 			early_memunmap(tbl, sizeof(*tbl));
779f4dc7fffSArd Biesheuvel 		}
780f4dc7fffSArd Biesheuvel 	}
781f4dc7fffSArd Biesheuvel 
7822053bc57SKirill A. Shutemov 	if (IS_ENABLED(CONFIG_UNACCEPTED_MEMORY) &&
7832053bc57SKirill A. Shutemov 	    efi.unaccepted != EFI_INVALID_TABLE_ADDR) {
7842053bc57SKirill A. Shutemov 		struct efi_unaccepted_memory *unaccepted;
7852053bc57SKirill A. Shutemov 
7862053bc57SKirill A. Shutemov 		unaccepted = early_memremap(efi.unaccepted, sizeof(*unaccepted));
7872053bc57SKirill A. Shutemov 		if (unaccepted) {
7882053bc57SKirill A. Shutemov 
7892053bc57SKirill A. Shutemov 			if (unaccepted->version == 1) {
7908dbe3395SKirill A. Shutemov 				reserve_unaccepted(unaccepted);
7912053bc57SKirill A. Shutemov 			} else {
7922053bc57SKirill A. Shutemov 				efi.unaccepted = EFI_INVALID_TABLE_ADDR;
7932053bc57SKirill A. Shutemov 			}
7942053bc57SKirill A. Shutemov 
7952053bc57SKirill A. Shutemov 			early_memunmap(unaccepted, sizeof(*unaccepted));
7962053bc57SKirill A. Shutemov 		}
7972053bc57SKirill A. Shutemov 	}
7982053bc57SKirill A. Shutemov 
7997bb68410SArd Biesheuvel 	return 0;
8007bb68410SArd Biesheuvel }
8017bb68410SArd Biesheuvel 
efi_systab_check_header(const efi_table_hdr_t * systab_hdr)802234fa51dSArd Biesheuvel int __init efi_systab_check_header(const efi_table_hdr_t *systab_hdr)
80314fb4209SArd Biesheuvel {
80414fb4209SArd Biesheuvel 	if (systab_hdr->signature != EFI_SYSTEM_TABLE_SIGNATURE) {
80514fb4209SArd Biesheuvel 		pr_err("System table signature incorrect!\n");
80614fb4209SArd Biesheuvel 		return -EINVAL;
80714fb4209SArd Biesheuvel 	}
80814fb4209SArd Biesheuvel 
80914fb4209SArd Biesheuvel 	return 0;
81014fb4209SArd Biesheuvel }
81114fb4209SArd Biesheuvel 
81214fb4209SArd Biesheuvel #ifndef CONFIG_IA64
map_fw_vendor(unsigned long fw_vendor,size_t size)81314fb4209SArd Biesheuvel static const efi_char16_t *__init map_fw_vendor(unsigned long fw_vendor,
81414fb4209SArd Biesheuvel 						size_t size)
81514fb4209SArd Biesheuvel {
81614fb4209SArd Biesheuvel 	const efi_char16_t *ret;
81714fb4209SArd Biesheuvel 
81814fb4209SArd Biesheuvel 	ret = early_memremap_ro(fw_vendor, size);
81914fb4209SArd Biesheuvel 	if (!ret)
82014fb4209SArd Biesheuvel 		pr_err("Could not map the firmware vendor!\n");
82114fb4209SArd Biesheuvel 	return ret;
82214fb4209SArd Biesheuvel }
82314fb4209SArd Biesheuvel 
unmap_fw_vendor(const void * fw_vendor,size_t size)82414fb4209SArd Biesheuvel static void __init unmap_fw_vendor(const void *fw_vendor, size_t size)
82514fb4209SArd Biesheuvel {
82614fb4209SArd Biesheuvel 	early_memunmap((void *)fw_vendor, size);
82714fb4209SArd Biesheuvel }
82814fb4209SArd Biesheuvel #else
82914fb4209SArd Biesheuvel #define map_fw_vendor(p, s)	__va(p)
83014fb4209SArd Biesheuvel #define unmap_fw_vendor(v, s)
83114fb4209SArd Biesheuvel #endif
83214fb4209SArd Biesheuvel 
efi_systab_report_header(const efi_table_hdr_t * systab_hdr,unsigned long fw_vendor)83314fb4209SArd Biesheuvel void __init efi_systab_report_header(const efi_table_hdr_t *systab_hdr,
83414fb4209SArd Biesheuvel 				     unsigned long fw_vendor)
83514fb4209SArd Biesheuvel {
83614fb4209SArd Biesheuvel 	char vendor[100] = "unknown";
83714fb4209SArd Biesheuvel 	const efi_char16_t *c16;
83814fb4209SArd Biesheuvel 	size_t i;
8391758817eSArd Biesheuvel 	u16 rev;
84014fb4209SArd Biesheuvel 
84114fb4209SArd Biesheuvel 	c16 = map_fw_vendor(fw_vendor, sizeof(vendor) * sizeof(efi_char16_t));
84214fb4209SArd Biesheuvel 	if (c16) {
84314fb4209SArd Biesheuvel 		for (i = 0; i < sizeof(vendor) - 1 && c16[i]; ++i)
84414fb4209SArd Biesheuvel 			vendor[i] = c16[i];
84514fb4209SArd Biesheuvel 		vendor[i] = '\0';
84614fb4209SArd Biesheuvel 
84714fb4209SArd Biesheuvel 		unmap_fw_vendor(c16, sizeof(vendor) * sizeof(efi_char16_t));
84814fb4209SArd Biesheuvel 	}
84914fb4209SArd Biesheuvel 
8501758817eSArd Biesheuvel 	rev = (u16)systab_hdr->revision;
8511758817eSArd Biesheuvel 	pr_info("EFI v%u.%u", systab_hdr->revision >> 16, rev / 10);
8521758817eSArd Biesheuvel 
8531758817eSArd Biesheuvel 	rev %= 10;
8541758817eSArd Biesheuvel 	if (rev)
8551758817eSArd Biesheuvel 		pr_cont(".%u", rev);
8561758817eSArd Biesheuvel 
8571758817eSArd Biesheuvel 	pr_cont(" by %s\n", vendor);
858f5390cd0SArd Biesheuvel 
859f5390cd0SArd Biesheuvel 	if (IS_ENABLED(CONFIG_X86_64) &&
860f5390cd0SArd Biesheuvel 	    systab_hdr->revision > EFI_1_10_SYSTEM_TABLE_REVISION &&
861f5390cd0SArd Biesheuvel 	    !strcmp(vendor, "Apple")) {
862f5390cd0SArd Biesheuvel 		pr_info("Apple Mac detected, using EFI v1.10 runtime services only\n");
863f5390cd0SArd Biesheuvel 		efi.runtime_version = EFI_1_10_SYSTEM_TABLE_REVISION;
864f5390cd0SArd Biesheuvel 	}
86514fb4209SArd Biesheuvel }
86614fb4209SArd Biesheuvel 
8676277e374SArd Biesheuvel static __initdata char memory_type_name[][13] = {
86898d2a6caSLaszlo Ersek 	"Reserved",
86998d2a6caSLaszlo Ersek 	"Loader Code",
87098d2a6caSLaszlo Ersek 	"Loader Data",
87198d2a6caSLaszlo Ersek 	"Boot Code",
87298d2a6caSLaszlo Ersek 	"Boot Data",
87398d2a6caSLaszlo Ersek 	"Runtime Code",
87498d2a6caSLaszlo Ersek 	"Runtime Data",
8756277e374SArd Biesheuvel 	"Conventional",
8766277e374SArd Biesheuvel 	"Unusable",
8776277e374SArd Biesheuvel 	"ACPI Reclaim",
8786277e374SArd Biesheuvel 	"ACPI Mem NVS",
8796277e374SArd Biesheuvel 	"MMIO",
8806277e374SArd Biesheuvel 	"MMIO Port",
88135575e0eSRobert Elliott 	"PAL Code",
8826277e374SArd Biesheuvel 	"Persistent",
883745e3ed8SKirill A. Shutemov 	"Unaccepted",
88498d2a6caSLaszlo Ersek };
88598d2a6caSLaszlo Ersek 
efi_md_typeattr_format(char * buf,size_t size,const efi_memory_desc_t * md)88698d2a6caSLaszlo Ersek char * __init efi_md_typeattr_format(char *buf, size_t size,
88798d2a6caSLaszlo Ersek 				     const efi_memory_desc_t *md)
88898d2a6caSLaszlo Ersek {
88998d2a6caSLaszlo Ersek 	char *pos;
89098d2a6caSLaszlo Ersek 	int type_len;
89198d2a6caSLaszlo Ersek 	u64 attr;
89298d2a6caSLaszlo Ersek 
89398d2a6caSLaszlo Ersek 	pos = buf;
89498d2a6caSLaszlo Ersek 	if (md->type >= ARRAY_SIZE(memory_type_name))
89598d2a6caSLaszlo Ersek 		type_len = snprintf(pos, size, "[type=%u", md->type);
89698d2a6caSLaszlo Ersek 	else
89798d2a6caSLaszlo Ersek 		type_len = snprintf(pos, size, "[%-*s",
89898d2a6caSLaszlo Ersek 				    (int)(sizeof(memory_type_name[0]) - 1),
89998d2a6caSLaszlo Ersek 				    memory_type_name[md->type]);
90098d2a6caSLaszlo Ersek 	if (type_len >= size)
90198d2a6caSLaszlo Ersek 		return buf;
90298d2a6caSLaszlo Ersek 
90398d2a6caSLaszlo Ersek 	pos += type_len;
90498d2a6caSLaszlo Ersek 	size -= type_len;
90598d2a6caSLaszlo Ersek 
90698d2a6caSLaszlo Ersek 	attr = md->attribute;
90798d2a6caSLaszlo Ersek 	if (attr & ~(EFI_MEMORY_UC | EFI_MEMORY_WC | EFI_MEMORY_WT |
90887db73aeSArd Biesheuvel 		     EFI_MEMORY_WB | EFI_MEMORY_UCE | EFI_MEMORY_RO |
90987db73aeSArd Biesheuvel 		     EFI_MEMORY_WP | EFI_MEMORY_RP | EFI_MEMORY_XP |
9106277e374SArd Biesheuvel 		     EFI_MEMORY_NV | EFI_MEMORY_SP | EFI_MEMORY_CPU_CRYPTO |
911*b5bfb235SArd Biesheuvel 		     EFI_MEMORY_MORE_RELIABLE | EFI_MEMORY_HOT_PLUGGABLE |
912*b5bfb235SArd Biesheuvel 		     EFI_MEMORY_RUNTIME))
91398d2a6caSLaszlo Ersek 		snprintf(pos, size, "|attr=0x%016llx]",
91498d2a6caSLaszlo Ersek 			 (unsigned long long)attr);
91598d2a6caSLaszlo Ersek 	else
916c016ca08SRobert Elliott 		snprintf(pos, size,
917*b5bfb235SArd Biesheuvel 			 "|%3s|%2s|%2s|%2s|%2s|%2s|%2s|%2s|%2s|%2s|%3s|%2s|%2s|%2s|%2s]",
91898d2a6caSLaszlo Ersek 			 attr & EFI_MEMORY_RUNTIME		? "RUN" : "",
919*b5bfb235SArd Biesheuvel 			 attr & EFI_MEMORY_HOT_PLUGGABLE	? "HP"  : "",
9208be4432eSTaku Izumi 			 attr & EFI_MEMORY_MORE_RELIABLE	? "MR"  : "",
9216277e374SArd Biesheuvel 			 attr & EFI_MEMORY_CPU_CRYPTO   	? "CC"  : "",
922fe3e5e65SDan Williams 			 attr & EFI_MEMORY_SP			? "SP"  : "",
923c016ca08SRobert Elliott 			 attr & EFI_MEMORY_NV			? "NV"  : "",
92498d2a6caSLaszlo Ersek 			 attr & EFI_MEMORY_XP			? "XP"  : "",
92598d2a6caSLaszlo Ersek 			 attr & EFI_MEMORY_RP			? "RP"  : "",
92698d2a6caSLaszlo Ersek 			 attr & EFI_MEMORY_WP			? "WP"  : "",
92787db73aeSArd Biesheuvel 			 attr & EFI_MEMORY_RO			? "RO"  : "",
92898d2a6caSLaszlo Ersek 			 attr & EFI_MEMORY_UCE			? "UCE" : "",
92998d2a6caSLaszlo Ersek 			 attr & EFI_MEMORY_WB			? "WB"  : "",
93098d2a6caSLaszlo Ersek 			 attr & EFI_MEMORY_WT			? "WT"  : "",
93198d2a6caSLaszlo Ersek 			 attr & EFI_MEMORY_WC			? "WC"  : "",
93298d2a6caSLaszlo Ersek 			 attr & EFI_MEMORY_UC			? "UC"  : "");
93398d2a6caSLaszlo Ersek 	return buf;
93498d2a6caSLaszlo Ersek }
9357bf79311SJonathan (Zhixiong) Zhang 
9367bf79311SJonathan (Zhixiong) Zhang /*
93723f0571cSJan Beulich  * IA64 has a funky EFI memory map that doesn't work the same way as
93823f0571cSJan Beulich  * other architectures.
93923f0571cSJan Beulich  */
94023f0571cSJan Beulich #ifndef CONFIG_IA64
94123f0571cSJan Beulich /*
9427bf79311SJonathan (Zhixiong) Zhang  * efi_mem_attributes - lookup memmap attributes for physical address
9437bf79311SJonathan (Zhixiong) Zhang  * @phys_addr: the physical address to lookup
9447bf79311SJonathan (Zhixiong) Zhang  *
9457bf79311SJonathan (Zhixiong) Zhang  * Search in the EFI memory map for the region covering
9467bf79311SJonathan (Zhixiong) Zhang  * @phys_addr. Returns the EFI memory attributes if the region
9477bf79311SJonathan (Zhixiong) Zhang  * was found in the memory map, 0 otherwise.
9487bf79311SJonathan (Zhixiong) Zhang  */
efi_mem_attributes(unsigned long phys_addr)94923f0571cSJan Beulich u64 efi_mem_attributes(unsigned long phys_addr)
9507bf79311SJonathan (Zhixiong) Zhang {
9517bf79311SJonathan (Zhixiong) Zhang 	efi_memory_desc_t *md;
9527bf79311SJonathan (Zhixiong) Zhang 
9537bf79311SJonathan (Zhixiong) Zhang 	if (!efi_enabled(EFI_MEMMAP))
9547bf79311SJonathan (Zhixiong) Zhang 		return 0;
9557bf79311SJonathan (Zhixiong) Zhang 
95678ce248fSMatt Fleming 	for_each_efi_memory_desc(md) {
9577bf79311SJonathan (Zhixiong) Zhang 		if ((md->phys_addr <= phys_addr) &&
9587bf79311SJonathan (Zhixiong) Zhang 		    (phys_addr < (md->phys_addr +
9597bf79311SJonathan (Zhixiong) Zhang 		    (md->num_pages << EFI_PAGE_SHIFT))))
9607bf79311SJonathan (Zhixiong) Zhang 			return md->attribute;
9617bf79311SJonathan (Zhixiong) Zhang 	}
9627bf79311SJonathan (Zhixiong) Zhang 	return 0;
9637bf79311SJonathan (Zhixiong) Zhang }
964806b0351SMatt Fleming 
96523f0571cSJan Beulich /*
96623f0571cSJan Beulich  * efi_mem_type - lookup memmap type for physical address
96723f0571cSJan Beulich  * @phys_addr: the physical address to lookup
96823f0571cSJan Beulich  *
96923f0571cSJan Beulich  * Search in the EFI memory map for the region covering @phys_addr.
97023f0571cSJan Beulich  * Returns the EFI memory type if the region was found in the memory
97162b605b5SAnshuman Khandual  * map, -EINVAL otherwise.
97223f0571cSJan Beulich  */
efi_mem_type(unsigned long phys_addr)97323f0571cSJan Beulich int efi_mem_type(unsigned long phys_addr)
97423f0571cSJan Beulich {
97523f0571cSJan Beulich 	const efi_memory_desc_t *md;
97623f0571cSJan Beulich 
97723f0571cSJan Beulich 	if (!efi_enabled(EFI_MEMMAP))
97823f0571cSJan Beulich 		return -ENOTSUPP;
97923f0571cSJan Beulich 
98023f0571cSJan Beulich 	for_each_efi_memory_desc(md) {
98123f0571cSJan Beulich 		if ((md->phys_addr <= phys_addr) &&
98223f0571cSJan Beulich 		    (phys_addr < (md->phys_addr +
98323f0571cSJan Beulich 				  (md->num_pages << EFI_PAGE_SHIFT))))
98423f0571cSJan Beulich 			return md->type;
98523f0571cSJan Beulich 	}
98623f0571cSJan Beulich 	return -EINVAL;
98723f0571cSJan Beulich }
98823f0571cSJan Beulich #endif
98923f0571cSJan Beulich 
efi_status_to_err(efi_status_t status)990806b0351SMatt Fleming int efi_status_to_err(efi_status_t status)
991806b0351SMatt Fleming {
992806b0351SMatt Fleming 	int err;
993806b0351SMatt Fleming 
994806b0351SMatt Fleming 	switch (status) {
995806b0351SMatt Fleming 	case EFI_SUCCESS:
996806b0351SMatt Fleming 		err = 0;
997806b0351SMatt Fleming 		break;
998806b0351SMatt Fleming 	case EFI_INVALID_PARAMETER:
999806b0351SMatt Fleming 		err = -EINVAL;
1000806b0351SMatt Fleming 		break;
1001806b0351SMatt Fleming 	case EFI_OUT_OF_RESOURCES:
1002806b0351SMatt Fleming 		err = -ENOSPC;
1003806b0351SMatt Fleming 		break;
1004806b0351SMatt Fleming 	case EFI_DEVICE_ERROR:
1005806b0351SMatt Fleming 		err = -EIO;
1006806b0351SMatt Fleming 		break;
1007806b0351SMatt Fleming 	case EFI_WRITE_PROTECTED:
1008806b0351SMatt Fleming 		err = -EROFS;
1009806b0351SMatt Fleming 		break;
1010806b0351SMatt Fleming 	case EFI_SECURITY_VIOLATION:
1011806b0351SMatt Fleming 		err = -EACCES;
1012806b0351SMatt Fleming 		break;
1013806b0351SMatt Fleming 	case EFI_NOT_FOUND:
1014806b0351SMatt Fleming 		err = -ENOENT;
1015806b0351SMatt Fleming 		break;
1016dce48e35SArd Biesheuvel 	case EFI_ABORTED:
1017dce48e35SArd Biesheuvel 		err = -EINTR;
1018dce48e35SArd Biesheuvel 		break;
1019806b0351SMatt Fleming 	default:
1020806b0351SMatt Fleming 		err = -EINVAL;
1021806b0351SMatt Fleming 	}
1022806b0351SMatt Fleming 
1023806b0351SMatt Fleming 	return err;
1024806b0351SMatt Fleming }
10252d82e622SArd Biesheuvel EXPORT_SYMBOL_GPL(efi_status_to_err);
102663625988SArd Biesheuvel 
1027a23d3bb0SArd Biesheuvel static DEFINE_SPINLOCK(efi_mem_reserve_persistent_lock);
102863eb322dSArd Biesheuvel static struct linux_efi_memreserve *efi_memreserve_root __ro_after_init;
1029a23d3bb0SArd Biesheuvel 
efi_memreserve_map_root(void)1030976b4891SArd Biesheuvel static int __init efi_memreserve_map_root(void)
1031976b4891SArd Biesheuvel {
1032b7846e6bSArd Biesheuvel 	if (mem_reserve == EFI_INVALID_TABLE_ADDR)
1033976b4891SArd Biesheuvel 		return -ENODEV;
1034976b4891SArd Biesheuvel 
1035b7846e6bSArd Biesheuvel 	efi_memreserve_root = memremap(mem_reserve,
1036976b4891SArd Biesheuvel 				       sizeof(*efi_memreserve_root),
1037976b4891SArd Biesheuvel 				       MEMREMAP_WB);
1038976b4891SArd Biesheuvel 	if (WARN_ON_ONCE(!efi_memreserve_root))
1039976b4891SArd Biesheuvel 		return -ENOMEM;
1040976b4891SArd Biesheuvel 	return 0;
1041976b4891SArd Biesheuvel }
1042976b4891SArd Biesheuvel 
efi_mem_reserve_iomem(phys_addr_t addr,u64 size)1043ab0eb162SArd Biesheuvel static int efi_mem_reserve_iomem(phys_addr_t addr, u64 size)
1044ab0eb162SArd Biesheuvel {
1045ab0eb162SArd Biesheuvel 	struct resource *res, *parent;
10462bab693aSMarc Zyngier 	int ret;
1047ab0eb162SArd Biesheuvel 
1048ab0eb162SArd Biesheuvel 	res = kzalloc(sizeof(struct resource), GFP_ATOMIC);
1049ab0eb162SArd Biesheuvel 	if (!res)
1050ab0eb162SArd Biesheuvel 		return -ENOMEM;
1051ab0eb162SArd Biesheuvel 
1052ab0eb162SArd Biesheuvel 	res->name	= "reserved";
1053ab0eb162SArd Biesheuvel 	res->flags	= IORESOURCE_MEM;
1054ab0eb162SArd Biesheuvel 	res->start	= addr;
1055ab0eb162SArd Biesheuvel 	res->end	= addr + size - 1;
1056ab0eb162SArd Biesheuvel 
1057ab0eb162SArd Biesheuvel 	/* we expect a conflict with a 'System RAM' region */
1058ab0eb162SArd Biesheuvel 	parent = request_resource_conflict(&iomem_resource, res);
10592bab693aSMarc Zyngier 	ret = parent ? request_resource(parent, res) : 0;
10602bab693aSMarc Zyngier 
10612bab693aSMarc Zyngier 	/*
10622bab693aSMarc Zyngier 	 * Given that efi_mem_reserve_iomem() can be called at any
10632bab693aSMarc Zyngier 	 * time, only call memblock_reserve() if the architecture
10642bab693aSMarc Zyngier 	 * keeps the infrastructure around.
10652bab693aSMarc Zyngier 	 */
10662bab693aSMarc Zyngier 	if (IS_ENABLED(CONFIG_ARCH_KEEP_MEMBLOCK) && !ret)
10672bab693aSMarc Zyngier 		memblock_reserve(addr, size);
10682bab693aSMarc Zyngier 
10692bab693aSMarc Zyngier 	return ret;
1070ab0eb162SArd Biesheuvel }
1071ab0eb162SArd Biesheuvel 
efi_mem_reserve_persistent(phys_addr_t addr,u64 size)1072976b4891SArd Biesheuvel int __ref efi_mem_reserve_persistent(phys_addr_t addr, u64 size)
1073a23d3bb0SArd Biesheuvel {
107463eb322dSArd Biesheuvel 	struct linux_efi_memreserve *rsv;
107580424b02SArd Biesheuvel 	unsigned long prsv;
107680424b02SArd Biesheuvel 	int rc, index;
1077a23d3bb0SArd Biesheuvel 
1078976b4891SArd Biesheuvel 	if (efi_memreserve_root == (void *)ULONG_MAX)
1079a23d3bb0SArd Biesheuvel 		return -ENODEV;
1080a23d3bb0SArd Biesheuvel 
1081976b4891SArd Biesheuvel 	if (!efi_memreserve_root) {
1082976b4891SArd Biesheuvel 		rc = efi_memreserve_map_root();
1083976b4891SArd Biesheuvel 		if (rc)
1084976b4891SArd Biesheuvel 			return rc;
1085976b4891SArd Biesheuvel 	}
1086976b4891SArd Biesheuvel 
108780424b02SArd Biesheuvel 	/* first try to find a slot in an existing linked list entry */
10889ceee7d0SLv Yunlong 	for (prsv = efi_memreserve_root->next; prsv; ) {
108918df7577SArd Biesheuvel 		rsv = memremap(prsv, sizeof(*rsv), MEMREMAP_WB);
1090966d47e1SAnton Gusev 		if (!rsv)
1091966d47e1SAnton Gusev 			return -ENOMEM;
109280424b02SArd Biesheuvel 		index = atomic_fetch_add_unless(&rsv->count, 1, rsv->size);
109380424b02SArd Biesheuvel 		if (index < rsv->size) {
109480424b02SArd Biesheuvel 			rsv->entry[index].base = addr;
109580424b02SArd Biesheuvel 			rsv->entry[index].size = size;
109680424b02SArd Biesheuvel 
109718df7577SArd Biesheuvel 			memunmap(rsv);
1098ab0eb162SArd Biesheuvel 			return efi_mem_reserve_iomem(addr, size);
109980424b02SArd Biesheuvel 		}
11009ceee7d0SLv Yunlong 		prsv = rsv->next;
110118df7577SArd Biesheuvel 		memunmap(rsv);
110280424b02SArd Biesheuvel 	}
110380424b02SArd Biesheuvel 
110480424b02SArd Biesheuvel 	/* no slot found - allocate a new linked list entry */
110580424b02SArd Biesheuvel 	rsv = (struct linux_efi_memreserve *)__get_free_page(GFP_ATOMIC);
1106a23d3bb0SArd Biesheuvel 	if (!rsv)
1107a23d3bb0SArd Biesheuvel 		return -ENOMEM;
1108a23d3bb0SArd Biesheuvel 
1109ab0eb162SArd Biesheuvel 	rc = efi_mem_reserve_iomem(__pa(rsv), SZ_4K);
1110ab0eb162SArd Biesheuvel 	if (rc) {
1111ab0eb162SArd Biesheuvel 		free_page((unsigned long)rsv);
1112ab0eb162SArd Biesheuvel 		return rc;
1113ab0eb162SArd Biesheuvel 	}
1114ab0eb162SArd Biesheuvel 
111518df7577SArd Biesheuvel 	/*
111618df7577SArd Biesheuvel 	 * The memremap() call above assumes that a linux_efi_memreserve entry
111718df7577SArd Biesheuvel 	 * never crosses a page boundary, so let's ensure that this remains true
111818df7577SArd Biesheuvel 	 * even when kexec'ing a 4k pages kernel from a >4k pages kernel, by
111918df7577SArd Biesheuvel 	 * using SZ_4K explicitly in the size calculation below.
112018df7577SArd Biesheuvel 	 */
112118df7577SArd Biesheuvel 	rsv->size = EFI_MEMRESERVE_COUNT(SZ_4K);
11225f0b0ecfSArd Biesheuvel 	atomic_set(&rsv->count, 1);
11235f0b0ecfSArd Biesheuvel 	rsv->entry[0].base = addr;
11245f0b0ecfSArd Biesheuvel 	rsv->entry[0].size = size;
1125a23d3bb0SArd Biesheuvel 
1126a23d3bb0SArd Biesheuvel 	spin_lock(&efi_mem_reserve_persistent_lock);
112763eb322dSArd Biesheuvel 	rsv->next = efi_memreserve_root->next;
112863eb322dSArd Biesheuvel 	efi_memreserve_root->next = __pa(rsv);
1129a23d3bb0SArd Biesheuvel 	spin_unlock(&efi_mem_reserve_persistent_lock);
1130a23d3bb0SArd Biesheuvel 
1131ab0eb162SArd Biesheuvel 	return efi_mem_reserve_iomem(addr, size);
1132a23d3bb0SArd Biesheuvel }
1133a23d3bb0SArd Biesheuvel 
efi_memreserve_root_init(void)113463eb322dSArd Biesheuvel static int __init efi_memreserve_root_init(void)
113563eb322dSArd Biesheuvel {
1136976b4891SArd Biesheuvel 	if (efi_memreserve_root)
1137976b4891SArd Biesheuvel 		return 0;
1138976b4891SArd Biesheuvel 	if (efi_memreserve_map_root())
1139976b4891SArd Biesheuvel 		efi_memreserve_root = (void *)ULONG_MAX;
114063eb322dSArd Biesheuvel 	return 0;
114163eb322dSArd Biesheuvel }
114263eb322dSArd Biesheuvel early_initcall(efi_memreserve_root_init);
114363eb322dSArd Biesheuvel 
114463625988SArd Biesheuvel #ifdef CONFIG_KEXEC
update_efi_random_seed(struct notifier_block * nb,unsigned long code,void * unused)114563625988SArd Biesheuvel static int update_efi_random_seed(struct notifier_block *nb,
114663625988SArd Biesheuvel 				  unsigned long code, void *unused)
114763625988SArd Biesheuvel {
114863625988SArd Biesheuvel 	struct linux_efi_random_seed *seed;
114963625988SArd Biesheuvel 	u32 size = 0;
115063625988SArd Biesheuvel 
115163625988SArd Biesheuvel 	if (!kexec_in_progress)
115263625988SArd Biesheuvel 		return NOTIFY_DONE;
115363625988SArd Biesheuvel 
1154badc6198STom Lendacky 	seed = memremap(efi_rng_seed, sizeof(*seed), MEMREMAP_WB);
115563625988SArd Biesheuvel 	if (seed != NULL) {
1156c2ceb5fdSArd Biesheuvel 		size = min(seed->size, EFI_RANDOM_SEED_SIZE);
115763625988SArd Biesheuvel 		memunmap(seed);
115863625988SArd Biesheuvel 	} else {
115963625988SArd Biesheuvel 		pr_err("Could not map UEFI random seed!\n");
116063625988SArd Biesheuvel 	}
116163625988SArd Biesheuvel 	if (size > 0) {
1162badc6198STom Lendacky 		seed = memremap(efi_rng_seed, sizeof(*seed) + size,
1163badc6198STom Lendacky 				MEMREMAP_WB);
116463625988SArd Biesheuvel 		if (seed != NULL) {
116563625988SArd Biesheuvel 			seed->size = size;
116663625988SArd Biesheuvel 			get_random_bytes(seed->bits, seed->size);
116763625988SArd Biesheuvel 			memunmap(seed);
116863625988SArd Biesheuvel 		} else {
116963625988SArd Biesheuvel 			pr_err("Could not map UEFI random seed!\n");
117063625988SArd Biesheuvel 		}
117163625988SArd Biesheuvel 	}
117263625988SArd Biesheuvel 	return NOTIFY_DONE;
117363625988SArd Biesheuvel }
117463625988SArd Biesheuvel 
117563625988SArd Biesheuvel static struct notifier_block efi_random_seed_nb = {
117663625988SArd Biesheuvel 	.notifier_call = update_efi_random_seed,
117763625988SArd Biesheuvel };
117863625988SArd Biesheuvel 
register_update_efi_random_seed(void)11795d288dbdSArd Biesheuvel static int __init register_update_efi_random_seed(void)
118063625988SArd Biesheuvel {
1181badc6198STom Lendacky 	if (efi_rng_seed == EFI_INVALID_TABLE_ADDR)
118263625988SArd Biesheuvel 		return 0;
118363625988SArd Biesheuvel 	return register_reboot_notifier(&efi_random_seed_nb);
118463625988SArd Biesheuvel }
118563625988SArd Biesheuvel late_initcall(register_update_efi_random_seed);
118663625988SArd Biesheuvel #endif
1187