18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci#ifndef __ASMARM_TLS_H
38c2ecf20Sopenharmony_ci#define __ASMARM_TLS_H
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <linux/compiler.h>
68c2ecf20Sopenharmony_ci#include <asm/thread_info.h>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#ifdef __ASSEMBLY__
98c2ecf20Sopenharmony_ci#include <asm/asm-offsets.h>
108c2ecf20Sopenharmony_ci	.macro switch_tls_none, base, tp, tpuser, tmp1, tmp2
118c2ecf20Sopenharmony_ci	.endm
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci	.macro switch_tls_v6k, base, tp, tpuser, tmp1, tmp2
148c2ecf20Sopenharmony_ci	mrc	p15, 0, \tmp2, c13, c0, 2	@ get the user r/w register
158c2ecf20Sopenharmony_ci	mcr	p15, 0, \tp, c13, c0, 3		@ set TLS register
168c2ecf20Sopenharmony_ci	mcr	p15, 0, \tpuser, c13, c0, 2	@ and the user r/w register
178c2ecf20Sopenharmony_ci	str	\tmp2, [\base, #TI_TP_VALUE + 4] @ save it
188c2ecf20Sopenharmony_ci	.endm
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	.macro switch_tls_v6, base, tp, tpuser, tmp1, tmp2
218c2ecf20Sopenharmony_ci	ldr	\tmp1, =elf_hwcap
228c2ecf20Sopenharmony_ci	ldr	\tmp1, [\tmp1, #0]
238c2ecf20Sopenharmony_ci	mov	\tmp2, #0xffff0fff
248c2ecf20Sopenharmony_ci	tst	\tmp1, #HWCAP_TLS		@ hardware TLS available?
258c2ecf20Sopenharmony_ci	streq	\tp, [\tmp2, #-15]		@ set TLS value at 0xffff0ff0
268c2ecf20Sopenharmony_ci	mrcne	p15, 0, \tmp2, c13, c0, 2	@ get the user r/w register
278c2ecf20Sopenharmony_ci	mcrne	p15, 0, \tp, c13, c0, 3		@ yes, set TLS register
288c2ecf20Sopenharmony_ci	mcrne	p15, 0, \tpuser, c13, c0, 2	@ set user r/w register
298c2ecf20Sopenharmony_ci	strne	\tmp2, [\base, #TI_TP_VALUE + 4] @ save it
308c2ecf20Sopenharmony_ci	.endm
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	.macro switch_tls_software, base, tp, tpuser, tmp1, tmp2
338c2ecf20Sopenharmony_ci	mov	\tmp1, #0xffff0fff
348c2ecf20Sopenharmony_ci	str	\tp, [\tmp1, #-15]		@ set TLS value at 0xffff0ff0
358c2ecf20Sopenharmony_ci	.endm
368c2ecf20Sopenharmony_ci#endif
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#ifdef CONFIG_TLS_REG_EMUL
398c2ecf20Sopenharmony_ci#define tls_emu		1
408c2ecf20Sopenharmony_ci#define has_tls_reg		1
418c2ecf20Sopenharmony_ci#define switch_tls	switch_tls_none
428c2ecf20Sopenharmony_ci#elif defined(CONFIG_CPU_V6)
438c2ecf20Sopenharmony_ci#define tls_emu		0
448c2ecf20Sopenharmony_ci#define has_tls_reg		(elf_hwcap & HWCAP_TLS)
458c2ecf20Sopenharmony_ci#define switch_tls	switch_tls_v6
468c2ecf20Sopenharmony_ci#elif defined(CONFIG_CPU_32v6K)
478c2ecf20Sopenharmony_ci#define tls_emu		0
488c2ecf20Sopenharmony_ci#define has_tls_reg		1
498c2ecf20Sopenharmony_ci#define switch_tls	switch_tls_v6k
508c2ecf20Sopenharmony_ci#else
518c2ecf20Sopenharmony_ci#define tls_emu		0
528c2ecf20Sopenharmony_ci#define has_tls_reg		0
538c2ecf20Sopenharmony_ci#define switch_tls	switch_tls_software
548c2ecf20Sopenharmony_ci#endif
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#ifndef __ASSEMBLY__
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic inline void set_tls(unsigned long val)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	struct thread_info *thread;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	thread = current_thread_info();
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	thread->tp_value[0] = val;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	/*
678c2ecf20Sopenharmony_ci	 * This code runs with preemption enabled and therefore must
688c2ecf20Sopenharmony_ci	 * be reentrant with respect to switch_tls.
698c2ecf20Sopenharmony_ci	 *
708c2ecf20Sopenharmony_ci	 * We need to ensure ordering between the shadow state and the
718c2ecf20Sopenharmony_ci	 * hardware state, so that we don't corrupt the hardware state
728c2ecf20Sopenharmony_ci	 * with a stale shadow state during context switch.
738c2ecf20Sopenharmony_ci	 *
748c2ecf20Sopenharmony_ci	 * If we're preempted here, switch_tls will load TPIDRURO from
758c2ecf20Sopenharmony_ci	 * thread_info upon resuming execution and the following mcr
768c2ecf20Sopenharmony_ci	 * is merely redundant.
778c2ecf20Sopenharmony_ci	 */
788c2ecf20Sopenharmony_ci	barrier();
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if (!tls_emu) {
818c2ecf20Sopenharmony_ci		if (has_tls_reg) {
828c2ecf20Sopenharmony_ci			asm("mcr p15, 0, %0, c13, c0, 3"
838c2ecf20Sopenharmony_ci			    : : "r" (val));
848c2ecf20Sopenharmony_ci		} else {
858c2ecf20Sopenharmony_ci#ifdef CONFIG_KUSER_HELPERS
868c2ecf20Sopenharmony_ci			/*
878c2ecf20Sopenharmony_ci			 * User space must never try to access this
888c2ecf20Sopenharmony_ci			 * directly.  Expect your app to break
898c2ecf20Sopenharmony_ci			 * eventually if you do so.  The user helper
908c2ecf20Sopenharmony_ci			 * at 0xffff0fe0 must be used instead.  (see
918c2ecf20Sopenharmony_ci			 * entry-armv.S for details)
928c2ecf20Sopenharmony_ci			 */
938c2ecf20Sopenharmony_ci			*((unsigned int *)0xffff0ff0) = val;
948c2ecf20Sopenharmony_ci#endif
958c2ecf20Sopenharmony_ci		}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic inline unsigned long get_tpuser(void)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	unsigned long reg = 0;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	if (has_tls_reg && !tls_emu)
1058c2ecf20Sopenharmony_ci		__asm__("mrc p15, 0, %0, c13, c0, 2" : "=r" (reg));
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	return reg;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic inline void set_tpuser(unsigned long val)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	/* Since TPIDRURW is fully context-switched (unlike TPIDRURO),
1138c2ecf20Sopenharmony_ci	 * we need not update thread_info.
1148c2ecf20Sopenharmony_ci	 */
1158c2ecf20Sopenharmony_ci	if (has_tls_reg && !tls_emu) {
1168c2ecf20Sopenharmony_ci		asm("mcr p15, 0, %0, c13, c0, 2"
1178c2ecf20Sopenharmony_ci		    : : "r" (val));
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic inline void flush_tls(void)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	set_tls(0);
1248c2ecf20Sopenharmony_ci	set_tpuser(0);
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci#endif
1288c2ecf20Sopenharmony_ci#endif	/* __ASMARM_TLS_H */
129