summaryrefslogtreecommitdiffstats
path: root/include/asm-i386/bugs.h
blob: 23460fcb61d997d3fc01876bdb7937c1affcc002 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
/*
 *  include/asm-i386/bugs.h
 *
 *  Copyright (C) 1994  Linus Torvalds
 *
 *  Cyrix stuff, June 1998 by:
 *	- Rafael R. Reilova (moved everything from head.S),
 *        <rreilova@ececs.uc.edu>
 *	- Channing Corn (tests & fixes),
 *	- Andrew D. Balsa (code cleanup).
 *
 *  Pentium III FXSR, SSE support
 *	Gareth Hughes <gareth@valinux.com>, May 2000
 */

/*
 * This is included by init/main.c to check for architecture-dependent bugs.
 *
 * Needs:
 *	void check_bugs(void);
 */

#include <linux/config.h>
#include <asm/processor.h>
#include <asm/i387.h>
#include <asm/msr.h>

static int __init no_halt(char *s)
{
	boot_cpu_data.hlt_works_ok = 0;
	return 1;
}

__setup("no-hlt", no_halt);

static int __init mca_pentium(char *s)
{
	mca_pentium_flag = 1;
	return 1;
}

__setup("mca-pentium", mca_pentium);

static int __init no_387(char *s)
{
	boot_cpu_data.hard_math = 0;
	write_cr0(0xE | read_cr0());
	return 1;
}

__setup("no387", no_387);

static char __initdata fpu_error = 0;

static void __init copro_timeout(void)
{
	fpu_error = 1;
	timer_table[COPRO_TIMER].expires = jiffies+HZ;
	timer_active |= 1<<COPRO_TIMER;
	printk(KERN_ERR "387 failed: trying to reset\n");
	send_sig(SIGFPE, current, 1);
	outb_p(0,0xf1);
	outb_p(0,0xf0);
}

static double __initdata x = 4195835.0;
static double __initdata y = 3145727.0;

#ifdef CONFIG_X86_XMM
static float __initdata zero[4] = { 0.0, 0.0, 0.0, 0.0 };
static float __initdata one[4] = { 1.0, 1.0, 1.0, 1.0 };
#endif

