Skip to content

Commit 50596b7

Browse files
committed
ARM: smp: Store current pointer in TPIDRURO register if available
Now that the user space TLS register is assigned on every return to user space, we can use it to keep the 'current' pointer while running in the kernel. This removes the need to access it via thread_info, which is located at the base of the stack, but will be moved out of there in a subsequent patch. Use the __builtin_thread_pointer() helper when available - this will help GCC understand that reloading the value within the same function is not necessary, even when using the per-task stack protector (which also generates accesses via the TLS register). For example, the generated code below loads TPIDRURO only once, and uses it to access both the stack canary and the preempt_count fields. <do_one_initcall>: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr} ee1d 4f70 mrc 15, 0, r4, cr13, cr0, {3} 4606 mov r6, r0 b094 sub sp, #80 ; 0x50 f8d4 34e8 ldr.w r3, [r4, torvalds#1256] ; 0x4e8 <- stack canary 9313 str r3, [sp, #76] ; 0x4c f8d4 8004 ldr.w r8, [r4, #4] <- preempt count Co-developed-by: Keith Packard <[email protected]> Signed-off-by: Keith Packard <[email protected]> Signed-off-by: Ard Biesheuvel <[email protected]> Reviewed-by: Linus Walleij <[email protected]> Tested-by: Amit Daniel Kachhap <[email protected]>
1 parent 3855ab6 commit 50596b7

File tree

12 files changed

+105
-2
lines changed

12 files changed

+105
-2
lines changed

arch/arm/Kconfig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,6 +1157,11 @@ config SMP_ON_UP
11571157

11581158
If you don't know what to do here, say Y.
11591159

1160+
1161+
config CURRENT_POINTER_IN_TPIDRURO
1162+
def_bool y
1163+
depends on SMP && CPU_32v6K && !CPU_V6
1164+
11601165
config ARM_CPU_TOPOLOGY
11611166
bool "Support cpu topology definition"
11621167
depends on SMP && CPU_V7

arch/arm/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ ifeq ($(CONFIG_CC_IS_CLANG),y)
113113
CFLAGS_ABI += -meabi gnu
114114
endif
115115

116+
ifeq ($(CONFIG_CURRENT_POINTER_IN_TPIDRURO),y)
117+
CFLAGS_ABI += -mtp=cp15
118+
endif
119+
116120
# Accept old syntax despite ".syntax unified"
117121
AFLAGS_NOWARN :=$(call as-option,-Wa$(comma)-mno-warn-deprecated,-Wa$(comma)-W)
118122

arch/arm/include/asm/assembler.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,30 @@
199199
.endm
200200
.endr
201201

