patch-2.0.36 linux/include/asm-i386/bugs.h

Next file: linux/include/asm-i386/irq.h
Previous file: linux/include/asm-alpha/unistd.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.0.35/linux/include/asm-i386/bugs.h linux/include/asm-i386/bugs.h
@@ -145,9 +145,192 @@
 	}
 }
 
+/*
+ *      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 (free) CPU exchange.
+ *
+ *      See     http://www.chorus.com/~poulot/k6bug.html
+ *              http://www.amd.com/K6/k6docs/revgd.html
+ *
+ *      The following test is erm... interesting. AMD neglected to up
+ *      the chip stepping 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 check_k6_bug(void)
+{
+
+        if ((strcmp(x86_vendor_id, "AuthenticAMD") == 0) &&
+            (x86_model == 6) && (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;
+                __asm__ ("rdtsc" : "=a" (d));
+                while (n--) 
+                        f_vide();
+                __asm__ ("rdtsc" : "=a" (d2));
+                d = d2-d;
+
+                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");
+        }
+}
+
+/* Cyrix stuff from this point on */
+
+/* Cyrix 5/2 test (return 0x200 if it's a Cyrix) */
+static inline int test_cyrix_52div(void)
+{
+	int test;
+
+	__asm__ __volatile__("xor %%eax,%%eax\n\t"
+	     "sahf\n\t"
+	     "movb $5,%%al\n\t"
+	     "movb $2,%%bl\n\t"
+	     "div %%bl\n\t"
+	     "lahf\n\t"
+	     "andl $0xff00,%%eax": "=eax" (test) : : "bx");
+
+	return test;
+}
+
+/* test for CCR3 bit 7 r/w */
+static char test_cyrix_cr3rw(void)
+{
+	char temp, test;
+	
+	temp = getCx86(CX86_CCR3);	/* get current CCR3 value */
+	setCx86(CX86_CCR3, temp ^ 0x80); /* toggle test bit and write */
+	getCx86(0xc0);			/* dummy to change bus */
+	test = temp - getCx86(CX86_CCR3);	/* != 0 if ccr3 r/w */
+	setCx86(CX86_CCR3, temp);	/* return CCR3 to original value */
+	
+	return test;
+}
+
+/* redo the cpuid test in head.S, so that those 6x86(L) now get
+   detected properly (0 == no cpuid) */
+static inline int test_cpuid(void)
+{
+	int test;
+	
+	__asm__("pushfl\n\t"
+	     "popl %%eax\n\t"
+	     "movl %%eax,%%ecx\n\t"
+	     "xorl $0x200000,%%eax\n\t"
+	     "pushl %%eax\n\t"
+	     "popfl\n\t"
+	     "pushfl\n\t"
+	     "popl %%eax\n\t"
+	     "xorl %%ecx,%%eax\n\t"
+	     "pushl %%ecx\n\t"
+	     "popfl" : "=eax" (test) : : "cx");
+
+	return test;
+}
+
+/* All Cyrix 6x86 and 6x86L need the SLOP bit reset so that the udelay loop
+ * calibration works well.
+ * This routine must be called with MAPEN enabled, otherwise we don't
+ * have access to CCR5.
+ */
+ 
+static void check_6x86_slop(void)
+{
+	if (x86_model == 2)	/* if 6x86 or 6x86L */
+		setCx86(CX86_CCR5, getCx86(CX86_CCR5) & 0xfd); /* reset SLOP */
+}
+
+/* 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 check_cyrix_various(void)
+{
+	if ((x86 == 4) && (test_cyrix_52div()==0x200)) 
+	{
+		/* if it's a Cyrix */
+	    
+		unsigned long flags;
+
+		/* default to an "old" Cx486 */
+		strcpy(x86_vendor_id, "CyrixInstead");
+		x86_model = -1;
+		x86_mask = 0;
+	    
+		/* Disable interrupts */
+		save_flags(flags);
+		cli();
+			
+		/* First check for very old CX486 models */
+		/* that did not have DIR0/DIR1. */
+		if (test_cyrix_cr3rw()) 
+		{	/* if has DIR0/DIR1 */
+		
+			char ccr3;
+			char dir0;
+			x86_model = 0;
+
+			/* Enable MAPEN */
+			ccr3 = getCx86(CX86_CCR3);
+			setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10);
+
+			dir0 = getCx86(CX86_DIR0);
+			if ((dir0 & 0xf0) == 0x30)	/* Use DIR0 to determine if this is a 6x86 class processor */
+			{
+				/* try enabling cpuid */
+				setCx86(CX86_CCR4, getCx86(CX86_CCR4) | 0x80);
+			}
+
+			if (test_cpuid()) 
+			{
+				int eax, dummy;
+
+				/* get processor info */
+			
+				cpuid(1, &eax, &dummy, &dummy,
+				      &x86_capability);
+
+				have_cpuid = 1;
+				x86_model = (eax >> 4) & 0xf;
+				x86 = (eax >> 8) & 0xf;
+				check_6x86_slop();
+			}
+			/* disable MAPEN */
+			setCx86(CX86_CCR3, ccr3);
+		} /* endif has DIR0/DIR1 */
+		sti();
+		restore_flags(flags);	/* restore interrupt state */
+	} /* endif it's a Cyrix */
+}
+
+/* Check various processor bugs */
 
 static void check_bugs(void)
 {
+	check_cyrix_various();
+	check_k6_bug();
 	check_tlb();
 	check_fpu();
 	check_hlt();

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov