diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c index 82e6e56..4549321 100644 --- a/sys/amd64/amd64/machdep.c +++ b/sys/amd64/amd64/machdep.c @@ -86,6 +86,8 @@ __FBSDID("$FreeBSD: src/sys/amd64/amd64/ #include #include #include +#include +#include #include #include @@ -1910,3 +1912,106 @@ outb(u_int port, u_char data) } #endif /* KDB */ + +int banks; + +void mce_kthread(void *); +void handle_mce(void); +static void init_mce(void *); + +void +handle_mce(void) +{ + long addr, misc, status, tsc; + int i, regs[4]; + + addr = misc = 0; + for (i = 0; i < banks; i++) { + status = rdmsr(MSR_MC0_STATUS + 4 * i); + if ((status & MCE_STATUS_VAL) == 0) + continue; + + if ((status & MCE_STATUS_ADDR) != 0) + addr = rdmsr(MSR_MC0_ADDR + 4 * i); + if ((status & MCE_STATUS_MISC) != 0) + misc = rdmsr(MSR_MC0_MISC + 4 * i); + tsc = rdtsc(); + printf("cpu %d got %s MCE bank %d: status %lx addr %lx misc %lx tsc %lx\n", + cpu, (status & MCE_STATUS_UC) ? "uncorrected" : "corrected", + i, status, addr, misc, tsc); + wrmsr(MSR_MC0_STATUS + 4 * i, 0); + do_cpuid(1, regs); + } + wrmsr(MSR_MCG_STATUS, 0); + +} + +void +mce_kthread(void *unused) +{ + register_t rflags; + int cpu; + + while (1) { + for (cpu = 0; cpu < MAXCPU; cpu++) { + if (CPU_ABSENT(cpu)) + continue; + + thread_lock(curthread); + sched_bind(curthread, cpu); + thread_unlock(curthread); + + rflags = intr_disable(); + handle_mce(); + intr_restore(rflags); + + thread_lock(curthread); + sched_unbind(curthread); + thread_unlock(curthread); + } + tsleep(mce_kthread, PPAUSE, "mce poll", hz); + } +} + +static void +init_mce(void *unused) +{ + long cap; + int cpu, error, i, regs[4]; + + do_cpuid(1, regs); + /* MCA and MCE not supported. */ + if (((regs[3] & CPUID_MCA) == 0) || ((regs[3] & CPUID_MCE) == 0)) + return; + + cap = rdmsr(MSR_MCG_CAP); + banks = cap & 0xff; + printf("%s: found %d MCE banks\n", __func__, banks); + + for (cpu = 0; cpu < MAXCPU; cpu++) { + if (CPU_ABSENT(cpu)) + continue; + + thread_lock(curthread); + sched_bind(curthread, cpu); + thread_unlock(curthread); + load_cr4(rcr4() | CR4_MCE); + + if (cap & 0x100) + wrmsr(MSR_MCG_CTL, 0xffffffffffffffffUL); + + for (i = 0; i < banks; i++) { + wrmsr(MSR_MC0_CTL + 4 * i, 0xffffffffffffffffUL); + wrmsr(MSR_MC0_STATUS, 0); + } + thread_lock(curthread); + sched_unbind(curthread); + thread_unlock(curthread); + } + + error = kthread_create(mce_kthread, NULL, NULL, 0, 0, "mce poll"); + if (error) + printf("Couldn't start MCE poll thread\n"); +} + +SYSINIT(mce, SI_SUB_RUN_SCHEDULER, SI_ORDER_FIRST, init_mce, NULL) diff --git a/sys/amd64/amd64/mp_machdep.c b/sys/amd64/amd64/mp_machdep.c diff --git a/sys/amd64/amd64/trap.c b/sys/amd64/amd64/trap.c index 93d8fd6..ad7e3e3 100644 --- a/sys/amd64/amd64/trap.c +++ b/sys/amd64/amd64/trap.c @@ -147,6 +147,8 @@ SYSCTL_INT(_machdep, OID_AUTO, panic_on_ extern char *syscallnames[]; +void handle_mce(void); + /* * Exception, fault, and trap interface to the FreeBSD kernel. * This common code is called from assembly language IDT gate entry @@ -182,6 +184,11 @@ trap(struct trapframe *frame) goto out; } #endif + if (type == T_MCHK) { + printf("got MCE!!!\n"); + handle_mce(); + goto out; + } #ifdef HWPMC_HOOKS /* diff --git a/sys/amd64/include/specialreg.h b/sys/amd64/include/specialreg.h index 91436d0..404adb0 100644 --- a/sys/amd64/include/specialreg.h +++ b/sys/amd64/include/specialreg.h @@ -393,4 +393,12 @@ #define MSR_TOP_MEM 0xc001001a /* boundary for ram below 4G */ #define MSR_TOP_MEM2 0xc001001d /* boundary for ram above 4G */ +#define MCE_STATUS_PCC 0x0200000000000000UL +#define MCE_STATUS_ADDR 0x0400000000000000UL +#define MCE_STATUS_MISC 0x0800000000000000UL +#define MCE_STATUS_EN 0x1000000000000000UL +#define MCE_STATUS_UC 0x2000000000000000UL +#define MCE_STATUS_OVER 0x4000000000000000UL +#define MCE_STATUS_VAL 0x8000000000000000UL + #endif /* !_MACHINE_SPECIALREG_H_ */