diff options
author | Gary King <gking@nvidia.com> | 2010-03-04 21:27:18 -0800 |
---|---|---|
committer | Gary King <gking@nvidia.com> | 2010-05-20 13:05:53 -0700 |
commit | 74ca02c6b6f74770e1170d84cbb191e3874315de (patch) | |
tree | 99f02a56166eb20227855be004805eafc068489c /arch | |
parent | c91bac0014cc43223d00f844575f6a282b0948c8 (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/Kconfig | 13 | ||||
-rw-r--r-- | arch/arm/kernel/entry-armv.S | 12 | ||||
-rw-r--r-- | arch/arm/kernel/traps.c | 6 |
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. |