diff options
-rw-r--r-- | arch/x86/include/asm/efi.h | 7 | ||||
-rw-r--r-- | arch/x86/kernel/reboot.c | 18 | ||||
-rw-r--r-- | arch/x86/platform/efi/efi.c | 23 | ||||
-rw-r--r-- | drivers/firmware/efi/reboot.c | 8 | ||||
-rw-r--r-- | include/linux/efi.h | 1 |
5 files changed, 55 insertions, 2 deletions
diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 1eb5f6433ad8..81396a9a9277 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -156,6 +156,9 @@ static inline efi_status_t efi_thunk_set_virtual_address_map( return EFI_SUCCESS; } #endif /* CONFIG_EFI_MIXED */ + +extern bool efi_reboot_required(void); + #else /* * IF EFI is not configured, have the EFI calls return -ENOSYS. @@ -168,6 +171,10 @@ static inline efi_status_t efi_thunk_set_virtual_address_map( #define efi_call5(_f, _a1, _a2, _a3, _a4, _a5) (-ENOSYS) #define efi_call6(_f, _a1, _a2, _a3, _a4, _a5, _a6) (-ENOSYS) static inline void parse_efi_setup(u64 phys_addr, u32 data_len) {} +static inline bool efi_reboot_required(void) +{ + return false; +} #endif /* CONFIG_EFI */ #endif /* _ASM_X86_EFI_H */ diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index 7c138543c500..587be13be0be 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -28,6 +28,7 @@ #include <linux/mc146818rtc.h> #include <asm/realmode.h> #include <asm/x86_init.h> +#include <asm/efi.h> /* * Power off function, if any @@ -411,12 +412,25 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = { static int __init reboot_init(void) { + int rv; + /* * Only do the DMI check if reboot_type hasn't been overridden * on the command line */ - if (reboot_default) - dmi_check_system(reboot_dmi_table); + if (!reboot_default) + return 0; + + /* + * The DMI quirks table takes precedence. If no quirks entry + * matches and the ACPI Hardware Reduced bit is set, force EFI + * reboot. + */ + rv = dmi_check_system(reboot_dmi_table); + + if (!rv && efi_reboot_required()) + reboot_type = BOOT_EFI; + return 0; } core_initcall(reboot_init); diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 87fc96bcc13c..53a324606d11 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -44,6 +44,7 @@ #include <linux/io.h> #include <linux/reboot.h> #include <linux/bcd.h> +#include <linux/acpi.h> #include <asm/setup.h> #include <asm/efi.h> @@ -1340,3 +1341,25 @@ void __init efi_apply_memmap_quirks(void) if (is_uv_system()) set_bit(EFI_OLD_MEMMAP, &efi.flags); } + +/* + * For most modern platforms the preferred method of powering off is via + * ACPI. However, there are some that are known to require the use of + * EFI runtime services and for which ACPI does not work at all. + * + * Using EFI is a last resort, to be used only if no other option + * exists. + */ +bool efi_reboot_required(void) +{ + if (!acpi_gbl_reduced_hardware) + return false; + + efi_reboot_quirk_mode = EFI_RESET_WARM; + return true; +} + +bool efi_poweroff_required(void) +{ + return !!acpi_gbl_reduced_hardware; +} diff --git a/drivers/firmware/efi/reboot.c b/drivers/firmware/efi/reboot.c index e9eeeb3c6345..9c59d1c795d1 100644 --- a/drivers/firmware/efi/reboot.c +++ b/drivers/firmware/efi/reboot.c @@ -5,6 +5,8 @@ #include <linux/efi.h> #include <linux/reboot.h> +int efi_reboot_quirk_mode = -1; + void efi_reboot(enum reboot_mode reboot_mode, const char *__unused) { int efi_mode; @@ -22,6 +24,12 @@ void efi_reboot(enum reboot_mode reboot_mode, const char *__unused) break; } + /* + * If a quirk forced an EFI reset mode, always use that. + */ + if (efi_reboot_quirk_mode != -1) + efi_mode = efi_reboot_quirk_mode; + efi.reset_system(efi_mode, EFI_SUCCESS, 0, NULL); } diff --git a/include/linux/efi.h b/include/linux/efi.h index 2539aff31808..b3fac7c1656c 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -876,6 +876,7 @@ extern void efi_reserve_boot_services(void); extern int efi_get_fdt_params(struct efi_fdt_params *params, int verbose); extern struct efi_memory_map memmap; +extern int efi_reboot_quirk_mode; extern bool efi_poweroff_required(void); /* Iterate through an efi_memory_map */ |