aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaciej W. Rozycki <macro@linux-mips.org>2007-10-23 07:43:11 -0400
committerRalf Baechle <ralf@linux-mips.org>2008-01-29 05:14:54 -0500
commit20d60d9973c3b441902b0a3f4f6f7e7ade08f77d (patch)
tree2dbadcb5600fcb486612ec30d972e6c29195ee1d
parent2b22c034d04d3632a339d14d5803c8f94e412608 (diff)
[MIPS] R4000/R4400 errata workarounds
This is the gereric part of R4000/R4400 errata workarounds. They include compiler and assembler support as well as some source code modifications to address the problems with some combinations of multiply/divide+shift instructions as well as the daddi and daddiu instructions. Changes included are as follows: 1. New Kconfig options to select workarounds by platforms as necessary. 2. Arch top-level Makefile to pass necessary options to the compiler; also incompatible configurations are detected (-mno-sym32 unsupported as horribly intrusive for little gain). 3. Bug detection updated and shuffled -- the multiply/divide+shift problem is lethal enough that if not worked around it makes the kernel crash in time_init() because of a division by zero; the daddiu erratum might also trigger early potentially, though I have not observed it. On the other hand the daddi detection code requires the exception subsystem to have been initialised (and is there mainly for information). 4. r4k_daddiu_bug() added so that the existence of the erratum can be queried by code at the run time as necessary; useful for generated code like TLB fault and copy/clear page handlers. 5. __udelay() updated as it uses multiplication in inline assembly. Note that -mdaddi requires modified toolchain (which has been maintained by myself and available from my site for ~4years now -- versions covered are GCC 2.95.4 - 4.1.2 and binutils from 2.13 onwards). The -mfix-r4000 and -mfix-r4400 have been standard for a while though. Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org> Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
-rw-r--r--arch/mips/Kconfig16
-rw-r--r--arch/mips/Makefile12
-rw-r--r--arch/mips/kernel/cpu-bugs64.c47
-rw-r--r--arch/mips/kernel/setup.c4
-rw-r--r--include/asm-mips/bugs.h25
-rw-r--r--include/asm-mips/delay.h12
-rw-r--r--include/asm-mips/war.h62
7 files changed, 148 insertions, 30 deletions
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 12bf96334174..11bc17ce0ebf 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -91,6 +91,9 @@ config MACH_DECSTATION
91 select BOOT_ELF32 91 select BOOT_ELF32
92 select CEVT_R4K 92 select CEVT_R4K
93 select CSRC_R4K 93 select CSRC_R4K
94 select CPU_DADDI_WORKAROUNDS if 64BIT
95 select CPU_R4000_WORKAROUNDS if 64BIT
96 select CPU_R4400_WORKAROUNDS if 64BIT
94 select DMA_NONCOHERENT 97 select DMA_NONCOHERENT
95 select NO_IOPORT 98 select NO_IOPORT
96 select IRQ_CPU 99 select IRQ_CPU
@@ -1606,6 +1609,19 @@ config GENERIC_CLOCKEVENTS_BROADCAST
1606 bool 1609 bool
1607 1610
1608# 1611#
1612# CPU non-features
1613#
1614config CPU_DADDI_WORKAROUNDS
1615 bool
1616
1617config CPU_R4000_WORKAROUNDS
1618 bool
1619 select CPU_R4400_WORKAROUNDS
1620
1621config CPU_R4400_WORKAROUNDS
1622 bool
1623
1624#
1609# Use the generic interrupt handling code in kernel/irq/: 1625# Use the generic interrupt handling code in kernel/irq/:
1610# 1626#
1611config GENERIC_HARDIRQS 1627config GENERIC_HARDIRQS
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index a1f8d8b96b03..a49127af33c8 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -141,6 +141,10 @@ cflags-$(CONFIG_CPU_R8000) += -march=r8000 -Wa,--trap
141cflags-$(CONFIG_CPU_R10000) += $(call cc-option,-march=r10000,-march=r8000) \ 141cflags-$(CONFIG_CPU_R10000) += $(call cc-option,-march=r10000,-march=r8000) \
142 -Wa,--trap 142 -Wa,--trap
143 143
144cflags-$(CONFIG_CPU_R4000_WORKAROUNDS) += $(call cc-option,-mfix-r4000,)
145cflags-$(CONFIG_CPU_R4400_WORKAROUNDS) += $(call cc-option,-mfix-r4400,)
146cflags-$(CONFIG_CPU_DADDI_WORKAROUNDS) += $(call cc-option,-mno-daddi,)
147
144ifdef CONFIG_CPU_SB1 148ifdef CONFIG_CPU_SB1
145ifdef CONFIG_SB1_PASS_1_WORKAROUNDS 149ifdef CONFIG_SB1_PASS_1_WORKAROUNDS
146MODFLAGS += -msb1-pass1-workarounds 150MODFLAGS += -msb1-pass1-workarounds
@@ -602,9 +606,11 @@ ifdef CONFIG_64BIT
602 endif 606 endif
603 endif 607 endif
604 608
605 ifeq ($(KBUILD_SYM32), y) 609 ifeq ($(KBUILD_SYM32)$(call cc-option-yn,-msym32), yy)
606 ifeq ($(call cc-option-yn,-msym32), y) 610 cflags-y += -msym32 -DKBUILD_64BIT_SYM32
607 cflags-y += -msym32 -DKBUILD_64BIT_SYM32 611 else
612 ifeq ($(CONFIG_CPU_DADDI_WORKAROUNDS), y)
613 $(error CONFIG_CPU_DADDI_WORKAROUNDS unsupported without -msym32)
608 endif 614 endif
609 endif 615 endif
610endif 616endif
diff --git a/arch/mips/kernel/cpu-bugs64.c b/arch/mips/kernel/cpu-bugs64.c
index af78456d4138..417bb3e336ac 100644
--- a/arch/mips/kernel/cpu-bugs64.c
+++ b/arch/mips/kernel/cpu-bugs64.c
@@ -18,6 +18,15 @@
18#include <asm/mipsregs.h> 18#include <asm/mipsregs.h>
19#include <asm/system.h> 19#include <asm/system.h>
20 20
21static char bug64hit[] __initdata =
22 "reliable operation impossible!\n%s";
23static char nowar[] __initdata =
24 "Please report to <linux-mips@linux-mips.org>.";
25static char r4kwar[] __initdata =
26 "Enable CPU_R4000_WORKAROUNDS to rectify.";
27static char daddiwar[] __initdata =
28 "Enable CPU_DADDI_WORKAROUNDS to rectify.";
29
21static inline void align_mod(const int align, const int mod) 30static inline void align_mod(const int align, const int mod)
22{ 31{
23 asm volatile( 32 asm volatile(
@@ -155,13 +164,7 @@ static inline void check_mult_sh(void)
155 } 164 }
156 165
157 printk("no.\n"); 166 printk("no.\n");
158 panic("Reliable operation impossible!\n" 167 panic(bug64hit, !R4000_WAR ? r4kwar : nowar);
159#ifndef CONFIG_CPU_R4000
160 "Configure for R4000 to enable the workaround."
161#else
162 "Please report to <linux-mips@linux-mips.org>."
163#endif
164 );
165} 168}
166 169
167static volatile int daddi_ov __initdata = 0; 170static volatile int daddi_ov __initdata = 0;
@@ -233,15 +236,11 @@ static inline void check_daddi(void)
233 } 236 }
234 237
235 printk("no.\n"); 238 printk("no.\n");
236 panic("Reliable operation impossible!\n" 239 panic(bug64hit, !DADDI_WAR ? daddiwar : nowar);
237#if !defined(CONFIG_CPU_R4000) && !defined(CONFIG_CPU_R4400)
238 "Configure for R4000 or R4400 to enable the workaround."
239#else
240 "Please report to <linux-mips@linux-mips.org>."
241#endif
242 );
243} 240}
244 241
242int daddiu_bug __initdata = -1;
243
245static inline void check_daddiu(void) 244static inline void check_daddiu(void)
246{ 245{
247 long v, w, tmp; 246 long v, w, tmp;
@@ -281,7 +280,9 @@ static inline void check_daddiu(void)
281 : "=&r" (v), "=&r" (w), "=&r" (tmp) 280 : "=&r" (v), "=&r" (w), "=&r" (tmp)
282 : "I" (0xffffffffffffdb9aUL), "I" (0x1234)); 281 : "I" (0xffffffffffffdb9aUL), "I" (0x1234));
283 282
284 if (v == w) { 283 daddiu_bug = v != w;
284
285 if (!daddiu_bug) {
285 printk("no.\n"); 286 printk("no.\n");
286 return; 287 return;
287 } 288 }
@@ -303,18 +304,16 @@ static inline void check_daddiu(void)
303 } 304 }
304 305
305 printk("no.\n"); 306 printk("no.\n");
306 panic("Reliable operation impossible!\n" 307 panic(bug64hit, !DADDI_WAR ? daddiwar : nowar);
307#if !defined(CONFIG_CPU_R4000) && !defined(CONFIG_CPU_R4400)
308 "Configure for R4000 or R4400 to enable the workaround."
309#else
310 "Please report to <linux-mips@linux-mips.org>."
311#endif
312 );
313} 308}
314 309
315void __init check_bugs64(void) 310void __init check_bugs64_early(void)
316{ 311{
317 check_mult_sh(); 312 check_mult_sh();
318 check_daddi();
319 check_daddiu(); 313 check_daddiu();
320} 314}
315
316void __init check_bugs64(void)
317{
318 check_daddi();
319}
diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c
index f8a535afce39..7b4418dd5857 100644
--- a/arch/mips/kernel/setup.c
+++ b/arch/mips/kernel/setup.c
@@ -8,7 +8,7 @@
8 * Copyright (C) 1994, 95, 96, 97, 98, 99, 2000, 01, 02, 03 Ralf Baechle 8 * Copyright (C) 1994, 95, 96, 97, 98, 99, 2000, 01, 02, 03 Ralf Baechle
9 * Copyright (C) 1996 Stoned Elipot 9 * Copyright (C) 1996 Stoned Elipot
10 * Copyright (C) 1999 Silicon Graphics, Inc. 10 * Copyright (C) 1999 Silicon Graphics, Inc.
11 * Copyright (C) 2000 2001, 2002 Maciej W. Rozycki 11 * Copyright (C) 2000, 2001, 2002, 2007 Maciej W. Rozycki
12 */ 12 */
13#include <linux/init.h> 13#include <linux/init.h>
14#include <linux/ioport.h> 14#include <linux/ioport.h>
@@ -24,6 +24,7 @@
24 24
25#include <asm/addrspace.h> 25#include <asm/addrspace.h>
26#include <asm/bootinfo.h> 26#include <asm/bootinfo.h>
27#include <asm/bugs.h>
27#include <asm/cache.h> 28#include <asm/cache.h>
28#include <asm/cpu.h> 29#include <asm/cpu.h>
29#include <asm/sections.h> 30#include <asm/sections.h>
@@ -561,6 +562,7 @@ void __init setup_arch(char **cmdline_p)
561 } 562 }
562#endif 563#endif
563 cpu_report(); 564 cpu_report();
565 check_bugs_early();
564 566
565#if defined(CONFIG_VT) 567#if defined(CONFIG_VT)
566#if defined(CONFIG_VGA_CONSOLE) 568#if defined(CONFIG_VGA_CONSOLE)
diff --git a/include/asm-mips/bugs.h b/include/asm-mips/bugs.h
index 0d7f9c1f5546..9dc10df32078 100644
--- a/include/asm-mips/bugs.h
+++ b/include/asm-mips/bugs.h
@@ -1,19 +1,34 @@
1/* 1/*
2 * This is included by init/main.c to check for architecture-dependent bugs. 2 * This is included by init/main.c to check for architecture-dependent bugs.
3 * 3 *
4 * Copyright (C) 2007 Maciej W. Rozycki
5 *
4 * Needs: 6 * Needs:
5 * void check_bugs(void); 7 * void check_bugs(void);
6 */ 8 */
7#ifndef _ASM_BUGS_H 9#ifndef _ASM_BUGS_H
8#define _ASM_BUGS_H 10#define _ASM_BUGS_H
9 11
12#include <linux/bug.h>
10#include <linux/delay.h> 13#include <linux/delay.h>
14
11#include <asm/cpu.h> 15#include <asm/cpu.h>
12#include <asm/cpu-info.h> 16#include <asm/cpu-info.h>
13 17
18extern int daddiu_bug;
19
20extern void check_bugs64_early(void);
21
14extern void check_bugs32(void); 22extern void check_bugs32(void);
15extern void check_bugs64(void); 23extern void check_bugs64(void);
16 24
25static inline void check_bugs_early(void)
26{
27#ifdef CONFIG_64BIT
28 check_bugs64_early();
29#endif
30}
31
17static inline void check_bugs(void) 32static inline void check_bugs(void)
18{ 33{
19 unsigned int cpu = smp_processor_id(); 34 unsigned int cpu = smp_processor_id();
@@ -25,4 +40,14 @@ static inline void check_bugs(void)
25#endif 40#endif
26} 41}
27 42
43static inline int r4k_daddiu_bug(void)
44{
45#ifdef CONFIG_64BIT
46 WARN_ON(daddiu_bug < 0);
47 return daddiu_bug != 0;
48#else
49 return 0;
50#endif
51}
52
28#endif /* _ASM_BUGS_H */ 53#endif /* _ASM_BUGS_H */
diff --git a/include/asm-mips/delay.h b/include/asm-mips/delay.h
index fab32131e9b4..de5105d05f1e 100644
--- a/include/asm-mips/delay.h
+++ b/include/asm-mips/delay.h
@@ -6,13 +6,16 @@
6 * Copyright (C) 1994 by Waldorf Electronics 6 * Copyright (C) 1994 by Waldorf Electronics
7 * Copyright (C) 1995 - 2000, 01, 03 by Ralf Baechle 7 * Copyright (C) 1995 - 2000, 01, 03 by Ralf Baechle
8 * Copyright (C) 1999, 2000 Silicon Graphics, Inc. 8 * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
9 * Copyright (C) 2007 Maciej W. Rozycki
9 */ 10 */
10#ifndef _ASM_DELAY_H 11#ifndef _ASM_DELAY_H
11#define _ASM_DELAY_H 12#define _ASM_DELAY_H
12 13
13#include <linux/param.h> 14#include <linux/param.h>
14#include <linux/smp.h> 15#include <linux/smp.h>
16
15#include <asm/compiler.h> 17#include <asm/compiler.h>
18#include <asm/war.h>
16 19
17static inline void __delay(unsigned long loops) 20static inline void __delay(unsigned long loops)
18{ 21{
@@ -50,7 +53,7 @@ static inline void __delay(unsigned long loops)
50 53
51static inline void __udelay(unsigned long usecs, unsigned long lpj) 54static inline void __udelay(unsigned long usecs, unsigned long lpj)
52{ 55{
53 unsigned long lo; 56 unsigned long hi, lo;
54 57
55 /* 58 /*
56 * The rates of 128 is rounded wrongly by the catchall case 59 * The rates of 128 is rounded wrongly by the catchall case
@@ -70,11 +73,16 @@ static inline void __udelay(unsigned long usecs, unsigned long lpj)
70 : "=h" (usecs), "=l" (lo) 73 : "=h" (usecs), "=l" (lo)
71 : "r" (usecs), "r" (lpj) 74 : "r" (usecs), "r" (lpj)
72 : GCC_REG_ACCUM); 75 : GCC_REG_ACCUM);
73 else if (sizeof(long) == 8) 76 else if (sizeof(long) == 8 && !R4000_WAR)
74 __asm__("dmultu\t%2, %3" 77 __asm__("dmultu\t%2, %3"
75 : "=h" (usecs), "=l" (lo) 78 : "=h" (usecs), "=l" (lo)
76 : "r" (usecs), "r" (lpj) 79 : "r" (usecs), "r" (lpj)
77 : GCC_REG_ACCUM); 80 : GCC_REG_ACCUM);
81 else if (sizeof(long) == 8 && R4000_WAR)
82 __asm__("dmultu\t%3, %4\n\tmfhi\t%0"
83 : "=r" (usecs), "=h" (hi), "=l" (lo)
84 : "r" (usecs), "r" (lpj)
85 : GCC_REG_ACCUM);
78 86
79 __delay(usecs); 87 __delay(usecs);
80} 88}
diff --git a/include/asm-mips/war.h b/include/asm-mips/war.h
index d2808edfd4e9..22361d5e3bf0 100644
--- a/include/asm-mips/war.h
+++ b/include/asm-mips/war.h
@@ -4,6 +4,7 @@
4 * for more details. 4 * for more details.
5 * 5 *
6 * Copyright (C) 2002, 2004, 2007 by Ralf Baechle 6 * Copyright (C) 2002, 2004, 2007 by Ralf Baechle
7 * Copyright (C) 2007 Maciej W. Rozycki
7 */ 8 */
8#ifndef _ASM_WAR_H 9#ifndef _ASM_WAR_H
9#define _ASM_WAR_H 10#define _ASM_WAR_H
@@ -11,6 +12,67 @@
11#include <war.h> 12#include <war.h>
12 13
13/* 14/*
15 * Work around certain R4000 CPU errata (as implemented by GCC):
16 *
17 * - A double-word or a variable shift may give an incorrect result
18 * if executed immediately after starting an integer division:
19 * "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 3.0",
20 * erratum #28
21 * "MIPS R4000MC Errata, Processor Revision 2.2 and 3.0", erratum
22 * #19
23 *
24 * - A double-word or a variable shift may give an incorrect result
25 * if executed while an integer multiplication is in progress:
26 * "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 3.0",
27 * errata #16 & #28
28 *
29 * - An integer division may give an incorrect result if started in
30 * a delay slot of a taken branch or a jump:
31 * "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 3.0",
32 * erratum #52
33 */
34#ifdef CONFIG_CPU_R4000_WORKAROUNDS
35#define R4000_WAR 1
36#else
37#define R4000_WAR 0
38#endif
39
40/*
41 * Work around certain R4400 CPU errata (as implemented by GCC):
42 *
43 * - A double-word or a variable shift may give an incorrect result
44 * if executed immediately after starting an integer division:
45 * "MIPS R4400MC Errata, Processor Revision 1.0", erratum #10
46 * "MIPS R4400MC Errata, Processor Revision 2.0 & 3.0", erratum #4
47 */
48#ifdef CONFIG_CPU_R4400_WORKAROUNDS
49#define R4400_WAR 1
50#else
51#define R4400_WAR 0
52#endif
53
54/*
55 * Work around the "daddi" and "daddiu" CPU errata:
56 *
57 * - The `daddi' instruction fails to trap on overflow.
58 * "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 3.0",
59 * erratum #23
60 *
61 * - The `daddiu' instruction can produce an incorrect result.
62 * "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 3.0",
63 * erratum #41
64 * "MIPS R4000MC Errata, Processor Revision 2.2 and 3.0", erratum
65 * #15
66 * "MIPS R4400PC/SC Errata, Processor Revision 1.0", erratum #7
67 * "MIPS R4400MC Errata, Processor Revision 1.0", erratum #5
68 */
69#ifdef CONFIG_CPU_DADDI_WORKAROUNDS
70#define DADDI_WAR 1
71#else
72#define DADDI_WAR 0
73#endif
74
75/*
14 * Another R4600 erratum. Due to the lack of errata information the exact 76 * Another R4600 erratum. Due to the lack of errata information the exact
15 * technical details aren't known. I've experimentally found that disabling 77 * technical details aren't known. I've experimentally found that disabling
16 * interrupts during indexed I-cache flushes seems to be sufficient to deal 78 * interrupts during indexed I-cache flushes seems to be sufficient to deal