static void __init check_fpu(void)
{
	unsigned short control_word;

	if (!boot_cpu_data.hard_math) {
#ifndef CONFIG_MATH_EMULATION
		printk(KERN_EMERG "No coprocessor found and no math emulation present.\n");
		printk(KERN_EMERG "Giving up.\n");
		for (;;) ;
#endif
		return;
	}
	if (mca_pentium_flag) {
		/* The IBM Model 95 machines with pentiums lock up on
		 * fpu test, so we avoid it. All pentiums have inbuilt
		 * FPU and thus should use exception 16. We still do
		 * the FDIV test, although I doubt there where ever any
		 * MCA boxes built with non-FDIV-bug cpus.
		 */
		__asm__("fninit\n\t"
			"fldl %1\n\t"
			"fdivl %2\n\t"
			"fmull %2\n\t"
			"fldl %1\n\t"
			"fsubp %%st,%%st(1)\n\t"
			"fistpl %0\n\t"
			"fwait\n\t"
			"fninit"
			: "=m" (*&boot_cpu_data.fdiv_bug)
			: "m" (*&x), "m" (*&y));
		printk("mca-pentium specified, avoiding FPU coupling test... ");
		if (!boot_cpu_data.fdiv_bug)
			printk("??? No FDIV bug? Lucky you...\n");
		else
			printk("detected FDIV bug though.\n");
		return;
	}
	/*
	 * check if exception 16 works correctly.. This is truly evil
	 * code: it disables the high 8 interrupts to make sure that
	 * the irq13 doesn't happen. But as this will lead to a lockup
	 * if no exception16 arrives, it depends on the fact that the
	 * high 8 interrupts will be re-enabled by the next timer tick.
	 * So the irq13 will happen eventually, but the exception 16
	 * should get there first..
	 */
	printk(KERN_INFO "Checking 386/387 coupling... ");
	timer_table[COPRO_TIMER].expires = jiffies+HZ/2;
	timer_table[COPRO_TIMER].fn = copro_timeout;
	timer_active |= 1<<COPRO_TIMER;
	__asm__("clts ; fninit ; fnstcw %0 ; fwait":"=m" (*&control_word));
	control_word &= 0xffc0;
	__asm__("fldcw %0 ; fwait": :"m" (*&control_word));
	outb_p(inb_p(0x21) | (1 << 2), 0x21);
	__asm__("fldz ; fld1 ; fdiv %st,%st(1) ; fwait");
	timer_active &= ~(1<<COPRO_TIMER);
	if (fpu_error)
		return;
	if (!ignore_irq13) {
		printk("OK, FPU using old IRQ 13 error reporting\n");
		return;
	}
	__asm__("fninit\n\t"
		"fldl %1\n\t"
		"fdivl %2\n\t"
		"fmull %2\n\t"
		"fldl %1\n\t"
		"fsubp %%st,%%st(1)\n\t"
		"fistpl %0\n\t"
		"fwait\n\t"
		"fninit"
		: "=m" (*&boot_cpu_data.fdiv_bug)
		: "m" (*&x), "m" (*&y));
	if (!boot_cpu_data.fdiv_bug)
		printk("OK, FPU using exception 16 error reporting.\n");
	else
		printk("Hmm, FPU using exception 16 error reporting with FDIV bug.\n");

#if defined(CONFIG_X86_FXSR) || defined(CONFIG_X86_RUNTIME_FXSR)
	/*
	 * Verify that the FXSAVE/FXRSTOR data will be 16-byte aligned.
	 */
	if (offsetof(struct task_struct, thread.i387.fxsave) & 15)
		panic("Kernel compiled for PII/PIII+ with FXSR, data not 16-byte aligned!");

	if (cpu_has_fxsr) {
		printk(KERN_INFO "Enabling fast FPU save and restore... ");
		set_in_cr4(X86_CR4_OSFXSR);
		printk("done.\n");
	}
#endif
#ifdef CONFIG_X86_XMM
	if (cpu_has_xmm) {
		printk(KERN_INFO "Enabling unmasked SIMD FPU exception support... ");
		set_in_cr4(X86_CR4_OSXMMEXCPT);
		printk("done.\n");

		/* Check if exception 19 works okay. */
		load_mxcsr(0x0000);
		printk(KERN_INFO "Checking SIMD FPU exceptions... ");
		__asm__("movups %0,%%xmm0\n\t"
			"movups %1,%%xmm1\n\t"
			"divps %%xmm0,%%xmm1\n\t"
			: : "m" (*&zero), "m" (*&one));
		printk("OK, SIMD FPU using exception 19 error reporting.\n");
		load_mxcsr(0x1f80);
	}
#endif
}

static void __init check_hlt(void)
{
	printk(KERN_INFO "Checking 'hlt' instruction... ");
	if (!boot_cpu_data.hlt_works_ok) {
		printk("disabled\n");
		return;
	}
	__asm__ __volatile__("hlt ; hlt ; hlt ; hlt");
	printk("OK.\n");
}

/*
 *	Most 386 processors have a bug where a POPAD can lock the 
 *	machine even from user space.
 */
 
static void __init check_popad(void)
{
#ifndef CONFIG_X86_POPAD_OK
	int res, inp = (int) &res;

	printk(KERN_INFO "Checking for popad bug... ");
	__asm__ __volatile__( 
	  "movl $12345678,%%eax; movl $0,%%edi; pusha; popa; movl (%%edx,%%edi),%%ecx "
	  : "=&a" (res)
	  : "d" (inp)
	  : "ecx", "edi" );
	/* If this fails, it means that any user program may lock the CPU hard. Too bad. */
	if (res != 12345678) printk( "Buggy.\n" );
		        else printk( "OK.\n" );
#endif
}

/*
 *	B step AMD K6 before B 9730xxxx have hardware bugs that can cause
 *	misexecution of code under Linux. Owners of such processors should
 *	contact AMD for precise details and a CPU swap.
 *
 *	See	http://www.mygale.com/~poulot/k6bug.html
 *		http://www.amd.com/K6/k6docs/revgd.html
 *
 *	The following test is erm.. interesting. AMD neglected to up
 *	the chip setting when fixing the bug but they also tweaked some
 *	performance at the same time..
 */
 
extern void vide(void);
__asm__(".align 4\nvide: ret");

