]>
Commit | Line | Data |
---|---|---|
a9499fa7 TG |
1 | /* |
2 | * efi.c - EFI subsystem | |
3 | * | |
4 | * Copyright (C) 2001,2003,2004 Dell <[email protected]> | |
5 | * Copyright (C) 2004 Intel Corporation <[email protected]> | |
6 | * Copyright (C) 2013 Tom Gundersen <[email protected]> | |
7 | * | |
8 | * This code registers /sys/firmware/efi{,/efivars} when EFI is supported, | |
9 | * allowing the efivarfs to be mounted or the efivars module to be loaded. | |
10 | * The existance of /sys/firmware/efi may also be used by userspace to | |
11 | * determine that the system supports EFI. | |
12 | * | |
13 | * This file is released under the GPLv2. | |
14 | */ | |
15 | ||
272686bf LL |
16 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
17 | ||
a9499fa7 TG |
18 | #include <linux/kobject.h> |
19 | #include <linux/module.h> | |
20 | #include <linux/init.h> | |
21 | #include <linux/device.h> | |
22 | #include <linux/efi.h> | |
0302f71c MS |
23 | #include <linux/of.h> |
24 | #include <linux/of_fdt.h> | |
272686bf | 25 | #include <linux/io.h> |
63625988 | 26 | #include <linux/kexec.h> |
28d54022 | 27 | #include <linux/platform_device.h> |
63625988 AB |
28 | #include <linux/random.h> |
29 | #include <linux/reboot.h> | |
475fb4e8 OP |
30 | #include <linux/slab.h> |
31 | #include <linux/acpi.h> | |
32 | #include <linux/ucs2_string.h> | |
816e7612 | 33 | #include <linux/memblock.h> |
272686bf | 34 | |
0f7f2f0c | 35 | #include <asm/early_ioremap.h> |
f7d92489 | 36 | |
272686bf | 37 | struct efi __read_mostly efi = { |
bf924863 AB |
38 | .mps = EFI_INVALID_TABLE_ADDR, |
39 | .acpi = EFI_INVALID_TABLE_ADDR, | |
40 | .acpi20 = EFI_INVALID_TABLE_ADDR, | |
41 | .smbios = EFI_INVALID_TABLE_ADDR, | |
42 | .smbios3 = EFI_INVALID_TABLE_ADDR, | |
43 | .sal_systab = EFI_INVALID_TABLE_ADDR, | |
44 | .boot_info = EFI_INVALID_TABLE_ADDR, | |
45 | .hcdp = EFI_INVALID_TABLE_ADDR, | |
46 | .uga = EFI_INVALID_TABLE_ADDR, | |
47 | .uv_systab = EFI_INVALID_TABLE_ADDR, | |
48 | .fw_vendor = EFI_INVALID_TABLE_ADDR, | |
49 | .runtime = EFI_INVALID_TABLE_ADDR, | |
50 | .config_table = EFI_INVALID_TABLE_ADDR, | |
51 | .esrt = EFI_INVALID_TABLE_ADDR, | |
52 | .properties_table = EFI_INVALID_TABLE_ADDR, | |
a604af07 | 53 | .mem_attr_table = EFI_INVALID_TABLE_ADDR, |
63625988 | 54 | .rng_seed = EFI_INVALID_TABLE_ADDR, |
71e0940d AB |
55 | .tpm_log = EFI_INVALID_TABLE_ADDR, |
56 | .mem_reserve = EFI_INVALID_TABLE_ADDR, | |
272686bf LL |
57 | }; |
58 | EXPORT_SYMBOL(efi); | |
a9499fa7 | 59 | |
a19d66c5 TL |
60 | static unsigned long *efi_tables[] = { |
61 | &efi.mps, | |
62 | &efi.acpi, | |
63 | &efi.acpi20, | |
64 | &efi.smbios, | |
65 | &efi.smbios3, | |
66 | &efi.sal_systab, | |
67 | &efi.boot_info, | |
68 | &efi.hcdp, | |
69 | &efi.uga, | |
70 | &efi.uv_systab, | |
71 | &efi.fw_vendor, | |
72 | &efi.runtime, | |
73 | &efi.config_table, | |
74 | &efi.esrt, | |
75 | &efi.properties_table, | |
76 | &efi.mem_attr_table, | |
77 | }; | |
78 | ||
7e904a91 SP |
79 | struct mm_struct efi_mm = { |
80 | .mm_rb = RB_ROOT, | |
81 | .mm_users = ATOMIC_INIT(2), | |
82 | .mm_count = ATOMIC_INIT(1), | |
83 | .mmap_sem = __RWSEM_INITIALIZER(efi_mm.mmap_sem), | |
84 | .page_table_lock = __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock), | |
85 | .mmlist = LIST_HEAD_INIT(efi_mm.mmlist), | |
c1a2f7f0 | 86 | .cpu_bitmap = { [BITS_TO_LONGS(NR_CPUS)] = 0}, |
7e904a91 SP |
87 | }; |
88 | ||
3eb420e7 SP |
89 | struct workqueue_struct *efi_rts_wq; |
90 | ||
b2e0a54a DY |
91 | static bool disable_runtime; |
92 | static int __init setup_noefi(char *arg) | |
93 | { | |
94 | disable_runtime = true; | |
95 | return 0; | |
96 | } | |
97 | early_param("noefi", setup_noefi); | |
98 | ||
99 | bool efi_runtime_disabled(void) | |
100 | { | |
101 | return disable_runtime; | |
102 | } | |
103 | ||
5ae3683c DY |
104 | static int __init parse_efi_cmdline(char *str) |
105 | { | |
9115c758 RN |
106 | if (!str) { |
107 | pr_warn("need at least one option\n"); | |
108 | return -EINVAL; | |
109 | } | |
110 | ||
12dd00e8 LL |
111 | if (parse_option_str(str, "debug")) |
112 | set_bit(EFI_DBG, &efi.flags); | |
113 | ||
5ae3683c DY |
114 | if (parse_option_str(str, "noruntime")) |
115 | disable_runtime = true; | |
116 | ||
117 | return 0; | |
118 | } | |
119 | early_param("efi", parse_efi_cmdline); | |
120 | ||
0bb54905 | 121 | struct kobject *efi_kobj; |
a9499fa7 TG |
122 | |
123 | /* | |
124 | * Let's not leave out systab information that snuck into | |
125 | * the efivars driver | |
0b02e448 DY |
126 | * Note, do not add more fields in systab sysfs file as it breaks sysfs |
127 | * one value per file rule! | |
a9499fa7 TG |
128 | */ |
129 | static ssize_t systab_show(struct kobject *kobj, | |
130 | struct kobj_attribute *attr, char *buf) | |
131 | { | |
132 | char *str = buf; | |
133 | ||
134 | if (!kobj || !buf) | |
135 | return -EINVAL; | |
136 | ||
137 | if (efi.mps != EFI_INVALID_TABLE_ADDR) | |
138 | str += sprintf(str, "MPS=0x%lx\n", efi.mps); | |
139 | if (efi.acpi20 != EFI_INVALID_TABLE_ADDR) | |
140 | str += sprintf(str, "ACPI20=0x%lx\n", efi.acpi20); | |
141 | if (efi.acpi != EFI_INVALID_TABLE_ADDR) | |
142 | str += sprintf(str, "ACPI=0x%lx\n", efi.acpi); | |
b119fe08 JD |
143 | /* |
144 | * If both SMBIOS and SMBIOS3 entry points are implemented, the | |
145 | * SMBIOS3 entry point shall be preferred, so we list it first to | |
146 | * let applications stop parsing after the first match. | |
147 | */ | |
e1ccbbc9 AB |
148 | if (efi.smbios3 != EFI_INVALID_TABLE_ADDR) |
149 | str += sprintf(str, "SMBIOS3=0x%lx\n", efi.smbios3); | |
b119fe08 JD |
150 | if (efi.smbios != EFI_INVALID_TABLE_ADDR) |
151 | str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios); | |
a9499fa7 TG |
152 | if (efi.hcdp != EFI_INVALID_TABLE_ADDR) |
153 | str += sprintf(str, "HCDP=0x%lx\n", efi.hcdp); | |
154 | if (efi.boot_info != EFI_INVALID_TABLE_ADDR) | |
155 | str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info); | |
156 | if (efi.uga != EFI_INVALID_TABLE_ADDR) | |
157 | str += sprintf(str, "UGA=0x%lx\n", efi.uga); | |
158 | ||
159 | return str - buf; | |
160 | } | |
161 | ||
af97a77b | 162 | static struct kobj_attribute efi_attr_systab = __ATTR_RO_MODE(systab, 0400); |
a9499fa7 | 163 | |
a0998eb1 DY |
164 | #define EFI_FIELD(var) efi.var |
165 | ||
166 | #define EFI_ATTR_SHOW(name) \ | |
167 | static ssize_t name##_show(struct kobject *kobj, \ | |
168 | struct kobj_attribute *attr, char *buf) \ | |
169 | { \ | |
170 | return sprintf(buf, "0x%lx\n", EFI_FIELD(name)); \ | |
171 | } | |
172 | ||
173 | EFI_ATTR_SHOW(fw_vendor); | |
174 | EFI_ATTR_SHOW(runtime); | |
175 | EFI_ATTR_SHOW(config_table); | |
176 | ||
2859dff9 SM |
177 | static ssize_t fw_platform_size_show(struct kobject *kobj, |
178 | struct kobj_attribute *attr, char *buf) | |
179 | { | |
180 | return sprintf(buf, "%d\n", efi_enabled(EFI_64BIT) ? 64 : 32); | |
181 | } | |
182 | ||
a0998eb1 DY |
183 | static struct kobj_attribute efi_attr_fw_vendor = __ATTR_RO(fw_vendor); |
184 | static struct kobj_attribute efi_attr_runtime = __ATTR_RO(runtime); | |
185 | static struct kobj_attribute efi_attr_config_table = __ATTR_RO(config_table); | |
2859dff9 SM |
186 | static struct kobj_attribute efi_attr_fw_platform_size = |
187 | __ATTR_RO(fw_platform_size); | |
a0998eb1 | 188 | |
a9499fa7 TG |
189 | static struct attribute *efi_subsys_attrs[] = { |
190 | &efi_attr_systab.attr, | |
a0998eb1 DY |
191 | &efi_attr_fw_vendor.attr, |
192 | &efi_attr_runtime.attr, | |
193 | &efi_attr_config_table.attr, | |
2859dff9 | 194 | &efi_attr_fw_platform_size.attr, |
a0998eb1 | 195 | NULL, |
a9499fa7 TG |
196 | }; |
197 | ||
a0998eb1 DY |
198 | static umode_t efi_attr_is_visible(struct kobject *kobj, |
199 | struct attribute *attr, int n) | |
200 | { | |
9f27bc54 DK |
201 | if (attr == &efi_attr_fw_vendor.attr) { |
202 | if (efi_enabled(EFI_PARAVIRT) || | |
203 | efi.fw_vendor == EFI_INVALID_TABLE_ADDR) | |
204 | return 0; | |
205 | } else if (attr == &efi_attr_runtime.attr) { | |
206 | if (efi.runtime == EFI_INVALID_TABLE_ADDR) | |
207 | return 0; | |
208 | } else if (attr == &efi_attr_config_table.attr) { | |
209 | if (efi.config_table == EFI_INVALID_TABLE_ADDR) | |
210 | return 0; | |
211 | } | |
a0998eb1 | 212 | |
9f27bc54 | 213 | return attr->mode; |
a0998eb1 DY |
214 | } |
215 | ||
3ad6bd7c | 216 | static const struct attribute_group efi_subsys_attr_group = { |
a9499fa7 | 217 | .attrs = efi_subsys_attrs, |
a0998eb1 | 218 | .is_visible = efi_attr_is_visible, |
a9499fa7 TG |
219 | }; |
220 | ||
221 | static struct efivars generic_efivars; | |
222 | static struct efivar_operations generic_ops; | |
223 | ||
224 | static int generic_ops_register(void) | |
225 | { | |
226 | generic_ops.get_variable = efi.get_variable; | |
227 | generic_ops.set_variable = efi.set_variable; | |
9c6672ac | 228 | generic_ops.set_variable_nonblocking = efi.set_variable_nonblocking; |
a9499fa7 | 229 | generic_ops.get_next_variable = efi.get_next_variable; |
a614e192 | 230 | generic_ops.query_variable_store = efi_query_variable_store; |
a9499fa7 TG |
231 | |
232 | return efivars_register(&generic_efivars, &generic_ops, efi_kobj); | |
233 | } | |
234 | ||
235 | static void generic_ops_unregister(void) | |
236 | { | |
237 | efivars_unregister(&generic_efivars); | |
238 | } | |
239 | ||
475fb4e8 OP |
240 | #if IS_ENABLED(CONFIG_ACPI) |
241 | #define EFIVAR_SSDT_NAME_MAX 16 | |
242 | static char efivar_ssdt[EFIVAR_SSDT_NAME_MAX] __initdata; | |
243 | static int __init efivar_ssdt_setup(char *str) | |
244 | { | |
245 | if (strlen(str) < sizeof(efivar_ssdt)) | |
246 | memcpy(efivar_ssdt, str, strlen(str)); | |
247 | else | |
248 | pr_warn("efivar_ssdt: name too long: %s\n", str); | |
249 | return 0; | |
250 | } | |
251 | __setup("efivar_ssdt=", efivar_ssdt_setup); | |
252 | ||
253 | static __init int efivar_ssdt_iter(efi_char16_t *name, efi_guid_t vendor, | |
254 | unsigned long name_size, void *data) | |
255 | { | |
256 | struct efivar_entry *entry; | |
257 | struct list_head *list = data; | |
258 | char utf8_name[EFIVAR_SSDT_NAME_MAX]; | |
259 | int limit = min_t(unsigned long, EFIVAR_SSDT_NAME_MAX, name_size); | |
260 | ||
261 | ucs2_as_utf8(utf8_name, name, limit - 1); | |
262 | if (strncmp(utf8_name, efivar_ssdt, limit) != 0) | |
263 | return 0; | |
264 | ||
265 | entry = kmalloc(sizeof(*entry), GFP_KERNEL); | |
266 | if (!entry) | |
267 | return 0; | |
268 | ||
269 | memcpy(entry->var.VariableName, name, name_size); | |
270 | memcpy(&entry->var.VendorGuid, &vendor, sizeof(efi_guid_t)); | |
271 | ||
272 | efivar_entry_add(entry, list); | |
273 | ||
274 | return 0; | |
275 | } | |
276 | ||
277 | static __init int efivar_ssdt_load(void) | |
278 | { | |
279 | LIST_HEAD(entries); | |
280 | struct efivar_entry *entry, *aux; | |
281 | unsigned long size; | |
282 | void *data; | |
283 | int ret; | |
284 | ||
285 | ret = efivar_init(efivar_ssdt_iter, &entries, true, &entries); | |
286 | ||
287 | list_for_each_entry_safe(entry, aux, &entries, list) { | |
288 | pr_info("loading SSDT from variable %s-%pUl\n", efivar_ssdt, | |
289 | &entry->var.VendorGuid); | |
290 | ||
291 | list_del(&entry->list); | |
292 | ||
293 | ret = efivar_entry_size(entry, &size); | |
294 | if (ret) { | |
295 | pr_err("failed to get var size\n"); | |
296 | goto free_entry; | |
297 | } | |
298 | ||
299 | data = kmalloc(size, GFP_KERNEL); | |
a75dcb58 DC |
300 | if (!data) { |
301 | ret = -ENOMEM; | |
475fb4e8 | 302 | goto free_entry; |
a75dcb58 | 303 | } |
475fb4e8 OP |
304 | |
305 | ret = efivar_entry_get(entry, NULL, &size, data); | |
306 | if (ret) { | |
307 | pr_err("failed to get var data\n"); | |
308 | goto free_data; | |
309 | } | |
310 | ||
311 | ret = acpi_load_table(data); | |
312 | if (ret) { | |
313 | pr_err("failed to load table: %d\n", ret); | |
314 | goto free_data; | |
315 | } | |
316 | ||
317 | goto free_entry; | |
318 | ||
319 | free_data: | |
320 | kfree(data); | |
321 | ||
322 | free_entry: | |
323 | kfree(entry); | |
324 | } | |
325 | ||
326 | return ret; | |
327 | } | |
328 | #else | |
329 | static inline int efivar_ssdt_load(void) { return 0; } | |
330 | #endif | |
331 | ||
a9499fa7 TG |
332 | /* |
333 | * We register the efi subsystem with the firmware subsystem and the | |
334 | * efivars subsystem with the efi subsystem, if the system was booted with | |
335 | * EFI. | |
336 | */ | |
337 | static int __init efisubsys_init(void) | |
338 | { | |
339 | int error; | |
340 | ||
341 | if (!efi_enabled(EFI_BOOT)) | |
342 | return 0; | |
343 | ||
3eb420e7 SP |
344 | /* |
345 | * Since we process only one efi_runtime_service() at a time, an | |
346 | * ordered workqueue (which creates only one execution context) | |
347 | * should suffice all our needs. | |
348 | */ | |
349 | efi_rts_wq = alloc_ordered_workqueue("efi_rts_wq", 0); | |
350 | if (!efi_rts_wq) { | |
351 | pr_err("Creating efi_rts_wq failed, EFI runtime services disabled.\n"); | |
352 | clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); | |
353 | return 0; | |
354 | } | |
355 | ||
a9499fa7 TG |
356 | /* We register the efi directory at /sys/firmware/efi */ |
357 | efi_kobj = kobject_create_and_add("efi", firmware_kobj); | |
358 | if (!efi_kobj) { | |
359 | pr_err("efi: Firmware registration failed.\n"); | |
360 | return -ENOMEM; | |
361 | } | |
362 | ||
363 | error = generic_ops_register(); | |
364 | if (error) | |
365 | goto err_put; | |
366 | ||
475fb4e8 OP |
367 | if (efi_enabled(EFI_RUNTIME_SERVICES)) |
368 | efivar_ssdt_load(); | |
369 | ||
a9499fa7 TG |
370 | error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group); |
371 | if (error) { | |
372 | pr_err("efi: Sysfs attribute export failed with error %d.\n", | |
373 | error); | |
374 | goto err_unregister; | |
375 | } | |
376 | ||
926172d4 DY |
377 | error = efi_runtime_map_init(efi_kobj); |
378 | if (error) | |
379 | goto err_remove_group; | |
380 | ||
a9499fa7 | 381 | /* and the standard mountpoint for efivarfs */ |
f9bb4882 EB |
382 | error = sysfs_create_mount_point(efi_kobj, "efivars"); |
383 | if (error) { | |
a9499fa7 | 384 | pr_err("efivars: Subsystem registration failed.\n"); |
a9499fa7 TG |
385 | goto err_remove_group; |
386 | } | |
387 | ||
388 | return 0; | |
389 | ||
390 | err_remove_group: | |
391 | sysfs_remove_group(efi_kobj, &efi_subsys_attr_group); | |
392 | err_unregister: | |
393 | generic_ops_unregister(); | |
394 | err_put: | |
395 | kobject_put(efi_kobj); | |
396 | return error; | |
397 | } | |
398 | ||
399 | subsys_initcall(efisubsys_init); | |
272686bf | 400 | |
0bb54905 PJ |
401 | /* |
402 | * Find the efi memory descriptor for a given physical address. Given a | |
dca0f971 | 403 | * physical address, determine if it exists within an EFI Memory Map entry, |
0bb54905 PJ |
404 | * and if so, populate the supplied memory descriptor with the appropriate |
405 | * data. | |
406 | */ | |
7e1550b8 | 407 | int efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md) |
0bb54905 | 408 | { |
dca0f971 | 409 | efi_memory_desc_t *md; |
0bb54905 PJ |
410 | |
411 | if (!efi_enabled(EFI_MEMMAP)) { | |
412 | pr_err_once("EFI_MEMMAP is not enabled.\n"); | |
413 | return -EINVAL; | |
414 | } | |
415 | ||
0bb54905 PJ |
416 | if (!out_md) { |
417 | pr_err_once("out_md is null.\n"); | |
418 | return -EINVAL; | |
419 | } | |
0bb54905 | 420 | |
dca0f971 | 421 | for_each_efi_memory_desc(md) { |
0bb54905 PJ |
422 | u64 size; |
423 | u64 end; | |
424 | ||
0bb54905 PJ |
425 | size = md->num_pages << EFI_PAGE_SHIFT; |
426 | end = md->phys_addr + size; | |
427 | if (phys_addr >= md->phys_addr && phys_addr < end) { | |
428 | memcpy(out_md, md, sizeof(*out_md)); | |
0bb54905 PJ |
429 | return 0; |
430 | } | |
0bb54905 | 431 | } |
0bb54905 PJ |
432 | return -ENOENT; |
433 | } | |
434 | ||
435 | /* | |
436 | * Calculate the highest address of an efi memory descriptor. | |
437 | */ | |
438 | u64 __init efi_mem_desc_end(efi_memory_desc_t *md) | |
439 | { | |
440 | u64 size = md->num_pages << EFI_PAGE_SHIFT; | |
441 | u64 end = md->phys_addr + size; | |
442 | return end; | |
443 | } | |
272686bf | 444 | |
816e7612 MF |
445 | void __init __weak efi_arch_mem_reserve(phys_addr_t addr, u64 size) {} |
446 | ||
447 | /** | |
448 | * efi_mem_reserve - Reserve an EFI memory region | |
449 | * @addr: Physical address to reserve | |
450 | * @size: Size of reservation | |
451 | * | |
452 | * Mark a region as reserved from general kernel allocation and | |
453 | * prevent it being released by efi_free_boot_services(). | |
454 | * | |
455 | * This function should be called drivers once they've parsed EFI | |
456 | * configuration tables to figure out where their data lives, e.g. | |
457 | * efi_esrt_init(). | |
458 | */ | |
459 | void __init efi_mem_reserve(phys_addr_t addr, u64 size) | |
460 | { | |
461 | if (!memblock_is_region_reserved(addr, size)) | |
462 | memblock_reserve(addr, size); | |
463 | ||
464 | /* | |
465 | * Some architectures (x86) reserve all boot services ranges | |
466 | * until efi_free_boot_services() because of buggy firmware | |
467 | * implementations. This means the above memblock_reserve() is | |
468 | * superfluous on x86 and instead what it needs to do is | |
469 | * ensure the @start, @size is not freed. | |
470 | */ | |
471 | efi_arch_mem_reserve(addr, size); | |
472 | } | |
473 | ||
272686bf LL |
474 | static __initdata efi_config_table_type_t common_tables[] = { |
475 | {ACPI_20_TABLE_GUID, "ACPI 2.0", &efi.acpi20}, | |
476 | {ACPI_TABLE_GUID, "ACPI", &efi.acpi}, | |
477 | {HCDP_TABLE_GUID, "HCDP", &efi.hcdp}, | |
478 | {MPS_TABLE_GUID, "MPS", &efi.mps}, | |
479 | {SAL_SYSTEM_TABLE_GUID, "SALsystab", &efi.sal_systab}, | |
480 | {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios}, | |
e1ccbbc9 | 481 | {SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3}, |
272686bf | 482 | {UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga}, |
0bb54905 | 483 | {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt}, |
bf924863 | 484 | {EFI_PROPERTIES_TABLE_GUID, "PROP", &efi.properties_table}, |
a604af07 | 485 | {EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi.mem_attr_table}, |
63625988 | 486 | {LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", &efi.rng_seed}, |
33b6d034 | 487 | {LINUX_EFI_TPM_EVENT_LOG_GUID, "TPMEventLog", &efi.tpm_log}, |
71e0940d | 488 | {LINUX_EFI_MEMRESERVE_TABLE_GUID, "MEMRESERVE", &efi.mem_reserve}, |
69e60841 | 489 | {NULL_GUID, NULL, NULL}, |
272686bf LL |
490 | }; |
491 | ||
492 | static __init int match_config_table(efi_guid_t *guid, | |
493 | unsigned long table, | |
494 | efi_config_table_type_t *table_types) | |
495 | { | |
272686bf LL |
496 | int i; |
497 | ||
498 | if (table_types) { | |
272686bf | 499 | for (i = 0; efi_guidcmp(table_types[i].guid, NULL_GUID); i++) { |
272686bf LL |
500 | if (!efi_guidcmp(*guid, table_types[i].guid)) { |
501 | *(table_types[i].ptr) = table; | |
801820be AB |
502 | if (table_types[i].name) |
503 | pr_cont(" %s=0x%lx ", | |
504 | table_types[i].name, table); | |
272686bf LL |
505 | return 1; |
506 | } | |
507 | } | |
508 | } | |
509 | ||
510 | return 0; | |
511 | } | |
512 | ||
7bb68410 AB |
513 | int __init efi_config_parse_tables(void *config_tables, int count, int sz, |
514 | efi_config_table_type_t *arch_tables) | |
272686bf | 515 | { |
7bb68410 AB |
516 | void *tablep; |
517 | int i; | |
272686bf LL |
518 | |
519 | tablep = config_tables; | |
520 | pr_info(""); | |
7bb68410 | 521 | for (i = 0; i < count; i++) { |
272686bf LL |
522 | efi_guid_t guid; |
523 | unsigned long table; | |
524 | ||
525 | if (efi_enabled(EFI_64BIT)) { | |
526 | u64 table64; | |
527 | guid = ((efi_config_table_64_t *)tablep)->guid; | |
528 | table64 = ((efi_config_table_64_t *)tablep)->table; | |
529 | table = table64; | |
530 | #ifndef CONFIG_64BIT | |
531 | if (table64 >> 32) { | |
532 | pr_cont("\n"); | |
533 | pr_err("Table located above 4GB, disabling EFI.\n"); | |
272686bf LL |
534 | return -EINVAL; |
535 | } | |
536 | #endif | |
537 | } else { | |
538 | guid = ((efi_config_table_32_t *)tablep)->guid; | |
539 | table = ((efi_config_table_32_t *)tablep)->table; | |
540 | } | |
541 | ||
542 | if (!match_config_table(&guid, table, common_tables)) | |
543 | match_config_table(&guid, table, arch_tables); | |
544 | ||
545 | tablep += sz; | |
546 | } | |
547 | pr_cont("\n"); | |
0f8093a9 | 548 | set_bit(EFI_CONFIG_TABLES, &efi.flags); |
a1041713 | 549 | |
63625988 AB |
550 | if (efi.rng_seed != EFI_INVALID_TABLE_ADDR) { |
551 | struct linux_efi_random_seed *seed; | |
552 | u32 size = 0; | |
553 | ||
554 | seed = early_memremap(efi.rng_seed, sizeof(*seed)); | |
555 | if (seed != NULL) { | |
556 | size = seed->size; | |
557 | early_memunmap(seed, sizeof(*seed)); | |
558 | } else { | |
559 | pr_err("Could not map UEFI random seed!\n"); | |
560 | } | |
561 | if (size > 0) { | |
562 | seed = early_memremap(efi.rng_seed, | |
563 | sizeof(*seed) + size); | |
564 | if (seed != NULL) { | |
5b4e4c3a | 565 | pr_notice("seeding entropy pool\n"); |
63625988 AB |
566 | add_device_randomness(seed->bits, seed->size); |
567 | early_memunmap(seed, sizeof(*seed) + size); | |
568 | } else { | |
569 | pr_err("Could not map UEFI random seed!\n"); | |
570 | } | |
571 | } | |
572 | } | |
573 | ||
457ea3f7 DK |
574 | if (efi_enabled(EFI_MEMMAP)) |
575 | efi_memattr_init(); | |
3a6b6c6f | 576 | |
33b6d034 TW |
577 | efi_tpm_eventlog_init(); |
578 | ||
a1041713 AB |
579 | /* Parse the EFI Properties table if it exists */ |
580 | if (efi.properties_table != EFI_INVALID_TABLE_ADDR) { | |
581 | efi_properties_table_t *tbl; | |
582 | ||
583 | tbl = early_memremap(efi.properties_table, sizeof(*tbl)); | |
584 | if (tbl == NULL) { | |
585 | pr_err("Could not map Properties table!\n"); | |
586 | return -ENOMEM; | |
587 | } | |
588 | ||
589 | if (tbl->memory_protection_attribute & | |
590 | EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA) | |
591 | set_bit(EFI_NX_PE_DATA, &efi.flags); | |
592 | ||
593 | early_memunmap(tbl, sizeof(*tbl)); | |
594 | } | |
eff89628 AB |
595 | return 0; |
596 | } | |
a1041713 | 597 | |
eff89628 AB |
598 | int __init efi_apply_persistent_mem_reservations(void) |
599 | { | |
71e0940d AB |
600 | if (efi.mem_reserve != EFI_INVALID_TABLE_ADDR) { |
601 | unsigned long prsv = efi.mem_reserve; | |
602 | ||
603 | while (prsv) { | |
604 | struct linux_efi_memreserve *rsv; | |
605 | ||
606 | /* reserve the entry itself */ | |
607 | memblock_reserve(prsv, sizeof(*rsv)); | |
608 | ||
609 | rsv = early_memremap(prsv, sizeof(*rsv)); | |
610 | if (rsv == NULL) { | |
611 | pr_err("Could not map UEFI memreserve entry!\n"); | |
612 | return -ENOMEM; | |
613 | } | |
614 | ||
615 | if (rsv->size) | |
616 | memblock_reserve(rsv->base, rsv->size); | |
617 | ||
618 | prsv = rsv->next; | |
619 | early_memunmap(rsv, sizeof(*rsv)); | |
620 | } | |
621 | } | |
622 | ||
272686bf LL |
623 | return 0; |
624 | } | |
0302f71c | 625 | |
7bb68410 AB |
626 | int __init efi_config_init(efi_config_table_type_t *arch_tables) |
627 | { | |
628 | void *config_tables; | |
629 | int sz, ret; | |
630 | ||
631 | if (efi_enabled(EFI_64BIT)) | |
632 | sz = sizeof(efi_config_table_64_t); | |
633 | else | |
634 | sz = sizeof(efi_config_table_32_t); | |
635 | ||
636 | /* | |
637 | * Let's see what config tables the firmware passed to us. | |
638 | */ | |
639 | config_tables = early_memremap(efi.systab->tables, | |
640 | efi.systab->nr_tables * sz); | |
641 | if (config_tables == NULL) { | |
642 | pr_err("Could not map Configuration table!\n"); | |
643 | return -ENOMEM; | |
644 | } | |
645 | ||
646 | ret = efi_config_parse_tables(config_tables, efi.systab->nr_tables, sz, | |
647 | arch_tables); | |
648 | ||
649 | early_memunmap(config_tables, efi.systab->nr_tables * sz); | |
650 | return ret; | |
651 | } | |
652 | ||
28d54022 LCY |
653 | #ifdef CONFIG_EFI_VARS_MODULE |
654 | static int __init efi_load_efivars(void) | |
655 | { | |
656 | struct platform_device *pdev; | |
657 | ||
658 | if (!efi_enabled(EFI_RUNTIME_SERVICES)) | |
659 | return 0; | |
660 | ||
661 | pdev = platform_device_register_simple("efivars", 0, NULL, 0); | |
50342b2e | 662 | return PTR_ERR_OR_ZERO(pdev); |
28d54022 LCY |
663 | } |
664 | device_initcall(efi_load_efivars); | |
665 | #endif | |
666 | ||
0302f71c MS |
667 | #ifdef CONFIG_EFI_PARAMS_FROM_FDT |
668 | ||
669 | #define UEFI_PARAM(name, prop, field) \ | |
670 | { \ | |
671 | { name }, \ | |
672 | { prop }, \ | |
673 | offsetof(struct efi_fdt_params, field), \ | |
674 | FIELD_SIZEOF(struct efi_fdt_params, field) \ | |
675 | } | |
676 | ||
0cac5c30 | 677 | struct params { |
0302f71c MS |
678 | const char name[32]; |
679 | const char propname[32]; | |
680 | int offset; | |
681 | int size; | |
0cac5c30 SZ |
682 | }; |
683 | ||
684 | static __initdata struct params fdt_params[] = { | |
0302f71c MS |
685 | UEFI_PARAM("System Table", "linux,uefi-system-table", system_table), |
686 | UEFI_PARAM("MemMap Address", "linux,uefi-mmap-start", mmap), | |
687 | UEFI_PARAM("MemMap Size", "linux,uefi-mmap-size", mmap_size), | |
688 | UEFI_PARAM("MemMap Desc. Size", "linux,uefi-mmap-desc-size", desc_size), | |
689 | UEFI_PARAM("MemMap Desc. Version", "linux,uefi-mmap-desc-ver", desc_ver) | |
690 | }; | |
691 | ||
0cac5c30 SZ |
692 | static __initdata struct params xen_fdt_params[] = { |
693 | UEFI_PARAM("System Table", "xen,uefi-system-table", system_table), | |
694 | UEFI_PARAM("MemMap Address", "xen,uefi-mmap-start", mmap), | |
695 | UEFI_PARAM("MemMap Size", "xen,uefi-mmap-size", mmap_size), | |
696 | UEFI_PARAM("MemMap Desc. Size", "xen,uefi-mmap-desc-size", desc_size), | |
697 | UEFI_PARAM("MemMap Desc. Version", "xen,uefi-mmap-desc-ver", desc_ver) | |
698 | }; | |
699 | ||
700 | #define EFI_FDT_PARAMS_SIZE ARRAY_SIZE(fdt_params) | |
701 | ||
702 | static __initdata struct { | |
703 | const char *uname; | |
704 | const char *subnode; | |
705 | struct params *params; | |
706 | } dt_params[] = { | |
707 | { "hypervisor", "uefi", xen_fdt_params }, | |
708 | { "chosen", NULL, fdt_params }, | |
709 | }; | |
710 | ||
0302f71c | 711 | struct param_info { |
29e2435f | 712 | int found; |
0302f71c | 713 | void *params; |
0cac5c30 | 714 | const char *missing; |
0302f71c MS |
715 | }; |
716 | ||
0cac5c30 SZ |
717 | static int __init __find_uefi_params(unsigned long node, |
718 | struct param_info *info, | |
719 | struct params *params) | |
0302f71c | 720 | { |
6fb8cc82 CM |
721 | const void *prop; |
722 | void *dest; | |
0302f71c | 723 | u64 val; |
6fb8cc82 | 724 | int i, len; |
0302f71c | 725 | |
0cac5c30 SZ |
726 | for (i = 0; i < EFI_FDT_PARAMS_SIZE; i++) { |
727 | prop = of_get_flat_dt_prop(node, params[i].propname, &len); | |
728 | if (!prop) { | |
729 | info->missing = params[i].name; | |
0302f71c | 730 | return 0; |
0cac5c30 SZ |
731 | } |
732 | ||
733 | dest = info->params + params[i].offset; | |
29e2435f | 734 | info->found++; |
0302f71c MS |
735 | |
736 | val = of_read_number(prop, len / sizeof(u32)); | |
737 | ||
0cac5c30 | 738 | if (params[i].size == sizeof(u32)) |
0302f71c MS |
739 | *(u32 *)dest = val; |
740 | else | |
741 | *(u64 *)dest = val; | |
742 | ||
7968c0e3 | 743 | if (efi_enabled(EFI_DBG)) |
0cac5c30 SZ |
744 | pr_info(" %s: 0x%0*llx\n", params[i].name, |
745 | params[i].size * 2, val); | |
0302f71c | 746 | } |
0cac5c30 | 747 | |
0302f71c MS |
748 | return 1; |
749 | } | |
750 | ||
0cac5c30 SZ |
751 | static int __init fdt_find_uefi_params(unsigned long node, const char *uname, |
752 | int depth, void *data) | |
753 | { | |
754 | struct param_info *info = data; | |
755 | int i; | |
756 | ||
757 | for (i = 0; i < ARRAY_SIZE(dt_params); i++) { | |
758 | const char *subnode = dt_params[i].subnode; | |
759 | ||
760 | if (depth != 1 || strcmp(uname, dt_params[i].uname) != 0) { | |
761 | info->missing = dt_params[i].params[0].name; | |
762 | continue; | |
763 | } | |
764 | ||
765 | if (subnode) { | |
4af9ed57 AH |
766 | int err = of_get_flat_dt_subnode_by_name(node, subnode); |
767 | ||
768 | if (err < 0) | |
0cac5c30 | 769 | return 0; |
4af9ed57 AH |
770 | |
771 | node = err; | |
0cac5c30 SZ |
772 | } |
773 | ||
774 | return __find_uefi_params(node, info, dt_params[i].params); | |
775 | } | |
776 | ||
777 | return 0; | |
778 | } | |
779 | ||
7968c0e3 | 780 | int __init efi_get_fdt_params(struct efi_fdt_params *params) |
0302f71c MS |
781 | { |
782 | struct param_info info; | |
29e2435f CM |
783 | int ret; |
784 | ||
785 | pr_info("Getting EFI parameters from FDT:\n"); | |
0302f71c | 786 | |
29e2435f | 787 | info.found = 0; |
0302f71c MS |
788 | info.params = params; |
789 | ||
29e2435f CM |
790 | ret = of_scan_flat_dt(fdt_find_uefi_params, &info); |
791 | if (!info.found) | |
792 | pr_info("UEFI not found.\n"); | |
793 | else if (!ret) | |
794 | pr_err("Can't find '%s' in device tree!\n", | |
0cac5c30 | 795 | info.missing); |
29e2435f CM |
796 | |
797 | return ret; | |
0302f71c MS |
798 | } |
799 | #endif /* CONFIG_EFI_PARAMS_FROM_FDT */ | |
98d2a6ca LE |
800 | |
801 | static __initdata char memory_type_name[][20] = { | |
802 | "Reserved", | |
803 | "Loader Code", | |
804 | "Loader Data", | |
805 | "Boot Code", | |
806 | "Boot Data", | |
807 | "Runtime Code", | |
808 | "Runtime Data", | |
809 | "Conventional Memory", | |
810 | "Unusable Memory", | |
811 | "ACPI Reclaim Memory", | |
812 | "ACPI Memory NVS", | |
813 | "Memory Mapped I/O", | |
814 | "MMIO Port Space", | |
35575e0e RE |
815 | "PAL Code", |
816 | "Persistent Memory", | |
98d2a6ca LE |
817 | }; |
818 | ||
819 | char * __init efi_md_typeattr_format(char *buf, size_t size, | |
820 | const efi_memory_desc_t *md) | |
821 | { | |
822 | char *pos; | |
823 | int type_len; | |
824 | u64 attr; | |
825 | ||
826 | pos = buf; | |
827 | if (md->type >= ARRAY_SIZE(memory_type_name)) | |
828 | type_len = snprintf(pos, size, "[type=%u", md->type); | |
829 | else | |
830 | type_len = snprintf(pos, size, "[%-*s", | |
831 | (int)(sizeof(memory_type_name[0]) - 1), | |
832 | memory_type_name[md->type]); | |
833 | if (type_len >= size) | |
834 | return buf; | |
835 | ||
836 | pos += type_len; | |
837 | size -= type_len; | |
838 | ||
839 | attr = md->attribute; | |
840 | if (attr & ~(EFI_MEMORY_UC | EFI_MEMORY_WC | EFI_MEMORY_WT | | |
87db73ae AB |
841 | EFI_MEMORY_WB | EFI_MEMORY_UCE | EFI_MEMORY_RO | |
842 | EFI_MEMORY_WP | EFI_MEMORY_RP | EFI_MEMORY_XP | | |
c016ca08 | 843 | EFI_MEMORY_NV | |
8be4432e | 844 | EFI_MEMORY_RUNTIME | EFI_MEMORY_MORE_RELIABLE)) |
98d2a6ca LE |
845 | snprintf(pos, size, "|attr=0x%016llx]", |
846 | (unsigned long long)attr); | |
847 | else | |
c016ca08 RE |
848 | snprintf(pos, size, |
849 | "|%3s|%2s|%2s|%2s|%2s|%2s|%2s|%3s|%2s|%2s|%2s|%2s]", | |
98d2a6ca | 850 | attr & EFI_MEMORY_RUNTIME ? "RUN" : "", |
8be4432e | 851 | attr & EFI_MEMORY_MORE_RELIABLE ? "MR" : "", |
c016ca08 | 852 | attr & EFI_MEMORY_NV ? "NV" : "", |
98d2a6ca LE |
853 | attr & EFI_MEMORY_XP ? "XP" : "", |
854 | attr & EFI_MEMORY_RP ? "RP" : "", | |
855 | attr & EFI_MEMORY_WP ? "WP" : "", | |
87db73ae | 856 | attr & EFI_MEMORY_RO ? "RO" : "", |
98d2a6ca LE |
857 | attr & EFI_MEMORY_UCE ? "UCE" : "", |
858 | attr & EFI_MEMORY_WB ? "WB" : "", | |
859 | attr & EFI_MEMORY_WT ? "WT" : "", | |
860 | attr & EFI_MEMORY_WC ? "WC" : "", | |
861 | attr & EFI_MEMORY_UC ? "UC" : ""); | |
862 | return buf; | |
863 | } | |
7bf79311 | 864 | |
23f0571c JB |
865 | /* |
866 | * IA64 has a funky EFI memory map that doesn't work the same way as | |
867 | * other architectures. | |
868 | */ | |
869 | #ifndef CONFIG_IA64 | |
7bf79311 JZZ |
870 | /* |
871 | * efi_mem_attributes - lookup memmap attributes for physical address | |
872 | * @phys_addr: the physical address to lookup | |
873 | * | |
874 | * Search in the EFI memory map for the region covering | |
875 | * @phys_addr. Returns the EFI memory attributes if the region | |
876 | * was found in the memory map, 0 otherwise. | |
7bf79311 | 877 | */ |
23f0571c | 878 | u64 efi_mem_attributes(unsigned long phys_addr) |
7bf79311 JZZ |
879 | { |
880 | efi_memory_desc_t *md; | |
7bf79311 JZZ |
881 | |
882 | if (!efi_enabled(EFI_MEMMAP)) | |
883 | return 0; | |
884 | ||
78ce248f | 885 | for_each_efi_memory_desc(md) { |
7bf79311 JZZ |
886 | if ((md->phys_addr <= phys_addr) && |
887 | (phys_addr < (md->phys_addr + | |
888 | (md->num_pages << EFI_PAGE_SHIFT)))) | |
889 | return md->attribute; | |
890 | } | |
891 | return 0; | |
892 | } | |
806b0351 | 893 | |
23f0571c JB |
894 | /* |
895 | * efi_mem_type - lookup memmap type for physical address | |
896 | * @phys_addr: the physical address to lookup | |
897 | * | |
898 | * Search in the EFI memory map for the region covering @phys_addr. | |
899 | * Returns the EFI memory type if the region was found in the memory | |
900 | * map, EFI_RESERVED_TYPE (zero) otherwise. | |
901 | */ | |
902 | int efi_mem_type(unsigned long phys_addr) | |
903 | { | |
904 | const efi_memory_desc_t *md; | |
905 | ||
906 | if (!efi_enabled(EFI_MEMMAP)) | |
907 | return -ENOTSUPP; | |
908 | ||
909 | for_each_efi_memory_desc(md) { | |
910 | if ((md->phys_addr <= phys_addr) && | |
911 | (phys_addr < (md->phys_addr + | |
912 | (md->num_pages << EFI_PAGE_SHIFT)))) | |
913 | return md->type; | |
914 | } | |
915 | return -EINVAL; | |
916 | } | |
917 | #endif | |
918 | ||
806b0351 MF |
919 | int efi_status_to_err(efi_status_t status) |
920 | { | |
921 | int err; | |
922 | ||
923 | switch (status) { | |
924 | case EFI_SUCCESS: | |
925 | err = 0; | |
926 | break; | |
927 | case EFI_INVALID_PARAMETER: | |
928 | err = -EINVAL; | |
929 | break; | |
930 | case EFI_OUT_OF_RESOURCES: | |
931 | err = -ENOSPC; | |
932 | break; | |
933 | case EFI_DEVICE_ERROR: | |
934 | err = -EIO; | |
935 | break; | |
936 | case EFI_WRITE_PROTECTED: | |
937 | err = -EROFS; | |
938 | break; | |
939 | case EFI_SECURITY_VIOLATION: | |
940 | err = -EACCES; | |
941 | break; | |
942 | case EFI_NOT_FOUND: | |
943 | err = -ENOENT; | |
944 | break; | |
dce48e35 AB |
945 | case EFI_ABORTED: |
946 | err = -EINTR; | |
947 | break; | |
806b0351 MF |
948 | default: |
949 | err = -EINVAL; | |
950 | } | |
951 | ||
952 | return err; | |
a19d66c5 TL |
953 | } |
954 | ||
955 | bool efi_is_table_address(unsigned long phys_addr) | |
956 | { | |
957 | unsigned int i; | |
958 | ||
959 | if (phys_addr == EFI_INVALID_TABLE_ADDR) | |
960 | return false; | |
961 | ||
962 | for (i = 0; i < ARRAY_SIZE(efi_tables); i++) | |
963 | if (*(efi_tables[i]) == phys_addr) | |
964 | return true; | |
965 | ||
966 | return false; | |
806b0351 | 967 | } |
63625988 | 968 | |
a23d3bb0 | 969 | static DEFINE_SPINLOCK(efi_mem_reserve_persistent_lock); |
63eb322d | 970 | static struct linux_efi_memreserve *efi_memreserve_root __ro_after_init; |
a23d3bb0 | 971 | |
976b4891 AB |
972 | static int __init efi_memreserve_map_root(void) |
973 | { | |
974 | if (efi.mem_reserve == EFI_INVALID_TABLE_ADDR) | |
975 | return -ENODEV; | |
976 | ||
977 | efi_memreserve_root = memremap(efi.mem_reserve, | |
978 | sizeof(*efi_memreserve_root), | |
979 | MEMREMAP_WB); | |
980 | if (WARN_ON_ONCE(!efi_memreserve_root)) | |
981 | return -ENOMEM; | |
982 | return 0; | |
983 | } | |
984 | ||
985 | int __ref efi_mem_reserve_persistent(phys_addr_t addr, u64 size) | |
a23d3bb0 | 986 | { |
63eb322d | 987 | struct linux_efi_memreserve *rsv; |
976b4891 | 988 | int rc; |
a23d3bb0 | 989 | |
976b4891 | 990 | if (efi_memreserve_root == (void *)ULONG_MAX) |
a23d3bb0 AB |
991 | return -ENODEV; |
992 | ||
976b4891 AB |
993 | if (!efi_memreserve_root) { |
994 | rc = efi_memreserve_map_root(); | |
995 | if (rc) | |
996 | return rc; | |
997 | } | |
998 | ||
63eb322d | 999 | rsv = kmalloc(sizeof(*rsv), GFP_ATOMIC); |
a23d3bb0 AB |
1000 | if (!rsv) |
1001 | return -ENOMEM; | |
1002 | ||
a23d3bb0 AB |
1003 | rsv->base = addr; |
1004 | rsv->size = size; | |
1005 | ||
1006 | spin_lock(&efi_mem_reserve_persistent_lock); | |
63eb322d AB |
1007 | rsv->next = efi_memreserve_root->next; |
1008 | efi_memreserve_root->next = __pa(rsv); | |
a23d3bb0 AB |
1009 | spin_unlock(&efi_mem_reserve_persistent_lock); |
1010 | ||
63eb322d AB |
1011 | return 0; |
1012 | } | |
a23d3bb0 | 1013 | |
63eb322d AB |
1014 | static int __init efi_memreserve_root_init(void) |
1015 | { | |
976b4891 AB |
1016 | if (efi_memreserve_root) |
1017 | return 0; | |
1018 | if (efi_memreserve_map_root()) | |
1019 | efi_memreserve_root = (void *)ULONG_MAX; | |
a23d3bb0 AB |
1020 | return 0; |
1021 | } | |
63eb322d | 1022 | early_initcall(efi_memreserve_root_init); |
a23d3bb0 | 1023 | |
63625988 AB |
1024 | #ifdef CONFIG_KEXEC |
1025 | static int update_efi_random_seed(struct notifier_block *nb, | |
1026 | unsigned long code, void *unused) | |
1027 | { | |
1028 | struct linux_efi_random_seed *seed; | |
1029 | u32 size = 0; | |
1030 | ||
1031 | if (!kexec_in_progress) | |
1032 | return NOTIFY_DONE; | |
1033 | ||
1034 | seed = memremap(efi.rng_seed, sizeof(*seed), MEMREMAP_WB); | |
1035 | if (seed != NULL) { | |
c2ceb5fd | 1036 | size = min(seed->size, EFI_RANDOM_SEED_SIZE); |
63625988 AB |
1037 | memunmap(seed); |
1038 | } else { | |
1039 | pr_err("Could not map UEFI random seed!\n"); | |
1040 | } | |
1041 | if (size > 0) { | |
1042 | seed = memremap(efi.rng_seed, sizeof(*seed) + size, | |
1043 | MEMREMAP_WB); | |
1044 | if (seed != NULL) { | |
1045 | seed->size = size; | |
1046 | get_random_bytes(seed->bits, seed->size); | |
1047 | memunmap(seed); | |
1048 | } else { | |
1049 | pr_err("Could not map UEFI random seed!\n"); | |
1050 | } | |
1051 | } | |
1052 | return NOTIFY_DONE; | |
1053 | } | |
1054 | ||
1055 | static struct notifier_block efi_random_seed_nb = { | |
1056 | .notifier_call = update_efi_random_seed, | |
1057 | }; | |
1058 | ||
1059 | static int register_update_efi_random_seed(void) | |
1060 | { | |
1061 | if (efi.rng_seed == EFI_INVALID_TABLE_ADDR) | |
1062 | return 0; | |
1063 | return register_reboot_notifier(&efi_random_seed_nb); | |
1064 | } | |
1065 | late_initcall(register_update_efi_random_seed); | |
1066 | #endif |