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