]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] x86 bluesmoke update.
authorDave Jones <davej@suse.de>
Wed, 3 Apr 2002 04:02:27 +0000 (20:02 -0800)
committerDave Jones <davej@suse.de>
Wed, 3 Apr 2002 04:02:27 +0000 (20:02 -0800)
o  Make MCE compile time optional (Paul Gortmaker)
o  P4 thermal trip monitoring. (Zwane Mwaikambo)
o  Non-fatal MCE logging. (Me)

arch/i386/Config.help
arch/i386/config.in
arch/i386/kernel/apic.c
arch/i386/kernel/bluesmoke.c
include/asm-i386/apicdef.h
include/asm-i386/hw_irq.h
include/asm-i386/msr.h

index a1025540f5877057b495ae2aad7c4ff8c036e0e1..d4f8b78e91abcaf076879fd3cd0bb2fb0b15b5fd 100644 (file)
@@ -803,6 +803,30 @@ CONFIG_APM_REAL_MODE_POWER_OFF
   a work-around for a number of buggy BIOSes. Switch this option on if
   your computer crashes instead of powering off properly.
 
+CONFIG_X86_MCE
+  Machine Check Exception support allows the processor to notify the
+  kernel if it detects a problem (e.g. overheating, component failure).
+  The action the kernel takes depends on the severity of the problem,
+  ranging from a warning message on the console, to halting the machine.
+  Your processor must be a Pentium or newer to support this - check the
+  flags in /proc/cpuinfo for mce.  Note that some older Pentium systems
+  have a design flaw which leads to false MCE events - hence MCE is
+  disabled on all P5 processors, unless explicitly enabled with "mce"
+  as a boot argument.  Similarly, if MCE is built in and creates a
+  problem on some new non-standard machine, you can boot with "nomce"
+  to disable it.  MCE support simply ignores non-MCE processors like
+  the 386 and 486, so nearly everyone can say Y here.
+
+CONFIG_X86_MCE_NONFATAL
+  Enabling this feature starts a timer that triggers every 5 seconds which
+  will look at the machine check registers to see if anything happened.
+  Non-fatal problems automatically get corrected (but still logged).
+  Disable this if you don't want to see these messages.
+  Seeing the messages this option prints out may be indicative of dying hardware,
+  or out-of-spec (ie, overclocked) hardware.
+  This option only does something on hardware with Intel P6 style MCE.
+  (Pentium Pro and above, AMD Athlon/Duron)
+                                
 CONFIG_TOSHIBA
   This adds a driver to safely access the System Management Mode of
   the CPU on Toshiba portables with a genuine Toshiba BIOS. It does
@@ -909,3 +933,6 @@ CONFIG_DEBUG_BUGVERBOSE
   of the BUG call as well as the EIP and oops trace.  This aids
   debugging but costs about 70-100K of memory.
 
+CONFIG_DEBUG_OBSOLETE
+  Say Y here if you want to reduce the chances of the tree compiling,
+  and are prepared to dig into driver internals to fix compile errors.
index f4640e2a8e7c34c18a5ed3dc2d9bea9d7c9e578f..771fa43e2830c37d697da180ed22c5687adb7ca0 100644 (file)
@@ -156,6 +156,10 @@ if [ "$CONFIG_MWINCHIP3D" = "y" ]; then
    define_bool CONFIG_X86_USE_PPRO_CHECKSUM y
    define_bool CONFIG_X86_OOSTORE y
 fi
+
+bool 'Machine Check Exception' CONFIG_X86_MCE
+dep_bool 'Check for non-fatal errors' CONFIG_X86_MCE_NONFATAL $CONFIG_X86_MCE
+
 tristate 'Toshiba Laptop support' CONFIG_TOSHIBA
 tristate 'Dell laptop support' CONFIG_I8K
 
index c89b744cad4597a1d27c0c1def47efe9042e3530..cb80ad40d1dbda2e3f9444ad11dd1d5bfa7e7346 100644 (file)
@@ -449,6 +449,7 @@ static struct {
        unsigned int apic_lvterr;
        unsigned int apic_tmict;
        unsigned int apic_tdcr;
+       unsigned int apic_thmr;
 } apic_pm_state;
 
 static void apic_pm_suspend(void *data)
@@ -470,6 +471,7 @@ static void apic_pm_suspend(void *data)
        apic_pm_state.apic_lvterr = apic_read(APIC_LVTERR);
        apic_pm_state.apic_tmict = apic_read(APIC_TMICT);
        apic_pm_state.apic_tdcr = apic_read(APIC_TDCR);
+       apic_pm_state.apic_thmr = apic_read(APIC_LVTTHMR);
        __save_flags(flags);
        __cli();
        disable_local_APIC();
