diff options
-rw-r--r-- | sys/amd64/amd64/local_apic.c | 23 | ||||
-rw-r--r-- | sys/amd64/amd64/machdep.c | 73 | ||||
-rw-r--r-- | sys/i386/i386/local_apic.c | 23 | ||||
-rw-r--r-- | sys/i386/i386/machdep.c | 77 |
4 files changed, 150 insertions, 46 deletions
diff --git a/sys/amd64/amd64/local_apic.c b/sys/amd64/amd64/local_apic.c index 0a102ee..6ed6c9c 100644 --- a/sys/amd64/amd64/local_apic.c +++ b/sys/amd64/amd64/local_apic.c @@ -329,29 +329,6 @@ lapic_setup(int boot) /* XXX: Error and thermal LVTs */ - if (cpu_vendor_id == CPU_VENDOR_AMD) { - /* - * Detect the presence of C1E capability mostly on latest - * dual-cores (or future) k8 family. This feature 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 - */ - if ((cpu_id & 0x00000f00) == 0x00000f00 && - (cpu_id & 0x0fff0000) >= 0x00040000) { - uint64_t msr; - - msr = rdmsr(0xc0010055); - if (msr & 0x18000000) - wrmsr(0xc0010055, msr & ~0x18000000ULL); - } - } - intr_restore(eflags); } diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c index 6993dea..de89c3b 100644 --- a/sys/amd64/amd64/machdep.c +++ b/sys/amd64/amd64/machdep.c @@ -600,6 +600,69 @@ cpu_idle_acpi(int busy) __asm __volatile("sti; hlt"); } +static int cpu_ident_amdc1e = 0; + +static int +cpu_probe_amdc1e(void) +{ + int i; + + /* + * Forget it, if we're not using local APIC timer. + */ + if (resource_disabled("apic", 0) || + (resource_int_value("apic", 0, "clock", &i) == 0 && i == 0)) + return (0); + + /* + * 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; + return (1); + } + + return (0); +} + +/* + * 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) + +static void +cpu_idle_amdc1e(int busy) +{ + + disable_intr(); + if (sched_runnable()) + enable_intr(); + else { + uint64_t msr; + + msr = rdmsr(MSR_AMDK8_IPM); + if (msr & AMDK8_CMPHALT) + wrmsr(MSR_AMDK8_IPM, msr & ~AMDK8_CMPHALT); + + if (cpu_idle_hook) + cpu_idle_hook(); + else + __asm __volatile("sti; hlt"); + } +} + static void cpu_idle_spin(int busy) { @@ -697,6 +760,7 @@ struct { { cpu_idle_spin, "spin" }, { cpu_idle_mwait, "mwait" }, { cpu_idle_mwait_hlt, "mwait_hlt" }, + { cpu_idle_amdc1e, "amdc1e" }, { cpu_idle_hlt, "hlt" }, { cpu_idle_acpi, "acpi" }, { NULL, NULL } @@ -715,6 +779,9 @@ idle_sysctl_available(SYSCTL_HANDLER_ARGS) if (strstr(idle_tbl[i].id_name, "mwait") && (cpu_feature2 & CPUID2_MON) == 0) continue; + if (strcmp(idle_tbl[i].id_name, "amdc1e") == 0 && + cpu_ident_amdc1e == 0) + continue; p += sprintf(p, "%s, ", idle_tbl[i].id_name); } error = sysctl_handle_string(oidp, avail, 0, req); @@ -745,6 +812,9 @@ idle_sysctl(SYSCTL_HANDLER_ARGS) if (strstr(idle_tbl[i].id_name, "mwait") && (cpu_feature2 & CPUID2_MON) == 0) continue; + if (strcmp(idle_tbl[i].id_name, "amdc1e") == 0 && + cpu_ident_amdc1e == 0) + continue; if (strcmp(idle_tbl[i].id_name, buf)) continue; cpu_idle_fn = idle_tbl[i].id_fn; @@ -1593,6 +1663,9 @@ hammer_time(u_int64_t modulep, u_int64_t physfree) } #endif + if (cpu_probe_amdc1e()) + cpu_idle_fn = cpu_idle_amdc1e; + /* Location of kernel stack for locore */ return ((u_int64_t)thread0.td_pcb); } diff --git a/sys/i386/i386/local_apic.c b/sys/i386/i386/local_apic.c index 7969a40..26a1dcf 100644 --- a/sys/i386/i386/local_apic.c +++ b/sys/i386/i386/local_apic.c @@ -331,29 +331,6 @@ lapic_setup(int boot) /* XXX: Error and thermal LVTs */ - if (cpu_vendor_id == CPU_VENDOR_AMD) { - /* - * Detect the presence of C1E capability mostly on latest - * dual-cores (or future) k8 family. This feature 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 - */ - if ((cpu_id & 0x00000f00) == 0x00000f00 && - (cpu_id & 0x0fff0000) >= 0x00040000) { - uint64_t msr; - - msr = rdmsr(0xc0010055); - if (msr & 0x18000000) - wrmsr(0xc0010055, msr & ~0x18000000ULL); - } - } - intr_restore(eflags); } diff --git a/sys/i386/i386/machdep.c b/sys/i386/i386/machdep.c index e64bcd2..a7177f7 100644 --- a/sys/i386/i386/machdep.c +++ b/sys/i386/i386/machdep.c @@ -1231,6 +1231,70 @@ cpu_idle_acpi(int busy) __asm __volatile("sti; hlt"); } +static int cpu_ident_amdc1e = 0; + +static int +cpu_probe_amdc1e(void) +{ +#ifdef DEV_APIC + int i; + + /* + * Forget it, if we're not using local APIC timer. + */ + if (resource_disabled("apic", 0) || + (resource_int_value("apic", 0, "clock", &i) == 0 && i == 0)) + return (0); + + /* + * 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; + return (1); + } +#endif + return (0); +} + +/* + * 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) + +static void +cpu_idle_amdc1e(int busy) +{ + + disable_intr(); + if (sched_runnable()) + enable_intr(); + else { + uint64_t msr; + + msr = rdmsr(MSR_AMDK8_IPM); + if (msr & AMDK8_CMPHALT) + wrmsr(MSR_AMDK8_IPM, msr & ~AMDK8_CMPHALT); + + if (cpu_idle_hook) + cpu_idle_hook(); + else + __asm __volatile("sti; hlt"); + } +} + static void cpu_idle_spin(int busy) { @@ -1332,6 +1396,7 @@ struct { { cpu_idle_spin, "spin" }, { cpu_idle_mwait, "mwait" }, { cpu_idle_mwait_hlt, "mwait_hlt" }, + { cpu_idle_amdc1e, "amdc1e" }, { cpu_idle_hlt, "hlt" }, { cpu_idle_acpi, "acpi" }, { NULL, NULL } @@ -1350,6 +1415,9 @@ idle_sysctl_available(SYSCTL_HANDLER_ARGS) if (strstr(idle_tbl[i].id_name, "mwait") && (cpu_feature2 & CPUID2_MON) == 0) continue; + if (strcmp(idle_tbl[i].id_name, "amdc1e") == 0 && + cpu_ident_amdc1e == 0) + continue; p += sprintf(p, "%s, ", idle_tbl[i].id_name); } error = sysctl_handle_string(oidp, avail, 0, req); @@ -1380,6 +1448,9 @@ idle_sysctl(SYSCTL_HANDLER_ARGS) if (strstr(idle_tbl[i].id_name, "mwait") && (cpu_feature2 & CPUID2_MON) == 0) continue; + if (strcmp(idle_tbl[i].id_name, "amdc1e") == 0 && + cpu_ident_amdc1e == 0) + continue; if (strcmp(idle_tbl[i].id_name, buf)) continue; cpu_idle_fn = idle_tbl[i].id_fn; @@ -2583,6 +2654,9 @@ init386(first) thread0.td_frame = &proc0_tf; thread0.td_pcb->pcb_fsd = PCPU_GET(fsgs_gdt)[0]; thread0.td_pcb->pcb_gsd = PCPU_GET(fsgs_gdt)[1]; + + if (cpu_probe_amdc1e()) + cpu_idle_fn = cpu_idle_amdc1e; } #else @@ -2847,6 +2921,9 @@ init386(first) #endif thread0.td_pcb->pcb_ext = 0; thread0.td_frame = &proc0_tf; + + if (cpu_probe_amdc1e()) + cpu_idle_fn = cpu_idle_amdc1e; } #endif |