linux/arch/arm/include/asm/tls.h
<<
>>
Prefs
   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
  62static 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
 104static 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
 114static 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
 125static inline void flush_tls(void)
 126{
 127        set_tls(0);
 128        set_tpuser(0);
 129}
 130
 131#endif
 132#endif  /* __ASMARM_TLS_H */
 133