Loading...
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 | /* * 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> #include <asm/msr.h> #define CONFIG_BUGi386 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; 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"); } 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 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. */ 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 (!(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 } static void __init 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(); #ifndef CONFIG_M686 check_pentium_f00f(); #endif check_cyrix_coma(); system_utsname.machine[1] = '0' + boot_cpu_data.x86; } |