1 /* SPDX-License-Identifier: GPL-2.0 */ 2 #ifndef __ASMARM_TLS_H 3 #define __ASMARM_TLS_H 4 5 #include <linux/compiler.h> 6 #include <asm/thread_info.h> 7 8 #ifdef __ASSEMBLY__ 9 #include <asm/asm-offsets.h> 10 .macro switch_tls_none, base, tp, tpuser, tmp1, tmp2 11 .endm 12 13 .macro switch_tls_v6k, base, tp, tpuser, tmp1, tmp2 14 mrc p15, 0, \tmp2, c13, c0, 2 @ get the user r/w register 15 @ TLS register update is deferred until return to user space 16 mcr p15, 0, \tpuser, c13, c0, 2 @ set the user r/w register 17 str \tmp2, [\base, #TI_TP_VALUE + 4] @ save it 18 .endm 19 20 .macro switch_tls_v6, base, tp, tpuser, tmp1, tmp2 21 ldr \tmp1, =elf_hwcap 22 ldr \tmp1, [\tmp1, #0] 23 mov \tmp2, #0xffff0fff 24 tst \tmp1, #HWCAP_TLS @ hardware TLS available? 25 streq \tp, [\tmp2, #-15] @ set TLS value at 0xffff0ff0 26 mrcne p15, 0, \tmp2, c13, c0, 2 @ get the user r/w register 27 mcrne p15, 0, \tp, c13, c0, 3 @ yes, set TLS register 28 mcrne p15, 0, \tpuser, c13, c0, 2 @ set user r/w register 29 strne \tmp2, [\base, #TI_TP_VALUE + 4] @ save it 30 .endm 31 32 .macro switch_tls_software, base, tp, tpuser, tmp1, tmp2 33 mov \tmp1, #0xffff0fff 34 str \tp, [\tmp1, #-15] @ set TLS value at 0xffff0ff0 35 .endm 36 #endif 37 38 #ifdef CONFIG_TLS_REG_EMUL 39 #define tls_emu 1 40 #define has_tls_reg 1 41 #define defer_tls_reg_update 0 42 #define switch_tls switch_tls_none 43 #elif defined(CONFIG_CPU_V6) 44 #define tls_emu 0 45 #define has_tls_reg (elf_hwcap & HWCAP_TLS) 46 #define defer_tls_reg_update 0 47 #define switch_tls switch_tls_v6 48 #elif defined(CONFIG_CPU_32v6K) 49 #define tls_emu 0 50 #define has_tls_reg 1 51 #define defer_tls_reg_update 1 52 #define switch_tls switch_tls_v6k 53 #else 54 #define tls_emu 0 55 #define has_tls_reg 0 56 #define defer_tls_reg_update 0 57 #define switch_tls switch_tls_software 58 #endif 59 60 #ifndef __ASSEMBLY__ 61 62 static inline void set_tls(unsigned long val) 63 { 64 struct thread_info *thread; 65 66 thread = current_thread_info(); 67 68 thread->tp_value[0] = val; 69 70 /* 71 * This code runs with preemption enabled and therefore must 72 * be reentrant with respect to switch_tls. 73 * 74 * We need to ensure ordering between the shadow state and the 75 * hardware state, so that we don't corrupt the hardware state 76 * with a stale shadow state during context switch. 77 * 78 * If we're preempted here, switch_tls will load TPIDRURO from 79 * thread_info upon resuming execution and the following mcr 80 * is merely redundant. 81 */ 82 barrier(); 83 84 if (!tls_emu && !defer_tls_reg_update) { 85 if (has_tls_reg) { 86 asm("mcr p15, 0, %0, c13, c0, 3" 87 : : "r" (val)); 88 } else { 89 #ifdef CONFIG_KUSER_HELPERS 90 /* 91 * User space must never try to access this 92 * directly. Expect your app to break 93 * eventually if you do so. The user helper 94 * at 0xffff0fe0 must be used instead. (see 95 * entry-armv.S for details) 96 */ 97 *((unsigned int *)0xffff0ff0) = val; 98 #endif 99 } 100 101 } 102 } 103 104 static inline unsigned long get_tpuser(void) 105 { 106 unsigned long reg = 0; 107 108 if (has_tls_reg && !tls_emu) 109 __asm__("mrc p15, 0, %0, c13, c0, 2" : "=r" (reg)); 110 111 return reg; 112 } 113 114 static inline void set_tpuser(unsigned long val) 115 { 116 /* Since TPIDRURW is fully context-switched (unlike TPIDRURO), 117 * we need not update thread_info. 118 */ 119 if (has_tls_reg && !tls_emu) { 120 asm("mcr p15, 0, %0, c13, c0, 2" 121 : : "r" (val)); 122 } 123 } 124 125 static inline void flush_tls(void) 126 { 127 set_tls(0); 128 set_tpuser(0); 129 } 130 131 #endif 132 #endif /* __ASMARM_TLS_H */ 133