static void __init check_amd_k6(void)
{
	if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
	    boot_cpu_data.x86_model == 6 &&
	    boot_cpu_data.x86_mask == 1)
	{
		int n;
		void (*f_vide)(void);
		unsigned long d, d2;

		printk(KERN_INFO "AMD K6 stepping B detected - ");

#define K6_BUG_LOOP 1000000

		/*
		 * It looks like AMD fixed the 2.6.2 bug and improved indirect 
		 * calls at the same time.
		 */

		n = K6_BUG_LOOP;
		f_vide = vide;
		rdtscl(d);
		while (n--) 
			f_vide();
		rdtscl(d2);
		d = d2-d;

		/* Knock these two lines out if it debugs out ok */
		printk(KERN_INFO "K6 BUG %ld %d (Report these if test report is incorrect)\n", d, 20*K6_BUG_LOOP);
		printk(KERN_INFO "AMD K6 stepping B detected - ");
		/* -- cut here -- */
		if (d > 20*K6_BUG_LOOP) 
			printk("system stability may be impaired when more than 32 MB are used.\n");
		else 
			printk("probably OK (after B9730xxxx).\n");
		printk(KERN_INFO "Please see http://www.mygale.com/~poulot/k6bug.html\n");
	}
}

/*
 * All current models of Pentium and Pentium with MMX technology CPUs
 * have the F0 0F bug, which lets nonpriviledged users lock up the system:
 */

#ifndef CONFIG_M686
extern void trap_init_f00f_bug(void);

static void __init check_pentium_f00f(void)
{
	/*
	 * Pentium and Pentium MMX
	 */
	boot_cpu_data.f00f_bug = 0;
	if (boot_cpu_data.x86 == 5 && boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) {
		printk(KERN_INFO "Intel Pentium with F0 0F bug - workaround enabled.\n");
		boot_cpu_data.f00f_bug = 1;
		trap_init_f00f_bug();
	}
}
#endif

/*
 * Perform the Cyrix 5/2 test. A Cyrix won't change
 * the flags, while other 486 chips will.
 */

static inline int test_cyrix_52div(void)
{
	unsigned int test;

	__asm__ __volatile__(
	     "sahf\n\t"		/* clear flags (%eax = 0x0005) */
	     "div %b2\n\t"	/* divide 5 by 2 */
	     "lahf"		/* store flags into %ah */
	     : "=a" (test)
	     : "0" (5), "q" (2)
	     : "cc");

	/* AH is 0x02 on Cyrix after the divide.. */
	return (unsigned char) (test >> 8) == 0x02;
}

/*
 * Fix cpuid problems with Cyrix CPU's:
 *   -- on the Cx686(L) the cpuid is disabled on power up.
 *   -- braindamaged BIOS disable cpuid on the Cx686MX.
 */

extern unsigned char Cx86_dir0_msb;  /* exported HACK from cyrix_model() */

static void __init check_cx686_cpuid(void)
{
	if (boot_cpu_data.cpuid_level == -1 &&
	    ((Cx86_dir0_msb == 5) || (Cx86_dir0_msb == 3))) {
		int eax, dummy;
		unsigned char ccr3, ccr4;
		__u32 old_cap;

		cli();
		ccr3 = getCx86(CX86_CCR3);
		setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN  */
		ccr4 = getCx86(CX86_CCR4);
		setCx86(CX86_CCR4, ccr4 | 0x80);          /* enable cpuid  */
		setCx86(CX86_CCR3, ccr3);                 /* disable MAPEN */
		sti();

		/* we have up to level 1 available on the Cx6x86(L|MX) */
		boot_cpu_data.cpuid_level = 1;
		/*  Need to preserve some externally computed capabilities  */
		old_cap = boot_cpu_data.x86_capability & X86_FEATURE_MTRR;
		cpuid(1, &eax, &dummy, &dummy,
		      &boot_cpu_data.x86_capability);
		boot_cpu_data.x86_capability |= old_cap;

		boot_cpu_data.x86 = (eax >> 8) & 15;
		/*
 		 * we already have a cooked step/rev number from DIR1
		 * so we don't use the cpuid-provided ones.
		 */
	}
}

/*
 * Reset the slow-loop (SLOP) bit on the 686(L) which is set by some old
 * BIOSes for compatability with DOS games.  This makes the udelay loop
 * work correctly, and improves performance.
 */

extern void calibrate_delay(void) __init;

