summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/Kconfig5
-rw-r--r--arch/x86/kernel/machine_kexec_32.c12
-rw-r--r--include/linux/suspend.h2
-rw-r--r--kernel/kexec.c39
-rw-r--r--kernel/power/power.h2
5 files changed, 56 insertions, 4 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 7ecb679f0130..6b2debfabddc 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1282,9 +1282,10 @@ config CRASH_DUMP
config KEXEC_JUMP
bool "kexec jump (EXPERIMENTAL)"
depends on EXPERIMENTAL
- depends on KEXEC && PM_SLEEP && X86_32
+ depends on KEXEC && HIBERNATION && X86_32
help
- Invoke code in physical address mode via KEXEC
+ Jump between original kernel and kexeced kernel and invoke
+ code in physical address mode via KEXEC
config PHYSICAL_START
hex "Physical address where the kernel is loaded" if (EMBEDDED || CRASH_DUMP)
diff --git a/arch/x86/kernel/machine_kexec_32.c b/arch/x86/kernel/machine_kexec_32.c
index 2b67609d0a1c..9fe478d98406 100644
--- a/arch/x86/kernel/machine_kexec_32.c
+++ b/arch/x86/kernel/machine_kexec_32.c
@@ -125,6 +125,18 @@ void machine_kexec(struct kimage *image)
/* Interrupts aren't acceptable while we reboot */
local_irq_disable();
+ if (image->preserve_context) {
+#ifdef CONFIG_X86_IO_APIC
+ /* We need to put APICs in legacy mode so that we can
+ * get timer interrupts in second kernel. kexec/kdump
+ * paths already have calls to disable_IO_APIC() in
+ * one form or other. kexec jump path also need
+ * one.
+ */
+ disable_IO_APIC();
+#endif
+ }
+
control_page = page_address(image->control_code_page);
memcpy(control_page, relocate_kernel, PAGE_SIZE/2);
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index e8e69159af71..c63435095970 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -278,4 +278,6 @@ static inline void register_nosave_region_late(unsigned long b, unsigned long e)
}
#endif
+extern struct mutex pm_mutex;
+
#endif /* _LINUX_SUSPEND_H */
diff --git a/kernel/kexec.c b/kernel/kexec.c
index a0d920915b38..c8a4370e2a34 100644
--- a/kernel/kexec.c
+++ b/kernel/kexec.c
@@ -26,6 +26,10 @@
#include <linux/numa.h>
#include <linux/suspend.h>
#include <linux/device.h>
+#include <linux/freezer.h>
+#include <linux/pm.h>
+#include <linux/cpu.h>
+#include <linux/console.h>
#include <asm/page.h>
#include <asm/uaccess.h>
@@ -1441,7 +1445,31 @@ int kernel_kexec(void)
if (kexec_image->preserve_context) {
#ifdef CONFIG_KEXEC_JUMP
+ mutex_lock(&pm_mutex);
+ pm_prepare_console();
+ error = freeze_processes();
+ if (error) {
+ error = -EBUSY;
+ goto Restore_console;
+ }
+ suspend_console();
+ error = device_suspend(PMSG_FREEZE);
+ if (error)
+ goto Resume_console;
+ error = disable_nonboot_cpus();
+ if (error)
+ goto Resume_devices;
local_irq_disable();
+ /* At this point, device_suspend() has been called,
+ * but *not* device_power_down(). We *must*
+ * device_power_down() now. Otherwise, drivers for
+ * some devices (e.g. interrupt controllers) become
+ * desynchronized with the actual state of the
+ * hardware at resume time, and evil weirdness ensues.
+ */
+ error = device_power_down(PMSG_FREEZE);
+ if (error)
+ goto Enable_irqs;
save_processor_state();
#endif
} else {
@@ -1459,7 +1487,18 @@ int kernel_kexec(void)
if (kexec_image->preserve_context) {
#ifdef CONFIG_KEXEC_JUMP
restore_processor_state();
+ device_power_up(PMSG_RESTORE);
+ Enable_irqs:
local_irq_enable();
+ enable_nonboot_cpus();
+ Resume_devices:
+ device_resume(PMSG_RESTORE);
+ Resume_console:
+ resume_console();
+ thaw_processes();
+ Restore_console:
+ pm_restore_console();
+ mutex_unlock(&pm_mutex);
#endif
}
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 700f44ec8406..acc0c101dbd5 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -53,8 +53,6 @@ extern int hibernation_platform_enter(void);
extern int pfn_is_nosave(unsigned long);
-extern struct mutex pm_mutex;
-
#define power_attr(_name) \
static struct kobj_attribute _name##_attr = { \
.attr = { \