Skip to content

Commit

Permalink
Merge tag 'pr-arm32-ti-in-task' of git://git.kernel.org/pub/scm/linux…
Browse files Browse the repository at this point in the history
…/kernel/git/ardb/linux into devel-stable

ARM: support THREAD_INFO_IN_TASK

Move thread_info off the stack and into the task struct, as is done by
many other architectures. This requires a method to find the task struct
of the task currently running on the CPU, which is provided in this case
by the user space TLS (Thread Local Storage) register. This implies that
the feature is only supported on CPUs that implement this register,
i.e., ARM v6k or later.

Kindly tested by Amit and reviewed by Linus. The first patch is against
the GCC plugins subsystem, but was reviewed by the maintainer and can be
taken through the ARM tree.
  • Loading branch information
Russell King (Oracle) committed Oct 19, 2021
2 parents 6880fa6 + 18ed1c0 commit 13a695a
Show file tree
Hide file tree
Showing 20 changed files with 174 additions and 50 deletions.
8 changes: 7 additions & 1 deletion arch/arm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ config ARM
select PERF_USE_VMALLOC
select RTC_LIB
select SYS_SUPPORTS_APM_EMULATION
select THREAD_INFO_IN_TASK if CURRENT_POINTER_IN_TPIDRURO
select TRACE_IRQFLAGS_SUPPORT if !CPU_V7M
# Above selects are sorted alphabetically; please add new ones
# according to that. Thanks.
Expand Down Expand Up @@ -1157,6 +1158,11 @@ config SMP_ON_UP

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


config CURRENT_POINTER_IN_TPIDRURO
def_bool y
depends on SMP && CPU_32v6K && !CPU_V6

config ARM_CPU_TOPOLOGY
bool "Support cpu topology definition"
depends on SMP && CPU_V7
Expand Down Expand Up @@ -1600,7 +1606,7 @@ config XEN

config STACKPROTECTOR_PER_TASK
bool "Use a unique stack canary value for each task"
depends on GCC_PLUGINS && STACKPROTECTOR && SMP && !XIP_DEFLATED_DATA
depends on GCC_PLUGINS && STACKPROTECTOR && THREAD_INFO_IN_TASK && !XIP_DEFLATED_DATA
select GCC_PLUGIN_ARM_SSP_PER_TASK
default y
help
Expand Down
9 changes: 5 additions & 4 deletions arch/arm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ ifeq ($(CONFIG_CC_IS_CLANG),y)
CFLAGS_ABI += -meabi gnu
endif

ifeq ($(CONFIG_CURRENT_POINTER_IN_TPIDRURO),y)
CFLAGS_ABI += -mtp=cp15
endif

# Accept old syntax despite ".syntax unified"
AFLAGS_NOWARN :=$(call as-option,-Wa$(comma)-mno-warn-deprecated,-Wa$(comma)-W)

Expand Down Expand Up @@ -273,11 +277,8 @@ ifeq ($(CONFIG_STACKPROTECTOR_PER_TASK),y)
prepare: stack_protector_prepare
stack_protector_prepare: prepare0
$(eval SSP_PLUGIN_CFLAGS := \
-fplugin-arg-arm_ssp_per_task_plugin-tso=$(shell \
awk '{if ($$2 == "THREAD_SZ_ORDER") print $$3;}'\
include/generated/asm-offsets.h) \
-fplugin-arg-arm_ssp_per_task_plugin-offset=$(shell \
awk '{if ($$2 == "TI_STACK_CANARY") print $$3;}'\
awk '{if ($$2 == "TSK_STACK_CANARY") print $$3;}'\
include/generated/asm-offsets.h))
$(eval KBUILD_CFLAGS += $(SSP_PLUGIN_CFLAGS))
$(eval GCC_PLUGINS_CFLAGS += $(SSP_PLUGIN_CFLAGS))
Expand Down
29 changes: 29 additions & 0 deletions arch/arm/include/asm/assembler.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,14 +199,43 @@
.endm
.endr

