ARM: smp: Free up the TLS register while running in the kernel
authorArd Biesheuvel <ardb@kernel.org>
Sat, 18 Sep 2021 08:44:36 +0000 (10:44 +0200)
committerArd Biesheuvel <ardb@kernel.org>
Mon, 27 Sep 2021 14:54:02 +0000 (16:54 +0200)
To prepare for a subsequent patch that stores the current task pointer
in the user space TLS register while running in the kernel, modify the
set_tls and switch_tls routines not to touch the register directly, and
update the return to user space code to load the correct value.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Tested-by: Amit Daniel Kachhap <amit.kachhap@arm.com>
arch/arm/include/asm/tls.h
arch/arm/kernel/entry-header.S

index 5a66c3b13c92f2fec67a32b61640aface3cedc1b..c3296499176cec290e89b9bab5ebc6bfeaa99096 100644 (file)
@@ -12,8 +12,8 @@
 
        .macro switch_tls_v6k, base, tp, tpuser, tmp1, tmp2
        mrc     p15, 0, \tmp2, c13, c0, 2       @ get the user r/w register
-       mcr     p15, 0, \tp, c13, c0, 3         @ set TLS register
-       mcr     p15, 0, \tpuser, c13, c0, 2     @ and the user r/w register
+       @ TLS register update is deferred until return to user space
+       mcr     p15, 0, \tpuser, c13, c0, 2     @ set the user r/w register
        str     \tmp2, [\base, #TI_TP_VALUE + 4] @ save it
        .endm
 
 #ifdef CONFIG_TLS_REG_EMUL
 #define tls_emu                1
 #define has_tls_reg            1
+#define defer_tls_reg_update   0
 #define switch_tls     switch_tls_none
 #elif defined(CONFIG_CPU_V6)
 #define tls_emu                0
 #define has_tls_reg            (elf_hwcap & HWCAP_TLS)
+#define defer_tls_reg_update   0
 #define switch_tls     switch_tls_v6
 #elif defined(CONFIG_CPU_32v6K)
 #define tls_emu                0
 #define has_tls_reg            1
+#define defer_tls_reg_update   1
 #define switch_tls     switch_tls_v6k
 #else
 #define tls_emu                0
 #define has_tls_reg            0
+#define defer_tls_reg_update   0
 #define switch_tls     switch_tls_software
 #endif
 
@@ -77,7 +81,7 @@ static inline void set_tls(unsigned long val)
         */
        barrier();
 
-       if (!tls_emu) {
+       if (!tls_emu && !defer_tls_reg_update) {
                if (has_tls_reg) {
                        asm("mcr p15, 0, %0, c13, c0, 3"
                            : : "r" (val));
index 40db0f9188b69e9e4323405c8c44e9a3fe153890..ae24dd54e9efbd247b7d5c875eaaedcef3a2d195 100644 (file)
 
 
        .macro  restore_user_regs, fast = 0, offset = 0
+#if defined(CONFIG_CPU_32v6K) && !defined(CONFIG_CPU_V6)
+       @ The TLS register update is deferred until return to user space so we
+       @ can use it for other things while running in the kernel
+       get_thread_info r1
+       ldr     r1, [r1, #TI_TP_VALUE]
+       mcr     p15, 0, r1, c13, c0, 3          @ set TLS register
+#endif
+
        uaccess_enable r1, isb=0
 #ifndef CONFIG_THUMB2_KERNEL
        @ ARM mode restore