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        mcr     p15, 0, \tp, c13, c0, 3         @ set TLS register
  16        mcr     p15, 0, \tpuser, c13, c0, 2     @ and 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 switch_tls      switch_tls_none
  42#elif defined(CONFIG_CPU_V6)
  43#define tls_emu         0
  44#define has_tls_reg             (elf_hwcap & HWCAP_TLS)
  45#define switch_tls      switch_tls_v6
  46#elif defined(CONFIG_CPU_32v6K)
  47#define tls_emu         0
  48#define has_tls_reg             1
  49#define switch_tls      switch_tls_v6k
  50#else
  51#define tls_emu         0
  52#define has_tls_reg             0
  53#define switch_tls      switch_tls_software
  54#endif
  55
  56#ifndef __ASSEMBLY__
  57
  58static inline void set_tls(unsigned long val)
  59{
  60        struct thread_info *thread;
  61
  62        thread = current_thread_info();
  63
  64        thread->tp_value[0] = val;
  65
  66        /*
  67         * This code runs with preemption enabled and therefore must
  68         * be reentrant with respect to switch_tls.
  69         *
  70         * We need to ensure ordering between the shadow state and the
  71         * hardware state, so that we don't corrupt the hardware state
  72         * with a stale shadow state during context switch.
  73         *
  74         * If we're preempted here, switch_tls will load TPIDRURO from
  75         * thread_info upon resuming execution and the following mcr
  76         * is merely redundant.
  77         */
  78        barrier();
  79
  80        if (!tls_emu) {
  81                if (has_tls_reg) {
  82                        asm("mcr p15, 0, %0, c13, c0, 3"
  83                            : : "r" (val));
  84                } else {
  85#ifdef CONFIG_KUSER_HELPERS
  86                        /*
  87                         * User space must never try to access this
  88                         * directly.  Expect your app to break
  89                         * eventually if you do so.  The user helper
  90                         * at 0xffff0fe0 must be used instead.  (see
  91                         * entry-armv.S for details)
  92                         */
  93                        *((unsigned int *)0xffff0ff0) = val;
  94#endif
  95                }
  96
  97        }
  98}
  99
 100static inline unsigned long get_tpuser(void)
 101{
 102        unsigned long reg = 0;
 103
 104        if (has_tls_reg && !tls_emu)
 105                __asm__("mrc p15, 0, %0, c13, c0, 2" : "=r" (reg));
 106
 107        return reg;
 108}
 109
 110static inline void set_tpuser(unsigned long val)
 111{
 112        /* Since TPIDRURW is fully context-switched (unlike TPIDRURO),
 113         * we need not update thread_info.
 114         */
 115        if (has_tls_reg && !tls_emu) {
 116                asm("mcr p15, 0, %0, c13, c0, 2"
 117                    : : "r" (val));
 118        }
 119}
 120
 121static inline void flush_tls(void)
 122{
 123        set_tls(0);
 124        set_tpuser(0);
 125}
 126
 127#endif
 128#endif  /* __ASMARM_TLS_H */
 129