diff options
author | Maciej W. Rozycki <macro@linux-mips.org> | 2007-10-23 07:43:11 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2008-01-29 05:14:54 -0500 |
commit | 20d60d9973c3b441902b0a3f4f6f7e7ade08f77d (patch) | |
tree | 2dbadcb5600fcb486612ec30d972e6c29195ee1d /arch/mips | |
parent | 2b22c034d04d3632a339d14d5803c8f94e412608 (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>
Diffstat (limited to 'arch/mips')
-rw-r--r-- | arch/mips/Kconfig | 16 | ||||
-rw-r--r-- | arch/mips/Makefile | 12 | ||||
-rw-r--r-- | arch/mips/kernel/cpu-bugs64.c | 47 | ||||
-rw-r--r-- | arch/mips/kernel/setup.c | 4 |
4 files changed, 51 insertions, 28 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 | # | ||
1614 | config CPU_DADDI_WORKAROUNDS | ||
1615 | bool | ||
1616 | |||
1617 | config CPU_R4000_WORKAROUNDS | ||
1618 | bool | ||
1619 | select CPU_R4400_WORKAROUNDS | ||
1620 | |||
1621 | config 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 | # |
1611 | config GENERIC_HARDIRQS | 1627 | config 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 | |||
141 | cflags-$(CONFIG_CPU_R10000) += $(call cc-option,-march=r10000,-march=r8000) \ | 141 | cflags-$(CONFIG_CPU_R10000) += $(call cc-option,-march=r10000,-march=r8000) \ |
142 | -Wa,--trap | 142 | -Wa,--trap |
143 | 143 | ||
144 | cflags-$(CONFIG_CPU_R4000_WORKAROUNDS) += $(call cc-option,-mfix-r4000,) | ||
145 | cflags-$(CONFIG_CPU_R4400_WORKAROUNDS) += $(call cc-option,-mfix-r4400,) | ||
146 | cflags-$(CONFIG_CPU_DADDI_WORKAROUNDS) += $(call cc-option,-mno-daddi,) | ||
147 | |||
144 | ifdef CONFIG_CPU_SB1 | 148 | ifdef CONFIG_CPU_SB1 |
145 | ifdef CONFIG_SB1_PASS_1_WORKAROUNDS | 149 | ifdef CONFIG_SB1_PASS_1_WORKAROUNDS |
146 | MODFLAGS += -msb1-pass1-workarounds | 150 | MODFLAGS += -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 |
610 | endif | 616 | endif |
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 | ||
21 | static char bug64hit[] __initdata = | ||
22 | "reliable operation impossible!\n%s"; | ||
23 | static char nowar[] __initdata = | ||
24 | "Please report to <linux-mips@linux-mips.org>."; | ||
25 | static char r4kwar[] __initdata = | ||
26 | "Enable CPU_R4000_WORKAROUNDS to rectify."; | ||
27 | static char daddiwar[] __initdata = | ||
28 | "Enable CPU_DADDI_WORKAROUNDS to rectify."; | ||
29 | |||
21 | static inline void align_mod(const int align, const int mod) | 30 | static 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 | ||
167 | static volatile int daddi_ov __initdata = 0; | 170 | static 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 | ||
242 | int daddiu_bug __initdata = -1; | ||
243 | |||
245 | static inline void check_daddiu(void) | 244 | static 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 | ||
315 | void __init check_bugs64(void) | 310 | void __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 | |||
316 | void __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) |