diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-22 20:10:44 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-22 20:10:44 -0400 |
commit | cfe3eceb7a2eb91284d5605c5315249bb165e9d3 (patch) | |
tree | a18d2440ae8dac55119ef13b8b434431408ddc1b | |
parent | 5ef6ca4f24b59af7f7c2c19502a3923a4bc10e0a (diff) | |
parent | d4f7743542f20c2b20b68bba856c281ba9c84e42 (diff) |
Merge branch 'x86-efi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 EFI updates from Ingo Molnar:
"EFI changes:
- Use idiomatic negative error values in efivar_create_sysfs_entry()
instead of returning '1' to indicate error (Dan Carpenter)
- Implement new support to expose the EFI System Resource Tables in
sysfs, which provides information for performing firmware updates
(Peter Jones)
- Documentation cleanup in the EFI handover protocol section which
falsely claimed that 'cmdline_size' needed to be filled out by the
boot loader (Alex Smith)
- Align the order of SMBIOS tables in /sys/firmware/efi/systab to
match the way that we do things for ACPI and add documentation to
Documentation/ABI (Jean Delvare)"
* 'x86-efi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
efi: Work around ia64 build problem with ESRT driver
efi: Add 'systab' information to Documentation/ABI
efi: dmi: List SMBIOS3 table before SMBIOS table
efi/esrt: Fix some compiler warnings
x86, doc: Remove cmdline_size from list of fields to be filled in for EFI handover
efi: Add esrt support
efi: efivar_create_sysfs_entry() should return negative error codes
-rw-r--r-- | Documentation/ABI/testing/sysfs-firmware-efi | 10 | ||||
-rw-r--r-- | Documentation/ABI/testing/sysfs-firmware-efi-esrt | 81 | ||||
-rw-r--r-- | Documentation/x86/boot.txt | 1 | ||||
-rw-r--r-- | arch/x86/platform/efi/efi.c | 2 | ||||
-rw-r--r-- | drivers/firmware/efi/Kconfig | 5 | ||||
-rw-r--r-- | drivers/firmware/efi/Makefile | 1 | ||||
-rw-r--r-- | drivers/firmware/efi/efi.c | 91 | ||||
-rw-r--r-- | drivers/firmware/efi/efivars.c | 11 | ||||
-rw-r--r-- | drivers/firmware/efi/esrt.c | 471 | ||||
-rw-r--r-- | include/linux/efi.h | 12 |
10 files changed, 676 insertions, 9 deletions
diff --git a/Documentation/ABI/testing/sysfs-firmware-efi b/Documentation/ABI/testing/sysfs-firmware-efi index 05874da7ce80..e794eac32a90 100644 --- a/Documentation/ABI/testing/sysfs-firmware-efi +++ b/Documentation/ABI/testing/sysfs-firmware-efi | |||
@@ -18,3 +18,13 @@ Contact: Dave Young <dyoung@redhat.com> | |||
18 | Description: It shows the physical address of config table entry in the EFI | 18 | Description: It shows the physical address of config table entry in the EFI |
19 | system table. | 19 | system table. |
20 | Users: Kexec | 20 | Users: Kexec |
21 | |||
22 | What: /sys/firmware/efi/systab | ||
23 | Date: April 2005 | ||
24 | Contact: linux-efi@vger.kernel.org | ||
25 | Description: Displays the physical addresses of all EFI Configuration | ||
26 | Tables found via the EFI System Table. The order in | ||
27 | which the tables are printed forms an ABI and newer | ||
28 | versions are always printed first, i.e. ACPI20 comes | ||
29 | before ACPI. | ||
30 | Users: dmidecode | ||
diff --git a/Documentation/ABI/testing/sysfs-firmware-efi-esrt b/Documentation/ABI/testing/sysfs-firmware-efi-esrt new file mode 100644 index 000000000000..6e431d1a4e79 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-firmware-efi-esrt | |||
@@ -0,0 +1,81 @@ | |||
1 | What: /sys/firmware/efi/esrt/ | ||
2 | Date: February 2015 | ||
3 | Contact: Peter Jones <pjones@redhat.com> | ||
4 | Description: Provides userland access to read the EFI System Resource Table | ||
5 | (ESRT), a catalog of firmware for which can be updated with | ||
6 | the UEFI UpdateCapsule mechanism described in section 7.5 of | ||
7 | the UEFI Standard. | ||
8 | Users: fwupdate - https://github.com/rhinstaller/fwupdate | ||
9 | |||
10 | What: /sys/firmware/efi/esrt/fw_resource_count | ||
11 | Date: February 2015 | ||
12 | Contact: Peter Jones <pjones@redhat.com> | ||
13 | Description: The number of entries in the ESRT | ||
14 | |||
15 | What: /sys/firmware/efi/esrt/fw_resource_count_max | ||
16 | Date: February 2015 | ||
17 | Contact: Peter Jones <pjones@redhat.com> | ||
18 | Description: The maximum number of entries that /could/ be registered | ||
19 | in the allocation the table is currently in. This is | ||
20 | really only useful to the system firmware itself. | ||
21 | |||
22 | What: /sys/firmware/efi/esrt/fw_resource_version | ||
23 | Date: February 2015 | ||
24 | Contact: Peter Jones <pjones@redhat.com> | ||
25 | Description: The version of the ESRT structure provided by the firmware. | ||
26 | |||
27 | What: /sys/firmware/efi/esrt/entries/entry$N/ | ||
28 | Date: February 2015 | ||
29 | Contact: Peter Jones <pjones@redhat.com> | ||
30 | Description: Each ESRT entry is identified by a GUID, and each gets a | ||
31 | subdirectory under entries/ . | ||
32 | example: /sys/firmware/efi/esrt/entries/entry0/ | ||
33 | |||
34 | What: /sys/firmware/efi/esrt/entries/entry$N/fw_type | ||
35 | Date: February 2015 | ||
36 | Contact: Peter Jones <pjones@redhat.com> | ||
37 | Description: What kind of firmware entry this is: | ||
38 | 0 - Unknown | ||
39 | 1 - System Firmware | ||
40 | 2 - Device Firmware | ||
41 | 3 - UEFI Driver | ||
42 | |||
43 | What: /sys/firmware/efi/esrt/entries/entry$N/fw_class | ||
44 | Date: February 2015 | ||
45 | Contact: Peter Jones <pjones@redhat.com> | ||
46 | Description: This is the entry's guid, and will match the directory name. | ||
47 | |||
48 | What: /sys/firmware/efi/esrt/entries/entry$N/fw_version | ||
49 | Date: February 2015 | ||
50 | Contact: Peter Jones <pjones@redhat.com> | ||
51 | Description: The version of the firmware currently installed. This is a | ||
52 | 32-bit unsigned integer. | ||
53 | |||
54 | What: /sys/firmware/efi/esrt/entries/entry$N/lowest_supported_fw_version | ||
55 | Date: February 2015 | ||
56 | Contact: Peter Jones <pjones@redhat.com> | ||
57 | Description: The lowest version of the firmware that can be installed. | ||
58 | |||
59 | What: /sys/firmware/efi/esrt/entries/entry$N/capsule_flags | ||
60 | Date: February 2015 | ||
61 | Contact: Peter Jones <pjones@redhat.com> | ||
62 | Description: Flags that must be passed to UpdateCapsule() | ||
63 | |||
64 | What: /sys/firmware/efi/esrt/entries/entry$N/last_attempt_version | ||
65 | Date: February 2015 | ||
66 | Contact: Peter Jones <pjones@redhat.com> | ||
67 | Description: The last firmware version for which an update was attempted. | ||
68 | |||
69 | What: /sys/firmware/efi/esrt/entries/entry$N/last_attempt_status | ||
70 | Date: February 2015 | ||
71 | Contact: Peter Jones <pjones@redhat.com> | ||
72 | Description: The result of the last firmware update attempt for the | ||
73 | firmware resource entry. | ||
74 | 0 - Success | ||
75 | 1 - Insufficient resources | ||
76 | 2 - Incorrect version | ||
77 | 3 - Invalid format | ||
78 | 4 - Authentication error | ||
79 | 5 - AC power event | ||
80 | 6 - Battery power event | ||
81 | |||
diff --git a/Documentation/x86/boot.txt b/Documentation/x86/boot.txt index 88b85899d309..7c1f9fad6674 100644 --- a/Documentation/x86/boot.txt +++ b/Documentation/x86/boot.txt | |||
@@ -1124,7 +1124,6 @@ The boot loader *must* fill out the following fields in bp, | |||
1124 | 1124 | ||
1125 | o hdr.code32_start | 1125 | o hdr.code32_start |
1126 | o hdr.cmd_line_ptr | 1126 | o hdr.cmd_line_ptr |
1127 | o hdr.cmdline_size | ||
1128 | o hdr.ramdisk_image (if applicable) | 1127 | o hdr.ramdisk_image (if applicable) |
1129 | o hdr.ramdisk_size (if applicable) | 1128 | o hdr.ramdisk_size (if applicable) |
1130 | 1129 | ||
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 02744df576d5..3b984c3aa1b0 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c | |||
@@ -501,6 +501,8 @@ void __init efi_init(void) | |||
501 | 501 | ||
502 | if (efi_enabled(EFI_DBG)) | 502 | if (efi_enabled(EFI_DBG)) |
503 | print_efi_memmap(); | 503 | print_efi_memmap(); |
504 | |||
505 | efi_esrt_init(); | ||
504 | } | 506 | } |
505 | 507 | ||
506 | void __init efi_late_init(void) | 508 | void __init efi_late_init(void) |
diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 8de4da5c9ab6..54071c148340 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig | |||
@@ -18,6 +18,11 @@ config EFI_VARS | |||
18 | Subsequent efibootmgr releases may be found at: | 18 | Subsequent efibootmgr releases may be found at: |
19 | <http://github.com/vathpela/efibootmgr> | 19 | <http://github.com/vathpela/efibootmgr> |
20 | 20 | ||
21 | config EFI_ESRT | ||
22 | bool | ||
23 | depends on EFI && !IA64 | ||
24 | default y | ||
25 | |||
21 | config EFI_VARS_PSTORE | 26 | config EFI_VARS_PSTORE |
22 | tristate "Register efivars backend for pstore" | 27 | tristate "Register efivars backend for pstore" |
23 | depends on EFI_VARS && PSTORE | 28 | depends on EFI_VARS && PSTORE |
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index d8be608a9f3b..6fd3da938717 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile | |||
@@ -3,6 +3,7 @@ | |||
3 | # | 3 | # |
4 | obj-$(CONFIG_EFI) += efi.o vars.o reboot.o | 4 | obj-$(CONFIG_EFI) += efi.o vars.o reboot.o |
5 | obj-$(CONFIG_EFI_VARS) += efivars.o | 5 | obj-$(CONFIG_EFI_VARS) += efivars.o |
6 | obj-$(CONFIG_EFI_ESRT) += esrt.o | ||
6 | obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o | 7 | obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o |
7 | obj-$(CONFIG_UEFI_CPER) += cper.o | 8 | obj-$(CONFIG_UEFI_CPER) += cper.o |
8 | obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o | 9 | obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o |
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 3061bb8629dc..ca617f40574a 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c | |||
@@ -39,6 +39,7 @@ struct efi __read_mostly efi = { | |||
39 | .fw_vendor = EFI_INVALID_TABLE_ADDR, | 39 | .fw_vendor = EFI_INVALID_TABLE_ADDR, |
40 | .runtime = EFI_INVALID_TABLE_ADDR, | 40 | .runtime = EFI_INVALID_TABLE_ADDR, |
41 | .config_table = EFI_INVALID_TABLE_ADDR, | 41 | .config_table = EFI_INVALID_TABLE_ADDR, |
42 | .esrt = EFI_INVALID_TABLE_ADDR, | ||
42 | }; | 43 | }; |
43 | EXPORT_SYMBOL(efi); | 44 | EXPORT_SYMBOL(efi); |
44 | 45 | ||
@@ -64,7 +65,7 @@ static int __init parse_efi_cmdline(char *str) | |||
64 | } | 65 | } |
65 | early_param("efi", parse_efi_cmdline); | 66 | early_param("efi", parse_efi_cmdline); |
66 | 67 | ||
67 | static struct kobject *efi_kobj; | 68 | struct kobject *efi_kobj; |
68 | static struct kobject *efivars_kobj; | 69 | static struct kobject *efivars_kobj; |
69 | 70 | ||
70 | /* | 71 | /* |
@@ -85,10 +86,15 @@ static ssize_t systab_show(struct kobject *kobj, | |||
85 | str += sprintf(str, "ACPI20=0x%lx\n", efi.acpi20); | 86 | str += sprintf(str, "ACPI20=0x%lx\n", efi.acpi20); |
86 | if (efi.acpi != EFI_INVALID_TABLE_ADDR) | 87 | if (efi.acpi != EFI_INVALID_TABLE_ADDR) |
87 | str += sprintf(str, "ACPI=0x%lx\n", efi.acpi); | 88 | str += sprintf(str, "ACPI=0x%lx\n", efi.acpi); |
88 | if (efi.smbios != EFI_INVALID_TABLE_ADDR) | 89 | /* |
89 | str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios); | 90 | * If both SMBIOS and SMBIOS3 entry points are implemented, the |
91 | * SMBIOS3 entry point shall be preferred, so we list it first to | ||
92 | * let applications stop parsing after the first match. | ||
93 | */ | ||
90 | if (efi.smbios3 != EFI_INVALID_TABLE_ADDR) | 94 | if (efi.smbios3 != EFI_INVALID_TABLE_ADDR) |
91 | str += sprintf(str, "SMBIOS3=0x%lx\n", efi.smbios3); | 95 | str += sprintf(str, "SMBIOS3=0x%lx\n", efi.smbios3); |
96 | if (efi.smbios != EFI_INVALID_TABLE_ADDR) | ||
97 | str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios); | ||
92 | if (efi.hcdp != EFI_INVALID_TABLE_ADDR) | 98 | if (efi.hcdp != EFI_INVALID_TABLE_ADDR) |
93 | str += sprintf(str, "HCDP=0x%lx\n", efi.hcdp); | 99 | str += sprintf(str, "HCDP=0x%lx\n", efi.hcdp); |
94 | if (efi.boot_info != EFI_INVALID_TABLE_ADDR) | 100 | if (efi.boot_info != EFI_INVALID_TABLE_ADDR) |
@@ -232,6 +238,84 @@ err_put: | |||
232 | 238 | ||
233 | subsys_initcall(efisubsys_init); | 239 | subsys_initcall(efisubsys_init); |
234 | 240 | ||
241 | /* | ||
242 | * Find the efi memory descriptor for a given physical address. Given a | ||
243 | * physicall address, determine if it exists within an EFI Memory Map entry, | ||
244 | * and if so, populate the supplied memory descriptor with the appropriate | ||
245 | * data. | ||
246 | */ | ||
247 | int __init efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md) | ||
248 | { | ||
249 | struct efi_memory_map *map = efi.memmap; | ||
250 | void *p, *e; | ||
251 | |||
252 | if (!efi_enabled(EFI_MEMMAP)) { | ||
253 | pr_err_once("EFI_MEMMAP is not enabled.\n"); | ||
254 | return -EINVAL; | ||
255 | } | ||
256 | |||
257 | if (!map) { | ||
258 | pr_err_once("efi.memmap is not set.\n"); | ||
259 | return -EINVAL; | ||
260 | } | ||
261 | if (!out_md) { | ||
262 | pr_err_once("out_md is null.\n"); | ||
263 | return -EINVAL; | ||
264 | } | ||
265 | if (WARN_ON_ONCE(!map->phys_map)) | ||
266 | return -EINVAL; | ||
267 | if (WARN_ON_ONCE(map->nr_map == 0) || WARN_ON_ONCE(map->desc_size == 0)) | ||
268 | return -EINVAL; | ||
269 | |||
270 | e = map->phys_map + map->nr_map * map->desc_size; | ||
271 | for (p = map->phys_map; p < e; p += map->desc_size) { | ||
272 | efi_memory_desc_t *md; | ||
273 | u64 size; | ||
274 | u64 end; | ||
275 | |||
276 | /* | ||
277 | * If a driver calls this after efi_free_boot_services, | ||
278 | * ->map will be NULL, and the target may also not be mapped. | ||
279 | * So just always get our own virtual map on the CPU. | ||
280 | * | ||
281 | */ | ||
282 | md = early_memremap((phys_addr_t)p, sizeof (*md)); | ||
283 | if (!md) { | ||
284 | pr_err_once("early_memremap(%p, %zu) failed.\n", | ||
285 | p, sizeof (*md)); | ||
286 | return -ENOMEM; | ||
287 | } | ||
288 | |||
289 | if (!(md->attribute & EFI_MEMORY_RUNTIME) && | ||
290 | md->type != EFI_BOOT_SERVICES_DATA && | ||
291 | md->type != EFI_RUNTIME_SERVICES_DATA) { | ||
292 | early_memunmap(md, sizeof (*md)); | ||
293 | continue; | ||
294 | } | ||
295 | |||
296 | size = md->num_pages << EFI_PAGE_SHIFT; | ||
297 | end = md->phys_addr + size; | ||
298 | if (phys_addr >= md->phys_addr && phys_addr < end) { | ||
299 | memcpy(out_md, md, sizeof(*out_md)); | ||
300 | early_memunmap(md, sizeof (*md)); | ||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | early_memunmap(md, sizeof (*md)); | ||
305 | } | ||
306 | pr_err_once("requested map not found.\n"); | ||
307 | return -ENOENT; | ||
308 | } | ||
309 | |||
310 | /* | ||
311 | * Calculate the highest address of an efi memory descriptor. | ||
312 | */ | ||
313 | u64 __init efi_mem_desc_end(efi_memory_desc_t *md) | ||
314 | { | ||
315 | u64 size = md->num_pages << EFI_PAGE_SHIFT; | ||
316 | u64 end = md->phys_addr + size; | ||
317 | return end; | ||
318 | } | ||
235 | 319 | ||
236 | /* | 320 | /* |
237 | * We can't ioremap data in EFI boot services RAM, because we've already mapped | 321 | * We can't ioremap data in EFI boot services RAM, because we've already mapped |
@@ -274,6 +358,7 @@ static __initdata efi_config_table_type_t common_tables[] = { | |||
274 | {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios}, | 358 | {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios}, |
275 | {SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3}, | 359 | {SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3}, |
276 | {UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga}, | 360 | {UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga}, |
361 | {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt}, | ||
277 | {NULL_GUID, NULL, NULL}, | 362 | {NULL_GUID, NULL, NULL}, |
278 | }; | 363 | }; |
279 | 364 | ||
diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c index 7b2e0496e0c0..756eca8c4cf8 100644 --- a/drivers/firmware/efi/efivars.c +++ b/drivers/firmware/efi/efivars.c | |||
@@ -535,7 +535,7 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj, | |||
535 | * efivar_create_sysfs_entry - create a new entry in sysfs | 535 | * efivar_create_sysfs_entry - create a new entry in sysfs |
536 | * @new_var: efivar entry to create | 536 | * @new_var: efivar entry to create |
537 | * | 537 | * |
538 | * Returns 1 on failure, 0 on success | 538 | * Returns 0 on success, negative error code on failure |
539 | */ | 539 | */ |
540 | static int | 540 | static int |
541 | efivar_create_sysfs_entry(struct efivar_entry *new_var) | 541 | efivar_create_sysfs_entry(struct efivar_entry *new_var) |
@@ -544,6 +544,7 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var) | |||
544 | char *short_name; | 544 | char *short_name; |
545 | unsigned long variable_name_size; | 545 | unsigned long variable_name_size; |
546 | efi_char16_t *variable_name; | 546 | efi_char16_t *variable_name; |
547 | int ret; | ||
547 | 548 | ||
548 | variable_name = new_var->var.VariableName; | 549 | variable_name = new_var->var.VariableName; |
549 | variable_name_size = ucs2_strlen(variable_name) * sizeof(efi_char16_t); | 550 | variable_name_size = ucs2_strlen(variable_name) * sizeof(efi_char16_t); |
@@ -558,7 +559,7 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var) | |||
558 | short_name = kzalloc(short_name_size, GFP_KERNEL); | 559 | short_name = kzalloc(short_name_size, GFP_KERNEL); |
559 | 560 | ||
560 | if (!short_name) | 561 | if (!short_name) |
561 | return 1; | 562 | return -ENOMEM; |
562 | 563 | ||
563 | /* Convert Unicode to normal chars (assume top bits are 0), | 564 | /* Convert Unicode to normal chars (assume top bits are 0), |
564 | ala UTF-8 */ | 565 | ala UTF-8 */ |
@@ -574,11 +575,11 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var) | |||
574 | 575 | ||
575 | new_var->kobj.kset = efivars_kset; | 576 | new_var->kobj.kset = efivars_kset; |
576 | 577 | ||
577 | i = kobject_init_and_add(&new_var->kobj, &efivar_ktype, | 578 | ret = kobject_init_and_add(&new_var->kobj, &efivar_ktype, |
578 | NULL, "%s", short_name); | 579 | NULL, "%s", short_name); |
579 | kfree(short_name); | 580 | kfree(short_name); |
580 | if (i) | 581 | if (ret) |
581 | return 1; | 582 | return ret; |
582 | 583 | ||
583 | kobject_uevent(&new_var->kobj, KOBJ_ADD); | 584 | kobject_uevent(&new_var->kobj, KOBJ_ADD); |
584 | efivar_entry_add(new_var, &efivar_sysfs_list); | 585 | efivar_entry_add(new_var, &efivar_sysfs_list); |
diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c new file mode 100644 index 000000000000..a5b95d61ae71 --- /dev/null +++ b/drivers/firmware/efi/esrt.c | |||
@@ -0,0 +1,471 @@ | |||
1 | /* | ||
2 | * esrt.c | ||
3 | * | ||
4 | * This module exports EFI System Resource Table (ESRT) entries into userspace | ||
5 | * through the sysfs file system. The ESRT provides a read-only catalog of | ||
6 | * system components for which the system accepts firmware upgrades via UEFI's | ||
7 | * "Capsule Update" feature. This module allows userland utilities to evaluate | ||
8 | * what firmware updates can be applied to this system, and potentially arrange | ||
9 | * for those updates to occur. | ||
10 | * | ||
11 | * Data is currently found below /sys/firmware/efi/esrt/... | ||
12 | */ | ||
13 | #define pr_fmt(fmt) "esrt: " fmt | ||
14 | |||
15 | #include <linux/capability.h> | ||
16 | #include <linux/device.h> | ||
17 | #include <linux/efi.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/kobject.h> | ||
21 | #include <linux/list.h> | ||
22 | #include <linux/memblock.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/types.h> | ||
26 | |||
27 | #include <asm/io.h> | ||
28 | #include <asm/early_ioremap.h> | ||
29 | |||
30 | struct efi_system_resource_entry_v1 { | ||
31 | efi_guid_t fw_class; | ||
32 | u32 fw_type; | ||
33 | u32 fw_version; | ||
34 | u32 lowest_supported_fw_version; | ||
35 | u32 capsule_flags; | ||
36 | u32 last_attempt_version; | ||
37 | u32 last_attempt_status; | ||
38 | }; | ||
39 | |||
40 | /* | ||
41 | * _count and _version are what they seem like. _max is actually just | ||
42 | * accounting info for the firmware when creating the table; it should never | ||
43 | * have been exposed to us. To wit, the spec says: | ||
44 | * The maximum number of resource array entries that can be within the | ||
45 | * table without reallocating the table, must not be zero. | ||
46 | * Since there's no guidance about what that means in terms of memory layout, | ||
47 | * it means nothing to us. | ||
48 | */ | ||
49 | struct efi_system_resource_table { | ||
50 | u32 fw_resource_count; | ||
51 | u32 fw_resource_count_max; | ||
52 | u64 fw_resource_version; | ||
53 | u8 entries[]; | ||
54 | }; | ||
55 | |||
56 | static phys_addr_t esrt_data; | ||
57 | static size_t esrt_data_size; | ||
58 | |||
59 | static struct efi_system_resource_table *esrt; | ||
60 | |||
61 | struct esre_entry { | ||
62 | union { | ||
63 | struct efi_system_resource_entry_v1 *esre1; | ||
64 | } esre; | ||
65 | |||
66 | struct kobject kobj; | ||
67 | struct list_head list; | ||
68 | }; | ||
69 | |||
70 | /* global list of esre_entry. */ | ||
71 | static LIST_HEAD(entry_list); | ||
72 | |||
73 | /* entry attribute */ | ||
74 | struct esre_attribute { | ||
75 | struct attribute attr; | ||
76 | ssize_t (*show)(struct esre_entry *entry, char *buf); | ||
77 | ssize_t (*store)(struct esre_entry *entry, | ||
78 | const char *buf, size_t count); | ||
79 | }; | ||
80 | |||
81 | static struct esre_entry *to_entry(struct kobject *kobj) | ||
82 | { | ||
83 | return container_of(kobj, struct esre_entry, kobj); | ||
84 | } | ||
85 | |||
86 | static struct esre_attribute *to_attr(struct attribute *attr) | ||
87 | { | ||
88 | return container_of(attr, struct esre_attribute, attr); | ||
89 | } | ||
90 | |||
91 | static ssize_t esre_attr_show(struct kobject *kobj, | ||
92 | struct attribute *_attr, char *buf) | ||
93 | { | ||
94 | struct esre_entry *entry = to_entry(kobj); | ||
95 | struct esre_attribute *attr = to_attr(_attr); | ||
96 | |||
97 | /* Don't tell normal users what firmware versions we've got... */ | ||
98 | if (!capable(CAP_SYS_ADMIN)) | ||
99 | return -EACCES; | ||
100 | |||
101 | return attr->show(entry, buf); | ||
102 | } | ||
103 | |||
104 | static const struct sysfs_ops esre_attr_ops = { | ||
105 | .show = esre_attr_show, | ||
106 | }; | ||
107 | |||
108 | /* Generic ESRT Entry ("ESRE") support. */ | ||
109 | static ssize_t esre_fw_class_show(struct esre_entry *entry, char *buf) | ||
110 | { | ||
111 | char *str = buf; | ||
112 | |||
113 | efi_guid_to_str(&entry->esre.esre1->fw_class, str); | ||
114 | str += strlen(str); | ||
115 | str += sprintf(str, "\n"); | ||
116 | |||
117 | return str - buf; | ||
118 | } | ||
119 | |||
120 | static struct esre_attribute esre_fw_class = __ATTR(fw_class, 0400, | ||
121 | esre_fw_class_show, NULL); | ||
122 | |||
123 | #define esre_attr_decl(name, size, fmt) \ | ||
124 | static ssize_t esre_##name##_show(struct esre_entry *entry, char *buf) \ | ||
125 | { \ | ||
126 | return sprintf(buf, fmt "\n", \ | ||
127 | le##size##_to_cpu(entry->esre.esre1->name)); \ | ||
128 | } \ | ||
129 | \ | ||
130 | static struct esre_attribute esre_##name = __ATTR(name, 0400, \ | ||
131 | esre_##name##_show, NULL) | ||
132 | |||
133 | esre_attr_decl(fw_type, 32, "%u"); | ||
134 | esre_attr_decl(fw_version, 32, "%u"); | ||
135 | esre_attr_decl(lowest_supported_fw_version, 32, "%u"); | ||
136 | esre_attr_decl(capsule_flags, 32, "0x%x"); | ||
137 | esre_attr_decl(last_attempt_version, 32, "%u"); | ||
138 | esre_attr_decl(last_attempt_status, 32, "%u"); | ||
139 | |||
140 | static struct attribute *esre1_attrs[] = { | ||
141 | &esre_fw_class.attr, | ||
142 | &esre_fw_type.attr, | ||
143 | &esre_fw_version.attr, | ||
144 | &esre_lowest_supported_fw_version.attr, | ||
145 | &esre_capsule_flags.attr, | ||
146 | &esre_last_attempt_version.attr, | ||
147 | &esre_last_attempt_status.attr, | ||
148 | NULL | ||
149 | }; | ||
150 | static void esre_release(struct kobject *kobj) | ||
151 | { | ||
152 | struct esre_entry *entry = to_entry(kobj); | ||
153 | |||
154 | list_del(&entry->list); | ||
155 | kfree(entry); | ||
156 | } | ||
157 | |||
158 | static struct kobj_type esre1_ktype = { | ||
159 | .release = esre_release, | ||
160 | .sysfs_ops = &esre_attr_ops, | ||
161 | .default_attrs = esre1_attrs, | ||
162 | }; | ||
163 | |||
164 | |||
165 | static struct kobject *esrt_kobj; | ||
166 | static struct kset *esrt_kset; | ||
167 | |||
168 | static int esre_create_sysfs_entry(void *esre, int entry_num) | ||
169 | { | ||
170 | struct esre_entry *entry; | ||
171 | char name[20]; | ||
172 | |||
173 | entry = kzalloc(sizeof(*entry), GFP_KERNEL); | ||
174 | if (!entry) | ||
175 | return -ENOMEM; | ||
176 | |||
177 | sprintf(name, "entry%d", entry_num); | ||
178 | |||
179 | entry->kobj.kset = esrt_kset; | ||
180 | |||
181 | if (esrt->fw_resource_version == 1) { | ||
182 | int rc = 0; | ||
183 | |||
184 | entry->esre.esre1 = esre; | ||
185 | rc = kobject_init_and_add(&entry->kobj, &esre1_ktype, NULL, | ||
186 | "%s", name); | ||
187 | if (rc) { | ||
188 | kfree(entry); | ||
189 | return rc; | ||
190 | } | ||
191 | } | ||
192 | |||
193 | list_add_tail(&entry->list, &entry_list); | ||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | /* support for displaying ESRT fields at the top level */ | ||
198 | #define esrt_attr_decl(name, size, fmt) \ | ||
199 | static ssize_t esrt_##name##_show(struct kobject *kobj, \ | ||
200 | struct kobj_attribute *attr, char *buf)\ | ||
201 | { \ | ||
202 | return sprintf(buf, fmt "\n", le##size##_to_cpu(esrt->name)); \ | ||
203 | } \ | ||
204 | \ | ||
205 | static struct kobj_attribute esrt_##name = __ATTR(name, 0400, \ | ||
206 | esrt_##name##_show, NULL) | ||
207 | |||
208 | esrt_attr_decl(fw_resource_count, 32, "%u"); | ||
209 | esrt_attr_decl(fw_resource_count_max, 32, "%u"); | ||
210 | esrt_attr_decl(fw_resource_version, 64, "%llu"); | ||
211 | |||
212 | static struct attribute *esrt_attrs[] = { | ||
213 | &esrt_fw_resource_count.attr, | ||
214 | &esrt_fw_resource_count_max.attr, | ||
215 | &esrt_fw_resource_version.attr, | ||
216 | NULL, | ||
217 | }; | ||
218 | |||
219 | static inline int esrt_table_exists(void) | ||
220 | { | ||
221 | if (!efi_enabled(EFI_CONFIG_TABLES)) | ||
222 | return 0; | ||
223 | if (efi.esrt == EFI_INVALID_TABLE_ADDR) | ||
224 | return 0; | ||
225 | return 1; | ||
226 | } | ||
227 | |||
228 | static umode_t esrt_attr_is_visible(struct kobject *kobj, | ||
229 | struct attribute *attr, int n) | ||
230 | { | ||
231 | if (!esrt_table_exists()) | ||
232 | return 0; | ||
233 | return attr->mode; | ||
234 | } | ||
235 | |||
236 | static struct attribute_group esrt_attr_group = { | ||
237 | .attrs = esrt_attrs, | ||
238 | .is_visible = esrt_attr_is_visible, | ||
239 | }; | ||
240 | |||
241 | /* | ||
242 | * remap the table, copy it to kmalloced pages, and unmap it. | ||
243 | */ | ||
244 | void __init efi_esrt_init(void) | ||
245 | { | ||
246 | void *va; | ||
247 | struct efi_system_resource_table tmpesrt; | ||
248 | struct efi_system_resource_entry_v1 *v1_entries; | ||
249 | size_t size, max, entry_size, entries_size; | ||
250 | efi_memory_desc_t md; | ||
251 | int rc; | ||
252 | phys_addr_t end; | ||
253 | |||
254 | pr_debug("esrt-init: loading.\n"); | ||
255 | if (!esrt_table_exists()) | ||
256 | return; | ||
257 | |||
258 | rc = efi_mem_desc_lookup(efi.esrt, &md); | ||
259 | if (rc < 0) { | ||
260 | pr_err("ESRT header is not in the memory map.\n"); | ||
261 | return; | ||
262 | } | ||
263 | |||
264 | max = efi_mem_desc_end(&md); | ||
265 | if (max < efi.esrt) { | ||
266 | pr_err("EFI memory descriptor is invalid. (esrt: %p max: %p)\n", | ||
267 | (void *)efi.esrt, (void *)max); | ||
268 | return; | ||
269 | } | ||
270 | |||
271 | size = sizeof(*esrt); | ||
272 | max -= efi.esrt; | ||
273 | |||
274 | if (max < size) { | ||
275 | pr_err("ESRT header doen't fit on single memory map entry. (size: %zu max: %zu)\n", | ||
276 | size, max); | ||
277 | return; | ||
278 | } | ||
279 | |||
280 | va = early_memremap(efi.esrt, size); | ||
281 | if (!va) { | ||
282 | pr_err("early_memremap(%p, %zu) failed.\n", (void *)efi.esrt, | ||
283 | size); | ||
284 | return; | ||
285 | } | ||
286 | |||
287 | memcpy(&tmpesrt, va, sizeof(tmpesrt)); | ||
288 | |||
289 | if (tmpesrt.fw_resource_version == 1) { | ||
290 | entry_size = sizeof (*v1_entries); | ||
291 | } else { | ||
292 | pr_err("Unsupported ESRT version %lld.\n", | ||
293 | tmpesrt.fw_resource_version); | ||
294 | return; | ||
295 | } | ||
296 | |||
297 | if (tmpesrt.fw_resource_count > 0 && max - size < entry_size) { | ||
298 | pr_err("ESRT memory map entry can only hold the header. (max: %zu size: %zu)\n", | ||
299 | max - size, entry_size); | ||
300 | goto err_memunmap; | ||
301 | } | ||
302 | |||
303 | /* | ||
304 | * The format doesn't really give us any boundary to test here, | ||
305 | * so I'm making up 128 as the max number of individually updatable | ||
306 | * components we support. | ||
307 | * 128 should be pretty excessive, but there's still some chance | ||
308 | * somebody will do that someday and we'll need to raise this. | ||
309 | */ | ||
310 | if (tmpesrt.fw_resource_count > 128) { | ||
311 | pr_err("ESRT says fw_resource_count has very large value %d.\n", | ||
312 | tmpesrt.fw_resource_count); | ||
313 | goto err_memunmap; | ||
314 | } | ||
315 | |||
316 | /* | ||
317 | * We know it can't be larger than N * sizeof() here, and N is limited | ||
318 | * by the previous test to a small number, so there's no overflow. | ||
319 | */ | ||
320 | entries_size = tmpesrt.fw_resource_count * entry_size; | ||
321 | if (max < size + entries_size) { | ||
322 | pr_err("ESRT does not fit on single memory map entry (size: %zu max: %zu)\n", | ||
323 | size, max); | ||
324 | goto err_memunmap; | ||
325 | } | ||
326 | |||
327 | /* remap it with our (plausible) new pages */ | ||
328 | early_memunmap(va, size); | ||
329 | size += entries_size; | ||
330 | va = early_memremap(efi.esrt, size); | ||
331 | if (!va) { | ||
332 | pr_err("early_memremap(%p, %zu) failed.\n", (void *)efi.esrt, | ||
333 | size); | ||
334 | return; | ||
335 | } | ||
336 | |||
337 | esrt_data = (phys_addr_t)efi.esrt; | ||
338 | esrt_data_size = size; | ||
339 | |||
340 | end = esrt_data + size; | ||
341 | pr_info("Reserving ESRT space from %pa to %pa.\n", &esrt_data, &end); | ||
342 | memblock_reserve(esrt_data, esrt_data_size); | ||
343 | |||
344 | pr_debug("esrt-init: loaded.\n"); | ||
345 | err_memunmap: | ||
346 | early_memunmap(va, size); | ||
347 | } | ||
348 | |||
349 | static int __init register_entries(void) | ||
350 | { | ||
351 | struct efi_system_resource_entry_v1 *v1_entries = (void *)esrt->entries; | ||
352 | int i, rc; | ||
353 | |||
354 | if (!esrt_table_exists()) | ||
355 | return 0; | ||
356 | |||
357 | for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) { | ||
358 | void *esre = NULL; | ||
359 | if (esrt->fw_resource_version == 1) { | ||
360 | esre = &v1_entries[i]; | ||
361 | } else { | ||
362 | pr_err("Unsupported ESRT version %lld.\n", | ||
363 | esrt->fw_resource_version); | ||
364 | return -EINVAL; | ||
365 | } | ||
366 | |||
367 | rc = esre_create_sysfs_entry(esre, i); | ||
368 | if (rc < 0) { | ||
369 | pr_err("ESRT entry creation failed with error %d.\n", | ||
370 | rc); | ||
371 | return rc; | ||
372 | } | ||
373 | } | ||
374 | return 0; | ||
375 | } | ||
376 | |||
377 | static void cleanup_entry_list(void) | ||
378 | { | ||
379 | struct esre_entry *entry, *next; | ||
380 | |||
381 | list_for_each_entry_safe(entry, next, &entry_list, list) { | ||
382 | kobject_put(&entry->kobj); | ||
383 | } | ||
384 | } | ||
385 | |||
386 | static int __init esrt_sysfs_init(void) | ||
387 | { | ||
388 | int error; | ||
389 | struct efi_system_resource_table __iomem *ioesrt; | ||
390 | |||
391 | pr_debug("esrt-sysfs: loading.\n"); | ||
392 | if (!esrt_data || !esrt_data_size) | ||
393 | return -ENOSYS; | ||
394 | |||
395 | ioesrt = ioremap(esrt_data, esrt_data_size); | ||
396 | if (!ioesrt) { | ||
397 | pr_err("ioremap(%pa, %zu) failed.\n", &esrt_data, | ||
398 | esrt_data_size); | ||
399 | return -ENOMEM; | ||
400 | } | ||
401 | |||
402 | esrt = kmalloc(esrt_data_size, GFP_KERNEL); | ||
403 | if (!esrt) { | ||
404 | pr_err("kmalloc failed. (wanted %zu bytes)\n", esrt_data_size); | ||
405 | iounmap(ioesrt); | ||
406 | return -ENOMEM; | ||
407 | } | ||
408 | |||
409 | memcpy_fromio(esrt, ioesrt, esrt_data_size); | ||
410 | |||
411 | esrt_kobj = kobject_create_and_add("esrt", efi_kobj); | ||
412 | if (!esrt_kobj) { | ||
413 | pr_err("Firmware table registration failed.\n"); | ||
414 | error = -ENOMEM; | ||
415 | goto err; | ||
416 | } | ||
417 | |||
418 | error = sysfs_create_group(esrt_kobj, &esrt_attr_group); | ||
419 | if (error) { | ||
420 | pr_err("Sysfs attribute export failed with error %d.\n", | ||
421 | error); | ||
422 | goto err_remove_esrt; | ||
423 | } | ||
424 | |||
425 | esrt_kset = kset_create_and_add("entries", NULL, esrt_kobj); | ||
426 | if (!esrt_kset) { | ||
427 | pr_err("kset creation failed.\n"); | ||
428 | error = -ENOMEM; | ||
429 | goto err_remove_group; | ||
430 | } | ||
431 | |||
432 | error = register_entries(); | ||
433 | if (error) | ||
434 | goto err_cleanup_list; | ||
435 | |||
436 | memblock_remove(esrt_data, esrt_data_size); | ||
437 | |||
438 | pr_debug("esrt-sysfs: loaded.\n"); | ||
439 | |||
440 | return 0; | ||
441 | err_cleanup_list: | ||
442 | cleanup_entry_list(); | ||
443 | kset_unregister(esrt_kset); | ||
444 | err_remove_group: | ||
445 | sysfs_remove_group(esrt_kobj, &esrt_attr_group); | ||
446 | err_remove_esrt: | ||
447 | kobject_put(esrt_kobj); | ||
448 | err: | ||
449 | kfree(esrt); | ||
450 | esrt = NULL; | ||
451 | return error; | ||
452 | } | ||
453 | |||
454 | static void __exit esrt_sysfs_exit(void) | ||
455 | { | ||
456 | pr_debug("esrt-sysfs: unloading.\n"); | ||
457 | cleanup_entry_list(); | ||
458 | kset_unregister(esrt_kset); | ||
459 | sysfs_remove_group(esrt_kobj, &esrt_attr_group); | ||
460 | kfree(esrt); | ||
461 | esrt = NULL; | ||
462 | kobject_del(esrt_kobj); | ||
463 | kobject_put(esrt_kobj); | ||
464 | } | ||
465 | |||
466 | module_init(esrt_sysfs_init); | ||
467 | module_exit(esrt_sysfs_exit); | ||
468 | |||
469 | MODULE_AUTHOR("Peter Jones <pjones@redhat.com>"); | ||
470 | MODULE_DESCRIPTION("EFI System Resource Table support"); | ||
471 | MODULE_LICENSE("GPL"); | ||
diff --git a/include/linux/efi.h b/include/linux/efi.h index af5be0368dec..2092965afca3 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h | |||
@@ -583,6 +583,9 @@ void efi_native_runtime_setup(void); | |||
583 | #define EFI_FILE_INFO_ID \ | 583 | #define EFI_FILE_INFO_ID \ |
584 | EFI_GUID( 0x9576e92, 0x6d3f, 0x11d2, 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b ) | 584 | EFI_GUID( 0x9576e92, 0x6d3f, 0x11d2, 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b ) |
585 | 585 | ||
586 | #define EFI_SYSTEM_RESOURCE_TABLE_GUID \ | ||
587 | EFI_GUID( 0xb122a263, 0x3661, 0x4f68, 0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80 ) | ||
588 | |||
586 | #define EFI_FILE_SYSTEM_GUID \ | 589 | #define EFI_FILE_SYSTEM_GUID \ |
587 | EFI_GUID( 0x964e5b22, 0x6459, 0x11d2, 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b ) | 590 | EFI_GUID( 0x964e5b22, 0x6459, 0x11d2, 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b ) |
588 | 591 | ||
@@ -823,6 +826,7 @@ extern struct efi { | |||
823 | unsigned long fw_vendor; /* fw_vendor */ | 826 | unsigned long fw_vendor; /* fw_vendor */ |
824 | unsigned long runtime; /* runtime table */ | 827 | unsigned long runtime; /* runtime table */ |
825 | unsigned long config_table; /* config tables */ | 828 | unsigned long config_table; /* config tables */ |
829 | unsigned long esrt; /* ESRT table */ | ||
826 | efi_get_time_t *get_time; | 830 | efi_get_time_t *get_time; |
827 | efi_set_time_t *set_time; | 831 | efi_set_time_t *set_time; |
828 | efi_get_wakeup_time_t *get_wakeup_time; | 832 | efi_get_wakeup_time_t *get_wakeup_time; |
@@ -875,6 +879,11 @@ static inline efi_status_t efi_query_variable_store(u32 attributes, unsigned lon | |||
875 | #endif | 879 | #endif |
876 | extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr); | 880 | extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr); |
877 | extern int efi_config_init(efi_config_table_type_t *arch_tables); | 881 | extern int efi_config_init(efi_config_table_type_t *arch_tables); |
882 | #ifdef CONFIG_EFI_ESRT | ||
883 | extern void __init efi_esrt_init(void); | ||
884 | #else | ||
885 | static inline void efi_esrt_init(void) { } | ||
886 | #endif | ||
878 | extern int efi_config_parse_tables(void *config_tables, int count, int sz, | 887 | extern int efi_config_parse_tables(void *config_tables, int count, int sz, |
879 | efi_config_table_type_t *arch_tables); | 888 | efi_config_table_type_t *arch_tables); |
880 | extern u64 efi_get_iobase (void); | 889 | extern u64 efi_get_iobase (void); |
@@ -882,12 +891,15 @@ extern u32 efi_mem_type (unsigned long phys_addr); | |||
882 | extern u64 efi_mem_attributes (unsigned long phys_addr); | 891 | extern u64 efi_mem_attributes (unsigned long phys_addr); |
883 | extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size); | 892 | extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size); |
884 | extern int __init efi_uart_console_only (void); | 893 | extern int __init efi_uart_console_only (void); |
894 | extern u64 efi_mem_desc_end(efi_memory_desc_t *md); | ||
895 | extern int efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md); | ||
885 | extern void efi_initialize_iomem_resources(struct resource *code_resource, | 896 | extern void efi_initialize_iomem_resources(struct resource *code_resource, |
886 | struct resource *data_resource, struct resource *bss_resource); | 897 | struct resource *data_resource, struct resource *bss_resource); |
887 | extern void efi_get_time(struct timespec *now); | 898 | extern void efi_get_time(struct timespec *now); |
888 | extern void efi_reserve_boot_services(void); | 899 | extern void efi_reserve_boot_services(void); |
889 | extern int efi_get_fdt_params(struct efi_fdt_params *params, int verbose); | 900 | extern int efi_get_fdt_params(struct efi_fdt_params *params, int verbose); |
890 | extern struct efi_memory_map memmap; | 901 | extern struct efi_memory_map memmap; |
902 | extern struct kobject *efi_kobj; | ||
891 | 903 | ||
892 | extern int efi_reboot_quirk_mode; | 904 | extern int efi_reboot_quirk_mode; |
893 | extern bool efi_poweroff_required(void); | 905 | extern bool efi_poweroff_required(void); |