162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Suspend support specific for i386/x86-64. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2007 Rafael J. Wysocki <rjw@sisk.pl> 662306a36Sopenharmony_ci * Copyright (c) 2002 Pavel Machek <pavel@ucw.cz> 762306a36Sopenharmony_ci * Copyright (c) 2001 Patrick Mochel <mochel@osdl.org> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/suspend.h> 1162306a36Sopenharmony_ci#include <linux/export.h> 1262306a36Sopenharmony_ci#include <linux/smp.h> 1362306a36Sopenharmony_ci#include <linux/perf_event.h> 1462306a36Sopenharmony_ci#include <linux/tboot.h> 1562306a36Sopenharmony_ci#include <linux/dmi.h> 1662306a36Sopenharmony_ci#include <linux/pgtable.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <asm/proto.h> 1962306a36Sopenharmony_ci#include <asm/mtrr.h> 2062306a36Sopenharmony_ci#include <asm/page.h> 2162306a36Sopenharmony_ci#include <asm/mce.h> 2262306a36Sopenharmony_ci#include <asm/suspend.h> 2362306a36Sopenharmony_ci#include <asm/fpu/api.h> 2462306a36Sopenharmony_ci#include <asm/debugreg.h> 2562306a36Sopenharmony_ci#include <asm/cpu.h> 2662306a36Sopenharmony_ci#include <asm/cacheinfo.h> 2762306a36Sopenharmony_ci#include <asm/mmu_context.h> 2862306a36Sopenharmony_ci#include <asm/cpu_device_id.h> 2962306a36Sopenharmony_ci#include <asm/microcode.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#ifdef CONFIG_X86_32 3262306a36Sopenharmony_ci__visible unsigned long saved_context_ebx; 3362306a36Sopenharmony_ci__visible unsigned long saved_context_esp, saved_context_ebp; 3462306a36Sopenharmony_ci__visible unsigned long saved_context_esi, saved_context_edi; 3562306a36Sopenharmony_ci__visible unsigned long saved_context_eflags; 3662306a36Sopenharmony_ci#endif 3762306a36Sopenharmony_cistruct saved_context saved_context; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic void msr_save_context(struct saved_context *ctxt) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct saved_msr *msr = ctxt->saved_msrs.array; 4262306a36Sopenharmony_ci struct saved_msr *end = msr + ctxt->saved_msrs.num; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci while (msr < end) { 4562306a36Sopenharmony_ci if (msr->valid) 4662306a36Sopenharmony_ci rdmsrl(msr->info.msr_no, msr->info.reg.q); 4762306a36Sopenharmony_ci msr++; 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic void msr_restore_context(struct saved_context *ctxt) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci struct saved_msr *msr = ctxt->saved_msrs.array; 5462306a36Sopenharmony_ci struct saved_msr *end = msr + ctxt->saved_msrs.num; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci while (msr < end) { 5762306a36Sopenharmony_ci if (msr->valid) 5862306a36Sopenharmony_ci wrmsrl(msr->info.msr_no, msr->info.reg.q); 5962306a36Sopenharmony_ci msr++; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/** 6462306a36Sopenharmony_ci * __save_processor_state() - Save CPU registers before creating a 6562306a36Sopenharmony_ci * hibernation image and before restoring 6662306a36Sopenharmony_ci * the memory state from it 6762306a36Sopenharmony_ci * @ctxt: Structure to store the registers contents in. 6862306a36Sopenharmony_ci * 6962306a36Sopenharmony_ci * NOTE: If there is a CPU register the modification of which by the 7062306a36Sopenharmony_ci * boot kernel (ie. the kernel used for loading the hibernation image) 7162306a36Sopenharmony_ci * might affect the operations of the restored target kernel (ie. the one 7262306a36Sopenharmony_ci * saved in the hibernation image), then its contents must be saved by this 7362306a36Sopenharmony_ci * function. In other words, if kernel A is hibernated and different 7462306a36Sopenharmony_ci * kernel B is used for loading the hibernation image into memory, the 7562306a36Sopenharmony_ci * kernel A's __save_processor_state() function must save all registers 7662306a36Sopenharmony_ci * needed by kernel A, so that it can operate correctly after the resume 7762306a36Sopenharmony_ci * regardless of what kernel B does in the meantime. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_cistatic void __save_processor_state(struct saved_context *ctxt) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci#ifdef CONFIG_X86_32 8262306a36Sopenharmony_ci mtrr_save_fixed_ranges(NULL); 8362306a36Sopenharmony_ci#endif 8462306a36Sopenharmony_ci kernel_fpu_begin(); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* 8762306a36Sopenharmony_ci * descriptor tables 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci store_idt(&ctxt->idt); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* 9262306a36Sopenharmony_ci * We save it here, but restore it only in the hibernate case. 9362306a36Sopenharmony_ci * For ACPI S3 resume, this is loaded via 'early_gdt_desc' in 64-bit 9462306a36Sopenharmony_ci * mode in "secondary_startup_64". In 32-bit mode it is done via 9562306a36Sopenharmony_ci * 'pmode_gdt' in wakeup_start. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_ci ctxt->gdt_desc.size = GDT_SIZE - 1; 9862306a36Sopenharmony_ci ctxt->gdt_desc.address = (unsigned long)get_cpu_gdt_rw(smp_processor_id()); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci store_tr(ctxt->tr); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* XMM0..XMM15 should be handled by kernel_fpu_begin(). */ 10362306a36Sopenharmony_ci /* 10462306a36Sopenharmony_ci * segment registers 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_ci savesegment(gs, ctxt->gs); 10762306a36Sopenharmony_ci#ifdef CONFIG_X86_64 10862306a36Sopenharmony_ci savesegment(fs, ctxt->fs); 10962306a36Sopenharmony_ci savesegment(ds, ctxt->ds); 11062306a36Sopenharmony_ci savesegment(es, ctxt->es); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci rdmsrl(MSR_FS_BASE, ctxt->fs_base); 11362306a36Sopenharmony_ci rdmsrl(MSR_GS_BASE, ctxt->kernelmode_gs_base); 11462306a36Sopenharmony_ci rdmsrl(MSR_KERNEL_GS_BASE, ctxt->usermode_gs_base); 11562306a36Sopenharmony_ci mtrr_save_fixed_ranges(NULL); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci rdmsrl(MSR_EFER, ctxt->efer); 11862306a36Sopenharmony_ci#endif 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* 12162306a36Sopenharmony_ci * control registers 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_ci ctxt->cr0 = read_cr0(); 12462306a36Sopenharmony_ci ctxt->cr2 = read_cr2(); 12562306a36Sopenharmony_ci ctxt->cr3 = __read_cr3(); 12662306a36Sopenharmony_ci ctxt->cr4 = __read_cr4(); 12762306a36Sopenharmony_ci ctxt->misc_enable_saved = !rdmsrl_safe(MSR_IA32_MISC_ENABLE, 12862306a36Sopenharmony_ci &ctxt->misc_enable); 12962306a36Sopenharmony_ci msr_save_context(ctxt); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* Needed by apm.c */ 13362306a36Sopenharmony_civoid save_processor_state(void) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci __save_processor_state(&saved_context); 13662306a36Sopenharmony_ci x86_platform.save_sched_clock_state(); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci#ifdef CONFIG_X86_32 13962306a36Sopenharmony_ciEXPORT_SYMBOL(save_processor_state); 14062306a36Sopenharmony_ci#endif 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic void do_fpu_end(void) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci /* 14562306a36Sopenharmony_ci * Restore FPU regs if necessary. 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ci kernel_fpu_end(); 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic void fix_processor_context(void) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci int cpu = smp_processor_id(); 15362306a36Sopenharmony_ci#ifdef CONFIG_X86_64 15462306a36Sopenharmony_ci struct desc_struct *desc = get_cpu_gdt_rw(cpu); 15562306a36Sopenharmony_ci tss_desc tss; 15662306a36Sopenharmony_ci#endif 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* 15962306a36Sopenharmony_ci * We need to reload TR, which requires that we change the 16062306a36Sopenharmony_ci * GDT entry to indicate "available" first. 16162306a36Sopenharmony_ci * 16262306a36Sopenharmony_ci * XXX: This could probably all be replaced by a call to 16362306a36Sopenharmony_ci * force_reload_TR(). 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci set_tss_desc(cpu, &get_cpu_entry_area(cpu)->tss.x86_tss); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci#ifdef CONFIG_X86_64 16862306a36Sopenharmony_ci memcpy(&tss, &desc[GDT_ENTRY_TSS], sizeof(tss_desc)); 16962306a36Sopenharmony_ci tss.type = 0x9; /* The available 64-bit TSS (see AMD vol 2, pg 91 */ 17062306a36Sopenharmony_ci write_gdt_entry(desc, GDT_ENTRY_TSS, &tss, DESC_TSS); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci syscall_init(); /* This sets MSR_*STAR and related */ 17362306a36Sopenharmony_ci#else 17462306a36Sopenharmony_ci if (boot_cpu_has(X86_FEATURE_SEP)) 17562306a36Sopenharmony_ci enable_sep_cpu(); 17662306a36Sopenharmony_ci#endif 17762306a36Sopenharmony_ci load_TR_desc(); /* This does ltr */ 17862306a36Sopenharmony_ci load_mm_ldt(current->active_mm); /* This does lldt */ 17962306a36Sopenharmony_ci initialize_tlbstate_and_flush(); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci fpu__resume_cpu(); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* The processor is back on the direct GDT, load back the fixmap */ 18462306a36Sopenharmony_ci load_fixmap_gdt(cpu); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/** 18862306a36Sopenharmony_ci * __restore_processor_state() - Restore the contents of CPU registers saved 18962306a36Sopenharmony_ci * by __save_processor_state() 19062306a36Sopenharmony_ci * @ctxt: Structure to load the registers contents from. 19162306a36Sopenharmony_ci * 19262306a36Sopenharmony_ci * The asm code that gets us here will have restored a usable GDT, although 19362306a36Sopenharmony_ci * it will be pointing to the wrong alias. 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_cistatic void notrace __restore_processor_state(struct saved_context *ctxt) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct cpuinfo_x86 *c; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (ctxt->misc_enable_saved) 20062306a36Sopenharmony_ci wrmsrl(MSR_IA32_MISC_ENABLE, ctxt->misc_enable); 20162306a36Sopenharmony_ci /* 20262306a36Sopenharmony_ci * control registers 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci /* cr4 was introduced in the Pentium CPU */ 20562306a36Sopenharmony_ci#ifdef CONFIG_X86_32 20662306a36Sopenharmony_ci if (ctxt->cr4) 20762306a36Sopenharmony_ci __write_cr4(ctxt->cr4); 20862306a36Sopenharmony_ci#else 20962306a36Sopenharmony_ci/* CONFIG X86_64 */ 21062306a36Sopenharmony_ci wrmsrl(MSR_EFER, ctxt->efer); 21162306a36Sopenharmony_ci __write_cr4(ctxt->cr4); 21262306a36Sopenharmony_ci#endif 21362306a36Sopenharmony_ci write_cr3(ctxt->cr3); 21462306a36Sopenharmony_ci write_cr2(ctxt->cr2); 21562306a36Sopenharmony_ci write_cr0(ctxt->cr0); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* Restore the IDT. */ 21862306a36Sopenharmony_ci load_idt(&ctxt->idt); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* 22162306a36Sopenharmony_ci * Just in case the asm code got us here with the SS, DS, or ES 22262306a36Sopenharmony_ci * out of sync with the GDT, update them. 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_ci loadsegment(ss, __KERNEL_DS); 22562306a36Sopenharmony_ci loadsegment(ds, __USER_DS); 22662306a36Sopenharmony_ci loadsegment(es, __USER_DS); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* 22962306a36Sopenharmony_ci * Restore percpu access. Percpu access can happen in exception 23062306a36Sopenharmony_ci * handlers or in complicated helpers like load_gs_index(). 23162306a36Sopenharmony_ci */ 23262306a36Sopenharmony_ci#ifdef CONFIG_X86_64 23362306a36Sopenharmony_ci wrmsrl(MSR_GS_BASE, ctxt->kernelmode_gs_base); 23462306a36Sopenharmony_ci#else 23562306a36Sopenharmony_ci loadsegment(fs, __KERNEL_PERCPU); 23662306a36Sopenharmony_ci#endif 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* Restore the TSS, RO GDT, LDT, and usermode-relevant MSRs. */ 23962306a36Sopenharmony_ci fix_processor_context(); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* 24262306a36Sopenharmony_ci * Now that we have descriptor tables fully restored and working 24362306a36Sopenharmony_ci * exception handling, restore the usermode segments. 24462306a36Sopenharmony_ci */ 24562306a36Sopenharmony_ci#ifdef CONFIG_X86_64 24662306a36Sopenharmony_ci loadsegment(ds, ctxt->es); 24762306a36Sopenharmony_ci loadsegment(es, ctxt->es); 24862306a36Sopenharmony_ci loadsegment(fs, ctxt->fs); 24962306a36Sopenharmony_ci load_gs_index(ctxt->gs); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* 25262306a36Sopenharmony_ci * Restore FSBASE and GSBASE after restoring the selectors, since 25362306a36Sopenharmony_ci * restoring the selectors clobbers the bases. Keep in mind 25462306a36Sopenharmony_ci * that MSR_KERNEL_GS_BASE is horribly misnamed. 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_ci wrmsrl(MSR_FS_BASE, ctxt->fs_base); 25762306a36Sopenharmony_ci wrmsrl(MSR_KERNEL_GS_BASE, ctxt->usermode_gs_base); 25862306a36Sopenharmony_ci#else 25962306a36Sopenharmony_ci loadsegment(gs, ctxt->gs); 26062306a36Sopenharmony_ci#endif 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci do_fpu_end(); 26362306a36Sopenharmony_ci tsc_verify_tsc_adjust(true); 26462306a36Sopenharmony_ci x86_platform.restore_sched_clock_state(); 26562306a36Sopenharmony_ci cache_bp_restore(); 26662306a36Sopenharmony_ci perf_restore_debug_store(); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci c = &cpu_data(smp_processor_id()); 26962306a36Sopenharmony_ci if (cpu_has(c, X86_FEATURE_MSR_IA32_FEAT_CTL)) 27062306a36Sopenharmony_ci init_ia32_feat_ctl(c); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci microcode_bsp_resume(); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* 27562306a36Sopenharmony_ci * This needs to happen after the microcode has been updated upon resume 27662306a36Sopenharmony_ci * because some of the MSRs are "emulated" in microcode. 27762306a36Sopenharmony_ci */ 27862306a36Sopenharmony_ci msr_restore_context(ctxt); 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci/* Needed by apm.c */ 28262306a36Sopenharmony_civoid notrace restore_processor_state(void) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci __restore_processor_state(&saved_context); 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci#ifdef CONFIG_X86_32 28762306a36Sopenharmony_ciEXPORT_SYMBOL(restore_processor_state); 28862306a36Sopenharmony_ci#endif 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci#if defined(CONFIG_HIBERNATION) && defined(CONFIG_HOTPLUG_CPU) 29162306a36Sopenharmony_cistatic void __noreturn resume_play_dead(void) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci play_dead_common(); 29462306a36Sopenharmony_ci tboot_shutdown(TB_SHUTDOWN_WFS); 29562306a36Sopenharmony_ci hlt_play_dead(); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ciint hibernate_resume_nonboot_cpu_disable(void) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci void (*play_dead)(void) = smp_ops.play_dead; 30162306a36Sopenharmony_ci int ret; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* 30462306a36Sopenharmony_ci * Ensure that MONITOR/MWAIT will not be used in the "play dead" loop 30562306a36Sopenharmony_ci * during hibernate image restoration, because it is likely that the 30662306a36Sopenharmony_ci * monitored address will be actually written to at that time and then 30762306a36Sopenharmony_ci * the "dead" CPU will attempt to execute instructions again, but the 30862306a36Sopenharmony_ci * address in its instruction pointer may not be possible to resolve 30962306a36Sopenharmony_ci * any more at that point (the page tables used by it previously may 31062306a36Sopenharmony_ci * have been overwritten by hibernate image data). 31162306a36Sopenharmony_ci * 31262306a36Sopenharmony_ci * First, make sure that we wake up all the potentially disabled SMT 31362306a36Sopenharmony_ci * threads which have been initially brought up and then put into 31462306a36Sopenharmony_ci * mwait/cpuidle sleep. 31562306a36Sopenharmony_ci * Those will be put to proper (not interfering with hibernation 31662306a36Sopenharmony_ci * resume) sleep afterwards, and the resumed kernel will decide itself 31762306a36Sopenharmony_ci * what to do with them. 31862306a36Sopenharmony_ci */ 31962306a36Sopenharmony_ci ret = cpuhp_smt_enable(); 32062306a36Sopenharmony_ci if (ret) 32162306a36Sopenharmony_ci return ret; 32262306a36Sopenharmony_ci smp_ops.play_dead = resume_play_dead; 32362306a36Sopenharmony_ci ret = freeze_secondary_cpus(0); 32462306a36Sopenharmony_ci smp_ops.play_dead = play_dead; 32562306a36Sopenharmony_ci return ret; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci#endif 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci/* 33062306a36Sopenharmony_ci * When bsp_check() is called in hibernate and suspend, cpu hotplug 33162306a36Sopenharmony_ci * is disabled already. So it's unnecessary to handle race condition between 33262306a36Sopenharmony_ci * cpumask query and cpu hotplug. 33362306a36Sopenharmony_ci */ 33462306a36Sopenharmony_cistatic int bsp_check(void) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci if (cpumask_first(cpu_online_mask) != 0) { 33762306a36Sopenharmony_ci pr_warn("CPU0 is offline.\n"); 33862306a36Sopenharmony_ci return -ENODEV; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return 0; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic int bsp_pm_callback(struct notifier_block *nb, unsigned long action, 34562306a36Sopenharmony_ci void *ptr) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci int ret = 0; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci switch (action) { 35062306a36Sopenharmony_ci case PM_SUSPEND_PREPARE: 35162306a36Sopenharmony_ci case PM_HIBERNATION_PREPARE: 35262306a36Sopenharmony_ci ret = bsp_check(); 35362306a36Sopenharmony_ci break; 35462306a36Sopenharmony_ci default: 35562306a36Sopenharmony_ci break; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci return notifier_from_errno(ret); 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic int __init bsp_pm_check_init(void) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci /* 36362306a36Sopenharmony_ci * Set this bsp_pm_callback as lower priority than 36462306a36Sopenharmony_ci * cpu_hotplug_pm_callback. So cpu_hotplug_pm_callback will be called 36562306a36Sopenharmony_ci * earlier to disable cpu hotplug before bsp online check. 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_ci pm_notifier(bsp_pm_callback, -INT_MAX); 36862306a36Sopenharmony_ci return 0; 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cicore_initcall(bsp_pm_check_init); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic int msr_build_context(const u32 *msr_id, const int num) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct saved_msrs *saved_msrs = &saved_context.saved_msrs; 37662306a36Sopenharmony_ci struct saved_msr *msr_array; 37762306a36Sopenharmony_ci int total_num; 37862306a36Sopenharmony_ci int i, j; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci total_num = saved_msrs->num + num; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci msr_array = kmalloc_array(total_num, sizeof(struct saved_msr), GFP_KERNEL); 38362306a36Sopenharmony_ci if (!msr_array) { 38462306a36Sopenharmony_ci pr_err("x86/pm: Can not allocate memory to save/restore MSRs during suspend.\n"); 38562306a36Sopenharmony_ci return -ENOMEM; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (saved_msrs->array) { 38962306a36Sopenharmony_ci /* 39062306a36Sopenharmony_ci * Multiple callbacks can invoke this function, so copy any 39162306a36Sopenharmony_ci * MSR save requests from previous invocations. 39262306a36Sopenharmony_ci */ 39362306a36Sopenharmony_ci memcpy(msr_array, saved_msrs->array, 39462306a36Sopenharmony_ci sizeof(struct saved_msr) * saved_msrs->num); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci kfree(saved_msrs->array); 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci for (i = saved_msrs->num, j = 0; i < total_num; i++, j++) { 40062306a36Sopenharmony_ci u64 dummy; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci msr_array[i].info.msr_no = msr_id[j]; 40362306a36Sopenharmony_ci msr_array[i].valid = !rdmsrl_safe(msr_id[j], &dummy); 40462306a36Sopenharmony_ci msr_array[i].info.reg.q = 0; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci saved_msrs->num = total_num; 40762306a36Sopenharmony_ci saved_msrs->array = msr_array; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci/* 41362306a36Sopenharmony_ci * The following sections are a quirk framework for problematic BIOSen: 41462306a36Sopenharmony_ci * Sometimes MSRs are modified by the BIOSen after suspended to 41562306a36Sopenharmony_ci * RAM, this might cause unexpected behavior after wakeup. 41662306a36Sopenharmony_ci * Thus we save/restore these specified MSRs across suspend/resume 41762306a36Sopenharmony_ci * in order to work around it. 41862306a36Sopenharmony_ci * 41962306a36Sopenharmony_ci * For any further problematic BIOSen/platforms, 42062306a36Sopenharmony_ci * please add your own function similar to msr_initialize_bdw. 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_cistatic int msr_initialize_bdw(const struct dmi_system_id *d) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci /* Add any extra MSR ids into this array. */ 42562306a36Sopenharmony_ci u32 bdw_msr_id[] = { MSR_IA32_THERM_CONTROL }; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci pr_info("x86/pm: %s detected, MSR saving is needed during suspending.\n", d->ident); 42862306a36Sopenharmony_ci return msr_build_context(bdw_msr_id, ARRAY_SIZE(bdw_msr_id)); 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic const struct dmi_system_id msr_save_dmi_table[] = { 43262306a36Sopenharmony_ci { 43362306a36Sopenharmony_ci .callback = msr_initialize_bdw, 43462306a36Sopenharmony_ci .ident = "BROADWELL BDX_EP", 43562306a36Sopenharmony_ci .matches = { 43662306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "GRANTLEY"), 43762306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_VERSION, "E63448-400"), 43862306a36Sopenharmony_ci }, 43962306a36Sopenharmony_ci }, 44062306a36Sopenharmony_ci {} 44162306a36Sopenharmony_ci}; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic int msr_save_cpuid_features(const struct x86_cpu_id *c) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci u32 cpuid_msr_id[] = { 44662306a36Sopenharmony_ci MSR_AMD64_CPUID_FN_1, 44762306a36Sopenharmony_ci }; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci pr_info("x86/pm: family %#hx cpu detected, MSR saving is needed during suspending.\n", 45062306a36Sopenharmony_ci c->family); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci return msr_build_context(cpuid_msr_id, ARRAY_SIZE(cpuid_msr_id)); 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic const struct x86_cpu_id msr_save_cpu_table[] = { 45662306a36Sopenharmony_ci X86_MATCH_VENDOR_FAM(AMD, 0x15, &msr_save_cpuid_features), 45762306a36Sopenharmony_ci X86_MATCH_VENDOR_FAM(AMD, 0x16, &msr_save_cpuid_features), 45862306a36Sopenharmony_ci {} 45962306a36Sopenharmony_ci}; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_citypedef int (*pm_cpu_match_t)(const struct x86_cpu_id *); 46262306a36Sopenharmony_cistatic int pm_cpu_check(const struct x86_cpu_id *c) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci const struct x86_cpu_id *m; 46562306a36Sopenharmony_ci int ret = 0; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci m = x86_match_cpu(msr_save_cpu_table); 46862306a36Sopenharmony_ci if (m) { 46962306a36Sopenharmony_ci pm_cpu_match_t fn; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci fn = (pm_cpu_match_t)m->driver_data; 47262306a36Sopenharmony_ci ret = fn(m); 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return ret; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic void pm_save_spec_msr(void) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci struct msr_enumeration { 48162306a36Sopenharmony_ci u32 msr_no; 48262306a36Sopenharmony_ci u32 feature; 48362306a36Sopenharmony_ci } msr_enum[] = { 48462306a36Sopenharmony_ci { MSR_IA32_SPEC_CTRL, X86_FEATURE_MSR_SPEC_CTRL }, 48562306a36Sopenharmony_ci { MSR_IA32_TSX_CTRL, X86_FEATURE_MSR_TSX_CTRL }, 48662306a36Sopenharmony_ci { MSR_TSX_FORCE_ABORT, X86_FEATURE_TSX_FORCE_ABORT }, 48762306a36Sopenharmony_ci { MSR_IA32_MCU_OPT_CTRL, X86_FEATURE_SRBDS_CTRL }, 48862306a36Sopenharmony_ci { MSR_AMD64_LS_CFG, X86_FEATURE_LS_CFG_SSBD }, 48962306a36Sopenharmony_ci { MSR_AMD64_DE_CFG, X86_FEATURE_LFENCE_RDTSC }, 49062306a36Sopenharmony_ci }; 49162306a36Sopenharmony_ci int i; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(msr_enum); i++) { 49462306a36Sopenharmony_ci if (boot_cpu_has(msr_enum[i].feature)) 49562306a36Sopenharmony_ci msr_build_context(&msr_enum[i].msr_no, 1); 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic int pm_check_save_msr(void) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci dmi_check_system(msr_save_dmi_table); 50262306a36Sopenharmony_ci pm_cpu_check(msr_save_cpu_table); 50362306a36Sopenharmony_ci pm_save_spec_msr(); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci return 0; 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cidevice_initcall(pm_check_save_msr); 509