bluesmoke.c 5.17 KB
Newer Older
Dave Jones's avatar
Dave Jones committed
1 2 3
/*
 * arch/x86_64/kernel/bluesmoke.c - x86-64 Machine Check Exception Reporting
 */
4 5 6 7

#include <linux/init.h>
#include <linux/types.h>
#include <linux/kernel.h>
Rusty Russell's avatar
Rusty Russell committed
8
#include <linux/jiffies.h>
9 10 11
#include <linux/smp.h>
#include <linux/config.h>
#include <linux/irq.h>
12
#include <asm/processor.h> 
13
#include <asm/system.h>
14
#include <asm/msr.h>
15 16 17
#include <asm/apic.h>
#include <asm/pgtable.h>
#include <asm/tlbflush.h>
18

Dave Jones's avatar
Dave Jones committed
19 20
#ifdef CONFIG_X86_MCE

21 22
static int mce_disabled __initdata = 0;

23 24 25 26
static int banks;


/*
Dave Jones's avatar
Dave Jones committed
27
 *	Machine Check Handler For Hammer
28
 */
29

Dave Jones's avatar
Dave Jones committed
30
static void hammer_machine_check(struct pt_regs * regs, long error_code)
31 32 33 34 35
{
	int recover=1;
	u32 alow, ahigh, high, low;
	u32 mcgstl, mcgsth;
	int i;
36

37 38 39 40 41
	rdmsr(MSR_IA32_MCG_STATUS, mcgstl, mcgsth);
	if(mcgstl&(1<<0))	/* Recoverable ? */
		recover=0;

	printk(KERN_EMERG "CPU %d: Machine Check Exception: %08x%08x\n", smp_processor_id(), mcgsth, mcgstl);
42
	preempt_disable();
Dave Jones's avatar
Dave Jones committed
43
	for (i=0;i<banks;i++) {
44
		rdmsr(MSR_IA32_MC0_STATUS+i*4,low, high);
Dave Jones's avatar
Dave Jones committed
45
		if(high&(1<<31)) {
46 47 48 49 50 51
			if(high&(1<<29))
				recover|=1;
			if(high&(1<<25))
				recover|=2;
			printk(KERN_EMERG "Bank %d: %08x%08x", i, high, low);
			high&=~(1<<31);
Dave Jones's avatar
Dave Jones committed
52
			if(high&(1<<27)) {
53
				rdmsr(MSR_IA32_MC0_MISC+i*4, alow, ahigh);
54
				printk("[%08x%08x]", ahigh, alow);
55
			}
Dave Jones's avatar
Dave Jones committed
56
			if(high&(1<<26)) {
57
				rdmsr(MSR_IA32_MC0_ADDR+i*4, alow, ahigh);
58
				printk(" at %08x%08x", ahigh, alow);
59 60 61 62 63 64 65 66
			}
			printk("\n");
			/* Clear it */
			wrmsr(MSR_IA32_MC0_STATUS+i*4, 0UL, 0UL);
			/* Serialize */
			wmb();
		}
	}
67
	preempt_enable();
Dave Jones's avatar
Dave Jones committed
68

69 70 71 72 73 74 75 76 77
	if(recover&2)
		panic("CPU context corrupt");
	if(recover&1)
		panic("Unable to continue");
	printk(KERN_EMERG "Attempting to continue.\n");
	mcgstl&=~(1<<2);
	wrmsr(MSR_IA32_MCG_STATUS,mcgstl, mcgsth);
}

Dave Jones's avatar
Dave Jones committed
78

79 80 81 82 83
/*
 *	Handle unconfigured int18 (should never happen)
 */

static void unexpected_machine_check(struct pt_regs * regs, long error_code)
Dave Jones's avatar
Dave Jones committed
84
{	
85
	printk(KERN_ERR "CPU#%d: Unexpected int18 (Machine Check).\n", smp_processor_id());
Dave Jones's avatar
Dave Jones committed
86
}
87 88 89

/*
 *	Call the installed machine check handler for this CPU setup.
Dave Jones's avatar
Dave Jones committed
90 91
 */

92 93
static void (*machine_check_vector)(struct pt_regs *, long error_code) = unexpected_machine_check;

94
asmlinkage void do_machine_check(struct pt_regs * regs, long error_code)
95 96 97 98
{
	machine_check_vector(regs, error_code);
}

99 100

#ifdef CONFIG_X86_MCE_NONFATAL
Dave Jones's avatar
Dave Jones committed
101 102
static struct timer_list mce_timer;
static int timerset = 0;
103

Dave Jones's avatar
Dave Jones committed
104 105 106
#define MCE_RATE	15*HZ	/* timer rate is 15s */

static void mce_checkregs (void *info)
107 108 109
{
	u32 low, high;
	int i;
Dave Jones's avatar
Dave Jones committed
110
	unsigned int *cpu = info;
111

Dave Jones's avatar
Dave Jones committed
112
	BUG_ON (*cpu != smp_processor_id());
113

114
	preempt_disable();
115 116 117 118 119 120 121
	for (i=0; i<banks; i++) {
		rdmsr(MSR_IA32_MC0_STATUS+i*4, low, high);

		if ((low | high) != 0) {
			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);

Dave Jones's avatar
Dave Jones committed
122
			/* Scrub the error so we don't pick it up in MCE_RATE seconds time. */
123 124 125 126 127 128
			wrmsr(MSR_IA32_MC0_STATUS+i*4, 0UL, 0UL);

			/* Serialize */
			wmb();
		}
	}
