Linux F00F Patch

Aleph One (aleph1@DFW.NET)
Wed, 12 Nov 1997 18:45:15 -0600

This are the relevant parts of the linux kernel 2.1.63 patch that fix the
Pentium bug that Alan mentioned.

Aleph One / aleph1@dfw.net
http://underground.org/
KeyID 1024/948FD6B5
Fingerprint EE C9 E8 AA CB AF 09 61 8C 39 EA 47 A8 6A B8 01

diff -u --recursive --new-file v2.1.62/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- v2.1.62/linux/arch/i386/kernel/setup.c Tue Sep 23 16:48:46 1997
+++ linux/arch/i386/kernel/setup.c Wed Nov 12 11:09:56 1997
@@ -42,6 +42,7 @@
char x86_mask = 0; /* set by kernel/head.S */
int x86_capability = 0; /* set by kernel/head.S */
int fdiv_bug = 0; /* set if Pentium(TM) with FP bug */
+int pentium_f00f_bug = 0; /* set if Pentium(TM) with F00F bug */
int have_cpuid = 0; /* set if CPUID instruction works */

char x86_vendor_id[13] = "unknown";
@@ -359,6 +360,7 @@
"fdiv_bug\t: %s\n"
"hlt_bug\t\t: %s\n"
"sep_bug\t\t: %s\n"
+ "pentium_f00f_bug\t\t: %s\n"
"fpu\t\t: %s\n"
"fpu_exception\t: %s\n"
"cpuid\t\t: %s\n"
@@ -367,6 +369,7 @@
CD(fdiv_bug) ? "yes" : "no",
CD(hlt_works_ok) ? "no" : "yes",
sep_bug ? "yes" : "no",
+ pentium_f00f_bug ? "yes" : "no",
CD(hard_math) ? "yes" : "no",
(CD(hard_math) && ignore_irq13)
? "yes" : "no",
diff -u --recursive --new-file v2.1.62/linux/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c
--- v2.1.62/linux/arch/i386/kernel/traps.c Sun Sep 7 13:10:42 1997
+++ linux/arch/i386/kernel/traps.c Wed Nov 12 11:09:56 1997
@@ -413,6 +413,51 @@

#endif /* CONFIG_MATH_EMULATION */

+static struct
+{
+ short limit __attribute__((packed));
+ void * addr __attribute__((packed));
+ short __pad __attribute__((packed));
+} idt_d;
+
+void * idt2;
+
+__initfunc(void trap_init_f00f_bug(void))
+{
+ pgd_t * pgd;
+ pmd_t * pmd;
+ pte_t * pte;
+ unsigned long twopage;
+
+ printk("moving IDT ... ");
+
+ twopage = (unsigned long) vmalloc (2*PAGE_SIZE);
+
+ idt2 = (void *)(twopage + 4096-7*8);
+
+ memcpy(idt2,&idt,sizeof(idt));
+
+ idt_d.limit = 256*8-1;
+ idt_d.addr = idt2;
+ idt_d.__pad = 0;
+
+ __asm__ __volatile__("\tlidt %0": "=m" (idt_d));
+
+ /*
+ * Unmap lower page:
+ */
+ pgd = pgd_offset(current->mm, twopage);
+ pmd = pmd_offset(pgd, twopage);
+ pte = pte_offset(pmd, twopage);
+
+ pte_clear(pte);
+ flush_tlb_all();
+
+ printk(" ... done\n");
+}
+
+
+
__initfunc(void trap_init(void))
{
int i;
diff -u --recursive --new-file v2.1.62/linux/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c
--- v2.1.62/linux/arch/i386/mm/fault.c Wed Oct 15 16:04:23 1997
+++ linux/arch/i386/mm/fault.c Wed Nov 12 11:09:55 1997
@@ -74,6 +74,25 @@
return 0;
}