.macro get_current, rd
#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
mrc p15, 0, \rd, c13, c0, 3 @ get TPIDRURO register
#else
get_thread_info \rd
ldr \rd, [\rd, #TI_TASK]
#endif
.endm

.macro set_current, rn
#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
mcr p15, 0, \rn, c13, c0, 3 @ set TPIDRURO register
#endif
.endm

.macro reload_current, t1:req, t2:req
#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
adr_l \t1, __entry_task @ get __entry_task base address
mrc p15, 0, \t2, c13, c0, 4 @ get per-CPU offset
ldr \t1, [\t1, \t2] @ load variable
mcr p15, 0, \t1, c13, c0, 3 @ store in TPIDRURO
#endif
.endm

/*
* Get current thread_info.
*/
.macro get_thread_info, rd
#ifdef CONFIG_THREAD_INFO_IN_TASK
/* thread_info is the first member of struct task_struct */
get_current \rd
#else
ARM( mov \rd, sp, lsr #THREAD_SIZE_ORDER + PAGE_SHIFT )
THUMB( mov \rd, sp )
THUMB( lsr \rd, \rd, #THREAD_SIZE_ORDER + PAGE_SHIFT )
mov \rd, \rd, lsl #THREAD_SIZE_ORDER + PAGE_SHIFT
#endif
.endm

/*
Expand Down
50 changes: 50 additions & 0 deletions arch/arm/include/asm/current.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021 Keith Packard <keithp@keithp.com>
* Copyright (c) 2021 Google, LLC <ardb@kernel.org>
*/

#ifndef _ASM_ARM_CURRENT_H
#define _ASM_ARM_CURRENT_H

#ifndef __ASSEMBLY__

struct task_struct;

static inline void set_current(struct task_struct *cur)
{
if (!IS_ENABLED(CONFIG_CURRENT_POINTER_IN_TPIDRURO))
return;

/* Set TPIDRURO */
asm("mcr p15, 0, %0, c13, c0, 3" :: "r"(cur) : "memory");
}

#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO

static inline struct task_struct *get_current(void)
{
struct task_struct *cur;

#if __has_builtin(__builtin_thread_pointer)
/*
* Use the __builtin helper when available - this results in better
* code, especially when using GCC in combination with the per-task
* stack protector, as the compiler will recognize that it needs to
* load the TLS register only once in every function.
*/
cur = __builtin_thread_pointer();
#else
asm("mrc p15, 0, %0, c13, c0, 3" : "=r"(cur));
#endif
return cur;
}

#define current get_current()
#else
#include <asm-generic/current.h>
#endif /* CONFIG_CURRENT_POINTER_IN_TPIDRURO */

#endif /* __ASSEMBLY__ */

#endif /* _ASM_ARM_CURRENT_H */
3 changes: 2 additions & 1 deletion arch/arm/include/asm/smp.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ extern void set_smp_ipi_range(int ipi_base, int nr_ipi);
* Called from platform specific assembly code, this is the
* secondary CPU entry point.
*/
asmlinkage void secondary_start_kernel(void);
asmlinkage void secondary_start_kernel(struct task_struct *task);


/*
Expand All @@ -61,6 +61,7 @@ struct secondary_data {
};
unsigned long swapper_pg_dir;
void *stack;
struct task_struct *task;
};
extern struct secondary_data secondary_data;
extern void secondary_startup(void);
Expand Down
2 changes: 0 additions & 2 deletions arch/arm/include/asm/stackprotector.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ static __always_inline void boot_init_stack_canary(void)
current->stack_canary = canary;
#ifndef CONFIG_STACKPROTECTOR_PER_TASK
__stack_chk_guard = current->stack_canary;
#else
current_thread_info()->stack_canary = current->stack_canary;
#endif
}

Expand Down
16 changes: 16 additions & 0 deletions arch/arm/include/asm/switch_to.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,25 @@
*/
extern struct task_struct *__switch_to(struct task_struct *, struct thread_info *, struct thread_info *);

static inline void set_ti_cpu(struct task_struct *p)
{
#ifdef CONFIG_THREAD_INFO_IN_TASK
/*
* The core code no longer maintains the thread_info::cpu field once
* CONFIG_THREAD_INFO_IN_TASK is in effect, but we rely on it for
* raw_smp_processor_id(), which cannot access struct task_struct*
* directly for reasons of circular #inclusion hell.
*/
task_thread_info(p)->cpu = task_cpu(p);
#endif
}

#define switch_to(prev,next,last) \
do { \
__complete_pending_tlbi(); \
set_ti_cpu(next); \
if (IS_ENABLED(CONFIG_CURRENT_POINTER_IN_TPIDRURO)) \
__this_cpu_write(__entry_task, next); \
last = __switch_to(prev,task_thread_info(prev), task_thread_info(next)); \
} while (0)

Expand Down
15 changes: 11 additions & 4 deletions arch/arm/include/asm/thread_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@

struct task_struct;

DECLARE_PER_CPU(struct task_struct *, __entry_task);

#include <asm/types.h>

struct cpu_context_save {
Expand All @@ -52,12 +54,11 @@ struct cpu_context_save {
struct thread_info {
unsigned long flags; /* low level flags */
int preempt_count; /* 0 => preemptable, <0 => bug */
#ifndef CONFIG_THREAD_INFO_IN_TASK
struct task_struct *task; /* main task structure */
#endif
__u32 cpu; /* cpu */
__u32 cpu_domain; /* cpu domain */
#ifdef CONFIG_STACKPROTECTOR_PER_TASK
unsigned long stack_canary;
#endif
struct cpu_context_save cpu_context; /* cpu context */
__u32 abi_syscall; /* ABI type and syscall nr */
__u8 used_cp[16]; /* thread used copro */
Expand All @@ -71,11 +72,16 @@ struct thread_info {

#define INIT_THREAD_INFO(tsk) \
{ \
.task = &tsk, \
INIT_THREAD_INFO_TASK(tsk) \
.flags = 0, \
.preempt_count = INIT_PREEMPT_COUNT, \
}

#ifdef CONFIG_THREAD_INFO_IN_TASK
#define INIT_THREAD_INFO_TASK(tsk)
#else
#define INIT_THREAD_INFO_TASK(tsk) .task = &(tsk),

/*
* how to get the thread information struct from C
*/
Expand All @@ -86,6 +92,7 @@ static inline struct thread_info *current_thread_info(void)
return (struct thread_info *)
(current_stack_pointer & ~(THREAD_SIZE - 1));
}
#endif

#define thread_saved_pc(tsk) \
((unsigned long)(task_thread_info(tsk)->cpu_context.pc))
Expand Down
10 changes: 7 additions & 3 deletions arch/arm/include/asm/tls.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -38,18 +38,22 @@
#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

Expand Down Expand Up @@ -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));
Expand Down
6 changes: 2 additions & 4 deletions arch/arm/kernel/asm-offsets.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ int main(void)
BLANK();
DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count));
#ifndef CONFIG_THREAD_INFO_IN_TASK
DEFINE(TI_TASK, offsetof(struct thread_info, task));
#endif
DEFINE(TI_CPU, offsetof(struct thread_info, cpu));
DEFINE(TI_CPU_DOMAIN, offsetof(struct thread_info, cpu_domain));
DEFINE(TI_CPU_SAVE, offsetof(struct thread_info, cpu_context));
Expand All @@ -63,10 +65,6 @@ int main(void)
#ifdef CONFIG_IWMMXT
DEFINE(TI_IWMMXT_STATE, offsetof(struct thread_info, fpstate.iwmmxt));
#endif
#ifdef CONFIG_STACKPROTECTOR_PER_TASK
DEFINE(TI_STACK_CANARY, offsetof(struct thread_info, stack_canary));
#endif
DEFINE(THREAD_SZ_ORDER, THREAD_SIZE_ORDER);
BLANK();
DEFINE(S_R0, offsetof(struct pt_regs, ARM_r0));
DEFINE(S_R1, offsetof(struct pt_regs, ARM_r1));
Expand Down
5 changes: 5 additions & 0 deletions arch/arm/kernel/entry-armv.S
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,8 @@ ENDPROC(__fiq_abt)
ATRAP( teq r8, r7)
ATRAP( mcrne p15, 0, r8, c1, c0, 0)

reload_current r7, r8

@
@ Clear FP to mark the first stack frame
@
Expand Down Expand Up @@ -762,6 +764,8 @@ ENTRY(__switch_to)
add r7, r7, #TSK_STACK_CANARY & ~IMM12_MASK
.endif
ldr r7, [r7, #TSK_STACK_CANARY & IMM12_MASK]
#elif defined(CONFIG_CURRENT_POINTER_IN_TPIDRURO)
mov r7, r2 @ Preserve 'next'
#endif
#ifdef CONFIG_CPU_USE_DOMAINS
mcr p15, 0, r6, c3, c0, 0 @ Set domain register
Expand All @@ -776,6 +780,7 @@ ENTRY(__switch_to)
#endif
THUMB( mov ip, r4 )
mov r0, r5
set_current r7
ARM( ldmia r4, {r4 - sl, fp, sp, pc} ) @ Load all regs saved previously
THUMB( ldmia ip!, {r4 - sl, fp} ) @ Load all regs saved previously
THUMB( ldr sp, [ip], #4 )
Expand Down
1 change: 1 addition & 0 deletions arch/arm/kernel/entry-common.S
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ ENTRY(vector_swi)
str saved_psr, [sp, #S_PSR] @ Save CPSR
str r0, [sp, #S_OLD_R0] @ Save OLD_R0
#endif
reload_current r10, ip
zero_fp
alignment_trap r10, ip, __cr_alignment
asm_trace_hardirqs_on save=0
Expand Down
8 changes: 8 additions & 0 deletions arch/arm/kernel/entry-header.S
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,14 @@


.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
Expand Down
5 changes: 5 additions & 0 deletions arch/arm/kernel/head-common.S
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ __mmap_switched:
mov r1, #0
bl __memset @ clear .bss

#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
adr_l r0, init_task @ get swapper task_struct
set_current r0
#endif

ldmia r4, {r0, r1, r2, r3}
str r9, [r0] @ Save processor ID
str r7, [r1] @ Save machine type
Expand Down
1 change: 1 addition & 0 deletions arch/arm/kernel/head-nommu.S
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ ENTRY(secondary_startup)
ret r12
1: bl __after_proc_init
ldr sp, [r7, #12] @ set up the stack pointer
ldr r0, [r7, #16] @ set up task pointer
mov fp, #0
b secondary_start_kernel
ENDPROC(secondary_startup)
Expand Down
5 changes: 3 additions & 2 deletions arch/arm/kernel/head.S
Original file line number Diff line number Diff line change
Expand Up @@ -424,8 +424,9 @@ ENDPROC(secondary_startup)
ENDPROC(secondary_startup_arm)

ENTRY(__secondary_switched)
ldr_l r7, secondary_data + 12 @ get secondary_data.stack
mov sp, r7
adr_l r7, secondary_data + 12 @ get secondary_data.stack
ldr sp, [r7]
ldr r0, [r7, #4] @ get secondary_data.task
mov fp, #0
b secondary_start_kernel
ENDPROC(__secondary_switched)
Expand Down
Loading

0 comments on commit 13a695a

Please sign in to comment.