linux-mips
[Top] [All Lists]

[patch] R4000/R4400 64-bit errata handling

To: Ralf Baechle <ralf@linux-mips.org>, linux-mips@linux-mips.org
Subject: [patch] R4000/R4400 64-bit errata handling
From: "Maciej W. Rozycki" <macro@ds2.pg.gda.pl>
Date: Fri, 10 Jan 2003 15:32:34 +0100 (MET)
Cc: Karsten Merker <karsten@excalibur.cologne.de>, Thiemo Seufer <ica2_ts@csv.ica.uni-stuttgart.de>
Organization: Technical University of Gdansk
Original-recipient: rfc822;linux-mips@linux-mips.org
Sender: linux-mips-bounce@linux-mips.org
Hello,

 As you might already know there are a few nasty errata in the R4000 and
the early R4400 that hit 64-bit operation badly.  Here is proposed code to
detect them.  If an erratum is found in the processor and no workaround is
applied to a kernel executable, the kernel refuses to run.  In all cases
the result of the probes is output to the bootstrap log.

 The code has bits that make use of features of non-standard tools
(binutils and gcc).  But it doesn't depend on them -- when built with
standard tools and run on an affected system, a kernel will simply fail,
and on good systems it will run normally.  Therefore it's safe to apply,
and if the ultimate implementation in the tools differs, the code may get
adjusted appropriately later. 

 I'd like to apply this code as soon as possible as I consider it a
prerequisite for integrating 64-bit support for the DECstation (to prevent
people from running unreliable code), so please tell me if there are any
doubts about it.  Errata descriptions are available at the MIPS site --
see: 'http://www.mips.com/publications/r400_r5000.html'.  Unfortunately,
despite several attempts to get a permission to duplicate them within
Linux sources, I failed to get one.

 I'd like to express my gratitude to Karsten and Thiemo for testing the
code with their hardware.  Without their help, I wouldn't be able to
prepare appropriate tests for errata my hardware doesn't suffer from. 

  Maciej

-- 
+  Maciej W. Rozycki, Technical University of Gdansk, Poland   +
+--------------------------------------------------------------+
+        e-mail: macro@ds2.pg.gda.pl, PGP key available        +

patch-mips-2.4.20-pre6-20021212-mips-bugs-20
diff -up --recursive --new-file 
linux-mips-2.4.20-pre6-20021212.macro/arch/mips64/kernel/Makefile 
linux-mips-2.4.20-pre6-20021212/arch/mips64/kernel/Makefile
--- linux-mips-2.4.20-pre6-20021212.macro/arch/mips64/kernel/Makefile   
2002-12-04 03:56:39.000000000 +0000
+++ linux-mips-2.4.20-pre6-20021212/arch/mips64/kernel/Makefile 2003-01-09 
19:04:31.000000000 +0000
@@ -32,6 +32,7 @@ ifndef CONFIG_MAPPED_PCI_IO
 obj-y                          += pci-dma.o
 endif
 
-AFLAGS_r4k_genex.o := -P
+CFLAGS_cpu-probe.o     = $(shell if $(CC) $(CFLAGS) -Wa,-mdaddi -c -o 
/dev/null -xc /dev/null >/dev/null 2>&1; then echo "-DHAVE_AS_SET_DADDI"; fi)
+AFLAGS_r4k_genex.o     = -P
 
 include $(TOPDIR)/Rules.make
diff -up --recursive --new-file 
linux-mips-2.4.20-pre6-20021212.macro/arch/mips64/kernel/cpu-probe.c 
linux-mips-2.4.20-pre6-20021212/arch/mips64/kernel/cpu-probe.c
--- linux-mips-2.4.20-pre6-20021212.macro/arch/mips64/kernel/cpu-probe.c        
2002-12-04 03:56:39.000000000 +0000
+++ linux-mips-2.4.20-pre6-20021212/arch/mips64/kernel/cpu-probe.c      
2003-01-09 20:19:32.000000000 +0000
@@ -1,10 +1,27 @@
+/*
+ *     arch/mips64/kernel/cpu-probe.c
+ *
+ *     Processor capabilities determination functions.
+ *
+ *     Copyright (C) xxxx  the Anonymous
+ *     Copyright (C) 2003  Maciej W. Rozycki
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License
+ *     as published by the Free Software Foundation; either version
+ *     2 of the License, or (at your option) any later version.
+ */
+
 #include <linux/init.h>
 #include <linux/kernel.h>
