From d3ba71b2b1abc77d1625d693191d7f6d2fcfc5cb Mon Sep 17 00:00:00 2001 From: Konstantin Belousov Date: Wed, 14 Oct 2020 22:57:50 +0000 Subject: [PATCH] Limit workaround for errata E400 to appropriate AMD cpus. From Linux sources and several datasheets I looked at, it seems that the workaround is only needed on families 0xf and 0x10. For instance, Ryzens do not implement the accessed MSR at all, it is documented as reserved. Also, hypervisors should not allow guest to put CPU into idle state, so activate workaround only when on bare hardware. While there, style the code: move MSR defines to specialreg.h move identification to initcpu.c Reported by: whu Reviewed by: avg Sponsored by: The FreeBSD Foundation MFC after: 1 week Differential revision: https://reviews.freebsd.org/D26470 --- sys/amd64/amd64/initcpu.c | 17 +++++++++++++++ sys/amd64/amd64/machdep.c | 2 -- sys/i386/i386/initcpu.c | 12 +++++++++-- sys/i386/i386/machdep.c | 2 -- sys/x86/include/specialreg.h | 4 ++++ sys/x86/include/x86_var.h | 1 + sys/x86/x86/cpu_machdep.c | 40 +++++++----------------------------- 7 files changed, 39 insertions(+), 39 deletions(-) diff --git a/sys/amd64/amd64/initcpu.c b/sys/amd64/amd64/initcpu.c index 88dc7f067284..44a294da205a 100644 --- a/sys/amd64/amd64/initcpu.c +++ b/sys/amd64/amd64/initcpu.c @@ -68,6 +68,23 @@ init_amd(void) { uint64_t msr; + /* + * C1E renders the local APIC timer dead, so we disable it by + * reading the Interrupt Pending Message register and clearing + * both C1eOnCmpHalt (bit 28) and SmiOnCmpHalt (bit 27). + * + * Reference: + * "BIOS and Kernel Developer's Guide for AMD NPT Family 0Fh Processors" + * #32559 revision 3.00+ + * + * Detect the presence of C1E capability mostly on latest + * dual-cores (or future) k8 family. Affected models range is + * taken from Linux sources. + */ + if ((CPUID_TO_FAMILY(cpu_id) == 0xf || + CPUID_TO_FAMILY(cpu_id) == 0x10) && (cpu_feature2 & CPUID2_HV) == 0) + cpu_amdc1e_bug = 1; + /* * Work around Erratum 721 for Family 10h and 12h processors. * These processors may incorrectly update the stack pointer diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c index 9557ea6f3a46..a6f725ebdcb4 100644 --- a/sys/amd64/amd64/machdep.c +++ b/sys/amd64/amd64/machdep.c @@ -1928,8 +1928,6 @@ hammer_time(u_int64_t modulep, u_int64_t physfree) if (env != NULL) strlcpy(kernelname, env, sizeof(kernelname)); - cpu_probe_amdc1e(); - kcsan_cpu_init(0); #ifdef FDT diff --git a/sys/i386/i386/initcpu.c b/sys/i386/i386/initcpu.c index a853e498d056..9ba269af2b32 100644 --- a/sys/i386/i386/initcpu.c +++ b/sys/i386/i386/initcpu.c @@ -720,8 +720,8 @@ initializecpu(void) break; } break; -#ifdef CPU_ATHLON_SSE_HACK case CPU_VENDOR_AMD: +#ifdef CPU_ATHLON_SSE_HACK /* * Sometimes the BIOS doesn't enable SSE instructions. * According to AMD document 20734, the mobile @@ -738,8 +738,16 @@ initializecpu(void) do_cpuid(1, regs); cpu_feature = regs[3]; } - break; #endif + /* + * Detect C1E that breaks APIC. See comment in + * amd64/initcpu.c. + */ + if ((CPUID_TO_FAMILY(cpu_id) == 0xf || + CPUID_TO_FAMILY(cpu_id) == 0x10) && + (cpu_feature2 & CPUID2_HV) == 0) + cpu_amdc1e_bug = 1; + break; case CPU_VENDOR_CENTAUR: init_via(); break; diff --git a/sys/i386/i386/machdep.c b/sys/i386/i386/machdep.c index b961a8f41b6c..e847d97b8bcf 100644 --- a/sys/i386/i386/machdep.c +++ b/sys/i386/i386/machdep.c @@ -2505,8 +2505,6 @@ init386(int first) thread0.td_pcb->pcb_ext = 0; thread0.td_frame = &proc0_tf; - cpu_probe_amdc1e(); - #ifdef FDT x86_init_fdt(); #endif diff --git a/sys/x86/include/specialreg.h b/sys/x86/include/specialreg.h index e7332c5fd5e7..22672d50efed 100644 --- a/sys/x86/include/specialreg.h +++ b/sys/x86/include/specialreg.h @@ -1126,6 +1126,7 @@ #define MSR_NB_CFG1 0xc001001f /* NB configuration 1 */ #define MSR_K8_UCODE_UPDATE 0xc0010020 /* update microcode */ #define MSR_MC0_CTL_MASK 0xc0010044 +#define MSR_AMDK8_IPM 0xc0010055 #define MSR_P_STATE_LIMIT 0xc0010061 /* P-state Current Limit Register */ #define MSR_P_STATE_CONTROL 0xc0010062 /* P-state Control Register */ #define MSR_P_STATE_STATUS 0xc0010063 /* P-state Status Register */ @@ -1143,6 +1144,9 @@ /* MSR_VM_CR related */ #define VM_CR_SVMDIS 0x10 /* SVM: disabled by BIOS */ +#define AMDK8_SMIONCMPHALT (1ULL << 27) +#define AMDK8_C1EONCMPHALT (1ULL << 28) + /* VIA ACE crypto featureset: for via_feature_rng */ #define VIA_HAS_RNG 1 /* cpu has RNG */ diff --git a/sys/x86/include/x86_var.h b/sys/x86/include/x86_var.h index f64fd5539b91..c1425755b5d1 100644 --- a/sys/x86/include/x86_var.h +++ b/sys/x86/include/x86_var.h @@ -94,6 +94,7 @@ extern int hw_ssb_active; extern int x86_taa_enable; extern int cpu_flush_rsb_ctxsw; extern int x86_rngds_mitg_enable; +extern int cpu_amdc1e_bug; struct pcb; struct thread; diff --git a/sys/x86/x86/cpu_machdep.c b/sys/x86/x86/cpu_machdep.c index e0ac128a31f7..4798f913d5b2 100644 --- a/sys/x86/x86/cpu_machdep.c +++ b/sys/x86/x86/cpu_machdep.c @@ -486,7 +486,9 @@ cpu_mwait_usable(void) } void (*cpu_idle_hook)(sbintime_t) = NULL; /* ACPI idle hook. */ -static int cpu_ident_amdc1e = 0; /* AMD C1E supported. */ + +int cpu_amdc1e_bug = 0; /* AMD C1E APIC workaround required. */ + static int idle_mwait = 1; /* Use MONITOR/MWAIT for short idle. */ SYSCTL_INT(_machdep, OID_AUTO, idle_mwait, CTLFLAG_RWTUN, &idle_mwait, 0, "Use MONITOR/MWAIT for short idle"); @@ -587,35 +589,6 @@ cpu_idle_spin(sbintime_t sbt) } } -/* - * C1E renders the local APIC timer dead, so we disable it by - * reading the Interrupt Pending Message register and clearing - * both C1eOnCmpHalt (bit 28) and SmiOnCmpHalt (bit 27). - * - * Reference: - * "BIOS and Kernel Developer's Guide for AMD NPT Family 0Fh Processors" - * #32559 revision 3.00+ - */ -#define MSR_AMDK8_IPM 0xc0010055 -#define AMDK8_SMIONCMPHALT (1ULL << 27) -#define AMDK8_C1EONCMPHALT (1ULL << 28) -#define AMDK8_CMPHALT (AMDK8_SMIONCMPHALT | AMDK8_C1EONCMPHALT) - -void -cpu_probe_amdc1e(void) -{ - - /* - * Detect the presence of C1E capability mostly on latest - * dual-cores (or future) k8 family. - */ - if (cpu_vendor_id == CPU_VENDOR_AMD && - (cpu_id & 0x00000f00) == 0x00000f00 && - (cpu_id & 0x0fff0000) >= 0x00040000) { - cpu_ident_amdc1e = 1; - } -} - void (*cpu_idle_fn)(sbintime_t) = cpu_idle_acpi; void @@ -645,10 +618,11 @@ cpu_idle(int busy) } /* Apply AMD APIC timer C1E workaround. */ - if (cpu_ident_amdc1e && cpu_disable_c3_sleep) { + if (cpu_amdc1e_bug && cpu_disable_c3_sleep) { msr = rdmsr(MSR_AMDK8_IPM); - if (msr & AMDK8_CMPHALT) - wrmsr(MSR_AMDK8_IPM, msr & ~AMDK8_CMPHALT); + if ((msr & (AMDK8_SMIONCMPHALT | AMDK8_C1EONCMPHALT)) != 0) + wrmsr(MSR_AMDK8_IPM, msr & ~(AMDK8_SMIONCMPHALT | + AMDK8_C1EONCMPHALT)); } /* Call main idle method. */