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