2005-04-16 22:20:36 +00:00
|
|
|
/*
|
2009-04-27 22:27:18 +00:00
|
|
|
* Suspend support specific for i386/x86-64.
|
2005-04-16 22:20:36 +00:00
|
|
|
*
|
|
|
|
* Distribute under GPLv2
|
|
|
|
*
|
2008-02-09 22:24:09 +00:00
|
|
|
* Copyright (c) 2007 Rafael J. Wysocki <rjw@sisk.pl>
|
2010-07-18 12:27:13 +00:00
|
|
|
* Copyright (c) 2002 Pavel Machek <pavel@ucw.cz>
|
2005-04-16 22:20:36 +00:00
|
|
|
* Copyright (c) 2001 Patrick Mochel <mochel@osdl.org>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/suspend.h>
|
2011-05-26 16:22:53 +00:00
|
|
|
#include <linux/export.h>
|
2009-04-27 22:26:22 +00:00
|
|
|
#include <linux/smp.h>
|
2013-03-15 13:26:07 +00:00
|
|
|
#include <linux/perf_event.h>
|
x86 / hibernate: Use hlt_play_dead() when resuming from hibernation
On Intel hardware, native_play_dead() uses mwait_play_dead() by
default and only falls back to the other methods if that fails.
That also happens during resume from hibernation, when the restore
(boot) kernel runs disable_nonboot_cpus() to take all of the CPUs
except for the boot one offline.
However, that is problematic, because the address passed to
__monitor() in mwait_play_dead() is likely to be written to in the
last phase of hibernate image restoration and that causes the "dead"
CPU to start executing instructions again. Unfortunately, the page
containing the address in that CPU's instruction pointer may not be
valid any more at that point.
First, that page may have been overwritten with image kernel memory
contents already, so the instructions the CPU attempts to execute may
simply be invalid. Second, the page tables previously used by that
CPU may have been overwritten by image kernel memory contents, so the
address in its instruction pointer is impossible to resolve then.
A report from Varun Koyyalagunta and investigation carried out by
Chen Yu show that the latter sometimes happens in practice.
To prevent it from happening, temporarily change the smp_ops.play_dead
pointer during resume from hibernation so that it points to a special
"play dead" routine which uses hlt_play_dead() and avoids the
inadvertent "revivals" of "dead" CPUs this way.
A slightly unpleasant consequence of this change is that if the
system is hibernated with one or more CPUs offline, it will generally
draw more power after resume than it did before hibernation, because
the physical state entered by CPUs via hlt_play_dead() is higher-power
than the mwait_play_dead() one in the majority of cases. It is
possible to work around this, but it is unclear how much of a problem
that's going to be in practice, so the workaround will be implemented
later if it turns out to be necessary.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=106371
Reported-by: Varun Koyyalagunta <cpudebug@centtech.com>
Original-by: Chen Yu <yu.c.chen@intel.com>
Tested-by: Chen Yu <yu.c.chen@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Ingo Molnar <mingo@kernel.org>
2016-07-14 01:55:23 +00:00
|
|
|
#include <linux/tboot.h>
|
2009-04-27 22:26:22 +00:00
|
|
|
|
[PATCH] x86_64: Set up safe page tables during resume
The following patch makes swsusp avoid the possible temporary corruption
of page translation tables during resume on x86-64. This is achieved by
creating a copy of the relevant page tables that will not be modified by
swsusp and can be safely used by it on resume.
The problem is that during resume on x86-64 swsusp may temporarily
corrupt the page tables used for the direct mapping of RAM. If that
happens, a page fault occurs and cannot be handled properly, which leads
to the solid hang of the affected system. This leads to the loss of the
system's state from before suspend and may result in the loss of data or
the corruption of filesystems, so it is a serious issue. Also, it
appears to happen quite often (for me, as often as 50% of the time).
The problem is related to the fact that (at least) one of the PMD
entries used in the direct memory mapping (starting at PAGE_OFFSET)
points to a page table the physical address of which is much greater
than the physical address of the PMD entry itself. Moreover,
unfortunately, the physical address of the page table before suspend
(i.e. the one stored in the suspend image) happens to be different to
the physical address of the corresponding page table used during resume
(i.e. the one that is valid right before swsusp_arch_resume() in
arch/x86_64/kernel/suspend_asm.S is executed). Thus while the image is
restored, the "offending" PMD entry gets overwritten, so it does not
point to the right physical address any more (i.e. there's no page
table at the address pointed to by it, because it points to the address
the page table has been at during suspend). Consequently, if the PMD
entry is used later on, and it _is_ used in the process of copying the
image pages, a page fault occurs, but it cannot be handled in the normal
way and the system hangs.
In principle we can call create_resume_mapping() from
swsusp_arch_resume() (ie. from suspend_asm.S), but then the memory
allocations in create_resume_mapping(), resume_pud_mapping(), and
resume_pmd_mapping() must be made carefully so that we use _only_
NosaveFree pages in them (the other pages are overwritten by the loop in
swsusp_arch_resume()). Additionally, we are in atomic context at that
time, so we cannot use GFP_KERNEL. Moreover, if one of the allocations
fails, we should free all of the allocated pages, so we need to trace
them somehow.
All of this is done in the appended patch, except that the functions
populating the page tables are located in arch/x86_64/kernel/suspend.c
rather than in init.c. It may be done in a more elegan way in the
future, with the help of some swsusp patches that are in the works now.
[AK: move some externs into headers, renamed a function]
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-09 19:19:40 +00:00
|
|
|
#include <asm/pgtable.h>
|
2009-04-27 22:26:22 +00:00
|
|
|
#include <asm/proto.h>
|
2007-05-02 17:27:17 +00:00
|
|
|
#include <asm/mtrr.h>
|
2009-04-27 22:26:22 +00:00
|
|
|
#include <asm/page.h>
|
|
|
|
#include <asm/mce.h>
|
2009-03-31 22:23:37 +00:00
|
|
|
#include <asm/suspend.h>
|
2015-04-26 14:56:05 +00:00
|
|
|
#include <asm/fpu/internal.h>
|
2009-06-01 18:14:26 +00:00
|
|
|
#include <asm/debugreg.h>
|
2012-11-13 19:32:51 +00:00
|
|
|
#include <asm/cpu.h>
|
2015-07-30 21:31:32 +00:00
|
|
|
#include <asm/mmu_context.h>
|
x86/pm: Introduce quirk framework to save/restore extra MSR registers around suspend/resume
A bug was reported that on certain Broadwell platforms, after
resuming from S3, the CPU is running at an anomalously low
speed.
It turns out that the BIOS has modified the value of the
THERM_CONTROL register during S3, and changed it from 0 to 0x10,
thus enabled clock modulation(bit4), but with undefined CPU Duty
Cycle(bit1:3) - which causes the problem.
Here is a simple scenario to reproduce the issue:
1. Boot up the system
2. Get MSR 0x19a, it should be 0
3. Put the system into sleep, then wake it up
4. Get MSR 0x19a, it shows 0x10, while it should be 0
Although some BIOSen want to change the CPU Duty Cycle during
S3, in our case we don't want the BIOS to do any modification.
Fix this issue by introducing a more generic x86 framework to
save/restore specified MSR registers(THERM_CONTROL in this case)
for suspend/resume. This allows us to fix similar bugs in a much
simpler way in the future.
When the kernel wants to protect certain MSRs during suspending,
we simply add a quirk entry in msr_save_dmi_table, and customize
the MSR registers inside the quirk callback, for example:
u32 msr_id_need_to_save[] = {MSR_ID0, MSR_ID1, MSR_ID2...};
and the quirk mechanism ensures that, once resumed from suspend,
the MSRs indicated by these IDs will be restored to their
original, pre-suspend values.
Since both 64-bit and 32-bit kernels are affected, this patch
covers the common 64/32-bit suspend/resume code path. And
because the MSRs specified by the user might not be available or
readable in any situation, we use rdmsrl_safe() to safely save
these MSRs.
Reported-and-tested-by: Marcin Kaszewski <marcin.kaszewski@intel.com>
Signed-off-by: Chen Yu <yu.c.chen@intel.com>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Pavel Machek <pavel@ucw.cz>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: bp@suse.de
Cc: len.brown@intel.com
Cc: linux@horizon.com
Cc: luto@kernel.org
Cc: rjw@rjwysocki.net
Link: http://lkml.kernel.org/r/c9abdcbc173dd2f57e8990e304376f19287e92ba.1448382971.git.yu.c.chen@intel.com
[ More edits to the naming of data structures. ]
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2015-11-24 17:03:41 +00:00
|
|
|
#include <linux/dmi.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2009-04-27 22:26:50 +00:00
|
|
|
#ifdef CONFIG_X86_32
|
2013-08-05 22:02:49 +00:00
|
|
|
__visible unsigned long saved_context_ebx;
|
|
|
|
__visible unsigned long saved_context_esp, saved_context_ebp;
|
|
|
|
__visible unsigned long saved_context_esi, saved_context_edi;
|
|
|
|
__visible unsigned long saved_context_eflags;
|
2009-04-27 22:26:50 +00:00
|
|
|
#endif
|
2013-05-02 01:53:30 +00:00
|
|
|
struct saved_context saved_context;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
x86/pm: Introduce quirk framework to save/restore extra MSR registers around suspend/resume
A bug was reported that on certain Broadwell platforms, after
resuming from S3, the CPU is running at an anomalously low
speed.
It turns out that the BIOS has modified the value of the
THERM_CONTROL register during S3, and changed it from 0 to 0x10,
thus enabled clock modulation(bit4), but with undefined CPU Duty
Cycle(bit1:3) - which causes the problem.
Here is a simple scenario to reproduce the issue:
1. Boot up the system
2. Get MSR 0x19a, it should be 0
3. Put the system into sleep, then wake it up
4. Get MSR 0x19a, it shows 0x10, while it should be 0
Although some BIOSen want to change the CPU Duty Cycle during
S3, in our case we don't want the BIOS to do any modification.
Fix this issue by introducing a more generic x86 framework to
save/restore specified MSR registers(THERM_CONTROL in this case)
for suspend/resume. This allows us to fix similar bugs in a much
simpler way in the future.
When the kernel wants to protect certain MSRs during suspending,
we simply add a quirk entry in msr_save_dmi_table, and customize
the MSR registers inside the quirk callback, for example:
u32 msr_id_need_to_save[] = {MSR_ID0, MSR_ID1, MSR_ID2...};
and the quirk mechanism ensures that, once resumed from suspend,
the MSRs indicated by these IDs will be restored to their
original, pre-suspend values.
Since both 64-bit and 32-bit kernels are affected, this patch
covers the common 64/32-bit suspend/resume code path. And
because the MSRs specified by the user might not be available or
readable in any situation, we use rdmsrl_safe() to safely save
these MSRs.
Reported-and-tested-by: Marcin Kaszewski <marcin.kaszewski@intel.com>
Signed-off-by: Chen Yu <yu.c.chen@intel.com>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Pavel Machek <pavel@ucw.cz>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: bp@suse.de
Cc: len.brown@intel.com
Cc: linux@horizon.com
Cc: luto@kernel.org
Cc: rjw@rjwysocki.net
Link: http://lkml.kernel.org/r/c9abdcbc173dd2f57e8990e304376f19287e92ba.1448382971.git.yu.c.chen@intel.com
[ More edits to the naming of data structures. ]
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2015-11-24 17:03:41 +00:00
|
|
|
static void msr_save_context(struct saved_context *ctxt)
|
|
|
|
{
|
|
|
|
struct saved_msr *msr = ctxt->saved_msrs.array;
|
|
|
|
struct saved_msr *end = msr + ctxt->saved_msrs.num;
|
|
|
|
|
|
|
|
while (msr < end) {
|
|
|
|
msr->valid = !rdmsrl_safe(msr->info.msr_no, &msr->info.reg.q);
|
|
|
|
msr++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void msr_restore_context(struct saved_context *ctxt)
|
|
|
|
{
|
|
|
|
struct saved_msr *msr = ctxt->saved_msrs.array;
|
|
|
|
struct saved_msr *end = msr + ctxt->saved_msrs.num;
|
|
|
|
|
|
|
|
while (msr < end) {
|
|
|
|
if (msr->valid)
|
|
|
|
wrmsrl(msr->info.msr_no, msr->info.reg.q);
|
|
|
|
msr++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-01-30 12:30:04 +00:00
|
|
|
/**
|
|
|
|
* __save_processor_state - save CPU registers before creating a
|
|
|
|
* hibernation image and before restoring the memory state from it
|
|
|
|
* @ctxt - structure to store the registers contents in
|
|
|
|
*
|
|
|
|
* NOTE: If there is a CPU register the modification of which by the
|
|
|
|
* boot kernel (ie. the kernel used for loading the hibernation image)
|
|
|
|
* might affect the operations of the restored target kernel (ie. the one
|
|
|
|
* saved in the hibernation image), then its contents must be saved by this
|
|
|
|
* function. In other words, if kernel A is hibernated and different
|
|
|
|
* kernel B is used for loading the hibernation image into memory, the
|
|
|
|
* kernel A's __save_processor_state() function must save all registers
|
|
|
|
* needed by kernel A, so that it can operate correctly after the resume
|
|
|
|
* regardless of what kernel B does in the meantime.
|
|
|
|
*/
|
2008-01-30 12:31:23 +00:00
|
|
|
static void __save_processor_state(struct saved_context *ctxt)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2009-04-27 22:27:00 +00:00
|
|
|
#ifdef CONFIG_X86_32
|
|
|
|
mtrr_save_fixed_ranges(NULL);
|
|
|
|
#endif
|
2005-04-16 22:20:36 +00:00
|
|
|
kernel_fpu_begin();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* descriptor tables
|
|
|
|
*/
|
2009-04-27 22:27:00 +00:00
|
|
|
#ifdef CONFIG_X86_32
|
|
|
|
store_idt(&ctxt->idt);
|
|
|
|
#else
|
|
|
|
/* CONFIG_X86_64 */
|
2007-10-19 18:35:03 +00:00
|
|
|
store_idt((struct desc_ptr *)&ctxt->idt_limit);
|
2009-04-27 22:27:00 +00:00
|
|
|
#endif
|
2013-05-02 01:53:30 +00:00
|
|
|
/*
|
|
|
|
* We save it here, but restore it only in the hibernate case.
|
|
|
|
* For ACPI S3 resume, this is loaded via 'early_gdt_desc' in 64-bit
|
|
|
|
* mode in "secondary_startup_64". In 32-bit mode it is done via
|
|
|
|
* 'pmode_gdt' in wakeup_start.
|
|
|
|
*/
|
|
|
|
ctxt->gdt_desc.size = GDT_SIZE - 1;
|
|
|
|
ctxt->gdt_desc.address = (unsigned long)get_cpu_gdt_table(smp_processor_id());
|
|
|
|
|
2007-10-19 18:35:03 +00:00
|
|
|
store_tr(ctxt->tr);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* XMM0..XMM15 should be handled by kernel_fpu_begin(). */
|
|
|
|
/*
|
|
|
|
* segment registers
|
|
|
|
*/
|
2009-04-27 22:27:00 +00:00
|
|
|
#ifdef CONFIG_X86_32
|
|
|
|
savesegment(es, ctxt->es);
|
|
|
|
savesegment(fs, ctxt->fs);
|
|
|
|
savesegment(gs, ctxt->gs);
|
|
|
|
savesegment(ss, ctxt->ss);
|
|
|
|
#else
|
|
|
|
/* CONFIG_X86_64 */
|
2005-04-16 22:20:36 +00:00
|
|
|
asm volatile ("movw %%ds, %0" : "=m" (ctxt->ds));
|
|
|
|
asm volatile ("movw %%es, %0" : "=m" (ctxt->es));
|
|
|
|
asm volatile ("movw %%fs, %0" : "=m" (ctxt->fs));
|
|
|
|
asm volatile ("movw %%gs, %0" : "=m" (ctxt->gs));
|
|
|
|
asm volatile ("movw %%ss, %0" : "=m" (ctxt->ss));
|
|
|
|
|
|
|
|
rdmsrl(MSR_FS_BASE, ctxt->fs_base);
|
|
|
|
rdmsrl(MSR_GS_BASE, ctxt->gs_base);
|
|
|
|
rdmsrl(MSR_KERNEL_GS_BASE, ctxt->gs_kernel_base);
|
2007-05-02 17:27:17 +00:00
|
|
|
mtrr_save_fixed_ranges(NULL);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2009-04-27 22:27:00 +00:00
|
|
|
rdmsrl(MSR_EFER, ctxt->efer);
|
|
|
|
#endif
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
2008-02-09 22:24:09 +00:00
|
|
|
* control registers
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2007-07-22 09:12:29 +00:00
|
|
|
ctxt->cr0 = read_cr0();
|
|
|
|
ctxt->cr2 = read_cr2();
|
|
|
|
ctxt->cr3 = read_cr3();
|
2016-09-29 19:48:12 +00:00
|
|
|
ctxt->cr4 = __read_cr4();
|
2014-10-24 22:58:08 +00:00
|
|
|
#ifdef CONFIG_X86_64
|
2007-07-22 09:12:29 +00:00
|
|
|
ctxt->cr8 = read_cr8();
|
2009-04-27 22:27:00 +00:00
|
|
|
#endif
|
2010-06-07 22:32:49 +00:00
|
|
|
ctxt->misc_enable_saved = !rdmsrl_safe(MSR_IA32_MISC_ENABLE,
|
|
|
|
&ctxt->misc_enable);
|
x86/pm: Introduce quirk framework to save/restore extra MSR registers around suspend/resume
A bug was reported that on certain Broadwell platforms, after
resuming from S3, the CPU is running at an anomalously low
speed.
It turns out that the BIOS has modified the value of the
THERM_CONTROL register during S3, and changed it from 0 to 0x10,
thus enabled clock modulation(bit4), but with undefined CPU Duty
Cycle(bit1:3) - which causes the problem.
Here is a simple scenario to reproduce the issue:
1. Boot up the system
2. Get MSR 0x19a, it should be 0
3. Put the system into sleep, then wake it up
4. Get MSR 0x19a, it shows 0x10, while it should be 0
Although some BIOSen want to change the CPU Duty Cycle during
S3, in our case we don't want the BIOS to do any modification.
Fix this issue by introducing a more generic x86 framework to
save/restore specified MSR registers(THERM_CONTROL in this case)
for suspend/resume. This allows us to fix similar bugs in a much
simpler way in the future.
When the kernel wants to protect certain MSRs during suspending,
we simply add a quirk entry in msr_save_dmi_table, and customize
the MSR registers inside the quirk callback, for example:
u32 msr_id_need_to_save[] = {MSR_ID0, MSR_ID1, MSR_ID2...};
and the quirk mechanism ensures that, once resumed from suspend,
the MSRs indicated by these IDs will be restored to their
original, pre-suspend values.
Since both 64-bit and 32-bit kernels are affected, this patch
covers the common 64/32-bit suspend/resume code path. And
because the MSRs specified by the user might not be available or
readable in any situation, we use rdmsrl_safe() to safely save
these MSRs.
Reported-and-tested-by: Marcin Kaszewski <marcin.kaszewski@intel.com>
Signed-off-by: Chen Yu <yu.c.chen@intel.com>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Pavel Machek <pavel@ucw.cz>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: bp@suse.de
Cc: len.brown@intel.com
Cc: linux@horizon.com
Cc: luto@kernel.org
Cc: rjw@rjwysocki.net
Link: http://lkml.kernel.org/r/c9abdcbc173dd2f57e8990e304376f19287e92ba.1448382971.git.yu.c.chen@intel.com
[ More edits to the naming of data structures. ]
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2015-11-24 17:03:41 +00:00
|
|
|
msr_save_context(ctxt);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2009-04-27 22:27:00 +00:00
|
|
|
/* Needed by apm.c */
|
2005-04-16 22:20:36 +00:00
|
|
|
void save_processor_state(void)
|
|
|
|
{
|
|
|
|
__save_processor_state(&saved_context);
|
2012-02-13 13:07:27 +00:00
|
|
|
x86_platform.save_sched_clock_state();
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2009-04-27 22:27:00 +00:00
|
|
|
#ifdef CONFIG_X86_32
|
|
|
|
EXPORT_SYMBOL(save_processor_state);
|
|
|
|
#endif
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-10-30 22:59:28 +00:00
|
|
|
static void do_fpu_end(void)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-10-30 22:59:28 +00:00
|
|
|
/*
|
2009-04-27 22:27:05 +00:00
|
|
|
* Restore FPU regs if necessary.
|
2005-10-30 22:59:28 +00:00
|
|
|
*/
|
|
|
|
kernel_fpu_end();
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2009-04-27 22:27:05 +00:00
|
|
|
static void fix_processor_context(void)
|
|
|
|
{
|
|
|
|
int cpu = smp_processor_id();
|
2015-03-06 03:19:05 +00:00
|
|
|
struct tss_struct *t = &per_cpu(cpu_tss, cpu);
|
2013-04-05 20:42:24 +00:00
|
|
|
#ifdef CONFIG_X86_64
|
|
|
|
struct desc_struct *desc = get_cpu_gdt_table(cpu);
|
|
|
|
tss_desc tss;
|
|
|
|
#endif
|
2009-04-27 22:27:05 +00:00
|
|
|
set_tss_desc(cpu, t); /*
|
|
|
|
* This just modifies memory; should not be
|
|
|
|
* necessary. But... This is necessary, because
|
|
|
|
* 386 hardware has concept of busy TSS or some
|
|
|
|
* similar stupidity.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef CONFIG_X86_64
|
2013-04-05 20:42:24 +00:00
|
|
|
memcpy(&tss, &desc[GDT_ENTRY_TSS], sizeof(tss_desc));
|
|
|
|
tss.type = 0x9; /* The available 64-bit TSS (see AMD vol 2, pg 91 */
|
|
|
|
write_gdt_entry(desc, GDT_ENTRY_TSS, &tss, DESC_TSS);
|
2009-04-27 22:27:05 +00:00
|
|
|
|
|
|
|
syscall_init(); /* This sets MSR_*STAR and related */
|
|
|
|
#endif
|
|
|
|
load_TR_desc(); /* This does ltr */
|
2015-07-30 21:31:32 +00:00
|
|
|
load_mm_ldt(current->active_mm); /* This does lldt */
|
2015-04-24 08:02:32 +00:00
|
|
|
|
|
|
|
fpu__resume_cpu();
|
2009-04-27 22:27:05 +00:00
|
|
|
}
|
|
|
|
|
2008-01-30 12:30:04 +00:00
|
|
|
/**
|
|
|
|
* __restore_processor_state - restore the contents of CPU registers saved
|
|
|
|
* by __save_processor_state()
|
|
|
|
* @ctxt - structure to load the registers contents from
|
|
|
|
*/
|
2014-06-25 00:58:26 +00:00
|
|
|
static void notrace __restore_processor_state(struct saved_context *ctxt)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2010-06-07 22:32:49 +00:00
|
|
|
if (ctxt->misc_enable_saved)
|
|
|
|
wrmsrl(MSR_IA32_MISC_ENABLE, ctxt->misc_enable);
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* control registers
|
|
|
|
*/
|
2009-04-27 22:27:05 +00:00
|
|
|
/* cr4 was introduced in the Pentium CPU */
|
|
|
|
#ifdef CONFIG_X86_32
|
|
|
|
if (ctxt->cr4)
|
2014-10-24 22:58:08 +00:00
|
|
|
__write_cr4(ctxt->cr4);
|
2009-04-27 22:27:05 +00:00
|
|
|
#else
|
|
|
|
/* CONFIG X86_64 */
|
2007-05-02 17:27:07 +00:00
|
|
|
wrmsrl(MSR_EFER, ctxt->efer);
|
2007-07-22 09:12:29 +00:00
|
|
|
write_cr8(ctxt->cr8);
|
2014-10-24 22:58:08 +00:00
|
|
|
__write_cr4(ctxt->cr4);
|
2009-04-27 22:27:05 +00:00
|
|
|
#endif
|
2007-07-22 09:12:29 +00:00
|
|
|
write_cr3(ctxt->cr3);
|
|
|
|
write_cr2(ctxt->cr2);
|
|
|
|
write_cr0(ctxt->cr0);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-06-25 21:55:14 +00:00
|
|
|
/*
|
|
|
|
* now restore the descriptor tables to their proper values
|
|
|
|
* ltr is done i fix_processor_context().
|
|
|
|
*/
|
2009-04-27 22:27:05 +00:00
|
|
|
#ifdef CONFIG_X86_32
|
|
|
|
load_idt(&ctxt->idt);
|
|
|
|
#else
|
|
|
|
/* CONFIG_X86_64 */
|
2007-10-19 18:35:03 +00:00
|
|
|
load_idt((const struct desc_ptr *)&ctxt->idt_limit);
|
2009-04-27 22:27:05 +00:00
|
|
|
#endif
|
2005-06-25 21:55:14 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* segment registers
|
|
|
|
*/
|
2009-04-27 22:27:05 +00:00
|
|
|
#ifdef CONFIG_X86_32
|
|
|
|
loadsegment(es, ctxt->es);
|
|
|
|
loadsegment(fs, ctxt->fs);
|
|
|
|
loadsegment(gs, ctxt->gs);
|
|
|
|
loadsegment(ss, ctxt->ss);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sysenter MSRs
|
|
|
|
*/
|
|
|
|
if (boot_cpu_has(X86_FEATURE_SEP))
|
|
|
|
enable_sep_cpu();
|
|
|
|
#else
|
|
|
|
/* CONFIG_X86_64 */
|
2005-04-16 22:20:36 +00:00
|
|
|
asm volatile ("movw %0, %%ds" :: "r" (ctxt->ds));
|
|
|
|
asm volatile ("movw %0, %%es" :: "r" (ctxt->es));
|
|
|
|
asm volatile ("movw %0, %%fs" :: "r" (ctxt->fs));
|
|
|
|
load_gs_index(ctxt->gs);
|
|
|
|
asm volatile ("movw %0, %%ss" :: "r" (ctxt->ss));
|
|
|
|
|
|
|
|
wrmsrl(MSR_FS_BASE, ctxt->fs_base);
|
|
|
|
wrmsrl(MSR_GS_BASE, ctxt->gs_base);
|
|
|
|
wrmsrl(MSR_KERNEL_GS_BASE, ctxt->gs_kernel_base);
|
2009-04-27 22:27:05 +00:00
|
|
|
#endif
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
fix_processor_context();
|
|
|
|
|
|
|
|
do_fpu_end();
|
2016-12-13 13:14:17 +00:00
|
|
|
tsc_verify_tsc_adjust(true);
|
2012-04-01 16:53:36 +00:00
|
|
|
x86_platform.restore_sched_clock_state();
|
2009-08-20 01:05:36 +00:00
|
|
|
mtrr_bp_restore();
|
2013-03-15 13:26:07 +00:00
|
|
|
perf_restore_debug_store();
|
x86/pm: Introduce quirk framework to save/restore extra MSR registers around suspend/resume
A bug was reported that on certain Broadwell platforms, after
resuming from S3, the CPU is running at an anomalously low
speed.
It turns out that the BIOS has modified the value of the
THERM_CONTROL register during S3, and changed it from 0 to 0x10,
thus enabled clock modulation(bit4), but with undefined CPU Duty
Cycle(bit1:3) - which causes the problem.
Here is a simple scenario to reproduce the issue:
1. Boot up the system
2. Get MSR 0x19a, it should be 0
3. Put the system into sleep, then wake it up
4. Get MSR 0x19a, it shows 0x10, while it should be 0
Although some BIOSen want to change the CPU Duty Cycle during
S3, in our case we don't want the BIOS to do any modification.
Fix this issue by introducing a more generic x86 framework to
save/restore specified MSR registers(THERM_CONTROL in this case)
for suspend/resume. This allows us to fix similar bugs in a much
simpler way in the future.
When the kernel wants to protect certain MSRs during suspending,
we simply add a quirk entry in msr_save_dmi_table, and customize
the MSR registers inside the quirk callback, for example:
u32 msr_id_need_to_save[] = {MSR_ID0, MSR_ID1, MSR_ID2...};
and the quirk mechanism ensures that, once resumed from suspend,
the MSRs indicated by these IDs will be restored to their
original, pre-suspend values.
Since both 64-bit and 32-bit kernels are affected, this patch
covers the common 64/32-bit suspend/resume code path. And
because the MSRs specified by the user might not be available or
readable in any situation, we use rdmsrl_safe() to safely save
these MSRs.
Reported-and-tested-by: Marcin Kaszewski <marcin.kaszewski@intel.com>
Signed-off-by: Chen Yu <yu.c.chen@intel.com>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Pavel Machek <pavel@ucw.cz>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: bp@suse.de
Cc: len.brown@intel.com
Cc: linux@horizon.com
Cc: luto@kernel.org
Cc: rjw@rjwysocki.net
Link: http://lkml.kernel.org/r/c9abdcbc173dd2f57e8990e304376f19287e92ba.1448382971.git.yu.c.chen@intel.com
[ More edits to the naming of data structures. ]
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2015-11-24 17:03:41 +00:00
|
|
|
msr_restore_context(ctxt);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2009-04-27 22:27:05 +00:00
|
|
|
/* Needed by apm.c */
|
2014-06-25 00:58:26 +00:00
|
|
|
void notrace restore_processor_state(void)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
__restore_processor_state(&saved_context);
|
|
|
|
}
|
2009-04-27 22:27:05 +00:00
|
|
|
#ifdef CONFIG_X86_32
|
|
|
|
EXPORT_SYMBOL(restore_processor_state);
|
|
|
|
#endif
|
2012-11-13 19:32:42 +00:00
|
|
|
|
x86 / hibernate: Use hlt_play_dead() when resuming from hibernation
On Intel hardware, native_play_dead() uses mwait_play_dead() by
default and only falls back to the other methods if that fails.
That also happens during resume from hibernation, when the restore
(boot) kernel runs disable_nonboot_cpus() to take all of the CPUs
except for the boot one offline.
However, that is problematic, because the address passed to
__monitor() in mwait_play_dead() is likely to be written to in the
last phase of hibernate image restoration and that causes the "dead"
CPU to start executing instructions again. Unfortunately, the page
containing the address in that CPU's instruction pointer may not be
valid any more at that point.
First, that page may have been overwritten with image kernel memory
contents already, so the instructions the CPU attempts to execute may
simply be invalid. Second, the page tables previously used by that
CPU may have been overwritten by image kernel memory contents, so the
address in its instruction pointer is impossible to resolve then.
A report from Varun Koyyalagunta and investigation carried out by
Chen Yu show that the latter sometimes happens in practice.
To prevent it from happening, temporarily change the smp_ops.play_dead
pointer during resume from hibernation so that it points to a special
"play dead" routine which uses hlt_play_dead() and avoids the
inadvertent "revivals" of "dead" CPUs this way.
A slightly unpleasant consequence of this change is that if the
system is hibernated with one or more CPUs offline, it will generally
draw more power after resume than it did before hibernation, because
the physical state entered by CPUs via hlt_play_dead() is higher-power
than the mwait_play_dead() one in the majority of cases. It is
possible to work around this, but it is unclear how much of a problem
that's going to be in practice, so the workaround will be implemented
later if it turns out to be necessary.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=106371
Reported-by: Varun Koyyalagunta <cpudebug@centtech.com>
Original-by: Chen Yu <yu.c.chen@intel.com>
Tested-by: Chen Yu <yu.c.chen@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Ingo Molnar <mingo@kernel.org>
2016-07-14 01:55:23 +00:00
|
|
|
#if defined(CONFIG_HIBERNATION) && defined(CONFIG_HOTPLUG_CPU)
|
|
|
|
static void resume_play_dead(void)
|
|
|
|
{
|
|
|
|
play_dead_common();
|
|
|
|
tboot_shutdown(TB_SHUTDOWN_WFS);
|
|
|
|
hlt_play_dead();
|
|
|
|
}
|
|
|
|
|
|
|
|
int hibernate_resume_nonboot_cpu_disable(void)
|
|
|
|
{
|
|
|
|
void (*play_dead)(void) = smp_ops.play_dead;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ensure that MONITOR/MWAIT will not be used in the "play dead" loop
|
|
|
|
* during hibernate image restoration, because it is likely that the
|
|
|
|
* monitored address will be actually written to at that time and then
|
|
|
|
* the "dead" CPU will attempt to execute instructions again, but the
|
|
|
|
* address in its instruction pointer may not be possible to resolve
|
|
|
|
* any more at that point (the page tables used by it previously may
|
|
|
|
* have been overwritten by hibernate image data).
|
|
|
|
*/
|
|
|
|
smp_ops.play_dead = resume_play_dead;
|
|
|
|
ret = disable_nonboot_cpus();
|
|
|
|
smp_ops.play_dead = play_dead;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-11-13 19:32:42 +00:00
|
|
|
/*
|
|
|
|
* When bsp_check() is called in hibernate and suspend, cpu hotplug
|
|
|
|
* is disabled already. So it's unnessary to handle race condition between
|
|
|
|
* cpumask query and cpu hotplug.
|
|
|
|
*/
|
|
|
|
static int bsp_check(void)
|
|
|
|
{
|
|
|
|
if (cpumask_first(cpu_online_mask) != 0) {
|
|
|
|
pr_warn("CPU0 is offline.\n");
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bsp_pm_callback(struct notifier_block *nb, unsigned long action,
|
|
|
|
void *ptr)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
switch (action) {
|
|
|
|
case PM_SUSPEND_PREPARE:
|
|
|
|
case PM_HIBERNATION_PREPARE:
|
|
|
|
ret = bsp_check();
|
|
|
|
break;
|
2012-11-13 19:32:51 +00:00
|
|
|
#ifdef CONFIG_DEBUG_HOTPLUG_CPU0
|
|
|
|
case PM_RESTORE_PREPARE:
|
|
|
|
/*
|
|
|
|
* When system resumes from hibernation, online CPU0 because
|
|
|
|
* 1. it's required for resume and
|
|
|
|
* 2. the CPU was online before hibernation
|
|
|
|
*/
|
|
|
|
if (!cpu_online(0))
|
|
|
|
_debug_hotplug_cpu(0, 1);
|
|
|
|
break;
|
|
|
|
case PM_POST_RESTORE:
|
|
|
|
/*
|
|
|
|
* When a resume really happens, this code won't be called.
|
|
|
|
*
|
|
|
|
* This code is called only when user space hibernation software
|
|
|
|
* prepares for snapshot device during boot time. So we just
|
|
|
|
* call _debug_hotplug_cpu() to restore to CPU0's state prior to
|
|
|
|
* preparing the snapshot device.
|
|
|
|
*
|
|
|
|
* This works for normal boot case in our CPU0 hotplug debug
|
|
|
|
* mode, i.e. CPU0 is offline and user mode hibernation
|
|
|
|
* software initializes during boot time.
|
|
|
|
*
|
|
|
|
* If CPU0 is online and user application accesses snapshot
|
|
|
|
* device after boot time, this will offline CPU0 and user may
|
|
|
|
* see different CPU0 state before and after accessing
|
|
|
|
* the snapshot device. But hopefully this is not a case when
|
|
|
|
* user debugging CPU0 hotplug. Even if users hit this case,
|
|
|
|
* they can easily online CPU0 back.
|
|
|
|
*
|
|
|
|
* To simplify this debug code, we only consider normal boot
|
|
|
|
* case. Otherwise we need to remember CPU0's state and restore
|
|
|
|
* to that state and resolve racy conditions etc.
|
|
|
|
*/
|
|
|
|
_debug_hotplug_cpu(0, 0);
|
|
|
|
break;
|
|
|
|
#endif
|
2012-11-13 19:32:42 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return notifier_from_errno(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __init bsp_pm_check_init(void)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Set this bsp_pm_callback as lower priority than
|
|
|
|
* cpu_hotplug_pm_callback. So cpu_hotplug_pm_callback will be called
|
|
|
|
* earlier to disable cpu hotplug before bsp online check.
|
|
|
|
*/
|
|
|
|
pm_notifier(bsp_pm_callback, -INT_MAX);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
core_initcall(bsp_pm_check_init);
|
x86/pm: Introduce quirk framework to save/restore extra MSR registers around suspend/resume
A bug was reported that on certain Broadwell platforms, after
resuming from S3, the CPU is running at an anomalously low
speed.
It turns out that the BIOS has modified the value of the
THERM_CONTROL register during S3, and changed it from 0 to 0x10,
thus enabled clock modulation(bit4), but with undefined CPU Duty
Cycle(bit1:3) - which causes the problem.
Here is a simple scenario to reproduce the issue:
1. Boot up the system
2. Get MSR 0x19a, it should be 0
3. Put the system into sleep, then wake it up
4. Get MSR 0x19a, it shows 0x10, while it should be 0
Although some BIOSen want to change the CPU Duty Cycle during
S3, in our case we don't want the BIOS to do any modification.
Fix this issue by introducing a more generic x86 framework to
save/restore specified MSR registers(THERM_CONTROL in this case)
for suspend/resume. This allows us to fix similar bugs in a much
simpler way in the future.
When the kernel wants to protect certain MSRs during suspending,
we simply add a quirk entry in msr_save_dmi_table, and customize
the MSR registers inside the quirk callback, for example:
u32 msr_id_need_to_save[] = {MSR_ID0, MSR_ID1, MSR_ID2...};
and the quirk mechanism ensures that, once resumed from suspend,
the MSRs indicated by these IDs will be restored to their
original, pre-suspend values.
Since both 64-bit and 32-bit kernels are affected, this patch
covers the common 64/32-bit suspend/resume code path. And
because the MSRs specified by the user might not be available or
readable in any situation, we use rdmsrl_safe() to safely save
these MSRs.
Reported-and-tested-by: Marcin Kaszewski <marcin.kaszewski@intel.com>
Signed-off-by: Chen Yu <yu.c.chen@intel.com>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Pavel Machek <pavel@ucw.cz>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: bp@suse.de
Cc: len.brown@intel.com
Cc: linux@horizon.com
Cc: luto@kernel.org
Cc: rjw@rjwysocki.net
Link: http://lkml.kernel.org/r/c9abdcbc173dd2f57e8990e304376f19287e92ba.1448382971.git.yu.c.chen@intel.com
[ More edits to the naming of data structures. ]
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2015-11-24 17:03:41 +00:00
|
|
|
|
|
|
|
static int msr_init_context(const u32 *msr_id, const int total_num)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
struct saved_msr *msr_array;
|
|
|
|
|
|
|
|
if (saved_context.saved_msrs.array || saved_context.saved_msrs.num > 0) {
|
|
|
|
pr_err("x86/pm: MSR quirk already applied, please check your DMI match table.\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
msr_array = kmalloc_array(total_num, sizeof(struct saved_msr), GFP_KERNEL);
|
|
|
|
if (!msr_array) {
|
|
|
|
pr_err("x86/pm: Can not allocate memory to save/restore MSRs during suspend.\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < total_num; i++) {
|
|
|
|
msr_array[i].info.msr_no = msr_id[i];
|
|
|
|
msr_array[i].valid = false;
|
|
|
|
msr_array[i].info.reg.q = 0;
|
|
|
|
}
|
|
|
|
saved_context.saved_msrs.num = total_num;
|
|
|
|
saved_context.saved_msrs.array = msr_array;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The following section is a quirk framework for problematic BIOSen:
|
|
|
|
* Sometimes MSRs are modified by the BIOSen after suspended to
|
|
|
|
* RAM, this might cause unexpected behavior after wakeup.
|
|
|
|
* Thus we save/restore these specified MSRs across suspend/resume
|
|
|
|
* in order to work around it.
|
|
|
|
*
|
|
|
|
* For any further problematic BIOSen/platforms,
|
|
|
|
* please add your own function similar to msr_initialize_bdw.
|
|
|
|
*/
|
|
|
|
static int msr_initialize_bdw(const struct dmi_system_id *d)
|
|
|
|
{
|
|
|
|
/* Add any extra MSR ids into this array. */
|
|
|
|
u32 bdw_msr_id[] = { MSR_IA32_THERM_CONTROL };
|
|
|
|
|
|
|
|
pr_info("x86/pm: %s detected, MSR saving is needed during suspending.\n", d->ident);
|
|
|
|
return msr_init_context(bdw_msr_id, ARRAY_SIZE(bdw_msr_id));
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct dmi_system_id msr_save_dmi_table[] = {
|
|
|
|
{
|
|
|
|
.callback = msr_initialize_bdw,
|
|
|
|
.ident = "BROADWELL BDX_EP",
|
|
|
|
.matches = {
|
|
|
|
DMI_MATCH(DMI_PRODUCT_NAME, "GRANTLEY"),
|
|
|
|
DMI_MATCH(DMI_PRODUCT_VERSION, "E63448-400"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
static int pm_check_save_msr(void)
|
|
|
|
{
|
|
|
|
dmi_check_system(msr_save_dmi_table);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
device_initcall(pm_check_save_msr);
|