[Patch 2-3] Enable CMCI for Intel CPUs -- add CMCI LVT entry in APIC This patch is the routines for adding a new CMCI LVT entry in APIC. And also it does small clean-up for Thermal since Thermal is not only used by P4, but also later CPU families. We'll remove P4 thermal code to the intel common code in the next patch. Signed-off-by Yunhong Jiang Signed-off-by Liping Ke diff -r ccd94aa88f31 xen/arch/x86/apic.c --- a/xen/arch/x86/apic.c Mon Dec 22 09:42:10 2008 +0800 +++ b/xen/arch/x86/apic.c Mon Dec 22 10:17:19 2008 +0800 @@ -99,8 +99,11 @@ /* Performance Counters Interrupt */ set_intr_gate(PMU_APIC_VECTOR, pmu_apic_interrupt); - /* thermal monitor LVT interrupt */ -#ifdef CONFIG_X86_MCE_P4THERMAL + /* CMCI Correctable Machine Check Interrupt */ + set_intr_gate(CMCI_APIC_VECTOR, cmci_interrupt); + + /* thermal monitor LVT interrupt, for P4 and latest Intel CPU*/ +#ifdef CONFIG_X86_MCE_THERMAL set_intr_gate(THERMAL_APIC_VECTOR, thermal_interrupt); #endif } @@ -172,12 +175,17 @@ } /* lets not touch this if we didn't frob it */ -#ifdef CONFIG_X86_MCE_P4THERMAL +#ifdef CONFIG_X86_MCE_THERMAL if (maxlvt >= 5) { v = apic_read(APIC_LVTTHMR); apic_write_around(APIC_LVTTHMR, v | APIC_LVT_MASKED); } #endif + + if (maxlvt >= 6) { + v = apic_read(APIC_CMCI); + apic_write_around(APIC_CMCI, v | APIC_LVT_MASKED); + } /* * Clean APIC state for other OSs: */ @@ -189,10 +197,13 @@ if (maxlvt >= 4) apic_write_around(APIC_LVTPC, APIC_LVT_MASKED); -#ifdef CONFIG_X86_MCE_P4THERMAL +#ifdef CONFIG_X86_MCE_THERMAL if (maxlvt >= 5) apic_write_around(APIC_LVTTHMR, APIC_LVT_MASKED); #endif + if (maxlvt >= 6) + apic_write_around(APIC_CMCI, APIC_LVT_MASKED); + v = GET_APIC_VERSION(apic_read(APIC_LVR)); if (APIC_INTEGRATED(v)) { /* !82489DX */ if (maxlvt > 3) /* Due to Pentium errata 3AP and 11AP. */ @@ -597,6 +608,7 @@ unsigned int apic_spiv; unsigned int apic_lvtt; unsigned int apic_lvtpc; + unsigned int apic_lvtcmci; unsigned int apic_lvt0; unsigned int apic_lvt1; unsigned int apic_lvterr; @@ -608,7 +620,7 @@ int lapic_suspend(void) { unsigned long flags; - + int maxlvt = get_maxlvt(); if (!apic_pm_state.active) return 0; @@ -620,6 +632,11 @@ apic_pm_state.apic_spiv = apic_read(APIC_SPIV); apic_pm_state.apic_lvtt = apic_read(APIC_LVTT); apic_pm_state.apic_lvtpc = apic_read(APIC_LVTPC); + + if (maxlvt >= 6) { + apic_pm_state.apic_lvtcmci = apic_read(APIC_CMCI); + } + apic_pm_state.apic_lvt0 = apic_read(APIC_LVT0); apic_pm_state.apic_lvt1 = apic_read(APIC_LVT1); apic_pm_state.apic_lvterr = apic_read(APIC_LVTERR); @@ -637,6 +654,7 @@ { unsigned int l, h; unsigned long flags; + int maxlvt = get_maxlvt(); if (!apic_pm_state.active) return 0; @@ -669,6 +687,11 @@ apic_write(APIC_LVT0, apic_pm_state.apic_lvt0); apic_write(APIC_LVT1, apic_pm_state.apic_lvt1); apic_write(APIC_LVTTHMR, apic_pm_state.apic_thmr); + + if (maxlvt >= 6) { + apic_write(APIC_CMCI, apic_pm_state.apic_lvtcmci); + } + apic_write(APIC_LVTPC, apic_pm_state.apic_lvtpc); apic_write(APIC_LVTT, apic_pm_state.apic_lvtt); apic_write(APIC_TDCR, apic_pm_state.apic_tdcr); diff -r ccd94aa88f31 xen/arch/x86/cpu/mcheck/Makefile --- a/xen/arch/x86/cpu/mcheck/Makefile Mon Dec 22 09:42:10 2008 +0800 +++ b/xen/arch/x86/cpu/mcheck/Makefile Mon Dec 22 10:17:19 2008 +0800 @@ -3,6 +3,7 @@ obj-y += amd_k8.o obj-y += amd_f10.o obj-y += mce.o +obj-y += mce_intel.o obj-y += non-fatal.o obj-y += p4.o obj-$(x86_32) += p5.o diff -r ccd94aa88f31 xen/arch/x86/cpu/mcheck/mce_intel.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xen/arch/x86/cpu/mcheck/mce_intel.c Mon Dec 22 10:17:19 2008 +0800 @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mce.h" +#include "x86_mca.h" + +#ifdef CONFIG_X86_MCE_THERMAL +static void unexpected_thermal_interrupt(struct cpu_user_regs *regs) +{ + printk(KERN_ERR "Thermal: CPU%d: Unexpected LVT TMR interrupt!\n", + smp_processor_id()); + add_taint(TAINT_MACHINE_CHECK); +} + +/* P4/Xeon Thermal transition interrupt handler */ +static void intel_thermal_interrupt(struct cpu_user_regs *regs) +{ + u32 l, h; + unsigned int cpu = smp_processor_id(); + static s_time_t next[NR_CPUS]; + + ack_APIC_irq(); + if (NOW() < next[cpu]) + return; + + next[cpu] = NOW() + MILLISECS(5000); + rdmsr(MSR_IA32_THERM_STATUS, l, h); + if (l & 0x1) { + printk(KERN_EMERG "CPU%d: Temperature above threshold\n", cpu); + printk(KERN_EMERG "CPU%d: Running in modulated clock mode\n", + cpu); + add_taint(TAINT_MACHINE_CHECK); + } else { + printk(KERN_INFO "CPU%d: Temperature/speed normal\n", cpu); + } +} + +/* Thermal interrupt handler for this CPU setup */ +static void (*vendor_thermal_interrupt)(struct cpu_user_regs *regs) + = unexpected_thermal_interrupt; + +fastcall void smp_thermal_interrupt(struct cpu_user_regs *regs) +{ + irq_enter(); + vendor_thermal_interrupt(regs); + irq_exit(); +} + +/* P4/Xeon Thermal regulation detect and init */ +static void intel_init_thermal(struct cpuinfo_x86 *c) +{ + u32 l, h; + int tm2 = 0; + unsigned int cpu = smp_processor_id(); + + /* Thermal monitoring */ + if (!cpu_has(c, X86_FEATURE_ACPI)) + return; /* -ENODEV */ + + /* Clock modulation */ + if (!cpu_has(c, X86_FEATURE_ACC)) + return; /* -ENODEV */ + + /* first check if its enabled already, in which case there might + * be some SMM goo which handles it, so we can't even put a handler + * since it might be delivered via SMI already -zwanem. + */ + rdmsr (MSR_IA32_MISC_ENABLE, l, h); + h = apic_read(APIC_LVTTHMR); + if ((l & (1<<3)) && (h & APIC_DM_SMI)) { + printk(KERN_DEBUG "CPU%d: Thermal monitoring handled by SMI\n",cpu); + return; /* -EBUSY */ + } + + if (cpu_has(c, X86_FEATURE_TM2) && (l & (1 << 13))) + tm2 = 1; + + /* check whether a vector already exists, temporarily masked? */ + if (h & APIC_VECTOR_MASK) { + printk(KERN_DEBUG "CPU%d: Thermal LVT vector (%#x) already installed\n", + cpu, (h & APIC_VECTOR_MASK)); + return; /* -EBUSY */ + } + + /* The temperature transition interrupt handler setup */ + h = THERMAL_APIC_VECTOR; /* our delivery vector */ + h |= (APIC_DM_FIXED | APIC_LVT_MASKED); /* we'll mask till we're ready */ + apic_write_around(APIC_LVTTHMR, h); + + rdmsr (MSR_IA32_THERM_INTERRUPT, l, h); + wrmsr (MSR_IA32_THERM_INTERRUPT, l | 0x03 , h); + + /* ok we're good to go... */ + vendor_thermal_interrupt = intel_thermal_interrupt; + + rdmsr (MSR_IA32_MISC_ENABLE, l, h); + wrmsr (MSR_IA32_MISC_ENABLE, l | (1<<3), h); + + l = apic_read (APIC_LVTTHMR); + apic_write_around (APIC_LVTTHMR, l & ~APIC_LVT_MASKED); + printk (KERN_INFO "CPU%d: Thermal monitoring enabled (%s)\n", + cpu, tm2 ? "TM2" : "TM1"); + return; +} +#endif /* CONFIG_X86_MCE_THERMAL */ + + +fastcall void smp_cmci_interrupt(struct cpu_user_regs *regs) +{ +} + diff -r ccd94aa88f31 xen/arch/x86/hvm/vmx/vmx.c --- a/xen/arch/x86/hvm/vmx/vmx.c Mon Dec 22 09:42:10 2008 +0800 +++ b/xen/arch/x86/hvm/vmx/vmx.c Mon Dec 22 10:17:19 2008 +0800 @@ -2030,7 +2030,8 @@ fastcall void smp_spurious_interrupt(struct cpu_user_regs *regs); fastcall void smp_error_interrupt(struct cpu_user_regs *regs); fastcall void smp_pmu_apic_interrupt(struct cpu_user_regs *regs); -#ifdef CONFIG_X86_MCE_P4THERMAL + fastcall void smp_cmci_interrupt(struct cpu_user_regs *regs); +#ifdef CONFIG_X86_MCE_THERMAL fastcall void smp_thermal_interrupt(struct cpu_user_regs *regs); #endif @@ -2060,10 +2061,13 @@ case ERROR_APIC_VECTOR: smp_error_interrupt(regs); break; + case CMCI_APIC_VECTOR: + smp_cmci_interrupt(regs); + break; case PMU_APIC_VECTOR: smp_pmu_apic_interrupt(regs); break; -#ifdef CONFIG_X86_MCE_P4THERMAL +#ifdef CONFIG_X86_MCE_THERMAL case THERMAL_APIC_VECTOR: smp_thermal_interrupt(regs); break; diff -r ccd94aa88f31 xen/arch/x86/i8259.c --- a/xen/arch/x86/i8259.c Mon Dec 22 09:42:10 2008 +0800 +++ b/xen/arch/x86/i8259.c Mon Dec 22 10:17:19 2008 +0800 @@ -74,6 +74,7 @@ BUILD_SMP_INTERRUPT(spurious_interrupt,SPURIOUS_APIC_VECTOR) BUILD_SMP_INTERRUPT(pmu_apic_interrupt,PMU_APIC_VECTOR) BUILD_SMP_INTERRUPT(thermal_interrupt,THERMAL_APIC_VECTOR) +BUILD_SMP_INTERRUPT(cmci_interrupt, CMCI_APIC_VECTOR) #define IRQ(x,y) \ IRQ##x##y##_interrupt diff -r ccd94aa88f31 xen/include/asm-x86/apicdef.h --- a/xen/include/asm-x86/apicdef.h Mon Dec 22 09:42:10 2008 +0800 +++ b/xen/include/asm-x86/apicdef.h Mon Dec 22 10:17:19 2008 +0800 @@ -80,6 +80,8 @@ #define APIC_LVTTHMR 0x330 #define APIC_LVTPC 0x340 #define APIC_LVT0 0x350 +#define APIC_CMCI 0x2F0 + #define APIC_LVT_TIMER_BASE_MASK (0x3<<18) #define GET_APIC_TIMER_BASE(x) (((x)>>18)&0x3) #define SET_APIC_TIMER_BASE(x) (((x)<<18)) diff -r ccd94aa88f31 xen/include/asm-x86/irq.h --- a/xen/include/asm-x86/irq.h Mon Dec 22 09:42:10 2008 +0800 +++ b/xen/include/asm-x86/irq.h Mon Dec 22 10:17:19 2008 +0800 @@ -33,6 +33,7 @@ fastcall void pmu_apic_interrupt(void); fastcall void spurious_interrupt(void); fastcall void thermal_interrupt(void); +fastcall void cmci_interrupt(void); void disable_8259A_irq(unsigned int irq); void enable_8259A_irq(unsigned int irq); diff -r ccd94aa88f31 xen/include/asm-x86/mach-default/irq_vectors.h --- a/xen/include/asm-x86/mach-default/irq_vectors.h Mon Dec 22 09:42:10 2008 +0800 +++ b/xen/include/asm-x86/mach-default/irq_vectors.h Mon Dec 22 10:17:19 2008 +0800 @@ -10,13 +10,13 @@ #define THERMAL_APIC_VECTOR 0xfa #define LOCAL_TIMER_VECTOR 0xf9 #define PMU_APIC_VECTOR 0xf8 - +#define CMCI_APIC_VECTOR 0xf7 /* * High-priority dynamically-allocated vectors. For interrupts that * must be higher priority than any guest-bound interrupt. */ #define FIRST_HIPRIORITY_VECTOR 0xf0 -#define LAST_HIPRIORITY_VECTOR 0xf7 +#define LAST_HIPRIORITY_VECTOR 0xf6 /* Legacy PIC uses vectors 0xe0-0xef. */ #define FIRST_LEGACY_VECTOR 0xe0