Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

authored by

Maciej W. Rozycki and committed by
Ralf Baechle
20d60d99 2b22c034

+148 -30
+16
arch/mips/Kconfig
··· 91 91 select BOOT_ELF32 92 92 select CEVT_R4K 93 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 97 select DMA_NONCOHERENT 95 98 select NO_IOPORT 96 99 select IRQ_CPU ··· 1606 1603 default y 1607 1604 1608 1605 config GENERIC_CLOCKEVENTS_BROADCAST 1606 + bool 1607 + 1608 + # 1609 + # CPU non-features 1610 + # 1611 + config CPU_DADDI_WORKAROUNDS 1612 + bool 1613 + 1614 + config CPU_R4000_WORKAROUNDS 1615 + bool 1616 + select CPU_R4400_WORKAROUNDS 1617 + 1618 + config CPU_R4400_WORKAROUNDS 1609 1619 bool 1610 1620 1611 1621 #
+9 -3
arch/mips/Makefile
··· 141 141 cflags-$(CONFIG_CPU_R10000) += $(call cc-option,-march=r10000,-march=r8000) \ 142 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 148 ifdef CONFIG_CPU_SB1 145 149 ifdef CONFIG_SB1_PASS_1_WORKAROUNDS 146 150 MODFLAGS += -msb1-pass1-workarounds ··· 606 602 endif 607 603 endif 608 604 609 - ifeq ($(KBUILD_SYM32), y) 610 - ifeq ($(call cc-option-yn,-msym32), y) 611 - cflags-y += -msym32 -DKBUILD_64BIT_SYM32 605 + ifeq ($(KBUILD_SYM32)$(call cc-option-yn,-msym32), yy) 606 + cflags-y += -msym32 -DKBUILD_64BIT_SYM32 607 + else 608 + ifeq ($(CONFIG_CPU_DADDI_WORKAROUNDS), y) 609 + $(error CONFIG_CPU_DADDI_WORKAROUNDS unsupported without -msym32) 612 610 endif 613 611 endif 614 612 endif
+23 -24
arch/mips/kernel/cpu-bugs64.c
··· 18 18 #include <asm/mipsregs.h> 19 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 30 static inline void align_mod(const int align, const int mod) 22 31 { 23 32 asm volatile( ··· 164 155 } 165 156 166 157 printk("no.\n"); 167 - panic("Reliable operation impossible!\n" 168 - #ifndef CONFIG_CPU_R4000 169 - "Configure for R4000 to enable the workaround." 170 - #else 171 - "Please report to <linux-mips@linux-mips.org>." 172 - #endif 173 - ); 158 + panic(bug64hit, !R4000_WAR ? r4kwar : nowar); 174 159 } 175 160 176 161 static volatile int daddi_ov __initdata = 0; ··· 236 233 } 237 234 238 235 printk("no.\n"); 239 - panic("Reliable operation impossible!\n" 240 - #if !defined(CONFIG_CPU_R4000) && !defined(CONFIG_CPU_R4400) 241 - "Configure for R4000 or R4400 to enable the workaround." 242 - #else 243 - "Please report to <linux-mips@linux-mips.org>." 244 - #endif 245 - ); 236 + panic(bug64hit, !DADDI_WAR ? daddiwar : nowar); 246 237 } 238 + 239 + int daddiu_bug __initdata = -1; 247 240 248 241 static inline void check_daddiu(void) 249 242 { ··· 280 281 : "=&r" (v), "=&r" (w), "=&r" (tmp) 281 282 : "I" (0xffffffffffffdb9aUL), "I" (0x1234)); 282 283 283 - if (v == w) { 284 + daddiu_bug = v != w; 285 + 286 + if (!daddiu_bug) { 284 287 printk("no.\n"); 285 288 return; 286 289 } ··· 304 303 } 305 304 306 305 printk("no.\n"); 307 - panic("Reliable operation impossible!\n" 308 - #if !defined(CONFIG_CPU_R4000) && !defined(CONFIG_CPU_R4400) 309 - "Configure for R4000 or R4400 to enable the workaround." 310 - #else 311 - "Please report to <linux-mips@linux-mips.org>." 312 - #endif 313 - ); 306 + panic(bug64hit, !DADDI_WAR ? daddiwar : nowar); 307 + } 308 + 309 + void __init check_bugs64_early(void) 310 + { 311 + check_mult_sh(); 312 + check_daddiu(); 314 313 } 315 314 316 315 void __init check_bugs64(void) 317 316 { 318 - check_mult_sh(); 319 317 check_daddi(); 320 - check_daddiu(); 321 318 }
+3 -1
arch/mips/kernel/setup.c
··· 8 8 * Copyright (C) 1994, 95, 96, 97, 98, 99, 2000, 01, 02, 03 Ralf Baechle 9 9 * Copyright (C) 1996 Stoned Elipot 10 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 13 #include <linux/init.h> 14 14 #include <linux/ioport.h> ··· 24 24 25 25 #include <asm/addrspace.h> 26 26 #include <asm/bootinfo.h> 27 + #include <asm/bugs.h> 27 28 #include <asm/cache.h> 28 29 #include <asm/cpu.h> 29 30 #include <asm/sections.h> ··· 562 561 } 563 562 #endif 564 563 cpu_report(); 564 + check_bugs_early(); 565 565 566 566 #if defined(CONFIG_VT) 567 567 #if defined(CONFIG_VGA_CONSOLE)
+25
include/asm-mips/bugs.h
··· 1 1 /* 2 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 6 * Needs: 5 7 * void check_bugs(void); 6 8 */ 7 9 #ifndef _ASM_BUGS_H 8 10 #define _ASM_BUGS_H 9 11 12 + #include <linux/bug.h> 10 13 #include <linux/delay.h> 14 + 11 15 #include <asm/cpu.h> 12 16 #include <asm/cpu-info.h> 13 17 18 + extern int daddiu_bug; 19 + 20 + extern void check_bugs64_early(void); 21 + 14 22 extern void check_bugs32(void); 15 23 extern void check_bugs64(void); 24 + 25 + static inline void check_bugs_early(void) 26 + { 27 + #ifdef CONFIG_64BIT 28 + check_bugs64_early(); 29 + #endif 30 + } 16 31 17 32 static inline void check_bugs(void) 18 33 { ··· 37 22 check_bugs32(); 38 23 #ifdef CONFIG_64BIT 39 24 check_bugs64(); 25 + #endif 26 + } 27 + 28 + static inline int r4k_daddiu_bug(void) 29 + { 30 + #ifdef CONFIG_64BIT 31 + WARN_ON(daddiu_bug < 0); 32 + return daddiu_bug != 0; 33 + #else 34 + return 0; 40 35 #endif 41 36 } 42 37
+10 -2
include/asm-mips/delay.h
··· 6 6 * Copyright (C) 1994 by Waldorf Electronics 7 7 * Copyright (C) 1995 - 2000, 01, 03 by Ralf Baechle 8 8 * Copyright (C) 1999, 2000 Silicon Graphics, Inc. 9 + * Copyright (C) 2007 Maciej W. Rozycki 9 10 */ 10 11 #ifndef _ASM_DELAY_H 11 12 #define _ASM_DELAY_H 12 13 13 14 #include <linux/param.h> 14 15 #include <linux/smp.h> 16 + 15 17 #include <asm/compiler.h> 18 + #include <asm/war.h> 16 19 17 20 static inline void __delay(unsigned long loops) 18 21 { ··· 53 50 54 51 static inline void __udelay(unsigned long usecs, unsigned long lpj) 55 52 { 56 - unsigned long lo; 53 + unsigned long hi, lo; 57 54 58 55 /* 59 56 * The rates of 128 is rounded wrongly by the catchall case ··· 73 70 : "=h" (usecs), "=l" (lo) 74 71 : "r" (usecs), "r" (lpj) 75 72 : GCC_REG_ACCUM); 76 - else if (sizeof(long) == 8) 73 + else if (sizeof(long) == 8 && !R4000_WAR) 77 74 __asm__("dmultu\t%2, %3" 78 75 : "=h" (usecs), "=l" (lo) 76 + : "r" (usecs), "r" (lpj) 77 + : GCC_REG_ACCUM); 78 + else if (sizeof(long) == 8 && R4000_WAR) 79 + __asm__("dmultu\t%3, %4\n\tmfhi\t%0" 80 + : "=r" (usecs), "=h" (hi), "=l" (lo) 79 81 : "r" (usecs), "r" (lpj) 80 82 : GCC_REG_ACCUM); 81 83
+62
include/asm-mips/war.h
··· 4 4 * for more details. 5 5 * 6 6 * Copyright (C) 2002, 2004, 2007 by Ralf Baechle 7 + * Copyright (C) 2007 Maciej W. Rozycki 7 8 */ 8 9 #ifndef _ASM_WAR_H 9 10 #define _ASM_WAR_H 10 11 11 12 #include <war.h> 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 12 74 13 75 /* 14 76 * Another R4600 erratum. Due to the lack of errata information the exact