@@ -498,6 +500,7 @@ static void apic_pm_resume(void *data)
        apic_write(APIC_SPIV, apic_pm_state.apic_spiv);
        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);
        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);
index 63db0ab54101dd2dc9886fdb83122949c6213277..ac636f46937bc62f0e0514d90ddc20f78132f676 100644 (file)
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/config.h>
+#include <linux/irq.h>
 #include <asm/processor.h> 
+#include <asm/system.h>
 #include <asm/msr.h>
+#include <asm/apic.h>
+#include <asm/pgtable.h>
+
+#ifdef CONFIG_X86_MCE
 
 static int mce_disabled __initdata = 0;
 
+static int banks;
+
 /*
- *     Machine Check Handler For PII/PIII
+ * If we get an MCE, we don't know what state the caches/TLB's are
+ * going to be in, so we throw them all away.
  */
+static void inline flush_all (void)
+{   
+       __asm__ __volatile__ ("invd": : );
+       __flush_tlb();
+}
 
-static int banks;
+/*
+ *     P4/Xeon Thermal transition interrupt handler
+ */
+
+static void intel_thermal_interrupt(struct pt_regs *regs)
+{
+#ifdef CONFIG_X86_LOCAL_APIC
+       u32 l, h;
+       unsigned int cpu = smp_processor_id();
+
+       ack_APIC_irq();
+
+       rdmsr(MSR_IA32_THERM_STATUS, l, h);
+       if (l & 1) {
+               printk(KERN_EMERG "CPU#%d: Temperature above threshold\n", cpu);
+               printk(KERN_EMERG "CPU#%d: Running in modulated clock mode\n", cpu);
+       } else {
+               printk(KERN_INFO "CPU#%d: Temperature/speed normal\n", cpu);
+       }
+#endif
+}
+
+static void unexpected_thermal_interrupt(struct pt_regs *regs)
+{      
+       printk(KERN_ERR "CPU#%d: Unexpected LVT TMR interrupt!\n", smp_processor_id());
+}
+
+/*
+ *     Thermal interrupt handler for this CPU setup
+ */
+
+static void (*vendor_thermal_interrupt)(struct pt_regs *regs) = unexpected_thermal_interrupt;
+
+asmlinkage void smp_thermal_interrupt(struct pt_regs regs)
+{
+       vendor_thermal_interrupt(&regs);
+}
+
+/* P4/Xeon Thermal regulation detect and init */
+
+static void __init intel_init_thermal(struct cpuinfo_x86 *c)
+{
+#ifdef CONFIG_X86_LOCAL_APIC
+       u32 l, h;
+       unsigned int cpu = smp_processor_id();
+
+       /* Thermal monitoring */
+       if (!test_bit(X86_FEATURE_ACPI, &c->x86_capability))
+               return; /* -ENODEV */
+       
+       /* Clock modulation */
+       if (!test_bit(X86_FEATURE_ACC, &c->x86_capability))
+               return; /* -ENODEV */
+
+       rdmsr(MSR_IA32_MISC_ENABLE, l, h);
+       /* 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.
+        */
+
+       if (l & (1<<3)) {
+               printk(KERN_DEBUG "CPU#%d: Thermal monitoring already enabled\n", cpu);
+       } else {
+               wrmsr(MSR_IA32_MISC_ENABLE, l | (1<<3), h);
+               printk(KERN_INFO "CPU#%d: Thermal monitoring enabled\n", cpu);
+       }
+
+       /* check wether a vector already exists */      
+       l = apic_read(APIC_LVTTHMR);
+       if (l & 0xff) {
+               printk(KERN_DEBUG "CPU#%d: Thermal LVT already handled\n", cpu);
+               return; /* -EBUSY */
+       }
+       
+       wrmsr(MSR_IA32_MISC_ENABLE, l | (1<<3), h);
+       printk(KERN_INFO "CPU#%d: Thermal monitoring enabled\n", cpu);
+       
+       /* The temperature transition interrupt handler setup */
+       l = THERMAL_APIC_VECTOR;                /* our delivery vector */
+       l |= (APIC_DM_FIXED | APIC_LVT_MASKED); /* we'll mask till we're ready */
+       apic_write_around(APIC_LVTTHMR, l);
+
+       rdmsr(MSR_IA32_THERM_INTERRUPT, l, h);
+       wrmsr(MSR_IA32_THERM_INTERRUPT, l | 0x3 , h);
+
+       /* ok we're good to go... */
+       vendor_thermal_interrupt = intel_thermal_interrupt;
+       l = apic_read(APIC_LVTTHMR);
+       apic_write_around(APIC_LVTTHMR, l & ~APIC_LVT_MASKED);
+
+       return;
+#endif
+}
+
+/*
+ *     Machine Check Handler For PII/PIII
+ */
 
 static void intel_machine_check(struct pt_regs * regs, long error_code)
 {
@@ -20,7 +132,9 @@ static void intel_machine_check(struct pt_regs * regs, long error_code)
        u32 alow, ahigh, high, low;
        u32 mcgstl, mcgsth;
        int i;
-       
+
+       flush_all();
+
        rdmsr(MSR_IA32_MCG_STATUS, mcgstl, mcgsth);
        if(mcgstl&(1<<0))       /* Recoverable ? */
                recover=0;
@@ -41,13 +155,12 @@ static void intel_machine_check(struct pt_regs * regs, long error_code)
                        if(high&(1<<27))
                        {
                                rdmsr(MSR_IA32_MC0_MISC+i*4, alow, ahigh);
-                               printk("[%08x%08x]", alow, ahigh);
+                               printk("[%08x%08x]", ahigh, alow);
                        }
                        if(high&(1<<26))
                        {
                                rdmsr(MSR_IA32_MC0_ADDR+i*4, alow, ahigh);
-                               printk(" at %08x%08x", 
-                                       ahigh, alow);
+                               printk(" at %08x%08x", ahigh, alow);
                        }
                        printk("\n");
                        /* Clear it */
@@ -109,8 +222,55 @@ asmlinkage void do_machine_check(struct pt_regs * regs, long error_code)
        machine_check_vector(regs, error_code);
 }
 
