Commit | Line | Data |
---|---|---|
a9499fa7 TG |
1 | /* |
2 | * efi.c - EFI subsystem | |
3 | * | |
4 | * Copyright (C) 2001,2003,2004 Dell <Matt_Domsch@dell.com> | |
5 | * Copyright (C) 2004 Intel Corporation <matthew.e.tolentino@intel.com> | |
6 | * Copyright (C) 2013 Tom Gundersen <teg@jklm.no> | |
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> |
28d54022 | 26 | #include <linux/platform_device.h> |
475fb4e8 OP |
27 | #include <linux/slab.h> |
28 | #include <linux/acpi.h> | |
29 | #include <linux/ucs2_string.h> | |
272686bf | 30 | |
0f7f2f0c | 31 | #include <asm/early_ioremap.h> |
f7d92489 | 32 | |
272686bf | 33 | struct efi __read_mostly efi = { |
bf924863 AB |
34 | .mps = EFI_INVALID_TABLE_ADDR, |
35 | .acpi = EFI_INVALID_TABLE_ADDR, | |
36 | .acpi20 = EFI_INVALID_TABLE_ADDR, | |
37 | .smbios = EFI_INVALID_TABLE_ADDR, | |
38 | .smbios3 = EFI_INVALID_TABLE_ADDR, | |
39 | .sal_systab = EFI_INVALID_TABLE_ADDR, | |
40 | .boot_info = EFI_INVALID_TABLE_ADDR, | |
41 | .hcdp = EFI_INVALID_TABLE_ADDR, | |
42 | .uga = EFI_INVALID_TABLE_ADDR, | |
43 | .uv_systab = EFI_INVALID_TABLE_ADDR, | |
44 | .fw_vendor = EFI_INVALID_TABLE_ADDR, | |
45 | .runtime = EFI_INVALID_TABLE_ADDR, | |
46 | .config_table = EFI_INVALID_TABLE_ADDR, | |
47 | .esrt = EFI_INVALID_TABLE_ADDR, | |
48 | .properties_table = EFI_INVALID_TABLE_ADDR, | |
a604af07 | 49 | .mem_attr_table = EFI_INVALID_TABLE_ADDR, |
272686bf LL |
50 | }; |
51 | EXPORT_SYMBOL(efi); | |
a9499fa7 | 52 | |
b2e0a54a DY |
53 | static bool disable_runtime; |
54 | static int __init setup_noefi(char *arg) | |
55 | { | |
56 | disable_runtime = true; | |
57 | return 0; | |
58 | } | |
59 | early_param("noefi", setup_noefi); | |
60 | ||
61 | bool efi_runtime_disabled(void) | |
62 | { | |
63 | return disable_runtime; | |
64 | } | |
65 | ||
5ae3683c DY |
66 | static int __init parse_efi_cmdline(char *str) |
67 | { | |
9115c758 RN |
68 | if (!str) { |
69 | pr_warn("need at least one option\n"); | |
70 | return -EINVAL; | |
71 | } | |
72 | ||
12dd00e8 LL |
73 | if (parse_option_str(str, "debug")) |
74 | set_bit(EFI_DBG, &efi.flags); | |
75 | ||
5ae3683c DY |
76 | if (parse_option_str(str, "noruntime")) |
77 | disable_runtime = true; | |
78 | ||
79 | return 0; | |
80 | } | |
81 | early_param("efi", parse_efi_cmdline); | |
82 | ||
0bb54905 | 83 | struct kobject *efi_kobj; |
a9499fa7 TG |
84 | |
85 | /* | |
86 | * Let's not leave out systab information that snuck into | |
87 | * the efivars driver | |
88 | */ | |
89 | static ssize_t systab_show(struct kobject *kobj, | |
90 | struct kobj_attribute *attr, char *buf) | |
91 | { | |
92 | char *str = buf; | |
93 | ||
94 | if (!kobj || !buf) | |
95 | return -EINVAL; | |
96 | ||
97 | if (efi.mps != EFI_INVALID_TABLE_ADDR) | |
98 | str += sprintf(str, "MPS=0x%lx\n", efi.mps); | |
99 | if (efi.acpi20 != EFI_INVALID_TABLE_ADDR) | |
100 | str += sprintf(str, "ACPI20=0x%lx\n", efi.acpi20); | |
101 | if (efi.acpi != EFI_INVALID_TABLE_ADDR) | |
102 | str += sprintf(str, "ACPI=0x%lx\n", efi.acpi); | |
b119fe08 JD |
103 | /* |
104 | * If both SMBIOS and SMBIOS3 entry points are implemented, the | |
105 | * SMBIOS3 entry point shall be preferred, so we list it first to | |
106 | * let applications stop parsing after the first match. | |
107 | */ | |
e1ccbbc9 AB |
108 | if (efi.smbios3 != EFI_INVALID_TABLE_ADDR) |
109 | str += sprintf(str, "SMBIOS3=0x%lx\n", efi.smbios3); | |
b119fe08 JD |
110 | if (efi.smbios != EFI_INVALID_TABLE_ADDR) |
111 | str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios); | |
a9499fa7 TG |
112 | if (efi.hcdp != EFI_INVALID_TABLE_ADDR) |
113 | str += sprintf(str, "HCDP=0x%lx\n", efi.hcdp); | |
114 | if (efi.boot_info != EFI_INVALID_TABLE_ADDR) | |
115 | str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info); | |
116 | if (efi.uga != EFI_INVALID_TABLE_ADDR) | |
117 | str += sprintf(str, "UGA=0x%lx\n", efi.uga); | |
118 | ||
119 | return str - buf; | |
120 | } | |
121 | ||
122 | static struct kobj_attribute efi_attr_systab = | |
123 | __ATTR(systab, 0400, systab_show, NULL); | |
124 | ||
a0998eb1 DY |
125 | #define EFI_FIELD(var) efi.var |
126 | ||
127 | #define EFI_ATTR_SHOW(name) \ | |
128 | static ssize_t name##_show(struct kobject *kobj, \ | |
129 | struct kobj_attribute *attr, char *buf) \ | |
130 | { \ | |
131 | return sprintf(buf, "0x%lx\n", EFI_FIELD(name)); \ | |
132 | } | |
133 | ||
134 | EFI_ATTR_SHOW(fw_vendor); | |
135 | EFI_ATTR_SHOW(runtime); | |
136 | EFI_ATTR_SHOW(config_table); | |
137 | ||
2859dff9 SM |
138 | static ssize_t fw_platform_size_show(struct kobject *kobj, |
139 | struct kobj_attribute *attr, char *buf) | |
140 | { | |
141 | return sprintf(buf, "%d\n", efi_enabled(EFI_64BIT) ? 64 : 32); | |
142 | } | |
143 | ||
a0998eb1 DY |
144 | static struct kobj_attribute efi_attr_fw_vendor = __ATTR_RO(fw_vendor); |
145 | static struct kobj_attribute efi_attr_runtime = __ATTR_RO(runtime); | |
146 | static struct kobj_attribute efi_attr_config_table = __ATTR_RO(config_table); | |
2859dff9 SM |
147 | static struct kobj_attribute efi_attr_fw_platform_size = |
148 | __ATTR_RO(fw_platform_size); | |
a0998eb1 | 149 | |
a9499fa7 TG |
150 | static struct attribute *efi_subsys_attrs[] = { |
151 | &efi_attr_systab.attr, | |
a0998eb1 DY |
152 | &efi_attr_fw_vendor.attr, |
153 | &efi_attr_runtime.attr, | |
154 | &efi_attr_config_table.attr, | |
2859dff9 | 155 | &efi_attr_fw_platform_size.attr, |
a0998eb1 | 156 | NULL, |
a9499fa7 TG |
157 | }; |
158 | ||
a0998eb1 DY |
159 | static umode_t efi_attr_is_visible(struct kobject *kobj, |
160 | struct attribute *attr, int n) | |
161 | { | |
9f27bc54 DK |
162 | if (attr == &efi_attr_fw_vendor.attr) { |
163 | if (efi_enabled(EFI_PARAVIRT) || | |
164 | efi.fw_vendor == EFI_INVALID_TABLE_ADDR) | |
165 | return 0; | |
166 | } else if (attr == &efi_attr_runtime.attr) { | |
167 | if (efi.runtime == EFI_INVALID_TABLE_ADDR) | |
168 | return 0; | |
169 | } else if (attr == &efi_attr_config_table.attr) { | |
170 | if (efi.config_table == EFI_INVALID_TABLE_ADDR) | |
171 | return 0; | |
172 | } | |
a0998eb1 | 173 | |
9f27bc54 | 174 | return attr->mode; |
a0998eb1 DY |
175 | } |
176 | ||
a9499fa7 TG |
177 | static struct attribute_group efi_subsys_attr_group = { |
178 | .attrs = efi_subsys_attrs, | |
a0998eb1 | 179 | .is_visible = efi_attr_is_visible, |
a9499fa7 TG |
180 | }; |
181 | ||
182 | static struct efivars generic_efivars; | |
183 | static struct efivar_operations generic_ops; | |
184 | ||
185 | static int generic_ops_register(void) | |
186 | { | |
187 | generic_ops.get_variable = efi.get_variable; | |
188 | generic_ops.set_variable = efi.set_variable; | |
9c6672ac | 189 | generic_ops.set_variable_nonblocking = efi.set_variable_nonblocking; |
a9499fa7 | 190 | generic_ops.get_next_variable = efi.get_next_variable; |
a614e192 | 191 | generic_ops.query_variable_store = efi_query_variable_store; |
a9499fa7 TG |
192 | |
193 | return efivars_register(&generic_efivars, &generic_ops, efi_kobj); | |
194 | } | |
195 | ||
196 | static void generic_ops_unregister(void) | |
197 | { | |
198 | efivars_unregister(&generic_efivars); | |
199 | } | |
200 | ||
475fb4e8 OP |
201 | #if IS_ENABLED(CONFIG_ACPI) |
202 | #define EFIVAR_SSDT_NAME_MAX 16 | |
203 | static char efivar_ssdt[EFIVAR_SSDT_NAME_MAX] __initdata; | |
204 | static int __init efivar_ssdt_setup(char *str) | |
205 | { | |
206 | if (strlen(str) < sizeof(efivar_ssdt)) | |
207 | memcpy(efivar_ssdt, str, strlen(str)); | |
208 | else | |
209 | pr_warn("efivar_ssdt: name too long: %s\n", str); | |
210 | return 0; | |
211 | } | |
212 | __setup("efivar_ssdt=", efivar_ssdt_setup); | |
213 | ||
214 | static __init int efivar_ssdt_iter(efi_char16_t *name, efi_guid_t vendor, | |
215 | unsigned long name_size, void *data) | |
216 | { | |
217 | struct efivar_entry *entry; | |
218 | struct list_head *list = data; | |
219 | char utf8_name[EFIVAR_SSDT_NAME_MAX]; | |
220 | int limit = min_t(unsigned long, EFIVAR_SSDT_NAME_MAX, name_size); | |
221 | ||
222 | ucs2_as_utf8(utf8_name, name, limit - 1); | |
223 | if (strncmp(utf8_name, efivar_ssdt, limit) != 0) | |
224 | return 0; | |
225 | ||
226 | entry = kmalloc(sizeof(*entry), GFP_KERNEL); | |
227 | if (!entry) | |
228 | return 0; | |
229 | ||
230 | memcpy(entry->var.VariableName, name, name_size); | |
231 | memcpy(&entry->var.VendorGuid, &vendor, sizeof(efi_guid_t)); | |
232 | ||
233 | efivar_entry_add(entry, list); | |
234 | ||
235 | return 0; | |
236 | } | |
237 | ||
238 | static __init int efivar_ssdt_load(void) | |
239 | { | |
240 | LIST_HEAD(entries); | |
241 | struct efivar_entry *entry, *aux; | |
242 | unsigned long size; | |
243 | void *data; | |
244 | int ret; | |
245 | ||
246 | ret = efivar_init(efivar_ssdt_iter, &entries, true, &entries); | |
247 | ||
248 | list_for_each_entry_safe(entry, aux, &entries, list) { | |
249 | pr_info("loading SSDT from variable %s-%pUl\n", efivar_ssdt, | |
250 | &entry->var.VendorGuid); | |
251 | ||
252 | list_del(&entry->list); | |
253 | ||
254 | ret = efivar_entry_size(entry, &size); | |
255 | if (ret) { | |
256 | pr_err("failed to get var size\n"); | |
257 | goto free_entry; | |
258 | } | |
259 | ||
260 | data = kmalloc(size, GFP_KERNEL); | |
261 | if (!data) | |
262 | goto free_entry; | |
263 | ||
264 | ret = efivar_entry_get(entry, NULL, &size, data); | |
265 | if (ret) { | |
266 | pr_err("failed to get var data\n"); | |
267 | goto free_data; | |
268 | } | |
269 | ||
270 | ret = acpi_load_table(data); | |
271 | if (ret) { | |
272 | pr_err("failed to load table: %d\n", ret); | |
273 | goto free_data; | |
274 | } | |
275 | ||
276 | goto free_entry; | |
277 | ||
278 | free_data: | |
279 | kfree(data); | |
280 | ||
281 | free_entry: | |
282 | kfree(entry); | |
283 | } | |
284 | ||
285 | return ret; | |
286 | } | |
287 | #else | |
288 | static inline int efivar_ssdt_load(void) { return 0; } | |
289 | #endif | |
290 | ||
a9499fa7 TG |
291 | /* |
292 | * We register the efi subsystem with the firmware subsystem and the | |
293 | * efivars subsystem with the efi subsystem, if the system was booted with | |
294 | * EFI. | |
295 | */ | |
296 | static int __init efisubsys_init(void) | |
297 | { | |
298 | int error; | |
299 | ||
300 | if (!efi_enabled(EFI_BOOT)) | |
301 | return 0; | |
302 | ||
303 | /* We register the efi directory at /sys/firmware/efi */ | |
304 | efi_kobj = kobject_create_and_add("efi", firmware_kobj); | |
305 | if (!efi_kobj) { | |
306 | pr_err("efi: Firmware registration failed.\n"); | |
307 | return -ENOMEM; | |
308 | } | |
309 | ||
310 | error = generic_ops_register(); | |
311 | if (error) | |
312 | goto err_put; | |
313 | ||
475fb4e8 OP |
314 | if (efi_enabled(EFI_RUNTIME_SERVICES)) |
315 | efivar_ssdt_load(); | |
316 | ||
a9499fa7 TG |
317 | error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group); |
318 | if (error) { | |
319 | pr_err("efi: Sysfs attribute export failed with error %d.\n", | |
320 | error); | |
321 | goto err_unregister; | |
322 | } | |
323 | ||
926172d4 DY |
324 | error = efi_runtime_map_init(efi_kobj); |
325 | if (error) | |
326 | goto err_remove_group; | |
327 | ||
a9499fa7 | 328 | /* and the standard mountpoint for efivarfs */ |
f9bb4882 EB |
329 | error = sysfs_create_mount_point(efi_kobj, "efivars"); |
330 | if (error) { | |
a9499fa7 | 331 | pr_err("efivars: Subsystem registration failed.\n"); |
a9499fa7 TG |
332 | goto err_remove_group; |
333 | } | |
334 | ||
335 | return 0; | |
336 | ||
337 | err_remove_group: | |
338 | sysfs_remove_group(efi_kobj, &efi_subsys_attr_group); | |
339 | err_unregister: | |
340 | generic_ops_unregister(); | |
341 | err_put: | |
342 | kobject_put(efi_kobj); | |
343 | return error; | |
344 | } | |
345 | ||
346 | subsys_initcall(efisubsys_init); | |
272686bf | 347 | |
0bb54905 PJ |
348 | /* |
349 | * Find the efi memory descriptor for a given physical address. Given a | |
350 | * physicall address, determine if it exists within an EFI Memory Map entry, | |
351 | * and if so, populate the supplied memory descriptor with the appropriate | |
352 | * data. | |
353 | */ | |
354 | int __init efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md) | |
355 | { | |
884f4f66 | 356 | struct efi_memory_map *map = &efi.memmap; |
44511fb9 | 357 | phys_addr_t p, e; |
0bb54905 PJ |
358 | |
359 | if (!efi_enabled(EFI_MEMMAP)) { | |
360 | pr_err_once("EFI_MEMMAP is not enabled.\n"); | |
361 | return -EINVAL; | |
362 | } | |
363 | ||
364 | if (!map) { | |
365 | pr_err_once("efi.memmap is not set.\n"); | |
366 | return -EINVAL; | |
367 | } | |
368 | if (!out_md) { | |
369 | pr_err_once("out_md is null.\n"); | |
370 | return -EINVAL; | |
371 | } | |
372 | if (WARN_ON_ONCE(!map->phys_map)) | |
373 | return -EINVAL; | |
374 | if (WARN_ON_ONCE(map->nr_map == 0) || WARN_ON_ONCE(map->desc_size == 0)) | |
375 | return -EINVAL; | |
376 | ||
377 | e = map->phys_map + map->nr_map * map->desc_size; | |
378 | for (p = map->phys_map; p < e; p += map->desc_size) { | |
379 | efi_memory_desc_t *md; | |
380 | u64 size; | |
381 | u64 end; | |
382 | ||
383 | /* | |
384 | * If a driver calls this after efi_free_boot_services, | |
385 | * ->map will be NULL, and the target may also not be mapped. | |
386 | * So just always get our own virtual map on the CPU. | |
387 | * | |
388 | */ | |
44511fb9 | 389 | md = early_memremap(p, sizeof (*md)); |
0bb54905 | 390 | if (!md) { |
44511fb9 AB |
391 | pr_err_once("early_memremap(%pa, %zu) failed.\n", |
392 | &p, sizeof (*md)); | |
0bb54905 PJ |
393 | return -ENOMEM; |
394 | } | |
395 | ||
396 | if (!(md->attribute & EFI_MEMORY_RUNTIME) && | |
397 | md->type != EFI_BOOT_SERVICES_DATA && | |
398 | md->type != EFI_RUNTIME_SERVICES_DATA) { | |
399 | early_memunmap(md, sizeof (*md)); | |
400 | continue; | |
401 | } | |
402 | ||
403 | size = md->num_pages << EFI_PAGE_SHIFT; | |
404 | end = md->phys_addr + size; | |
405 | if (phys_addr >= md->phys_addr && phys_addr < end) { | |
406 | memcpy(out_md, md, sizeof(*out_md)); | |
407 | early_memunmap(md, sizeof (*md)); | |
408 | return 0; | |
409 | } | |
410 | ||
411 | early_memunmap(md, sizeof (*md)); | |
412 | } | |
413 | pr_err_once("requested map not found.\n"); | |
414 | return -ENOENT; | |
415 | } | |
416 | ||
417 | /* | |
418 | * Calculate the highest address of an efi memory descriptor. | |
419 | */ | |
420 | u64 __init efi_mem_desc_end(efi_memory_desc_t *md) | |
421 | { | |
422 | u64 size = md->num_pages << EFI_PAGE_SHIFT; | |
423 | u64 end = md->phys_addr + size; | |
424 | return end; | |
425 | } | |
272686bf LL |
426 | |
427 | static __initdata efi_config_table_type_t common_tables[] = { | |
428 | {ACPI_20_TABLE_GUID, "ACPI 2.0", &efi.acpi20}, | |
429 | {ACPI_TABLE_GUID, "ACPI", &efi.acpi}, | |
430 | {HCDP_TABLE_GUID, "HCDP", &efi.hcdp}, | |
431 | {MPS_TABLE_GUID, "MPS", &efi.mps}, | |
432 | {SAL_SYSTEM_TABLE_GUID, "SALsystab", &efi.sal_systab}, | |
433 | {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios}, | |
e1ccbbc9 | 434 | {SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3}, |
272686bf | 435 | {UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga}, |
0bb54905 | 436 | {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt}, |
bf924863 | 437 | {EFI_PROPERTIES_TABLE_GUID, "PROP", &efi.properties_table}, |
a604af07 | 438 | {EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi.mem_attr_table}, |
69e60841 | 439 | {NULL_GUID, NULL, NULL}, |
272686bf LL |
440 | }; |
441 | ||
442 | static __init int match_config_table(efi_guid_t *guid, | |
443 | unsigned long table, | |
444 | efi_config_table_type_t *table_types) | |
445 | { | |
272686bf LL |
446 | int i; |
447 | ||
448 | if (table_types) { | |
272686bf | 449 | for (i = 0; efi_guidcmp(table_types[i].guid, NULL_GUID); i++) { |
272686bf LL |
450 | if (!efi_guidcmp(*guid, table_types[i].guid)) { |
451 | *(table_types[i].ptr) = table; | |
801820be AB |
452 | if (table_types[i].name) |
453 | pr_cont(" %s=0x%lx ", | |
454 | table_types[i].name, table); | |
272686bf LL |
455 | return 1; |
456 | } | |
457 | } | |
458 | } | |
459 | ||
460 | return 0; | |
461 | } | |
462 | ||
7bb68410 AB |
463 | int __init efi_config_parse_tables(void *config_tables, int count, int sz, |
464 | efi_config_table_type_t *arch_tables) | |
272686bf | 465 | { |
7bb68410 AB |
466 | void *tablep; |
467 | int i; | |
272686bf LL |
468 | |
469 | tablep = config_tables; | |
470 | pr_info(""); | |
7bb68410 | 471 | for (i = 0; i < count; i++) { |
272686bf LL |
472 | efi_guid_t guid; |
473 | unsigned long table; | |
474 | ||
475 | if (efi_enabled(EFI_64BIT)) { | |
476 | u64 table64; | |
477 | guid = ((efi_config_table_64_t *)tablep)->guid; | |
478 | table64 = ((efi_config_table_64_t *)tablep)->table; | |
479 | table = table64; | |
480 | #ifndef CONFIG_64BIT | |
481 | if (table64 >> 32) { | |
482 | pr_cont("\n"); | |
483 | pr_err("Table located above 4GB, disabling EFI.\n"); | |
272686bf LL |
484 | return -EINVAL; |
485 | } | |
486 | #endif | |
487 | } else { | |
488 | guid = ((efi_config_table_32_t *)tablep)->guid; | |
489 | table = ((efi_config_table_32_t *)tablep)->table; | |
490 | } | |
491 | ||
492 | if (!match_config_table(&guid, table, common_tables)) | |
493 | match_config_table(&guid, table, arch_tables); | |
494 | ||
495 | tablep += sz; | |
496 | } | |
497 | pr_cont("\n"); | |
0f8093a9 | 498 | set_bit(EFI_CONFIG_TABLES, &efi.flags); |
a1041713 AB |
499 | |
500 | /* Parse the EFI Properties table if it exists */ | |
501 | if (efi.properties_table != EFI_INVALID_TABLE_ADDR) { | |
502 | efi_properties_table_t *tbl; | |
503 | ||
504 | tbl = early_memremap(efi.properties_table, sizeof(*tbl)); | |
505 | if (tbl == NULL) { | |
506 | pr_err("Could not map Properties table!\n"); | |
507 | return -ENOMEM; | |
508 | } | |
509 | ||
510 | if (tbl->memory_protection_attribute & | |
511 | EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA) | |
512 | set_bit(EFI_NX_PE_DATA, &efi.flags); | |
513 | ||
514 | early_memunmap(tbl, sizeof(*tbl)); | |
515 | } | |
516 | ||
272686bf LL |
517 | return 0; |
518 | } | |
0302f71c | 519 | |
7bb68410 AB |
520 | int __init efi_config_init(efi_config_table_type_t *arch_tables) |
521 | { | |
522 | void *config_tables; | |
523 | int sz, ret; | |
524 | ||
525 | if (efi_enabled(EFI_64BIT)) | |
526 | sz = sizeof(efi_config_table_64_t); | |
527 | else | |
528 | sz = sizeof(efi_config_table_32_t); | |
529 | ||
530 | /* | |
531 | * Let's see what config tables the firmware passed to us. | |
532 | */ | |
533 | config_tables = early_memremap(efi.systab->tables, | |
534 | efi.systab->nr_tables * sz); | |
535 | if (config_tables == NULL) { | |
536 | pr_err("Could not map Configuration table!\n"); | |
537 | return -ENOMEM; | |
538 | } | |
539 | ||
540 | ret = efi_config_parse_tables(config_tables, efi.systab->nr_tables, sz, | |
541 | arch_tables); | |
542 | ||
543 | early_memunmap(config_tables, efi.systab->nr_tables * sz); | |
544 | return ret; | |
545 | } | |
546 | ||
28d54022 LCY |
547 | #ifdef CONFIG_EFI_VARS_MODULE |
548 | static int __init efi_load_efivars(void) | |
549 | { | |
550 | struct platform_device *pdev; | |
551 | ||
552 | if (!efi_enabled(EFI_RUNTIME_SERVICES)) | |
553 | return 0; | |
554 | ||
555 | pdev = platform_device_register_simple("efivars", 0, NULL, 0); | |
556 | return IS_ERR(pdev) ? PTR_ERR(pdev) : 0; | |
557 | } | |
558 | device_initcall(efi_load_efivars); | |
559 | #endif | |
560 | ||
0302f71c MS |
561 | #ifdef CONFIG_EFI_PARAMS_FROM_FDT |
562 | ||
563 | #define UEFI_PARAM(name, prop, field) \ | |
564 | { \ | |
565 | { name }, \ | |
566 | { prop }, \ | |
567 | offsetof(struct efi_fdt_params, field), \ | |
568 | FIELD_SIZEOF(struct efi_fdt_params, field) \ | |
569 | } | |
570 | ||
0cac5c30 | 571 | struct params { |
0302f71c MS |
572 | const char name[32]; |
573 | const char propname[32]; | |
574 | int offset; | |
575 | int size; | |
0cac5c30 SZ |
576 | }; |
577 | ||
578 | static __initdata struct params fdt_params[] = { | |
0302f71c MS |
579 | UEFI_PARAM("System Table", "linux,uefi-system-table", system_table), |
580 | UEFI_PARAM("MemMap Address", "linux,uefi-mmap-start", mmap), | |
581 | UEFI_PARAM("MemMap Size", "linux,uefi-mmap-size", mmap_size), | |
582 | UEFI_PARAM("MemMap Desc. Size", "linux,uefi-mmap-desc-size", desc_size), | |
583 | UEFI_PARAM("MemMap Desc. Version", "linux,uefi-mmap-desc-ver", desc_ver) | |
584 | }; | |
585 | ||
0cac5c30 SZ |
586 | static __initdata struct params xen_fdt_params[] = { |
587 | UEFI_PARAM("System Table", "xen,uefi-system-table", system_table), | |
588 | UEFI_PARAM("MemMap Address", "xen,uefi-mmap-start", mmap), | |
589 | UEFI_PARAM("MemMap Size", "xen,uefi-mmap-size", mmap_size), | |
590 | UEFI_PARAM("MemMap Desc. Size", "xen,uefi-mmap-desc-size", desc_size), | |
591 | UEFI_PARAM("MemMap Desc. Version", "xen,uefi-mmap-desc-ver", desc_ver) | |
592 | }; | |
593 | ||
594 | #define EFI_FDT_PARAMS_SIZE ARRAY_SIZE(fdt_params) | |
595 | ||
596 | static __initdata struct { | |
597 | const char *uname; | |
598 | const char *subnode; | |
599 | struct params *params; | |
600 | } dt_params[] = { | |
601 | { "hypervisor", "uefi", xen_fdt_params }, | |
602 | { "chosen", NULL, fdt_params }, | |
603 | }; | |
604 | ||
0302f71c | 605 | struct param_info { |
29e2435f | 606 | int found; |
0302f71c | 607 | void *params; |
0cac5c30 | 608 | const char *missing; |
0302f71c MS |
609 | }; |
610 | ||
0cac5c30 SZ |
611 | static int __init __find_uefi_params(unsigned long node, |
612 | struct param_info *info, | |
613 | struct params *params) | |
0302f71c | 614 | { |
6fb8cc82 CM |
615 | const void *prop; |
616 | void *dest; | |
0302f71c | 617 | u64 val; |
6fb8cc82 | 618 | int i, len; |
0302f71c | 619 | |
0cac5c30 SZ |
620 | for (i = 0; i < EFI_FDT_PARAMS_SIZE; i++) { |
621 | prop = of_get_flat_dt_prop(node, params[i].propname, &len); | |
622 | if (!prop) { | |
623 | info->missing = params[i].name; | |
0302f71c | 624 | return 0; |
0cac5c30 SZ |
625 | } |
626 | ||
627 | dest = info->params + params[i].offset; | |
29e2435f | 628 | info->found++; |
0302f71c MS |
629 | |
630 | val = of_read_number(prop, len / sizeof(u32)); | |
631 | ||
0cac5c30 | 632 | if (params[i].size == sizeof(u32)) |
0302f71c MS |
633 | *(u32 *)dest = val; |
634 | else | |
635 | *(u64 *)dest = val; | |
636 | ||
7968c0e3 | 637 | if (efi_enabled(EFI_DBG)) |
0cac5c30 SZ |
638 | pr_info(" %s: 0x%0*llx\n", params[i].name, |
639 | params[i].size * 2, val); | |
0302f71c | 640 | } |
0cac5c30 | 641 | |
0302f71c MS |
642 | return 1; |
643 | } | |
644 | ||
0cac5c30 SZ |
645 | static int __init fdt_find_uefi_params(unsigned long node, const char *uname, |
646 | int depth, void *data) | |
647 | { | |
648 | struct param_info *info = data; | |
649 | int i; | |
650 | ||
651 | for (i = 0; i < ARRAY_SIZE(dt_params); i++) { | |
652 | const char *subnode = dt_params[i].subnode; | |
653 | ||
654 | if (depth != 1 || strcmp(uname, dt_params[i].uname) != 0) { | |
655 | info->missing = dt_params[i].params[0].name; | |
656 | continue; | |
657 | } | |
658 | ||
659 | if (subnode) { | |
660 | node = of_get_flat_dt_subnode_by_name(node, subnode); | |
661 | if (node < 0) | |
662 | return 0; | |
663 | } | |
664 | ||
665 | return __find_uefi_params(node, info, dt_params[i].params); | |
666 | } | |
667 | ||
668 | return 0; | |
669 | } | |
670 | ||
7968c0e3 | 671 | int __init efi_get_fdt_params(struct efi_fdt_params *params) |
0302f71c MS |
672 | { |
673 | struct param_info info; | |
29e2435f CM |
674 | int ret; |
675 | ||
676 | pr_info("Getting EFI parameters from FDT:\n"); | |
0302f71c | 677 | |
29e2435f | 678 | info.found = 0; |
0302f71c MS |
679 | info.params = params; |
680 | ||
29e2435f CM |
681 | ret = of_scan_flat_dt(fdt_find_uefi_params, &info); |
682 | if (!info.found) | |
683 | pr_info("UEFI not found.\n"); | |
684 | else if (!ret) | |
685 | pr_err("Can't find '%s' in device tree!\n", | |
0cac5c30 | 686 | info.missing); |
29e2435f CM |
687 | |
688 | return ret; | |
0302f71c MS |
689 | } |
690 | #endif /* CONFIG_EFI_PARAMS_FROM_FDT */ | |
98d2a6ca LE |
691 | |
692 | static __initdata char memory_type_name[][20] = { | |
693 | "Reserved", | |
694 | "Loader Code", | |
695 | "Loader Data", | |
696 | "Boot Code", | |
697 | "Boot Data", | |
698 | "Runtime Code", | |
699 | "Runtime Data", | |
700 | "Conventional Memory", | |
701 | "Unusable Memory", | |
702 | "ACPI Reclaim Memory", | |
703 | "ACPI Memory NVS", | |
704 | "Memory Mapped I/O", | |
705 | "MMIO Port Space", | |
35575e0e RE |
706 | "PAL Code", |
707 | "Persistent Memory", | |
98d2a6ca LE |
708 | }; |
709 | ||
710 | char * __init efi_md_typeattr_format(char *buf, size_t size, | |
711 | const efi_memory_desc_t *md) | |
712 | { | |
713 | char *pos; | |
714 | int type_len; | |
715 | u64 attr; | |
716 | ||
717 | pos = buf; | |
718 | if (md->type >= ARRAY_SIZE(memory_type_name)) | |
719 | type_len = snprintf(pos, size, "[type=%u", md->type); | |
720 | else | |
721 | type_len = snprintf(pos, size, "[%-*s", | |
722 | (int)(sizeof(memory_type_name[0]) - 1), | |
723 | memory_type_name[md->type]); | |
724 | if (type_len >= size) | |
725 | return buf; | |
726 | ||
727 | pos += type_len; | |
728 | size -= type_len; | |
729 | ||
730 | attr = md->attribute; | |
731 | if (attr & ~(EFI_MEMORY_UC | EFI_MEMORY_WC | EFI_MEMORY_WT | | |
87db73ae AB |
732 | EFI_MEMORY_WB | EFI_MEMORY_UCE | EFI_MEMORY_RO | |
733 | EFI_MEMORY_WP | EFI_MEMORY_RP | EFI_MEMORY_XP | | |
c016ca08 | 734 | EFI_MEMORY_NV | |
8be4432e | 735 | EFI_MEMORY_RUNTIME | EFI_MEMORY_MORE_RELIABLE)) |
98d2a6ca LE |
736 | snprintf(pos, size, "|attr=0x%016llx]", |
737 | (unsigned long long)attr); | |
738 | else | |
c016ca08 RE |
739 | snprintf(pos, size, |
740 | "|%3s|%2s|%2s|%2s|%2s|%2s|%2s|%3s|%2s|%2s|%2s|%2s]", | |
98d2a6ca | 741 | attr & EFI_MEMORY_RUNTIME ? "RUN" : "", |
8be4432e | 742 | attr & EFI_MEMORY_MORE_RELIABLE ? "MR" : "", |
c016ca08 | 743 | attr & EFI_MEMORY_NV ? "NV" : "", |
98d2a6ca LE |
744 | attr & EFI_MEMORY_XP ? "XP" : "", |
745 | attr & EFI_MEMORY_RP ? "RP" : "", | |
746 | attr & EFI_MEMORY_WP ? "WP" : "", | |
87db73ae | 747 | attr & EFI_MEMORY_RO ? "RO" : "", |
98d2a6ca LE |
748 | attr & EFI_MEMORY_UCE ? "UCE" : "", |
749 | attr & EFI_MEMORY_WB ? "WB" : "", | |
750 | attr & EFI_MEMORY_WT ? "WT" : "", | |
751 | attr & EFI_MEMORY_WC ? "WC" : "", | |
752 | attr & EFI_MEMORY_UC ? "UC" : ""); | |
753 | return buf; | |
754 | } | |
7bf79311 JZZ |
755 | |
756 | /* | |
757 | * efi_mem_attributes - lookup memmap attributes for physical address | |
758 | * @phys_addr: the physical address to lookup | |
759 | * | |
760 | * Search in the EFI memory map for the region covering | |
761 | * @phys_addr. Returns the EFI memory attributes if the region | |
762 | * was found in the memory map, 0 otherwise. | |
763 | * | |
764 | * Despite being marked __weak, most architectures should *not* | |
765 | * override this function. It is __weak solely for the benefit | |
766 | * of ia64 which has a funky EFI memory map that doesn't work | |
767 | * the same way as other architectures. | |
768 | */ | |
769 | u64 __weak efi_mem_attributes(unsigned long phys_addr) | |
770 | { | |
771 | efi_memory_desc_t *md; | |
7bf79311 JZZ |
772 | |
773 | if (!efi_enabled(EFI_MEMMAP)) | |
774 | return 0; | |
775 | ||
78ce248f | 776 | for_each_efi_memory_desc(md) { |
7bf79311 JZZ |
777 | if ((md->phys_addr <= phys_addr) && |
778 | (phys_addr < (md->phys_addr + | |
779 | (md->num_pages << EFI_PAGE_SHIFT)))) | |
780 | return md->attribute; | |
781 | } | |
782 | return 0; | |
783 | } | |
806b0351 MF |
784 | |
785 | int efi_status_to_err(efi_status_t status) | |
786 | { | |
787 | int err; | |
788 | ||
789 | switch (status) { | |
790 | case EFI_SUCCESS: | |
791 | err = 0; | |
792 | break; | |
793 | case EFI_INVALID_PARAMETER: | |
794 | err = -EINVAL; | |
795 | break; | |
796 | case EFI_OUT_OF_RESOURCES: | |
797 | err = -ENOSPC; | |
798 | break; | |
799 | case EFI_DEVICE_ERROR: | |
800 | err = -EIO; | |
801 | break; | |
802 | case EFI_WRITE_PROTECTED: | |
803 | err = -EROFS; | |
804 | break; | |
805 | case EFI_SECURITY_VIOLATION: | |
806 | err = -EACCES; | |
807 | break; | |
808 | case EFI_NOT_FOUND: | |
809 | err = -ENOENT; | |
810 | break; | |
811 | default: | |
812 | err = -EINVAL; | |
813 | } | |
814 | ||
815 | return err; | |
816 | } |