/*
 *  include/asm-i386/bugs.h
 *
 *  Copyright (C) 1994  Linus Torvalds
 */

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

#include <linux/config.h>

#define CONFIG_BUGi386

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

static void no_387(char *s, int *ints)
{
	hard_math = 0;
	__asm__("movl %%cr0,%%eax\n\t"
		"orl $0xE,%%eax\n\t"
		"movl %%eax,%%cr0\n\t" : : : "ax");
}

static char fpu_error = 0;

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

static void check_fpu(void)
{
	static double x = 4195835.0;
	static double y = 3145727.0;
	unsigned short control_word;

	if (!hard_math) {
#ifndef CONFIG_MATH_EMULATION
		printk("No coprocessor found and no math emulation present.\n");
		printk("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("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 IRQ13 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" (*&fdiv_bug)
		: "m" (*&x), "m" (*&y));
	if (!fdiv_bug) {
		printk("Ok, fpu using exception 16 error reporting.\n");
		return;

	}
	printk("Hmm, FDIV bug i%c86 system\n", '0'+x86);
}

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

static void check_tlb(void)
{
#ifndef CONFIG_M386
	/*
	 * The 386 chips don't support TLB finegrained invalidation.
	 * They will fault when they hit a invlpg instruction.
	 */
	if (x86 == 3) {
		printk("CPU is a 386 and this kernel was compiled for 486 or better.\n");
		printk("Giving up.\n");
		for (;;) ;
	}
#endif
}

/*
 * 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 int pentium_f00f_bug;
extern void trap_init_f00f_bug(void);

static void check_pentium_f00f(void)
{
	/*
	 * Pentium and Pentium MMX
	 */
	pentium_f00f_bug = 0;
	if (x86==5 && !memcmp(x86_vendor_id, "GenuineIntel", 12)) {
		printk(KERN_INFO "Intel Pentium with F0 0F bug - workaround enabled.\n");
		pentium_f00f_bug = 1;
		trap_init_f00f_bug();
	}
}


static void check_bugs(void)
{
	check_tlb();
	check_fpu();
	check_hlt();
	check_pentium_f00f();
	system_utsname.machine[1] = '0' + x86;
}
