162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci#ifndef __ASMARM_TLS_H 362306a36Sopenharmony_ci#define __ASMARM_TLS_H 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/compiler.h> 662306a36Sopenharmony_ci#include <asm/thread_info.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#ifdef __ASSEMBLY__ 962306a36Sopenharmony_ci#include <asm/asm-offsets.h> 1062306a36Sopenharmony_ci .macro switch_tls_none, base, tp, tpuser, tmp1, tmp2 1162306a36Sopenharmony_ci .endm 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci .macro switch_tls_v6k, base, tp, tpuser, tmp1, tmp2 1462306a36Sopenharmony_ci mrc p15, 0, \tmp2, c13, c0, 2 @ get the user r/w register 1562306a36Sopenharmony_ci @ TLS register update is deferred until return to user space 1662306a36Sopenharmony_ci mcr p15, 0, \tpuser, c13, c0, 2 @ set the user r/w register 1762306a36Sopenharmony_ci str \tmp2, [\base, #TI_TP_VALUE + 4] @ save it 1862306a36Sopenharmony_ci .endm 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci .macro switch_tls_v6, base, tp, tpuser, tmp1, tmp2 2162306a36Sopenharmony_ci#ifdef CONFIG_SMP 2262306a36Sopenharmony_ciALT_SMP(nop) 2362306a36Sopenharmony_ciALT_UP_B(.L0_\@) 2462306a36Sopenharmony_ci .subsection 1 2562306a36Sopenharmony_ci#endif 2662306a36Sopenharmony_ci.L0_\@: 2762306a36Sopenharmony_ci ldr_va \tmp1, elf_hwcap 2862306a36Sopenharmony_ci mov \tmp2, #0xffff0fff 2962306a36Sopenharmony_ci tst \tmp1, #HWCAP_TLS @ hardware TLS available? 3062306a36Sopenharmony_ci streq \tp, [\tmp2, #-15] @ set TLS value at 0xffff0ff0 3162306a36Sopenharmony_ci beq .L2_\@ 3262306a36Sopenharmony_ci mcr p15, 0, \tp, c13, c0, 3 @ yes, set TLS register 3362306a36Sopenharmony_ci#ifdef CONFIG_SMP 3462306a36Sopenharmony_ci b .L1_\@ 3562306a36Sopenharmony_ci .previous 3662306a36Sopenharmony_ci#endif 3762306a36Sopenharmony_ci.L1_\@: switch_tls_v6k \base, \tp, \tpuser, \tmp1, \tmp2 3862306a36Sopenharmony_ci.L2_\@: 3962306a36Sopenharmony_ci .endm 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci .macro switch_tls_software, base, tp, tpuser, tmp1, tmp2 4262306a36Sopenharmony_ci mov \tmp1, #0xffff0fff 4362306a36Sopenharmony_ci str \tp, [\tmp1, #-15] @ set TLS value at 0xffff0ff0 4462306a36Sopenharmony_ci .endm 4562306a36Sopenharmony_ci#else 4662306a36Sopenharmony_ci#include <asm/smp_plat.h> 4762306a36Sopenharmony_ci#endif 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#ifdef CONFIG_TLS_REG_EMUL 5062306a36Sopenharmony_ci#define tls_emu 1 5162306a36Sopenharmony_ci#define has_tls_reg 1 5262306a36Sopenharmony_ci#define defer_tls_reg_update 0 5362306a36Sopenharmony_ci#define switch_tls switch_tls_none 5462306a36Sopenharmony_ci#elif defined(CONFIG_CPU_V6) 5562306a36Sopenharmony_ci#define tls_emu 0 5662306a36Sopenharmony_ci#define has_tls_reg (elf_hwcap & HWCAP_TLS) 5762306a36Sopenharmony_ci#define defer_tls_reg_update is_smp() 5862306a36Sopenharmony_ci#define switch_tls switch_tls_v6 5962306a36Sopenharmony_ci#elif defined(CONFIG_CPU_32v6K) 6062306a36Sopenharmony_ci#define tls_emu 0 6162306a36Sopenharmony_ci#define has_tls_reg 1 6262306a36Sopenharmony_ci#define defer_tls_reg_update 1 6362306a36Sopenharmony_ci#define switch_tls switch_tls_v6k 6462306a36Sopenharmony_ci#else 6562306a36Sopenharmony_ci#define tls_emu 0 6662306a36Sopenharmony_ci#define has_tls_reg 0 6762306a36Sopenharmony_ci#define defer_tls_reg_update 0 6862306a36Sopenharmony_ci#define switch_tls switch_tls_software 6962306a36Sopenharmony_ci#endif 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#ifndef __ASSEMBLY__ 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic inline void set_tls(unsigned long val) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct thread_info *thread; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci thread = current_thread_info(); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci thread->tp_value[0] = val; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* 8262306a36Sopenharmony_ci * This code runs with preemption enabled and therefore must 8362306a36Sopenharmony_ci * be reentrant with respect to switch_tls. 8462306a36Sopenharmony_ci * 8562306a36Sopenharmony_ci * We need to ensure ordering between the shadow state and the 8662306a36Sopenharmony_ci * hardware state, so that we don't corrupt the hardware state 8762306a36Sopenharmony_ci * with a stale shadow state during context switch. 8862306a36Sopenharmony_ci * 8962306a36Sopenharmony_ci * If we're preempted here, switch_tls will load TPIDRURO from 9062306a36Sopenharmony_ci * thread_info upon resuming execution and the following mcr 9162306a36Sopenharmony_ci * is merely redundant. 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_ci barrier(); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (!tls_emu) { 9662306a36Sopenharmony_ci if (has_tls_reg && !defer_tls_reg_update) { 9762306a36Sopenharmony_ci asm("mcr p15, 0, %0, c13, c0, 3" 9862306a36Sopenharmony_ci : : "r" (val)); 9962306a36Sopenharmony_ci } else if (!has_tls_reg) { 10062306a36Sopenharmony_ci#ifdef CONFIG_KUSER_HELPERS 10162306a36Sopenharmony_ci /* 10262306a36Sopenharmony_ci * User space must never try to access this 10362306a36Sopenharmony_ci * directly. Expect your app to break 10462306a36Sopenharmony_ci * eventually if you do so. The user helper 10562306a36Sopenharmony_ci * at 0xffff0fe0 must be used instead. (see 10662306a36Sopenharmony_ci * entry-armv.S for details) 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ci *((unsigned int *)0xffff0ff0) = val; 10962306a36Sopenharmony_ci#endif 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic inline unsigned long get_tpuser(void) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci unsigned long reg = 0; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (has_tls_reg && !tls_emu) 12062306a36Sopenharmony_ci __asm__("mrc p15, 0, %0, c13, c0, 2" : "=r" (reg)); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return reg; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic inline void set_tpuser(unsigned long val) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci /* Since TPIDRURW is fully context-switched (unlike TPIDRURO), 12862306a36Sopenharmony_ci * we need not update thread_info. 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_ci if (has_tls_reg && !tls_emu) { 13162306a36Sopenharmony_ci asm("mcr p15, 0, %0, c13, c0, 2" 13262306a36Sopenharmony_ci : : "r" (val)); 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic inline void flush_tls(void) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci set_tls(0); 13962306a36Sopenharmony_ci set_tpuser(0); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci#endif 14362306a36Sopenharmony_ci#endif /* __ASMARM_TLS_H */ 144