+asmlinkage void divide_error(void);
+asmlinkage void debug(void);
+asmlinkage void nmi(void);
+asmlinkage void int3(void);
+asmlinkage void overflow(void);
+asmlinkage void bounds(void);
+asmlinkage void invalid_op(void);
+
+asmlinkage void do_divide_error (struct pt_regs *, unsigned long);
+asmlinkage void do_debug (struct pt_regs *, unsigned long);
+asmlinkage void do_nmi (struct pt_regs *, unsigned long);
+asmlinkage void do_int3 (struct pt_regs *, unsigned long);
+asmlinkage void do_overflow (struct pt_regs *, unsigned long);
+asmlinkage void do_bounds (struct pt_regs *, unsigned long);
+asmlinkage void do_invalid_op (struct pt_regs *, unsigned long);
+
+extern int * idt2;
+extern int pentium_f00f_bug;
+
/*
* This routine handles page faults. It determines the address,
* and the problem, and then passes it off to one of the appropriate
@@ -170,6 +189,46 @@
goto out;
}

+ printk("<%p/%p>\n", idt2, (void *)address);
+ /*
+ * Pentium F0 0F C7 C8 bug workaround:
+ */
+ if ( pentium_f00f_bug && (address >= (unsigned long)idt2) &&
+ (address < (unsigned long)idt2+256*8) ) {
+
+ void (*handler) (void);
+ int nr = (address-(unsigned long)idt2)/8;
+ unsigned long low, high;
+
+ low = idt[nr].a;
+ high = idt[nr].b;
+
+ handler = (void (*) (void)) ((low&0x0000ffff) | (high&0xffff0000));
+ printk("<handler %p... ", handler);
+ unlock_kernel();
+
+ if (handler==divide_error)
+ do_divide_error(regs,error_code);
+ else if (handler==debug)
+ do_debug(regs,error_code);
+ else if (handler==nmi)
+ do_nmi(regs,error_code);
+ else if (handler==int3)
+ do_int3(regs,error_code);
+ else if (handler==overflow)
+ do_overflow(regs,error_code);
+ else if (handler==bounds)
+ do_bounds(regs,error_code);
+ else if (handler==invalid_op)
+ do_invalid_op(regs,error_code);
+ else {
+ printk("INVALID HANDLER!\n");
+ for (;;) __cli();
+ }
+ printk("... done>\n");
+ goto out;
+ }
+
/* Are we prepared to handle this kernel fault? */
if ((fixup = search_exception_table(regs->eip)) != 0) {
printk(KERN_DEBUG "%s: Exception at [<%lx>] cr2=%lx (fixup: %lx)\n",
@@ -193,6 +252,7 @@
flush_tlb();
goto out;
}
+
if (address < PAGE_SIZE)
printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
else
diff -u --recursive --new-file v2.1.62/linux/include/asm-i386/bugs.h linux/include/asm-i386/bugs.h
--- v2.1.62/linux/include/asm-i386/bugs.h Thu Sep 11 09:02:24 1997
+++ linux/include/asm-i386/bugs.h Wed Nov 12 11:09:55 1997
@@ -166,6 +166,32 @@
}
}

+/*
+ * 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;
+
+__initfunc(static void check_pentium_f00f(void))
+{
+ /*
+ * Pentium and Pentium MMX
+ */
+ printk("checking for F00F bug ...");
+ if(x86==5 && !memcmp(x86_vendor_id, "GenuineIntel", 12))
+ {
+ extern void trap_init_f00f_bug(void);
+
+ printk(KERN_INFO "\nIntel Pentium/[MMX] F0 0F bug detected - turning on workaround.\n");
+ pentium_f00f_bug = 1;
+ trap_init_f00f_bug();
+ } else {
+ printk(KERN_INFO " no F0 0F bug in this CPU, great!\n");
+ pentium_f00f_bug = 0;
+ }
+}
+
__initfunc(static void check_bugs(void))
{
check_tlb();
@@ -173,5 +199,6 @@
check_hlt();
check_popad();
check_amd_k6();
+ check_pentium_f00f();
system_utsname.machine[1] = '0' + x86;
}