summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorGary King <gking@nvidia.com>2010-03-04 21:27:18 -0800
committerGary King <gking@nvidia.com>2010-05-20 13:05:53 -0700
commit74ca02c6b6f74770e1170d84cbb191e3874315de (patch)
tree99f02a56166eb20227855be004805eafc068489c /arch
parentc91bac0014cc43223d00f844575f6a282b0948c8 (diff)
[arm] implement TLS register workaround for Tegra errata 657451
tegra 2 systems have a hardware errata which causes bit 20 of the TLS register (CP15 c13, operations 2-4) to be unreliable. in common user space threading libraries (glibc pthreads, bionic pthreads), the value stored in this register is guaranteed to be at least word-aligned, leaving bit 0 free. the work-around for this hardware errata is storing bit 20 of the user space-provided TLS value into bit 0 of the register inside __set_tls, and restoring it in the get_tls helper.
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/Kconfig13
-rw-r--r--arch/arm/kernel/entry-armv.S12
-rw-r--r--arch/arm/kernel/traps.c6
3 files changed, 31 insertions, 0 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index a76d199332ac..bf425028412e 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -862,6 +862,19 @@ if !MMU
source "arch/arm/Kconfig-nommu"
endif
+config TEGRA_ERRATA_657451
+ bool "Store bit 20 of CP15 TLS register c13,3 in bit 0"
+ depends on ARCH_TEGRA_2x_SOC && HAS_TLS_REG
+ default y
+ help
+ This option enables a workaround for the 657451 Tegra 2 errata
+ which causes unreliable reads from bit 20 of the thread storage
+ register c13, 3 (used by the __set_tls system call). It stores the
+ value intended for bit 20 into bit 0 instead; in most user-space
+ environments, the value saved by __set_tls is an aligned address,
+ so repurposing bit 0 will not cause ill effects.
+
+
config ARM_ERRATA_411920
bool "ARM errata: Invalidation of the Instruction Cache operation can fail"
depends on CPU_V6 && !SMP
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index d2903e3bc861..3342aeae6281 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -740,6 +740,10 @@ ENTRY(__switch_to)
ldr r6, [r2, #TI_CPU_DOMAIN]
#endif
#if defined(CONFIG_HAS_TLS_REG)
+#if defined(CONFIG_TEGRA_ERRATA_657451)
+ and r4, r3, #(1<<20)
+ orr r3, r3, r4, lsr #20
+#endif
mcr p15, 0, r3, c13, c0, 3 @ set TLS register
#elif !defined(CONFIG_TLS_REG_EMUL)
mov r4, #0xffff0fff
@@ -1016,10 +1020,18 @@ __kuser_get_tls: @ 0xffff0fe0
ldr r0, [pc, #(16 - 8)] @ TLS stored at 0xffff0ff0
#else
mrc p15, 0, r0, c13, c0, 3 @ read TLS register
+#if defined(CONFIG_TEGRA_ERRATA_657451)
+ bfi r0, r0, #20, #1
+ bic r0, r0, #1
+#endif
#endif
usr_ret lr
+#if defined(CONFIG_TEGRA_ERRATA_657451)
+ .rep 3
+#else
.rep 5
+#endif
.word 0 @ pad up to __kuser_helper_version
.endr
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index 3f361a783f43..2e930f3f9718 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -504,7 +504,13 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)
case NR(set_tls):
thread->tp_value = regs->ARM_r0;
#if defined(CONFIG_HAS_TLS_REG)
+#if defined(CONFIG_TEGRA_ERRATA_657451)
+ BUG_ON(regs->ARM_r0 & 0x1);
+ asm ("mcr p15, 0, %0, c13, c0, 3" : :
+ "r" ((regs->ARM_r0) | ((regs->ARM_r0>>20) & 0x1)));
+#else
asm ("mcr p15, 0, %0, c13, c0, 3" : : "r" (regs->ARM_r0) );
+#endif
#elif !defined(CONFIG_TLS_REG_EMUL)
/*
* User space must never try to access this directly.