129
	preempt_enable();
130 131
}

Dave Jones's avatar
Dave Jones committed
132

133 134
static void mce_timerfunc (unsigned long data)
{
Dave Jones's avatar
Dave Jones committed
135
	unsigned int i;
136 137 138

	for (i=0; i<smp_num_cpus; i++) {
		if (i == smp_processor_id())
Dave Jones's avatar
Dave Jones committed
139
			mce_checkregs(&i);
140
		else
Dave Jones's avatar
Dave Jones committed
141
			smp_call_function (mce_checkregs, &i, 1, 1);
142
	}
Dave Jones's avatar
Dave Jones committed
143 144 145 146

	/* Refresh the timer. */
	mce_timer.expires = jiffies + MCE_RATE;
	add_timer (&mce_timer);
147 148 149 150
}
#endif


151
/*
152
 *	Set up machine check reporting for processors with Intel style MCE
153 154
 */

Dave Jones's avatar
Dave Jones committed
155
static void __init hammer_mcheck_init(struct cpuinfo_x86 *c)
156 157 158 159 160 161 162 163 164
{
	u32 l, h;
	int i;
	static int done;
	
	/*
	 *	Check for MCE support
	 */

165
	if( !test_bit(X86_FEATURE_MCE, c->x86_capability) )
166
		return;	
Dave Jones's avatar
Dave Jones committed
167 168

	/* Check for PPro style MCA */
169
	if( !test_bit(X86_FEATURE_MCA, c->x86_capability) )
170
		return;
Dave Jones's avatar
Dave Jones committed
171

172
	/* Ok machine check is available */
Dave Jones's avatar
Dave Jones committed
173
	machine_check_vector = hammer_machine_check;
174
	wmb();
Dave Jones's avatar
Dave Jones committed
175

176
	if(done==0)
Dave Jones's avatar
Dave Jones committed
177
		printk(KERN_INFO "Machine check architecture supported.\n");
178
	rdmsr(MSR_IA32_MCG_CAP, l, h);
179
	if(l&(1<<8))	/* Control register present ? */
180 181
		wrmsr(MSR_IA32_MCG_CTL, 0xffffffff, 0xffffffff);
	banks = l&0xff;
182

Dave Jones's avatar
Dave Jones committed
183
	for(i=0; i<banks; i++)
184
		wrmsr(MSR_IA32_MC0_CTL+4*i, 0xffffffff, 0xffffffff);
185 186

	for(i=0; i<banks; i++)
187
		wrmsr(MSR_IA32_MC0_STATUS+4*i, 0x0, 0x0);
188

189
	set_in_cr4(X86_CR4_MCE);
Dave Jones's avatar
Dave Jones committed
190
	printk(KERN_INFO "Machine check reporting enabled on CPU#%d.\n", smp_processor_id());
191

192 193 194 195 196 197 198 199 200
	done=1;
}

/*
 *	This has to be run for each processor
 */

void __init mcheck_init(struct cpuinfo_x86 *c)
{
Dave Jones's avatar
Dave Jones committed
201

202 203
	if(mce_disabled==1)
		return;
Dave Jones's avatar
Dave Jones committed
204

205 206 207
	switch(c->x86_vendor)
	{
		case X86_VENDOR_AMD:
Dave Jones's avatar
Dave Jones committed
208
			hammer_mcheck_init(c);
209
#ifdef CONFIG_X86_MCE_NONFATAL
Dave Jones's avatar
Dave Jones committed
210 211 212
			if (timerset == 0) {
				/* Set the timer to check for non-fatal
				   errors every MCE_RATE seconds */
213
				init_timer (&mce_timer);
Dave Jones's avatar
Dave Jones committed
214
				mce_timer.expires = jiffies + MCE_RATE;
215 216 217
				mce_timer.data = 0;
				mce_timer.function = &mce_timerfunc;
				add_timer (&mce_timer);
Dave Jones's avatar
Dave Jones committed
218 219
				timerset = 1;
				printk(KERN_INFO "Machine check exception polling timer started.\n");
220
			}
Dave Jones's avatar
Dave Jones committed
221
#endif
222
			break;
223

224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
		default:
			break;
	}
}

static int __init mcheck_disable(char *str)
{
	mce_disabled = 1;
	return 0;
}

static int __init mcheck_enable(char *str)
{
	mce_disabled = -1;
	return 0;
}

__setup("nomce", mcheck_disable);
__setup("mce", mcheck_enable);
Dave Jones's avatar
Dave Jones committed
243 244 245 246 247

#else
asmlinkage void do_machine_check(struct pt_regs * regs, long error_code) {}
void __init mcheck_init(struct cpuinfo_x86 *c) {}
#endif