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