summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/include/asm/efi.h7
-rw-r--r--arch/x86/kernel/reboot.c18
-rw-r--r--arch/x86/platform/efi/efi.c23
-rw-r--r--drivers/firmware/efi/reboot.c8
-rw-r--r--include/linux/efi.h1
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 */