From 4c448c7136806d4d2f059b28d3813d96dd694dc1 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Wed, 28 Aug 2019 00:08:07 -0400 Subject: [PATCH] fixes for 4.9 Signed-off-by: Sasha Levin --- queue-4.9/series | 1 + ...r-rdrand-cpuid-bit-on-amd-family-15h.patch | 331 ++++++++++++++++++ 2 files changed, 332 insertions(+) create mode 100644 queue-4.9/x86-cpu-amd-clear-rdrand-cpuid-bit-on-amd-family-15h.patch diff --git a/queue-4.9/series b/queue-4.9/series index c3853c8a38c..06d4ddb2a46 100644 --- a/queue-4.9/series +++ b/queue-4.9/series @@ -42,3 +42,4 @@ mm-page_owner-handle-thp-splits-correctly.patch mm-zsmalloc.c-migration-can-leave-pages-in-zs_empty-indefinitely.patch xfs-fix-missing-ilock-unlock-when-xfs_setattr_nonsize-fails-due-to-edquot.patch revert-perf-test-6-fix-missing-kvm-module-load-for-s.patch +x86-cpu-amd-clear-rdrand-cpuid-bit-on-amd-family-15h.patch diff --git a/queue-4.9/x86-cpu-amd-clear-rdrand-cpuid-bit-on-amd-family-15h.patch b/queue-4.9/x86-cpu-amd-clear-rdrand-cpuid-bit-on-amd-family-15h.patch new file mode 100644 index 00000000000..796f335df20 --- /dev/null +++ b/queue-4.9/x86-cpu-amd-clear-rdrand-cpuid-bit-on-amd-family-15h.patch @@ -0,0 +1,331 @@ +From 85490b42d1de430c446c1a331309259d72680c7b Mon Sep 17 00:00:00 2001 +From: Tom Lendacky +Date: Mon, 19 Aug 2019 15:52:35 +0000 +Subject: x86/CPU/AMD: Clear RDRAND CPUID bit on AMD family 15h/16h + +[ Upstream commit c49a0a80137c7ca7d6ced4c812c9e07a949f6f24 ] + +There have been reports of RDRAND issues after resuming from suspend on +some AMD family 15h and family 16h systems. This issue stems from a BIOS +not performing the proper steps during resume to ensure RDRAND continues +to function properly. + +RDRAND support is indicated by CPUID Fn00000001_ECX[30]. This bit can be +reset by clearing MSR C001_1004[62]. Any software that checks for RDRAND +support using CPUID, including the kernel, will believe that RDRAND is +not supported. + +Update the CPU initialization to clear the RDRAND CPUID bit for any family +15h and 16h processor that supports RDRAND. If it is known that the family +15h or family 16h system does not have an RDRAND resume issue or that the +system will not be placed in suspend, the "rdrand=force" kernel parameter +can be used to stop the clearing of the RDRAND CPUID bit. + +Additionally, update the suspend and resume path to save and restore the +MSR C001_1004 value to ensure that the RDRAND CPUID setting remains in +place after resuming from suspend. + +Note, that clearing the RDRAND CPUID bit does not prevent a processor +that normally supports the RDRAND instruction from executing it. So any +code that determined the support based on family and model won't #UD. + +Signed-off-by: Tom Lendacky +Signed-off-by: Borislav Petkov +Cc: Andrew Cooper +Cc: Andrew Morton +Cc: Chen Yu +Cc: "H. Peter Anvin" +Cc: Ingo Molnar +Cc: Jonathan Corbet +Cc: Josh Poimboeuf +Cc: Juergen Gross +Cc: Kees Cook +Cc: "linux-doc@vger.kernel.org" +Cc: "linux-pm@vger.kernel.org" +Cc: Nathan Chancellor +Cc: Paolo Bonzini +Cc: Pavel Machek +Cc: "Rafael J. Wysocki" +Cc: +Cc: Thomas Gleixner +Cc: "x86@kernel.org" +Link: https://lkml.kernel.org/r/7543af91666f491547bd86cebb1e17c66824ab9f.1566229943.git.thomas.lendacky@amd.com +[sl: adjust context in docs] +Signed-off-by: Sasha Levin +--- + Documentation/kernel-parameters.txt | 7 +++ + arch/x86/include/asm/msr-index.h | 1 + + arch/x86/kernel/cpu/amd.c | 66 ++++++++++++++++++++++ + arch/x86/power/cpu.c | 86 ++++++++++++++++++++++++----- + 4 files changed, 147 insertions(+), 13 deletions(-) + +diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt +index f4f0a1b9ba29e..61b73e42f488c 100644 +--- a/Documentation/kernel-parameters.txt ++++ b/Documentation/kernel-parameters.txt +@@ -3829,6 +3829,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted. + Run specified binary instead of /init from the ramdisk, + used for early userspace startup. See initrd. + ++ rdrand= [X86] ++ force - Override the decision by the kernel to hide the ++ advertisement of RDRAND support (this affects ++ certain AMD processors because of buggy BIOS ++ support, specifically around the suspend/resume ++ path). ++ + reboot= [KNL] + Format (x86 or x86_64): + [w[arm] | c[old] | h[ard] | s[oft] | g[pio]] \ +diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h +index 38f94d07920d1..86166868db8c1 100644 +--- a/arch/x86/include/asm/msr-index.h ++++ b/arch/x86/include/asm/msr-index.h +@@ -313,6 +313,7 @@ + #define MSR_AMD64_PATCH_LEVEL 0x0000008b + #define MSR_AMD64_TSC_RATIO 0xc0000104 + #define MSR_AMD64_NB_CFG 0xc001001f ++#define MSR_AMD64_CPUID_FN_1 0xc0011004 + #define MSR_AMD64_PATCH_LOADER 0xc0010020 + #define MSR_AMD64_OSVW_ID_LENGTH 0xc0010140 + #define MSR_AMD64_OSVW_STATUS 0xc0010141 +diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c +index 52a65f14db069..9428b54fff664 100644 +--- a/arch/x86/kernel/cpu/amd.c ++++ b/arch/x86/kernel/cpu/amd.c +@@ -746,6 +746,64 @@ static void init_amd_ln(struct cpuinfo_x86 *c) + msr_set_bit(MSR_AMD64_DE_CFG, 31); + } + ++static bool rdrand_force; ++ ++static int __init rdrand_cmdline(char *str) ++{ ++ if (!str) ++ return -EINVAL; ++ ++ if (!strcmp(str, "force")) ++ rdrand_force = true; ++ else ++ return -EINVAL; ++ ++ return 0; ++} ++early_param("rdrand", rdrand_cmdline); ++ ++static void clear_rdrand_cpuid_bit(struct cpuinfo_x86 *c) ++{ ++ /* ++ * Saving of the MSR used to hide the RDRAND support during ++ * suspend/resume is done by arch/x86/power/cpu.c, which is ++ * dependent on CONFIG_PM_SLEEP. ++ */ ++ if (!IS_ENABLED(CONFIG_PM_SLEEP)) ++ return; ++ ++ /* ++ * The nordrand option can clear X86_FEATURE_RDRAND, so check for ++ * RDRAND support using the CPUID function directly. ++ */ ++ if (!(cpuid_ecx(1) & BIT(30)) || rdrand_force) ++ return; ++ ++ msr_clear_bit(MSR_AMD64_CPUID_FN_1, 62); ++ ++ /* ++ * Verify that the CPUID change has occurred in case the kernel is ++ * running virtualized and the hypervisor doesn't support the MSR. ++ */ ++ if (cpuid_ecx(1) & BIT(30)) { ++ pr_info_once("BIOS may not properly restore RDRAND after suspend, but hypervisor does not support hiding RDRAND via CPUID.\n"); ++ return; ++ } ++ ++ clear_cpu_cap(c, X86_FEATURE_RDRAND); ++ pr_info_once("BIOS may not properly restore RDRAND after suspend, hiding RDRAND via CPUID. Use rdrand=force to reenable.\n"); ++} ++ ++static void init_amd_jg(struct cpuinfo_x86 *c) ++{ ++ /* ++ * Some BIOS implementations do not restore proper RDRAND support ++ * across suspend and resume. Check on whether to hide the RDRAND ++ * instruction support via CPUID. ++ */ ++ clear_rdrand_cpuid_bit(c); ++} ++ + static void init_amd_bd(struct cpuinfo_x86 *c) + { + u64 value; +@@ -760,6 +818,13 @@ static void init_amd_bd(struct cpuinfo_x86 *c) + wrmsrl_safe(MSR_F15H_IC_CFG, value); + } + } ++ ++ /* ++ * Some BIOS implementations do not restore proper RDRAND support ++ * across suspend and resume. Check on whether to hide the RDRAND ++ * instruction support via CPUID. ++ */ ++ clear_rdrand_cpuid_bit(c); + } + + static void init_amd_zn(struct cpuinfo_x86 *c) +@@ -804,6 +869,7 @@ static void init_amd(struct cpuinfo_x86 *c) + case 0x10: init_amd_gh(c); break; + case 0x12: init_amd_ln(c); break; + case 0x15: init_amd_bd(c); break; ++ case 0x16: init_amd_jg(c); break; + case 0x17: init_amd_zn(c); break; + } + +diff --git a/arch/x86/power/cpu.c b/arch/x86/power/cpu.c +index 29dc59baf0c21..c8f947a4aaf20 100644 +--- a/arch/x86/power/cpu.c ++++ b/arch/x86/power/cpu.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -24,7 +25,7 @@ + #include + #include + #include +-#include ++#include + + #ifdef CONFIG_X86_32 + __visible unsigned long saved_context_ebx; +@@ -391,15 +392,14 @@ static int __init bsp_pm_check_init(void) + + core_initcall(bsp_pm_check_init); + +-static int msr_init_context(const u32 *msr_id, const int total_num) ++static int msr_build_context(const u32 *msr_id, const int num) + { +- int i = 0; ++ struct saved_msrs *saved_msrs = &saved_context.saved_msrs; + struct saved_msr *msr_array; ++ int total_num; ++ int i, j; + +- 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; +- } ++ total_num = saved_msrs->num + num; + + msr_array = kmalloc_array(total_num, sizeof(struct saved_msr), GFP_KERNEL); + if (!msr_array) { +@@ -407,19 +407,30 @@ static int msr_init_context(const u32 *msr_id, const int total_num) + return -ENOMEM; + } + +- for (i = 0; i < total_num; i++) { +- msr_array[i].info.msr_no = msr_id[i]; ++ if (saved_msrs->array) { ++ /* ++ * Multiple callbacks can invoke this function, so copy any ++ * MSR save requests from previous invocations. ++ */ ++ memcpy(msr_array, saved_msrs->array, ++ sizeof(struct saved_msr) * saved_msrs->num); ++ ++ kfree(saved_msrs->array); ++ } ++ ++ for (i = saved_msrs->num, j = 0; i < total_num; i++, j++) { ++ msr_array[i].info.msr_no = msr_id[j]; + 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; ++ saved_msrs->num = total_num; ++ saved_msrs->array = msr_array; + + return 0; + } + + /* +- * The following section is a quirk framework for problematic BIOSen: ++ * The following sections are 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 +@@ -434,7 +445,7 @@ static int msr_initialize_bdw(const struct dmi_system_id *d) + 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)); ++ return msr_build_context(bdw_msr_id, ARRAY_SIZE(bdw_msr_id)); + } + + static struct dmi_system_id msr_save_dmi_table[] = { +@@ -449,9 +460,58 @@ static struct dmi_system_id msr_save_dmi_table[] = { + {} + }; + ++static int msr_save_cpuid_features(const struct x86_cpu_id *c) ++{ ++ u32 cpuid_msr_id[] = { ++ MSR_AMD64_CPUID_FN_1, ++ }; ++ ++ pr_info("x86/pm: family %#hx cpu detected, MSR saving is needed during suspending.\n", ++ c->family); ++ ++ return msr_build_context(cpuid_msr_id, ARRAY_SIZE(cpuid_msr_id)); ++} ++ ++static const struct x86_cpu_id msr_save_cpu_table[] = { ++ { ++ .vendor = X86_VENDOR_AMD, ++ .family = 0x15, ++ .model = X86_MODEL_ANY, ++ .feature = X86_FEATURE_ANY, ++ .driver_data = (kernel_ulong_t)msr_save_cpuid_features, ++ }, ++ { ++ .vendor = X86_VENDOR_AMD, ++ .family = 0x16, ++ .model = X86_MODEL_ANY, ++ .feature = X86_FEATURE_ANY, ++ .driver_data = (kernel_ulong_t)msr_save_cpuid_features, ++ }, ++ {} ++}; ++ ++typedef int (*pm_cpu_match_t)(const struct x86_cpu_id *); ++static int pm_cpu_check(const struct x86_cpu_id *c) ++{ ++ const struct x86_cpu_id *m; ++ int ret = 0; ++ ++ m = x86_match_cpu(msr_save_cpu_table); ++ if (m) { ++ pm_cpu_match_t fn; ++ ++ fn = (pm_cpu_match_t)m->driver_data; ++ ret = fn(m); ++ } ++ ++ return ret; ++} ++ + static int pm_check_save_msr(void) + { + dmi_check_system(msr_save_dmi_table); ++ pm_cpu_check(msr_save_cpu_table); ++ + return 0; + } + +-- +2.20.1 + -- 2.47.3