+
+#ifdef CONFIG_X86_MCE_NONFATAL
+struct timer_list mce_timer;
+
+static void mce_checkregs (unsigned int cpu)
+{
+       u32 low, high;
+       int i;
+
+       if (cpu!=smp_processor_id())
+               BUG();
+
+       for (i=0; i<banks; i++) {
+               rdmsr(MSR_IA32_MC0_STATUS+i*4, low, high);
+
+               if ((low | high) != 0) {
+                       flush_all();
+                       printk (KERN_EMERG "MCE: The hardware reports a non fatal, correctable incident occured on CPU %d.\n", smp_processor_id());
+                       printk (KERN_EMERG "Bank %d: %08x%08x\n", i, high, low);
+
+                       /* Scrub the error so we don't pick it up in 5 seconds time. */
+                       wrmsr(MSR_IA32_MC0_STATUS+i*4, 0UL, 0UL);
+
+                       /* Serialize */
+                       wmb();
+               }
+       }
+
+       /* Refresh the timer. */
+       mce_timer.expires = jiffies + 5 * HZ;
+       add_timer (&mce_timer);
+}
+
+static void mce_timerfunc (unsigned long data)
+{
+       int i;
+
+       for (i=0; i<smp_num_cpus; i++) {
+               if (i == smp_processor_id())
+                       mce_checkregs(i);
+               else
+                       smp_call_function (mce_checkregs, i, 1, 1);
+       }
+}
+#endif
+
+
 /*
- *     Set up machine check reporting for Intel processors
+ *     Set up machine check reporting for processors with Intel style MCE
  */
 
 static void __init intel_mcheck_init(struct cpuinfo_x86 *c)
@@ -164,19 +324,27 @@ static void __init intel_mcheck_init(struct cpuinfo_x86 *c)
        if(done==0)
                printk(KERN_INFO "Intel machine check architecture supported.\n");
        rdmsr(MSR_IA32_MCG_CAP, l, h);
-       if(l&(1<<8))
+       if(l&(1<<8))    /* Control register present ? */
                wrmsr(MSR_IA32_MCG_CTL, 0xffffffff, 0xffffffff);
        banks = l&0xff;
-       for(i=1;i<banks;i++)
-       {
-               wrmsr(MSR_IA32_MC0_CTL+4*i, 0xffffffff, 0xffffffff);
+
+       /* Don't enable bank 0 on intel P6 cores, it goes bang quickly. */
+       if (c->x86_vendor == X86_VENDOR_INTEL && c->x86 == 6) {
+               for(i=1; i<banks; i++)
+                       wrmsr(MSR_IA32_MC0_CTL+4*i, 0xffffffff, 0xffffffff);
+       } else {
+               for(i=0; i<banks; i++)
+                       wrmsr(MSR_IA32_MC0_CTL+4*i, 0xffffffff, 0xffffffff);
        }
-       for(i=0;i<banks;i++)
-       {
+
+       for(i=0; i<banks; i++)
                wrmsr(MSR_IA32_MC0_STATUS+4*i, 0x0, 0x0);
-       }
+
        set_in_cr4(X86_CR4_MCE);
        printk(KERN_INFO "Intel machine check reporting enabled on CPU#%d.\n", smp_processor_id());
+       
+       intel_init_thermal(c);
+
        done=1;
 }
 
