/* * include/asm-i386/bugs.h * * Copyright (C) 1994 Linus Torvalds * * Cyrix stuff, June 1998 by: * - Rafael R. Reilova (moved everything from head.S), * * - 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 #include #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< 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; }