+#include <linux/ptrace.h>
 #include <linux/stddef.h>
+
 #include <asm/bugs.h>
 #include <asm/cpu.h>
 #include <asm/fpu.h>
 #include <asm/mipsregs.h>
+#include <asm/system.h>
 
 /*
  * Not all of the MIPS CPUs have the "wait" instruction available. Moreover,
@@ -90,9 +107,229 @@ static inline void check_wait(void)
        }
 }
 
+static inline void check_mult_sh(void)
+{
+       unsigned long flags;
+       int m1, m2;
+       long p, s, v;
+
+       printk("Checking for the multiply/shift bug... ");
+
+       __save_and_cli(flags);
+       /*
+        * The following code leads to a wrong result of dsll32 when
+        * executed on R4000 rev. 2.2 or 3.0.
+        *
+        * See "MIPS R4000PC/SC Errata, Processor Revision 2.2 and
+        * 3.0" by MIPS Technologies, Inc., errata #16 and #28 for
+        * details.  I got no permission to duplicate them here,
+        * sigh... --macro
+        */
+       asm volatile(
+               ".set   push\n\t"
+               ".set   noat\n\t"
+               ".set   noreorder\n\t"
+               ".set   nomacro\n\t"
+               "mult   %1, %2\n\t"
+               "dsll32 %0, %3, %4\n\t"
+               "mflo   $0\n\t"
+               ".set   pop"
+               : "=r" (v)
+               : "r" (5), "r" (8), "r" (5), "I" (0)
+               : "hi", "lo", "accum");
+       __restore_flags(flags);
+
+       if (v == 5L << 32) {
+               printk("no.\n");
+               return;
+       }
+
+       printk("yes, workaround... ");
+       __save_and_cli(flags);
+       /*
+        * We want the multiply and the shift to be isolated from the
+        * rest of the code to disable gcc optimizations.  Hence the
+        * asm statements that execute nothing, but make gcc not know
+        * what the values of m1, m2 and s are and what v and p are
+        * used for.
+        *
+        * We have to use single integers for m1 and m2 and a double
+        * one for p to be sure the mulsidi3 gcc's RTL multiplication
+        * instruction has the workaround applied.  Older versions of
+        * gcc have correct mulsi3, but other multiplication variants
+        * lack the workaround.
+        */
+       asm volatile(
+               ""
+               : "=r" (m1), "=r" (m2), "=r" (s)
+               : "0" (5), "1" (8), "2" (5));
+       p = m1 * m2;
+       v = s << 32;
+       asm volatile(
+               ""
+               : "=r" (v)
+               : "0" (v), "r" (p));
+       __restore_flags(flags);
+
+       if (v == 5L << 32) {
+               printk("yes.\n");
+               return;
+       }
+
+       printk("no.\n");
+       panic("Reliable operation impossible!\n"
+#ifndef CONFIG_CPU_R4000
+             "Configure for R4000 to enable the workaround."
+#else
+             "Please report to <linux-mips@linux-mips.org>."
+#endif
+             );
+}
+
+static volatile int daddi_ov __initdata = 0;
+
+asmlinkage void __init do_daddi_ov(struct pt_regs *regs)
+{
+       daddi_ov = 1;
+       regs->cp0_epc += 4;
+}
+
+static inline void check_daddi(void)
+{
+       extern asmlinkage void handle_daddi_ov(void);
+       unsigned long flags;
+       void *handler;
+       long v;
+
+       printk("Checking for the daddi bug... ");
+
+       __save_and_cli(flags);
+       handler = set_except_vector(12, handle_daddi_ov);
+       /*
+        * The following code fails to trigger an overflow exception
+        * when executed on R4000 rev. 2.2 or 3.0.
+        *
+        * See "MIPS R4000PC/SC Errata, Processor Revision 2.2 and
+        * 3.0" by MIPS Technologies, Inc., erratum #23 for details.
+        * I got no permission to duplicate it here, sigh... --macro
+        */
+       asm volatile(
+               ".set   push\n\t"
+               ".set   noat\n\t"
+               ".set   noreorder\n\t"
+               ".set   nomacro\n\t"
+#ifdef HAVE_AS_SET_DADDI
+               ".set   daddi\n\t"
+#endif
+               "daddi  %0, %1, %2\n\t"
+               ".set   pop"
+               : "=r" (v)
+               : "r" (0x7fffffffffffedcd), "I" (0x1234));
+       set_except_vector(12, handler);
+       __restore_flags(flags);
+
+       if (daddi_ov) {
+               printk("no.\n");
+               return;
+       }
+
+       printk("yes, workaround... ");
+
+       __save_and_cli(flags);
+       handler = set_except_vector(12, handle_daddi_ov);
+       asm volatile(
+               "daddi  %0, %1, %2"
+               : "=r" (v)
+               : "r" (0x7fffffffffffedcd), "I" (0x1234));
+       set_except_vector(12, handler);
+       __restore_flags(flags);
+
+       if (daddi_ov) {
+               printk("yes.\n");
+               return;
+       }
+
+       printk("no.\n");
+       panic("Reliable operation impossible!\n"
+#if !defined(CONFIG_CPU_R4000) && !defined(CONFIG_CPU_R4400)
+             "Configure for R4000 or R4400 to enable the workaround."
+#else
+             "Please report to <linux-mips@linux-mips.org>."
+#endif
+             );
+}
+
+static inline void check_daddiu(void)
+{
+       long v, w;
+
+       printk("Checking for the daddiu bug... ");
+
+       /*
+        * The following code leads to a wrong result of daddiu when
+        * executed on R4400 rev. 1.0.
+        *
+        * See "MIPS R4400PC/SC Errata, Processor Revision 1.0" by
+        * MIPS Technologies, Inc., erratum #7 for details.
+        *
+        * According to "MIPS R4000PC/SC Errata, Processor Revision
+        * 2.2 and 3.0" by MIPS Technologies, Inc., erratum #41 this
+        * problem affects R4000 rev. 2.2 and 3.0, too.  Testing
+        * failed to trigger it so far.
+        *
+        * I got no permission to duplicate the errata here, sigh...
+        * --macro
+        */
+       asm volatile(
+               ".set   push\n\t"
+               ".set   noat\n\t"
+               ".set   noreorder\n\t"
+               ".set   nomacro\n\t"
+#ifdef HAVE_AS_SET_DADDI
+               ".set   daddi\n\t"
+#endif
+               "daddiu %0, %2, %3\n\t"
+               "addiu  %1, $0, %3\n\t"
+               "daddu  %1, %2\n\t"
+               ".set   pop"
+               : "=&r" (v), "=&r" (w)
+               : "r" (0x7fffffffffffedcd), "I" (0x1234));
+
+       if (v == w) {
+               printk("no.\n");
+               return;
+       }
+
+       printk("yes, workaround... ");
+
+       asm volatile(
+               "daddiu %0, %2, %3\n\t"
+               "addiu  %1, $0, %3\n\t"
+               "daddu  %1, %2"
+               : "=&r" (v), "=&r" (w)
+               : "r" (0x7fffffffffffedcd), "I" (0x1234));
+
+       if (v == w) {
+               printk("yes.\n");
+               return;
+       }
+
+       printk("no.\n");
+       panic("Reliable operation impossible!\n"
+#if !defined(CONFIG_CPU_R4000) && !defined(CONFIG_CPU_R4400)
+             "Configure for R4000 or R4400 to enable the workaround."
+#else
+             "Please report to <linux-mips@linux-mips.org>."
+#endif
+             );
+}
+
 void __init check_bugs(void)
 {
        check_wait();
+       check_mult_sh();
+       check_daddi();
+       check_daddiu();
 }
 
 /*
diff -up --recursive --new-file 
linux-mips-2.4.20-pre6-20021212.macro/arch/mips64/kernel/r4k_genex.S 
linux-mips-2.4.20-pre6-20021212/arch/mips64/kernel/r4k_genex.S
--- linux-mips-2.4.20-pre6-20021212.macro/arch/mips64/kernel/r4k_genex.S        
2002-10-02 17:22:18.000000000 +0000
+++ linux-mips-2.4.20-pre6-20021212/arch/mips64/kernel/r4k_genex.S      
2002-10-22 21:44:51.000000000 +0000
@@ -35,6 +35,11 @@
 
        __INIT
 
+/* A temporary overflow handler used by check_daddi(). */
+
+       BUILD_HANDLER  daddi_ov daddi_ov none silent    /* #12 */
+
+
 /* General exception handler for CPUs with virtual coherency exception.
  *
  * Be careful when changing this, it has to be at most 256 (as a special


<Prev in Thread] Current Thread [Next in Thread>