@@ -206,28 +374,36 @@ static void __init winchip_mcheck_init(struct cpuinfo_x86 *c)
  *     This has to be run for each processor
  */
 
-
-
 void __init mcheck_init(struct cpuinfo_x86 *c)
 {
        if(mce_disabled==1)
                return;
-               
+
        switch(c->x86_vendor)
        {
                case X86_VENDOR_AMD:
-                       /*
-                        *      AMD K7 machine check is Intel like
-                        */
-                       if(c->x86 == 6)
+                       /* AMD K7 machine check is Intel like */
+                       if(c->x86 == 6) {
                                intel_mcheck_init(c);
+#ifdef CONFIG_X86_MCE_NONFATAL
+                               /* Set the timer to check for non-fatal errors every 5 seconds */
+                               init_timer (&mce_timer);
+                               mce_timer.expires = jiffies + 5 * HZ;
+                               mce_timer.data = 0;
+                               mce_timer.function = &mce_timerfunc;
+                               add_timer (&mce_timer);
+#endif
+                       }
                        break;
+
                case X86_VENDOR_INTEL:
                        intel_mcheck_init(c);
                        break;
+
                case X86_VENDOR_CENTAUR:
                        winchip_mcheck_init(c);
                        break;
+
                default:
                        break;
        }
@@ -247,3 +423,9 @@ static int __init mcheck_enable(char *str)
 
 __setup("nomce", mcheck_disable);
 __setup("mce", mcheck_enable);
+
+#else
+asmlinkage void do_machine_check(struct pt_regs * regs, long error_code) {}
+asmlinkage void smp_thermal_interrupt(struct pt_regs regs) {}
+void __init mcheck_init(struct cpuinfo_x86 *c) {}
+#endif
index f855a7d88d828c160e981cc402be2be5f87208be..a91e6ede6b0afd2a726614bc8d2657e9c1273cf2 100644 (file)
@@ -71,6 +71,7 @@
 #define                        GET_APIC_DEST_FIELD(x)  (((x)>>24)&0xFF)
 #define                        SET_APIC_DEST_FIELD(x)  ((x)<<24)
 #define                APIC_LVTT       0x320
+#define                APIC_LVTTHMR    0x330
 #define                APIC_LVTPC      0x340
 #define                APIC_LVT0       0x350
 #define                        APIC_LVT_TIMER_BASE_MASK        (0x3<<18)
@@ -280,7 +281,16 @@ struct local_apic {
                u32 __reserved_4[3];
        } lvt_timer;
 
-/*330*/        struct { u32 __reserved[4]; } __reserved_15;
+/*330*/        struct { /* LVT - Thermal Sensor */
+               u32  vector             :  8,
+                       delivery_mode   :  3,
+                       __reserved_1    :  1,
+                       delivery_status :  1,
+                       __reserved_2    :  3,
+                       mask            :  1,
+                       __reserved_3    : 15;
+               u32 __reserved_4[3];
+       } lvt_thermal;
 
 /*340*/        struct { /* LVT - Performance Counter */
                u32   vector            :  8,
index 1461dab08d90af6a4a9e8b54c85709291ed00247..bf447a1b97b2d0d01ebf2eaeb32752a9b77e43c2 100644 (file)
@@ -43,6 +43,7 @@
 #define RESCHEDULE_VECTOR      0xfc
 #define CALL_FUNCTION_VECTOR   0xfb
 
+#define THERMAL_APIC_VECTOR    0xf0
 /*
  * Local APIC timer IRQ vector is on a different priority level,
  * to work around the 'lost local interrupt if more than 2 IRQ
index 4a16e96dd3610badcd71da53f6bf7f192a7a83fe..2a57654f0d0f3b732a100ea05becff6b69f9d84f 100644 (file)
 #define MSR_IA32_MCG_STATUS            0x17a
 #define MSR_IA32_MCG_CTL               0x17b
 
-#define MSR_IA32_EVNTSEL0              0x186
-#define MSR_IA32_EVNTSEL1              0x187
+#define MSR_P6_EVNTSEL0                0x186
+#define MSR_P6_EVNTSEL1                0x187
+
+#define MSR_IA32_THERM_CONTROL         0x19a
+#define MSR_IA32_THERM_INTERRUPT       0x19b
+#define MSR_IA32_THERM_STATUS          0x19c
+#define MSR_IA32_MISC_ENABLE           0x1a0
 
 #define MSR_IA32_DEBUGCTLMSR           0x1d9
 #define MSR_IA32_LASTBRANCHFROMIP      0x1db