summaryrefslogtreecommitdiffstats
path: root/include/asm-i386/bugs.h
blob: c9ca53c667ca9bac3f495a0db502903b3280d61a (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
/*
 *  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).
 */

/*
 * 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>

#define CONFIG_BUGi386

__initfunc(static void no_halt(char *s, int *ints))
{
	boot_cpu_data.hlt_works_ok = 0;
}

__initfunc(static void no_387(char *s, int *ints))
{
	boot_cpu_data.hard_math = 0;
	write_cr0(0xE | read_cr0());
}

static char __initdata fpu_error = 0;

__initfunc(static void copro_timeout(void))
{
	fpu_error = 1;
	timer_table[COPRO_TIMER].expires = jiffies+100;
	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;

__initfunc(static void 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;
	}
	/*
	 * 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+50;
	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");
}

__initfunc(static void 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.
 */
 
__initfunc(static void 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");

__initfunc(static void 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;
		__asm__ ("rdtsc" : "=a" (d));
		while (n--) 
			f_vide();
		__asm__ ("rdtsc" : "=a" (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:
 */

extern void trap_init_f00f_bug(void);

__initfunc(static void 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();
	}
}

/*
 * 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() */

__initfunc(static void 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;

		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;
		cpuid(1, &eax, &dummy, &dummy,
		      &boot_cpu_data.x86_capability);

		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;

__initfunc(static void 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.
 */

__initfunc(static void 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");
	}
}
 
/*
 * Check wether 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 work on a Pentium/SMP machine, we need to be
 *   compiled for a Pentium or lower, as a PPro config implies
 *   a properly working local APIC without the need to do extra
 *   reads from the APIC.
*/

__initfunc(static void 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 (!(boot_cpu_data.x86_capability & X86_FEATURE_TSC))
		panic("Kernel compiled for Pentium+, requires TSC");
#endif

/*
 * If we were told we had a good APIC for SMP, we'd better be a PPro
 */
#if defined(CONFIG_X86_GOOD_APIC) && defined(CONFIG_SMP)
	if (smp_found_config && boot_cpu_data.x86 <= 5)
		panic("Kernel compiled for PPro+, assumes local APIC without read-before-write bug");
#endif
}

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