Index: head/sys/amd64/amd64/initcpu.c =================================================================== --- head/sys/amd64/amd64/initcpu.c +++ head/sys/amd64/amd64/initcpu.c @@ -69,6 +69,23 @@ 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 * after a long series of push and/or near-call instructions, Index: head/sys/amd64/amd64/machdep.c =================================================================== --- head/sys/amd64/amd64/machdep.c +++ head/sys/amd64/amd64/machdep.c @@ -1928,8 +1928,6 @@ if (env != NULL) strlcpy(kernelname, env, sizeof(kernelname)); - cpu_probe_amdc1e(); - kcsan_cpu_init(0); #ifdef FDT Index: head/sys/i386/i386/initcpu.c =================================================================== --- head/sys/i386/i386/initcpu.c +++ head/sys/i386/i386/initcpu.c @@ -720,8 +720,8 @@ 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 @@ 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; Index: head/sys/i386/i386/machdep.c =================================================================== --- head/sys/i386/i386/machdep.c +++ head/sys/i386/i386/machdep.c @@ -2505,8 +2505,6 @@ thread0.td_pcb->pcb_ext = 0; thread0.td_frame = &proc0_tf; - cpu_probe_amdc1e(); - #ifdef FDT x86_init_fdt(); #endif Index: head/sys/x86/include/specialreg.h =================================================================== --- head/sys/x86/include/specialreg.h +++ head/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 */ @@ -1142,6 +1143,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 */ Index: head/sys/x86/include/x86_var.h =================================================================== --- head/sys/x86/include/x86_var.h +++ head/sys/x86/include/x86_var.h @@ -94,6 +94,7 @@ 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; Index: head/sys/x86/x86/cpu_machdep.c =================================================================== --- head/sys/x86/x86/cpu_machdep.c +++ head/sys/x86/x86/cpu_machdep.c @@ -486,7 +486,9 @@ } 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 @@ } } -/* - * 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 @@ } /* 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. */