added code again, which got removed my big merge:-(
[linux-2.6/linux-mips.git] / include / asm-i386 / bugs.h
blobba3a969aea665d87c3ef297970681360a23c9227
1 /*
2 * include/asm-i386/bugs.h
4 * Copyright (C) 1994 Linus Torvalds
6 * Cyrix stuff, June 1998 by:
7 * - Rafael R. Reilova (moved everything from head.S),
8 * - Channing Corn (tests & fixes),
9 * - Andrew D. Balsa (code cleanup).
13 * This is included by init/main.c to check for architecture-dependent bugs.
15 * Needs:
16 * void check_bugs(void);
19 #include <linux/config.h>
20 #include <asm/processor.h>
22 #define CONFIG_BUGi386
24 __initfunc(static void no_halt(char *s, int *ints))
26 boot_cpu_data.hlt_works_ok = 0;
29 __initfunc(static void no_387(char *s, int *ints))
31 boot_cpu_data.hard_math = 0;
32 __asm__("movl %%cr0,%%eax\n\t"
33 "orl $0xE,%%eax\n\t"
34 "movl %%eax,%%cr0\n\t" : : : "ax");
37 static char __initdata fpu_error = 0;
39 __initfunc(static void copro_timeout(void))
41 fpu_error = 1;
42 timer_table[COPRO_TIMER].expires = jiffies+100;
43 timer_active |= 1<<COPRO_TIMER;
44 printk(KERN_ERR "387 failed: trying to reset\n");
45 send_sig(SIGFPE, current, 1);
46 outb_p(0,0xf1);
47 outb_p(0,0xf0);
50 static double __initdata x = 4195835.0;
51 static double __initdata y = 3145727.0;
53 __initfunc(static void check_fpu(void))
55 unsigned short control_word;
57 if (!boot_cpu_data.hard_math) {
58 #ifndef CONFIG_MATH_EMULATION
59 printk(KERN_EMERG "No coprocessor found and no math emulation present.\n");
60 printk(KERN_EMERG "Giving up.\n");
61 for (;;) ;
62 #endif
63 return;
66 * check if exception 16 works correctly.. This is truly evil
67 * code: it disables the high 8 interrupts to make sure that
68 * the irq13 doesn't happen. But as this will lead to a lockup
69 * if no exception16 arrives, it depends on the fact that the
70 * high 8 interrupts will be re-enabled by the next timer tick.
71 * So the irq13 will happen eventually, but the exception 16
72 * should get there first..
74 printk(KERN_INFO "Checking 386/387 coupling... ");
75 timer_table[COPRO_TIMER].expires = jiffies+50;
76 timer_table[COPRO_TIMER].fn = copro_timeout;
77 timer_active |= 1<<COPRO_TIMER;
78 __asm__("clts ; fninit ; fnstcw %0 ; fwait":"=m" (*&control_word));
79 control_word &= 0xffc0;
80 __asm__("fldcw %0 ; fwait": :"m" (*&control_word));
81 outb_p(inb_p(0x21) | (1 << 2), 0x21);
82 __asm__("fldz ; fld1 ; fdiv %st,%st(1) ; fwait");
83 timer_active &= ~(1<<COPRO_TIMER);
84 if (fpu_error)
85 return;
86 if (!ignore_irq13) {
87 printk("OK, FPU using old IRQ 13 error reporting\n");
88 return;
90 __asm__("fninit\n\t"
91 "fldl %1\n\t"
92 "fdivl %2\n\t"
93 "fmull %2\n\t"
94 "fldl %1\n\t"
95 "fsubp %%st,%%st(1)\n\t"
96 "fistpl %0\n\t"
97 "fwait\n\t"
98 "fninit"
99 : "=m" (*&boot_cpu_data.fdiv_bug)
100 : "m" (*&x), "m" (*&y));
101 if (!boot_cpu_data.fdiv_bug)
102 printk("OK, FPU using exception 16 error reporting.\n");
103 else
104 printk("Hmm, FPU using exception 16 error reporting with FDIV bug.\n");
107 __initfunc(static void check_hlt(void))
109 printk(KERN_INFO "Checking 'hlt' instruction... ");
110 if (!boot_cpu_data.hlt_works_ok) {
111 printk("disabled\n");
112 return;
114 __asm__ __volatile__("hlt ; hlt ; hlt ; hlt");
115 printk("OK.\n");
118 __initfunc(static void check_tlb(void))
120 #ifndef CONFIG_M386
122 * The 386 chips don't support TLB finegrained invalidation.
123 * They will fault when they hit an invlpg instruction.
125 if (boot_cpu_data.x86 == 3) {
126 printk(KERN_EMERG "CPU is a 386 and this kernel was compiled for 486 or better.\n");
127 printk("Giving up.\n");
128 for (;;) ;
130 #endif
134 * Most 386 processors have a bug where a POPAD can lock the
135 * machine even from user space.
138 __initfunc(static void check_popad(void))
140 #ifdef CONFIG_M386
141 int res, inp = (int) &res;
143 printk(KERN_INFO "Checking for popad bug... ");
144 __asm__ __volatile__(
145 "movl $12345678,%%eax; movl $0,%%edi; pusha; popa; movl (%%edx,%%edi),%%ecx "
146 : "=eax" (res)
147 : "edx" (inp)
148 : "eax", "ecx", "edx", "edi" );
149 /* If this fails, it means that any user program may lock the CPU hard. Too bad. */
150 if (res != 12345678) printk( "Buggy.\n" );
151 else printk( "OK.\n" );
152 #endif
156 * B step AMD K6 before B 9730xxxx have hardware bugs that can cause
157 * misexecution of code under Linux. Owners of such processors should
158 * contact AMD for precise details and a CPU swap.
160 * See http://www.mygale.com/~poulot/k6bug.html
161 * http://www.amd.com/K6/k6docs/revgd.html
163 * The following test is erm.. interesting. AMD neglected to up
164 * the chip setting when fixing the bug but they also tweaked some
165 * performance at the same time..
168 extern void vide(void);
169 __asm__(".align 4\nvide: ret");
171 __initfunc(static void check_amd_k6(void))
173 if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
174 boot_cpu_data.x86_model == 6 &&
175 boot_cpu_data.x86_mask == 1)
177 int n;
178 void (*f_vide)(void);
179 unsigned long d, d2;
181 printk(KERN_INFO "AMD K6 stepping B detected - ");
183 #define K6_BUG_LOOP 1000000
186 * It looks like AMD fixed the 2.6.2 bug and improved indirect
187 * calls at the same time.
190 n = K6_BUG_LOOP;
191 f_vide = vide;
192 __asm__ ("rdtsc" : "=a" (d));
193 while (n--)
194 f_vide();
195 __asm__ ("rdtsc" : "=a" (d2));
196 d = d2-d;
198 /* Knock these two lines out if it debugs out ok */
199 printk(KERN_INFO "K6 BUG %ld %d (Report these if test report is incorrect)\n", d, 20*K6_BUG_LOOP);
200 printk(KERN_INFO "AMD K6 stepping B detected - ");
201 /* -- cut here -- */
202 if (d > 20*K6_BUG_LOOP)
203 printk("system stability may be impaired when more than 32 MB are used.\n");
204 else
205 printk("probably OK (after B9730xxxx).\n");
206 printk(KERN_INFO "Please see http://www.mygale.com/~poulot/k6bug.html\n");
211 * All current models of Pentium and Pentium with MMX technology CPUs
212 * have the F0 0F bug, which lets nonpriviledged users lock up the system:
215 extern void trap_init_f00f_bug(void);
217 __initfunc(static void check_pentium_f00f(void))
220 * Pentium and Pentium MMX
222 boot_cpu_data.f00f_bug = 0;
223 if (boot_cpu_data.x86 == 5 && boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) {
224 printk(KERN_INFO "Intel Pentium with F0 0F bug - workaround enabled.\n");
225 boot_cpu_data.f00f_bug = 1;
226 trap_init_f00f_bug();
231 * Perform the Cyrix 5/2 test. A Cyrix won't change
232 * the flags, while other 486 chips will.
235 static inline int test_cyrix_52div(void)
237 unsigned int test;
239 __asm__ __volatile__(
240 "sahf\n\t" /* clear flags (%eax = 0x0005) */
241 "div %b2\n\t" /* divide 5 by 2 */
242 "lahf" /* store flags into %ah */
243 : "=a" (test)
244 : "0" (5), "q" (2)
245 : "cc");
247 /* AH is 0x02 on Cyrix after the divide.. */
248 return (unsigned char) (test >> 8) == 0x02;
252 * Cyrix CPUs without cpuid or with cpuid not yet enabled can be detected
253 * by the fact that they preserve the flags across the division of 5/2.
254 * PII and PPro exhibit this behavior too, but they have cpuid available.
257 __initfunc(static void check_cyrix_cpu(void))
259 if ((boot_cpu_data.cpuid_level == -1) && (boot_cpu_data.x86 == 4)
260 && test_cyrix_52div()) {
262 /* default to an unknown Cx486, (we will differentiate later) */
263 /* NOTE: using 0xff since 0x00 is a valid DIR0 value */
264 strcpy(boot_cpu_data.x86_vendor_id, "CyrixInstead");
265 boot_cpu_data.x86_model = 0xff;
266 boot_cpu_data.x86_mask = 0;
271 * Fix two problems with the Cyrix 6x86 and 6x86L:
272 * -- the cpuid is disabled on power up, enable it, use it.
273 * -- the SLOP bit needs resetting on some motherboards due to old BIOS,
274 * so that the udelay loop calibration works well. Recalibrate.
277 extern void calibrate_delay(void) __init;
279 __initfunc(static void check_cx686_cpuid_slop(void))
281 if (boot_cpu_data.x86_vendor == X86_VENDOR_CYRIX &&
282 (boot_cpu_data.x86_model & 0xf0) == 0x30) { /* 6x86(L) */
283 int dummy;
284 unsigned char ccr3, ccr4, ccr5;
286 cli();
287 ccr3 = getCx86(CX86_CCR3);
288 setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */
289 ccr4 = getCx86(CX86_CCR4);
290 setCx86(CX86_CCR4, ccr4 | 0x80); /* enable cpuid */
291 ccr5 = getCx86(CX86_CCR5);
292 if (ccr5 & 2) /* reset SLOP if needed, old BIOS do this wrong */
293 setCx86(CX86_CCR5, ccr5 & 0xfd);
294 setCx86(CX86_CCR3, ccr3); /* disable MAPEN */
295 sti();
297 boot_cpu_data.cpuid_level = 1; /* should cover all 6x86(L) */
298 boot_cpu_data.x86 = 5;
300 /* we know we have level 1 available on the 6x86(L) */
301 cpuid(1, &dummy, &dummy, &dummy,
302 &boot_cpu_data.x86_capability);
304 * DON'T use the x86_mask and x86_model from cpuid, these are
305 * not as accurate (or the same) as those from the DIR regs.
306 * already in place after cyrix_model() in setup.c
309 if (ccr5 & 2) { /* possible wrong calibration done */
310 printk(KERN_INFO "Recalibrating delay loop with SLOP bit reset\n");
311 calibrate_delay();
312 boot_cpu_data.loops_per_sec = loops_per_sec;
317 __initfunc(static void check_bugs(void))
319 check_cyrix_cpu();
320 identify_cpu(&boot_cpu_data);
321 #ifndef __SMP__
322 printk("CPU: ");
323 print_cpu_info(&boot_cpu_data);
324 #endif
325 check_cx686_cpuid_slop();
326 check_tlb();
327 check_fpu();
328 check_hlt();
329 check_popad();
330 check_amd_k6();
331 check_pentium_f00f();
332 system_utsname.machine[1] = '0' + boot_cpu_data.x86;