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. */