Commit 20d60d99 authored by Maciej W. Rozycki's avatar Maciej W. Rozycki Committed by Ralf Baechle
Browse files

[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: default avatarMaciej W. Rozycki <macro@linux-mips.org>
Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent 2b22c034
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -91,6 +91,9 @@ config MACH_DECSTATION
	select BOOT_ELF32
	select CEVT_R4K
	select CSRC_R4K
	select CPU_DADDI_WORKAROUNDS if 64BIT
	select CPU_R4000_WORKAROUNDS if 64BIT
	select CPU_R4400_WORKAROUNDS if 64BIT
	select DMA_NONCOHERENT
	select NO_IOPORT
	select IRQ_CPU
@@ -1605,6 +1608,19 @@ config CPU_HAS_SYNC
config GENERIC_CLOCKEVENTS_BROADCAST
	bool

#
# CPU non-features
#
config CPU_DADDI_WORKAROUNDS
	bool

config CPU_R4000_WORKAROUNDS
	bool
	select CPU_R4400_WORKAROUNDS

config CPU_R4400_WORKAROUNDS
	bool

#
# Use the generic interrupt handling code in kernel/irq/:
#
+9 −3
Original line number Diff line number Diff line
@@ -141,6 +141,10 @@ cflags-$(CONFIG_CPU_R8000) += -march=r8000 -Wa,--trap
cflags-$(CONFIG_CPU_R10000)	+= $(call cc-option,-march=r10000,-march=r8000) \
			-Wa,--trap

cflags-$(CONFIG_CPU_R4000_WORKAROUNDS)	+= $(call cc-option,-mfix-r4000,)
cflags-$(CONFIG_CPU_R4400_WORKAROUNDS)	+= $(call cc-option,-mfix-r4400,)
cflags-$(CONFIG_CPU_DADDI_WORKAROUNDS)	+= $(call cc-option,-mno-daddi,)

ifdef CONFIG_CPU_SB1
ifdef CONFIG_SB1_PASS_1_WORKAROUNDS
MODFLAGS	+= -msb1-pass1-workarounds
@@ -602,9 +606,11 @@ ifdef CONFIG_64BIT
    endif
  endif

  ifeq ($(KBUILD_SYM32), y)
    ifeq ($(call cc-option-yn,-msym32), y)
  ifeq ($(KBUILD_SYM32)$(call cc-option-yn,-msym32), yy)
    cflags-y += -msym32 -DKBUILD_64BIT_SYM32
  else
    ifeq ($(CONFIG_CPU_DADDI_WORKAROUNDS), y)
      $(error CONFIG_CPU_DADDI_WORKAROUNDS unsupported without -msym32)
    endif
  endif
endif
+23 −24
Original line number Diff line number Diff line
@@ -18,6 +18,15 @@
#include <asm/mipsregs.h>
#include <asm/system.h>

static char bug64hit[] __initdata =
	"reliable operation impossible!\n%s";
static char nowar[] __initdata =
	"Please report to <linux-mips@linux-mips.org>.";
static char r4kwar[] __initdata =
	"Enable CPU_R4000_WORKAROUNDS to rectify.";
static char daddiwar[] __initdata =
	"Enable CPU_DADDI_WORKAROUNDS to rectify.";

static inline void align_mod(const int align, const int mod)
{
	asm volatile(
@@ -155,13 +164,7 @@ static inline void check_mult_sh(void)
	}

	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
	      );
	panic(bug64hit, !R4000_WAR ? r4kwar : nowar);
}

static volatile int daddi_ov __initdata = 0;
@@ -233,15 +236,11 @@ static inline void check_daddi(void)
	}

	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
	      );
	panic(bug64hit, !DADDI_WAR ? daddiwar : nowar);
}

int daddiu_bug __initdata = -1;

static inline void check_daddiu(void)
{
	long v, w, tmp;
@@ -281,7 +280,9 @@ static inline void check_daddiu(void)
		: "=&r" (v), "=&r" (w), "=&r" (tmp)
		: "I" (0xffffffffffffdb9aUL), "I" (0x1234));

	if (v == w) {
	daddiu_bug = v != w;

	if (!daddiu_bug) {
		printk("no.\n");
		return;
	}
@@ -303,18 +304,16 @@ static inline void check_daddiu(void)
	}

	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
	      );
	panic(bug64hit, !DADDI_WAR ? daddiwar : nowar);
}

void __init check_bugs64(void)
void __init check_bugs64_early(void)
{
	check_mult_sh();
	check_daddi();
	check_daddiu();
}

void __init check_bugs64(void)
{
	check_daddi();
}
+3 −1
Original line number Diff line number Diff line
@@ -8,7 +8,7 @@
 * Copyright (C) 1994, 95, 96, 97, 98, 99, 2000, 01, 02, 03  Ralf Baechle
 * Copyright (C) 1996 Stoned Elipot
 * Copyright (C) 1999 Silicon Graphics, Inc.
 * Copyright (C) 2000 2001, 2002  Maciej W. Rozycki
 * Copyright (C) 2000, 2001, 2002, 2007  Maciej W. Rozycki
 */
#include <linux/init.h>
#include <linux/ioport.h>
@@ -24,6 +24,7 @@

#include <asm/addrspace.h>
#include <asm/bootinfo.h>
#include <asm/bugs.h>
#include <asm/cache.h>
#include <asm/cpu.h>
#include <asm/sections.h>
@@ -561,6 +562,7 @@ void __init setup_arch(char **cmdline_p)
	}
#endif
	cpu_report();
	check_bugs_early();

#if defined(CONFIG_VT)
#if defined(CONFIG_VGA_CONSOLE)
+25 −0
Original line number Diff line number Diff line
/*
 * This is included by init/main.c to check for architecture-dependent bugs.
 *
 * Copyright (C) 2007  Maciej W. Rozycki
 *
 * Needs:
 *	void check_bugs(void);
 */
#ifndef _ASM_BUGS_H
#define _ASM_BUGS_H

#include <linux/bug.h>
#include <linux/delay.h>

#include <asm/cpu.h>
#include <asm/cpu-info.h>

extern int daddiu_bug;

extern void check_bugs64_early(void);

extern void check_bugs32(void);
extern void check_bugs64(void);

static inline void check_bugs_early(void)
{
#ifdef CONFIG_64BIT
	check_bugs64_early();
#endif
}

static inline void check_bugs(void)
{
	unsigned int cpu = smp_processor_id();
@@ -25,4 +40,14 @@ static inline void check_bugs(void)
#endif
}

static inline int r4k_daddiu_bug(void)
{
#ifdef CONFIG_64BIT
	WARN_ON(daddiu_bug < 0);
	return daddiu_bug != 0;
#else
	return 0;
#endif
}

#endif /* _ASM_BUGS_H */
Loading