202+
.macro get_current, rd
203+
#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
204+
mrc p15, 0, \rd, c13, c0, 3 @ get TPIDRURO register
205+
#else
206+
get_thread_info \rd
207+
ldr \rd, [\rd, #TI_TASK]
208+
#endif
209+
.endm
210+
211+
.macro set_current, rn
212+
#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
213+
mcr p15, 0, \rn, c13, c0, 3 @ set TPIDRURO register
214+
#endif
215+
.endm
216+
217+
.macro reload_current, t1:req, t2:req
218+
#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
219+
adr_l \t1, __entry_task @ get __entry_task base address
220+
mrc p15, 0, \t2, c13, c0, 4 @ get per-CPU offset
221+
ldr \t1, [\t1, \t2] @ load variable
222+
mcr p15, 0, \t1, c13, c0, 3 @ store in TPIDRURO
223+
#endif
224+
.endm
225+
202226
/*
203227
* Get current thread_info.
204228
*/

arch/arm/include/asm/current.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/*
3+
* Copyright (c) 2021 Keith Packard <[email protected]>
4+
* Copyright (c) 2021 Google, LLC <[email protected]>
5+
*/
6+
7+
#ifndef _ASM_ARM_CURRENT_H
8+
#define _ASM_ARM_CURRENT_H
9+
10+
#ifndef __ASSEMBLY__
11+
12+
struct task_struct;
13+
14+
static inline void set_current(struct task_struct *cur)
15+
{
16+
if (!IS_ENABLED(CONFIG_CURRENT_POINTER_IN_TPIDRURO))
17+
return;
18+
19+
/* Set TPIDRURO */
20+
asm("mcr p15, 0, %0, c13, c0, 3" :: "r"(cur) : "memory");
21+
}
22+
23+
#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
24+
25+
static inline struct task_struct *get_current(void)
26+
{
27+
struct task_struct *cur;
28+
29+
#if __has_builtin(__builtin_thread_pointer)
30+
/*
31+
* Use the __builtin helper when available - this results in better
32+
* code, especially when using GCC in combination with the per-task
33+
* stack protector, as the compiler will recognize that it needs to
34+
* load the TLS register only once in every function.
35+
*/
36+
cur = __builtin_thread_pointer();
37+
#else
38+
asm("mrc p15, 0, %0, c13, c0, 3" : "=r"(cur));
39+
#endif
40+
return cur;
41+
}
42+
43+
#define current get_current()
44+
#else
45+
#include <asm-generic/current.h>
46+
#endif /* CONFIG_CURRENT_POINTER_IN_TPIDRURO */
47+
48+
#endif /* __ASSEMBLY__ */
49+
50+
#endif /* _ASM_ARM_CURRENT_H */

arch/arm/include/asm/switch_to.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ extern struct task_struct *__switch_to(struct task_struct *, struct thread_info
2626
#define switch_to(prev,next,last) \
2727
do { \
2828
__complete_pending_tlbi(); \
29+
if (IS_ENABLED(CONFIG_CURRENT_POINTER_IN_TPIDRURO)) \
30+
__this_cpu_write(__entry_task, next); \
2931
last = __switch_to(prev,task_thread_info(prev), task_thread_info(next)); \
3032
} while (0)
3133

arch/arm/include/asm/thread_info.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929

3030
struct task_struct;
3131

32+
DECLARE_PER_CPU(struct task_struct *, __entry_task);
33+
3234
#include <asm/types.h>
3335

3436
struct cpu_context_save {

arch/arm/kernel/entry-armv.S

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,8 @@ ENDPROC(__fiq_abt)
384384
ATRAP( teq r8, r7)
385385
ATRAP( mcrne p15, 0, r8, c1, c0, 0)
386386

387+
reload_current r7, r8
388+
387389
@
388390
@ Clear FP to mark the first stack frame
389391
@
@@ -762,6 +764,8 @@ ENTRY(__switch_to)
762764
add r7, r7, #TSK_STACK_CANARY & ~IMM12_MASK
763765
.endif
764766
ldr r7, [r7, #TSK_STACK_CANARY & IMM12_MASK]
767+
#elif defined(CONFIG_CURRENT_POINTER_IN_TPIDRURO)
768+
ldr r7, [r2, #TI_TASK]
765769
#endif
766770
#ifdef CONFIG_CPU_USE_DOMAINS
767771
mcr p15, 0, r6, c3, c0, 0 @ Set domain register
@@ -776,6 +780,7 @@ ENTRY(__switch_to)
776780
#endif
777781
THUMB( mov ip, r4 )
778782
mov r0, r5
783+
set_current r7
779784
ARM( ldmia r4, {r4 - sl, fp, sp, pc} ) @ Load all regs saved previously
780785
THUMB( ldmia ip!, {r4 - sl, fp} ) @ Load all regs saved previously
781786
THUMB( ldr sp, [ip], #4 )

arch/arm/kernel/entry-common.S

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ ENTRY(vector_swi)
170170
str saved_psr, [sp, #S_PSR] @ Save CPSR
171171
str r0, [sp, #S_OLD_R0] @ Save OLD_R0
172172
#endif
173+
reload_current r10, ip
173174
zero_fp
174175
alignment_trap r10, ip, __cr_alignment
175176
asm_trace_hardirqs_on save=0

arch/arm/kernel/head-common.S

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@ __mmap_switched:
105105
mov r1, #0
106106
bl __memset @ clear .bss
107107

108+
#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
109+
adr_l r0, init_task @ get swapper task_struct
110+
set_current r0
111+
#endif
112+
108113
ldmia r4, {r0, r1, r2, r3}
109114
str r9, [r0] @ Save processor ID
110115
str r7, [r1] @ Save machine type

arch/arm/kernel/process.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636

3737
#include "signal.h"
3838

39+
#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
40+
DEFINE_PER_CPU(struct task_struct *, __entry_task);
41+
#endif
42+
3943
#if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_STACKPROTECTOR_PER_TASK)
4044
#include <linux/stackprotector.h>
4145
unsigned long __stack_chk_guard __read_mostly;

0 commit comments

Comments
 (0)