aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/include/asm/msr.h10
-rw-r--r--arch/x86/include/asm/suspend_32.h1
-rw-r--r--arch/x86/include/asm/suspend_64.h1
-rw-r--r--arch/x86/power/cpu.c92
4 files changed, 104 insertions, 0 deletions
diff --git a/arch/x86/include/asm/msr.h b/arch/x86/include/asm/msr.h
index 77d8b284e4a7..24feb3c1dc41 100644
--- a/arch/x86/include/asm/msr.h
+++ b/arch/x86/include/asm/msr.h
@@ -32,6 +32,16 @@ struct msr_regs_info {
32 int err; 32 int err;
33}; 33};
34 34
35struct saved_msr {
36 bool valid;
37 struct msr_info info;
38};
39
40struct saved_msrs {
41 unsigned int num;
42 struct saved_msr *array;
43};
44
35static inline unsigned long long native_read_tscp(unsigned int *aux) 45static inline unsigned long long native_read_tscp(unsigned int *aux)
36{ 46{
37 unsigned long low, high; 47 unsigned long low, high;
diff --git a/arch/x86/include/asm/suspend_32.h b/arch/x86/include/asm/suspend_32.h
index d1793f06854d..8e9dbe7b73a1 100644
--- a/arch/x86/include/asm/suspend_32.h
+++ b/arch/x86/include/asm/suspend_32.h
@@ -15,6 +15,7 @@ struct saved_context {
15 unsigned long cr0, cr2, cr3, cr4; 15 unsigned long cr0, cr2, cr3, cr4;
16 u64 misc_enable; 16 u64 misc_enable;
17 bool misc_enable_saved; 17 bool misc_enable_saved;
18 struct saved_msrs saved_msrs;
18 struct desc_ptr gdt_desc; 19 struct desc_ptr gdt_desc;
19 struct desc_ptr idt; 20 struct desc_ptr idt;
20 u16 ldt; 21 u16 ldt;
diff --git a/arch/x86/include/asm/suspend_64.h b/arch/x86/include/asm/suspend_64.h
index 7ebf0ebe4e68..6136a18152af 100644
--- a/arch/x86/include/asm/suspend_64.h
+++ b/arch/x86/include/asm/suspend_64.h
@@ -24,6 +24,7 @@ struct saved_context {
24 unsigned long cr0, cr2, cr3, cr4, cr8; 24 unsigned long cr0, cr2, cr3, cr4, cr8;
25 u64 misc_enable; 25 u64 misc_enable;
26 bool misc_enable_saved; 26 bool misc_enable_saved;
27 struct saved_msrs saved_msrs;
27 unsigned long efer; 28 unsigned long efer;
28 u16 gdt_pad; /* Unused */ 29 u16 gdt_pad; /* Unused */
29 struct desc_ptr gdt_desc; 30 struct desc_ptr gdt_desc;
diff --git a/arch/x86/power/cpu.c b/arch/x86/power/cpu.c
index 9ab52791fed5..d5f64996394a 100644
--- a/arch/x86/power/cpu.c
+++ b/arch/x86/power/cpu.c
@@ -23,6 +23,7 @@
23#include <asm/debugreg.h> 23#include <asm/debugreg.h>
24#include <asm/cpu.h> 24#include <asm/cpu.h>
25#include <asm/mmu_context.h> 25#include <asm/mmu_context.h>
26#include <linux/dmi.h>
26 27
27#ifdef CONFIG_X86_32 28#ifdef CONFIG_X86_32
28__visible unsigned long saved_context_ebx; 29__visible unsigned long saved_context_ebx;
@@ -32,6 +33,29 @@ __visible unsigned long saved_context_eflags;
32#endif 33#endif
33struct saved_context saved_context; 34struct saved_context saved_context;
34 35
36static void msr_save_context(struct saved_context *ctxt)
37{
38 struct saved_msr *msr = ctxt->saved_msrs.array;
39 struct saved_msr *end = msr + ctxt->saved_msrs.num;
40
41 while (msr < end) {
42 msr->valid = !rdmsrl_safe(msr->info.msr_no, &msr->info.reg.q);
43 msr++;
44 }
45}
46
47static void msr_restore_context(struct saved_context *ctxt)
48{
49 struct saved_msr *msr = ctxt->saved_msrs.array;
50 struct saved_msr *end = msr + ctxt->saved_msrs.num;
51
52 while (msr < end) {
53 if (msr->valid)
54 wrmsrl(msr->info.msr_no, msr->info.reg.q);
55 msr++;
56 }
57}
58
35/** 59/**
36 * __save_processor_state - save CPU registers before creating a 60 * __save_processor_state - save CPU registers before creating a
37 * hibernation image and before restoring the memory state from it 61 * hibernation image and before restoring the memory state from it
@@ -111,6 +135,7 @@ static void __save_processor_state(struct saved_context *ctxt)
111#endif 135#endif
112 ctxt->misc_enable_saved = !rdmsrl_safe(MSR_IA32_MISC_ENABLE, 136 ctxt->misc_enable_saved = !rdmsrl_safe(MSR_IA32_MISC_ENABLE,
113 &ctxt->misc_enable); 137 &ctxt->misc_enable);
138 msr_save_context(ctxt);
114} 139}
115 140
116/* Needed by apm.c */ 141/* Needed by apm.c */
@@ -229,6 +254,7 @@ static void notrace __restore_processor_state(struct saved_context *ctxt)
229 x86_platform.restore_sched_clock_state(); 254 x86_platform.restore_sched_clock_state();
230 mtrr_bp_restore(); 255 mtrr_bp_restore();
231 perf_restore_debug_store(); 256 perf_restore_debug_store();
257 msr_restore_context(ctxt);
232} 258}
233 259
234/* Needed by apm.c */ 260/* Needed by apm.c */
@@ -320,3 +346,69 @@ static int __init bsp_pm_check_init(void)
320} 346}
321 347
322core_initcall(bsp_pm_check_init); 348core_initcall(bsp_pm_check_init);
349
350static int msr_init_context(const u32 *msr_id, const int total_num)
351{
352 int i = 0;
353 struct saved_msr *msr_array;
354
355 if (saved_context.saved_msrs.array || saved_context.saved_msrs.num > 0) {
356 pr_err("x86/pm: MSR quirk already applied, please check your DMI match table.\n");
357 return -EINVAL;
358 }
359
360 msr_array = kmalloc_array(total_num, sizeof(struct saved_msr), GFP_KERNEL);
361 if (!msr_array) {
362 pr_err("x86/pm: Can not allocate memory to save/restore MSRs during suspend.\n");
363 return -ENOMEM;
364 }
365
366 for (i = 0; i < total_num; i++) {
367 msr_array[i].info.msr_no = msr_id[i];
368 msr_array[i].valid = false;
369 msr_array[i].info.reg.q = 0;
370 }
371 saved_context.saved_msrs.num = total_num;
372 saved_context.saved_msrs.array = msr_array;
373
374 return 0;
375}
376
377/*
378 * The following section is a quirk framework for problematic BIOSen:
379 * Sometimes MSRs are modified by the BIOSen after suspended to
380 * RAM, this might cause unexpected behavior after wakeup.
381 * Thus we save/restore these specified MSRs across suspend/resume
382 * in order to work around it.
383 *
384 * For any further problematic BIOSen/platforms,
385 * please add your own function similar to msr_initialize_bdw.
386 */
387static int msr_initialize_bdw(const struct dmi_system_id *d)
388{
389 /* Add any extra MSR ids into this array. */
390 u32 bdw_msr_id[] = { MSR_IA32_THERM_CONTROL };
391
392 pr_info("x86/pm: %s detected, MSR saving is needed during suspending.\n", d->ident);
393 return msr_init_context(bdw_msr_id, ARRAY_SIZE(bdw_msr_id));
394}
395
396static struct dmi_system_id msr_save_dmi_table[] = {
397 {
398 .callback = msr_initialize_bdw,
399 .ident = "BROADWELL BDX_EP",
400 .matches = {
401 DMI_MATCH(DMI_PRODUCT_NAME, "GRANTLEY"),
402 DMI_MATCH(DMI_PRODUCT_VERSION, "E63448-400"),
403 },
404 },
405 {}
406};
407
408static int pm_check_save_msr(void)
409{
410 dmi_check_system(msr_save_dmi_table);
411 return 0;
412}
413
414device_initcall(pm_check_save_msr);