static void __init check_cx686_slop(void)
{
	if (Cx86_dir0_msb == 3) {
		unsigned char ccr3, ccr5;

		cli();
		ccr3 = getCx86(CX86_CCR3);
		setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN  */
		ccr5 = getCx86(CX86_CCR5);
		if (ccr5 & 2)
			setCx86(CX86_CCR5, ccr5 & 0xfd);  /* reset SLOP */
		setCx86(CX86_CCR3, ccr3);                 /* disable MAPEN */
		sti();

		if (ccr5 & 2) { /* possible wrong calibration done */
			printk(KERN_INFO "Recalibrating delay loop with SLOP bit reset\n");
			calibrate_delay();
			boot_cpu_data.loops_per_sec = loops_per_sec;
		}
	}
}

/*
 * Cyrix CPUs without cpuid or with cpuid not yet enabled can be detected
 * by the fact that they preserve the flags across the division of 5/2.
 * PII and PPro exhibit this behavior too, but they have cpuid available.
 */

static void __init check_cyrix_cpu(void)
{
	if ((boot_cpu_data.cpuid_level == -1) && (boot_cpu_data.x86 == 4)
	    && test_cyrix_52div()) {

		strcpy(boot_cpu_data.x86_vendor_id, "CyrixInstead");
	}
}
 
/*
 * In setup.c's cyrix_model() we have set the boot_cpu_data.coma_bug
 * on certain processors that we know contain this bug and now we
 * enable the workaround for it.
 */

static void __init check_cyrix_coma(void)
{
}
 
/*
 * Check whether we are able to run this kernel safely on SMP.
 *
 * - In order to run on a i386, we need to be compiled for i386
 *   (for due to lack of "invlpg" and working WP on a i386)
 * - In order to run on anything without a TSC, we need to be
 *   compiled for a i486.
 * - In order to support the local APIC on a buggy Pentium machine,
 *   we need to be compiled with CONFIG_X86_GOOD_APIC disabled,
 *   which happens implicitly if compiled for a Pentium or lower
 *   (unless an advanced selection of CPU features is used) as an
 *   otherwise config implies a properly working local APIC without
 *   the need to do extra reads from the APIC.
*/

static void __init check_config(void)
{
/*
 * We'd better not be a i386 if we're configured to use some
 * i486+ only features! (WP works in supervisor mode and the
 * new "invlpg" and "bswap" instructions)
 */
#if defined(CONFIG_X86_WP_WORKS_OK) || defined(CONFIG_X86_INVLPG) || defined(CONFIG_X86_BSWAP)
	if (boot_cpu_data.x86 == 3)
		panic("Kernel requires i486+ for 'invlpg' and other features");
#endif

/*
 * If we configured ourselves for a TSC, we'd better have one!
 */
#ifdef CONFIG_X86_TSC
	if (!cpu_has_tsc)
		panic("Kernel compiled for Pentium+, requires TSC feature!");
#endif

/*
 * If we configured ourselves for PGE, we'd better have it.
 */
#ifdef CONFIG_X86_PGE
	if (!cpu_has_pge)
		panic("Kernel compiled for PPro+, requires PGE feature!");
#endif

/*
 * If we were told we had a good local APIC, check for buggy Pentia,
 * i.e. all B steppings and the C2 stepping of P54C when using their
 * integrated APIC (see 11AP erratum in "Pentium Processor
 * Specification Update").
 */
#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_X86_GOOD_APIC)
	if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL
	    && boot_cpu_data.x86_capability & X86_FEATURE_APIC
	    && boot_cpu_data.x86 == 5
	    && boot_cpu_data.x86_model == 2
	    && (boot_cpu_data.x86_mask < 6 || boot_cpu_data.x86_mask == 11))
		panic("Kernel compiled for PPro+, assumes a local APIC without the read-before-write bug!");
#endif

/*
 * If we configured ourselves for FXSR, we'd better have it.
 */
#ifdef CONFIG_X86_FXSR
	if (!cpu_has_fxsr)
		panic("Kernel compiled for PII/PIII+, requires FXSR feature!");
#endif
}

static void __init check_bugs(void)
{
	check_cyrix_cpu();
	identify_cpu(&boot_cpu_data);
	check_cx686_cpuid();
	check_cx686_slop();
#ifndef CONFIG_SMP
	printk("CPU: ");
	print_cpu_info(&boot_cpu_data);
#endif
	check_config();
	check_fpu();
	check_hlt();
	check_popad();
	check_amd_k6();
#ifndef CONFIG_M686
	check_pentium_f00f();
#endif
	check_cyrix_coma();
	system_utsname.machine[1] = '0' + boot_cpu_data.x86;
}