arm: Preserve TPIDRURW on context switch
Subsystems (3)
PATCH v2 1/1 arm: Preserve TPIDRURW on context switch
From: =?UTF-8?q?Andr=C3=A9=20Hentschel?= <[email protected]>
There are more and more applications coming to WinRT, Wine could support them, but mostly they expect to have the thread environment block (TEB) in TPIDRURW. This register must be preserved per thread instead of being cleared.
---
This patch is against a86d52667d8eda5de39393ce737794403bdce1eb
I could only test it with kernel 3.4.6
arch/arm/include/asm/thread_info.h | 2 +- arch/arm/include/asm/tls.h | 32 +++++++++++++++++++------------- arch/arm/kernel/entry-armv.S | 9 ++++----- arch/arm/kernel/process.c | 2 +- arch/arm/kernel/ptrace.c | 2 +- arch/arm/kernel/traps.c | 4 ++-- 6 files changed, 28 insertions(+), 23 deletions(-)
arch/arm/include/asm/thread_info.h +1 -1
@@ -58,7 +58,7 @@ struct thread_info {struct cpu_context_save cpu_context;/* cpu context */ __u32 syscall;/* syscall number */ __u8 used_cp[16 ];/* thread used copro */ -unsigned long tp_value; +unsigned long tp_value[2 ];#ifdef CONFIG_CRUNCH struct crunch_state crunchstate;#endif
arch/arm/include/asm/tls.h +19 -13
@@ -2,29 +2,35 @@#define __ASMARM_TLS_H#ifdef __ASSEMBLY__ - .macro set_tls_none, tp, tmp1, tmp2 + .macro set_tls_none, ntp, ptp, tmp1, tmp2 .endm - .macro set_tls_v6k, tp, tmp1, tmp2 - mcr p15, 0, \tp, c13, c0, 3 @ set TLS register - mov \tmp1, #0 - mcr p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register + .macro set_tls_v6k, ntp, ptp, tmp1, tmp2 + mrc p15, 0, \tmp2, c13, c0, 2 @ get user r/w TLS register + str \tmp2, [\ptp, #4] + ldrd \tmp1, \tmp2, [\ntp] + mcr p15, 0, \tmp1, c13, c0, 3 @ set user r/o TLS register + mcr p15, 0, \tmp2, c13, c0, 2 @ set user r/w TLS register .endm - .macro set_tls_v6, tp, tmp1, tmp2 + .macro set_tls_v6, ntp, ptp, tmp1, tmp2 ldr \tmp1, =elf_hwcap ldr \tmp1, [\tmp1, #0] mov \tmp2, #0xffff0fff tst \tmp1, #HWCAP_TLS @ hardware TLS available? - mcrne p15, 0, \tp, c13, c0, 3 @ yes, set TLS register - movne \tmp1, #0 - mcrne p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register - streq \tp, [\tmp2, #-15] @ set TLS value at 0xffff0ff0 + mrcne p15, 0, \tmp2, c13, c0, 2 @ get user r/w TLS register + strne \tmp2, [\ptp, #4] + ldrdne \tmp1, \tmp2, [\ntp] + ldreq \tmp1, [\ntp] + mcrne p15, 0, \tmp1, c13, c0, 3 @ yes, set user r/o TLS register + mcrne p15, 0, \tmp2, c13, c0, 2 @ set user r/w TLS register + streq \tmp1, [\tmp2, #-15] @ set TLS value at 0xffff0ff0 .endm - .macro set_tls_software, tp, tmp1, tmp2 - mov \tmp1, #0xffff0fff - str \tp, [\tmp1, #-15] @ set TLS value at 0xffff0ff0 + .macro set_tls_software, ntp, ptp, tmp1, tmp2 + ldr \tmp1, [\ntp] + mov \tmp2, #0xffff0fff + str \tmp1, [\tmp2, #-15] @ set TLS value at 0xffff0ff0 .endm #endif
arch/arm/kernel/entry-armv.S +4 -5
@@ -728,21 +728,20 @@ ENTRY(__switch_to) UNWIND(.fnstart ) UNWIND(.cantunwind ) add ip, r1, #TI_CPU_SAVE - ldr r3, [r2, #TI_TP_VALUE] ARM( stmia ip!, {r4 - sl, fp, sp, lr} ) @ Store most regs on stack THUMB( stmia ip!, {r4 - sl, fp} ) @ Store most regs on stack THUMB( str sp, [ip], #4 ) THUMB( str lr, [ip], #4 ) -#ifdef CONFIG_CPU_USE_DOMAINS - ldr r6, [r2, #TI_CPU_DOMAIN] -#endif - set_tls r3, r4, r5 + add r3, r2, #TI_TP_VALUE + add r4, r1, #TI_TP_VALUE + set_tls r3, r4, r6, r7 #if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP) ldr r7, [r2, #TI_TASK] ldr r8, =__stack_chk_guard ldr r7, [r7, #TSK_STACK_CANARY] #endif #ifdef CONFIG_CPU_USE_DOMAINS + ldr r6, [r2, #TI_CPU_DOMAIN] mcr p15, 0, r6, c3, c0, 0 @ Set domain register #endif mov r5, r0
arch/arm/kernel/process.c +1 -1
@@ -395,7 +395,7 @@ copy_thread(unsigned long clone_flags, unsigned long stack_start,clear_ptrace_hw_breakpoint (p);if (clone_flags &CLONE_SETTLS ) - thread->tp_value = childregs->ARM_r3; + thread->tp_value[0 ] = childregs->ARM_r3;thread_notify (THREAD_NOTIFY_COPY , thread);
arch/arm/kernel/ptrace.c +1 -1
@@ -849,7 +849,7 @@ long arch_ptrace(struct task_struct *child, long request,#endif case PTRACE_GET_THREAD_AREA : - ret =put_user (task_thread_info (child)->tp_value, + ret =put_user (task_thread_info (child)->tp_value[0 ], datap);break ;
arch/arm/kernel/traps.c +2 -2
@@ -588,7 +588,7 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)return regs->ARM_r0;case NR (set_tls): - thread->tp_value = regs->ARM_r0; + thread->tp_value[0 ] = regs->ARM_r0;if (tls_emu)return 0 ;if (has_tls_reg) {
@@ -706,7 +706,7 @@ static int get_tp_trap(struct pt_regs *regs, unsigned int instr)int reg = (instr >>12 ) &15 ;if (reg ==15 )return 1 ; - regs->uregs[reg] =current_thread_info ()->tp_value; + regs->uregs[reg] =current_thread_info ()->tp_value[0 ]; regs->ARM_pc +=4 ;return 0 ; }
Signature · 1 line
1.7.10.4
Quoted 7 lines · Andr? Hentschel
From: =?UTF-8?q?Andr=C3=A9=20Hentschel?= <[email protected]>
There are more and more applications coming to WinRT, Wine could support them, but mostly they expect to have the thread environment block (TEB) in TPIDRURW. This register must be preserved per thread instead of being cleared.
Signed-off-by Andr? Hentschel <[email protected]>
This actually makes things less efficient all round, because you now use the value immediately after loading, which means it will cause pipeline stalls, certainly on older CPUs.
Could you please rework the patch to try avoiding soo many modifications to the way things have been done here?
Quoted 7 lines · Russell King - ARM Linux
Quoted 6 lines · Andr? Hentschel
From: =?UTF-8?q?Andr=C3=A9=20Hentschel?= <[email protected]>
There are more and more applications coming to WinRT, Wine could support them, but mostly they expect to have the thread environment block (TEB) in TPIDRURW. This register must be preserved per thread instead of being cleared.
Quoted 8 lines
Quoted 1 line
Signed-off-by Andr? Hentschel <[email protected]>This actually makes things less efficient all round, because you now use the value immediately after loading, which means it will cause pipeline stalls, certainly on older CPUs.
Could you please rework the patch to try avoiding soo many modifications to the way things have been done here?
copy_thread also needs updating so that the *register* value for the parent is copied to the child, since the parent may have written the register after the last context-switch, meaning that tp_value is out-of-date.
Will
Am 22.04.2013 17:18, schrieb Will Deacon:
Quoted 14 lines · Russell King - ARM Linux
Quoted 13 lines · Andr? Hentschel
Quoted 7 lines
From: =?UTF-8?q?Andr=C3=A9=20Hentschel?= <[email protected]>
There are more and more applications coming to WinRT, Wine could support them, but mostly they expect to have the thread environment block (TEB) in TPIDRURW. This register must be preserved per thread instead of being cleared.
Signed-off-by Andr? Hentschel <[email protected]>This actually makes things less efficient all round, because you now use the value immediately after loading, which means it will cause pipeline stalls, certainly on older CPUs.
Quoted 6 lines
Quoted 2 lines
Could you please rework the patch to try avoiding soo many modifications to the way things have been done here?
copy_threadalso needs updating so that the *register* value for the parent is copied to the child, since the parent may have written the register after the last context-switch, meaning thattp_valueis out-of-date.
Thank you both for reviewing.
I guess you mostly mean "ldr r6, [r2, #TI_CPU_DOMAIN]".
I just thought about old CPUs and remembered again that we at Wine need that patch only on v7 (and later). So is it ok to introduce a set_tls_v7 in tls.h and make use of CONFIG_CPU_V7 compile-time check in
the changed files and in the copy_thread function?
Do i need any further flag checks in copy_thread or can i use the compile-time check to add unconditional code?
Quoted 22 lines · André Hentschel
Am 22.04.2013 17:18, schrieb Will Deacon:
Quoted 14 lines · Russell King - ARM Linux
Quoted 13 lines · André Hentschel
Quoted 7 lines
From: =?UTF-8?q?Andr=C3=A9=20Hentschel?= <[email protected]>
There are more and more applications coming to WinRT, Wine could support them, but mostly they expect to have the thread environment block (TEB) in TPIDRURW. This register must be preserved per thread instead of being cleared.
Signed-off-by André Hentschel <[email protected]>This actually makes things less efficient all round, because you now use the value immediately after loading, which means it will cause pipeline stalls, certainly on older CPUs.
Quoted 6 lines
Quoted 2 lines
Could you please rework the patch to try avoiding soo many modifications to the way things have been done here?
copy_threadalso needs updating so that the *register* value for the parent is copied to the child, since the parent may have written the register after the last context-switch, meaning thattp_valueis out-of-date.
Quoted 7 lines
Thank you both for reviewing.
I guess you mostly mean "ldr r6, [r2, #TI_CPU_DOMAIN]".
I just thought about old CPUs and remembered again that we at Wine need that patch only on v7 (and later). So is it ok to introduce aset_tls_v7intls.hand make use ofCONFIG_CPU_V7compile-time check in
the changed files and in thecopy_threadfunction?
No, we should support this feature on any CPU with the TPIDRURW register, otherwise it's going to get really confusing for userspace.
Quoted 2 lines
Do i need any further flag checks in
copy_threador can i use the compile-time check to add unconditional code?
You could introduce `get' tls functions, which don't do anything for CPUs without the relevant registers.
Will
Am 23.04.2013 11:15, schrieb Will Deacon:
Quoted 25 lines · Andr? Hentschel
Quoted 24 lines
Am 22.04.2013 17:18, schrieb Will Deacon:
Quoted 14 lines · Russell King - ARM Linux
Quoted 13 lines · Andr? Hentschel
Quoted 7 lines
From: =?UTF-8?q?Andr=C3=A9=20Hentschel?= <[email protected]>
There are more and more applications coming to WinRT, Wine could support them, but mostly they expect to have the thread environment block (TEB) in TPIDRURW. This register must be preserved per thread instead of being cleared.
Signed-off-by Andr? Hentschel <[email protected]>This actually makes things less efficient all round, because you now use the value immediately after loading, which means it will cause pipeline stalls, certainly on older CPUs.
Quoted 6 lines
Quoted 2 lines
Could you please rework the patch to try avoiding soo many modifications to the way things have been done here?
copy_threadalso needs updating so that the *register* value for the parent is copied to the child, since the parent may have written the register after the last context-switch, meaning thattp_valueis out-of-date.Thank you both for reviewing.
Quoted 8 lines
Quoted 5 lines
I guess you mostly mean "ldr r6, [r2, #TI_CPU_DOMAIN]".
I just thought about old CPUs and remembered again that we at Wine need that patch only on v7 (and later). So is it ok to introduce aset_tls_v7intls.hand make use ofCONFIG_CPU_V7compile-time check in
the changed files and in thecopy_threadfunction?No, we should support this feature on any CPU with the TPIDRURW register, otherwise it's going to get really confusing for userspace.
Sure, remembered that today.
Quoted 5 lines
Quoted 2 lines
Do i need any further flag checks in
copy_threador can i use the compile-time check to add unconditional code?You could introduce `get' tls functions, which don't do anything for CPUs without the relevant registers.
Before i have another round of testing and patch formatting/sending, what about the untested patch below?
arch/arm/include/asm/thread_info.h +1 -1
@@ -58,7 +58,7 @@ struct thread_info {struct cpu_context_save cpu_context;/* cpu context */ __u32 syscall;/* syscall number */ __u8 used_cp[16 ];/* thread used copro */ -unsigned long tp_value; +unsigned long tp_value[2 ];#ifdef CONFIG_CRUNCH struct crunch_state crunchstate;#endif
arch/arm/include/asm/tls.h +32 -9
78 lines not on this page · raw message
arch/arm/kernel/entry-armv.S +3 -1
@@ -728,7 +728,7 @@ ENTRY(__switch_to) UNWIND(.fnstart ) UNWIND(.cantunwind ) add ip, r1, #TI_CPU_SAVE - ldr r3, [r2, #TI_TP_VALUE] + add r3, r1, #TI_TP_VALUE ARM( stmia ip!, {r4 - sl, fp, sp, lr} ) @ Store most regs on stack THUMB( stmia ip!, {r4 - sl, fp} ) @ Store most regs on stack THUMB( str sp, [ip], #4 )
@@ -736,6 +736,8 @@ ENTRY(__switch_to) #ifdef CONFIG_CPU_USE_DOMAINS ldr r6, [r2, #TI_CPU_DOMAIN] #endif + get_tls2 r3, r4 + add r3, r2, #TI_TP_VALUE set_tls r3, r4, r5 #if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP) ldr r7, [r2, #TI_TASK]
arch/arm/kernel/process.c +2 -1
@@ -395,7 +395,8 @@ copy_thread(unsigned long clone_flags, unsigned long stack_start,clear_ptrace_hw_breakpoint (p);if (clone_flags &CLONE_SETTLS ) - thread->tp_value = childregs->ARM_r3; + thread->tp_value[0 ] = childregs->ARM_r3; + thread->tp_value[1 ] =current_thread_info ()->tp_value[1 ];thread_notify (THREAD_NOTIFY_COPY , thread);
arch/arm/kernel/ptrace.c +1 -1
@@ -849,7 +849,7 @@ long arch_ptrace(struct task_struct *child, long request,#endif case PTRACE_GET_THREAD_AREA : - ret =put_user (task_thread_info (child)->tp_value, + ret =put_user (task_thread_info (child)->tp_value[0 ], datap);break ;
arch/arm/kernel/traps.c +2 -2
@@ -588,7 +588,7 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)return regs->ARM_r0;case NR (set_tls): - thread->tp_value = regs->ARM_r0; + thread->tp_value[0 ] = regs->ARM_r0;if (tls_emu)return 0 ;if (has_tls_reg) {
@@ -706,7 +706,7 @@ static int get_tp_trap(struct pt_regs *regs, unsigned int instr)int reg = (instr >>12 ) &15 ;if (reg ==15 )return 1 ; - regs->uregs[reg] =current_thread_info ()->tp_value; + regs->uregs[reg] =current_thread_info ()->tp_value[0 ]; regs->ARM_pc +=4 ;return 0 ; }
Hi Andrew,
Quoted 6 lines · André Hentschel
Am 23.04.2013 11:15, schrieb Will Deacon:
Quoted 2 lines
You could introduce `get' tls functions, which don't do anything for CPUs without the relevant registers.
Before i have another round of testing and patch formatting/sending, what about the untested patch below?
Ok. Comments inline.
Quoted 18 lines
arch/arm/include/asm/thread_info.h +1
-1@@ -58,7 +58,7 @@ struct thread_info {struct cpu_context_save cpu_context;/* cpu context */ __u32 syscall;/* syscall number */ __u8 used_cp[16 ];/* thread used copro */ -unsigned long tp_value; +unsigned long tp_value[2 ];#ifdef CONFIG_CRUNCH struct crunch_state crunchstate;#endif arch/arm/include/asm/tls.h +0
-0
arch/arm/include/asm/tls.h line 6
Quoted 5 lines
arch/arm/include/asm/tls.h +2
-0@@ -2,13 +2,30 @@#define __ASMARM_TLS_H#ifdef __ASSEMBLY__ + .macro get_tls2_none, tp, tmp1 + .endm
Cosmetic, but these are really horrible macro names.
arch/arm/include/asm/tls.h line 18
Quoted 11 lines
arch/arm/include/asm/tls.h +11
-0+ .macro get_tls2_v6k, tp, tmp1 + mrc p15, 0, \tmp1, c13, c0, 2 @ get user r/w TLS register + str \tmp1, [\tp, #4] + .endm + + .macro get_tls2_v6, tp, tmp1 + ldr \tmp1, =elf_hwcap + ldr \tmp1, [\tmp1, #0] + tst \tmp1, #HWCAP_TLS @ hardware TLS available? + mrcne p15, 0, \tmp1, c13, c0, 2 @ get user r/w TLS register + strne \tmp1, [\tp, #4]
You could factor out some of this hwcap checking now that it's used by both set and get.
Quoted 17 lines
arch/arm/include/asm/tls.h +6
-3+ .endm + + .macro set_tls_none, tp, tmp1, tmp2 .endm .macro set_tls_v6k, tp, tmp1, tmp2 - mcr p15, 0, \tp, c13, c0, 3 @ set TLS register - mov \tmp1, #0 - mcr p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register + ldrd \tmp1, \tmp2, [\tp] + mcr p15, 0, \tmp1, c13, c0, 3 @ set user r/o TLS register + mcr p15, 0, \tmp2, c13, c0, 2 @ set user r/w TLS register .endm .macro set_tls_v6, tp, tmp1, tmp2
arch/arm/include/asm/tls.h line 36
Quoted 8 lines
arch/arm/include/asm/tls.h +1
-4@@ -16,33 +33,39 @@ ldr \tmp1, [\tmp1, #0] mov \tmp2, #0xffff0fff tst \tmp1, #HWCAP_TLS @ hardware TLS available? - mcrne p15, 0, \tp, c13, c0, 3 @ yes, set TLS register - movne \tmp1, #0 - mcrne p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register - streq \tp, [\tmp2, #-15] @ set TLS value at 0xffff0ff0 + ldrdne \tmp1, \tmp2, [\tp]
Does this work for big-endian CPUs?
Quoted 64 lines
arch/arm/include/asm/tls.h +11
-2+ ldreq \tmp1, [\tp] + mcrne p15, 0, \tmp1, c13, c0, 3 @ yes, set user r/o TLS register + mcrne p15, 0, \tmp2, c13, c0, 2 @ set user r/w TLS register + streq \tmp1, [\tmp2, #-15] @ set TLS value at 0xffff0ff0 .endm .macro set_tls_software, tp, tmp1, tmp2 - mov \tmp1, #0xffff0fff - str \tp, [\tmp1, #-15] @ set TLS value at 0xffff0ff0 + ldr \tmp1, [\tp] + mov \tmp2, #0xffff0fff + str \tmp1, [\tmp2, #-15] @ set TLS value at 0xffff0ff0 .endm #endif #ifdef CONFIG_TLS_REG_EMUL #define tls_emu 1 #define has_tls_reg 1 +#define get_tls2 get_tls2_none #define set_tls set_tls_none #elif defined(CONFIG_CPU_V6) #define tls_emu 0 #define has_tls_reg (elf_hwcap & HWCAP_TLS) +#define get_tls2 get_tls2_v6#define set_tls set_tls_v6#elif defined(CONFIG_CPU_32v6K)#define tls_emu 0#define has_tls_reg 1 +#define get_tls2 get_tls2_v6k#define set_tls set_tls_v6k#else #define tls_emu 0#define has_tls_reg 0 +#define get_tls2 get_tls2_none#define set_tls set_tls_software#endif arch/arm/kernel/entry-armv.S +3
-1@@ -728,7 +728,7 @@ ENTRY(__switch_to) UNWIND(.fnstart ) UNWIND(.cantunwind ) add ip, r1, #TI_CPU_SAVE - ldr r3, [r2, #TI_TP_VALUE] + add r3, r1, #TI_TP_VALUE ARM( stmia ip!, {r4 - sl, fp, sp, lr} ) @ Store most regs on stack THUMB( stmia ip!, {r4 - sl, fp} ) @ Store most regs on stack THUMB( str sp, [ip], #4 )@@ -736,6 +736,8 @@ ENTRY(__switch_to) #ifdef CONFIG_CPU_USE_DOMAINS ldr r6, [r2, #TI_CPU_DOMAIN] #endif + get_tls2 r3, r4 + add r3, r2, #TI_TP_VALUE set_tls r3, r4, r5 #if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP) ldr r7, [r2, #TI_TASK]arch/arm/kernel/process.c +0
-0
arch/arm/kernel/process.c copy_thread(unsigned long clone_flags, unsigned long stack_start, · line 400
Quoted 7 lines
arch/arm/kernel/process.c +2
-1@@ -395,7 +395,8 @@ copy_thread(unsigned long clone_flags, unsigned long stack_start,clear_ptrace_hw_breakpoint (p);if (clone_flags &CLONE_SETTLS ) - thread->tp_value = childregs->ARM_r3; + thread->tp_value[0 ] = childregs->ARM_r3; + thread->tp_value[1 ] =current_thread_info ()->tp_value[1 ];
This still isn't correct. Imagine the following sequence of events:
- Task foo writes its TPIDRURW register from userspace and then issues a fork() system call. No context switch occurs between these two events.
- We start creating the child task, bar, and end up in copy_thread with the `thread' pointing at foo's struct thread_info, which contains the *old* TPIDRURW value.
- We copy out the stale value into bar, which is then scheduled with an old TPIDRURW value.
The solution is to reload the value sitting in the register in copy_thread, rather than relying on the thread_info being up-to-date. That's why I previously suggested not using asm macros for the getters.
Quoted 7 lines
arch/arm/kernel/process.c +0
-0thread_notify (THREAD_NOTIFY_COPY , thread);arch/arm/kernel/ptrace.c +0
-0
arch/arm/kernel/ptrace.c long arch_ptrace(struct task_struct *child, long request, · line 854
Quoted 7 lines
arch/arm/kernel/ptrace.c +1
-1@@ -849,7 +849,7 @@ long arch_ptrace(struct task_struct *child, long request,#endif case PTRACE_GET_THREAD_AREA : - ret =put_user (task_thread_info (child)->tp_value, + ret =put_user (task_thread_info (child)->tp_value[0 ], datap);break ;
I'm guessing debuggers don't care about the new TLS value, or do we need a new ptrace request?
Will
Am 24.04.2013 11:42, schrieb Will Deacon:
Quoted 11 lines
Hi Andrew,
Quoted 6 lines · André Hentschel
Am 23.04.2013 11:15, schrieb Will Deacon:
Quoted 2 lines
You could introduce `get' tls functions, which don't do anything for CPUs without the relevant registers.
Before i have another round of testing and patch formatting/sending, what about the untested patch below?
Ok. Comments inline.
Thanks for them. My first kernel patch was adding an include, this second patch is a rather hard one. So every comment is appreciated.
Quoted 5 lines
Review
Quoted 3 lines
#ifdef
__ASSEMBLY__(quoted diff) +2
-0+ .macro get_tls2_none, tp, tmp1 + .endmCosmetic, but these are really horrible macro names.
I guess that's only about removing '2'?
Quoted 9 lines
arch/arm/include/asm/tls.h line 18
Quoted 6 lines
arch/arm/include/asm/tls.h +6
-0+ .macro get_tls2_v6, tp, tmp1 + ldr \tmp1, =elf_hwcap + ldr \tmp1, [\tmp1, #0] + tst \tmp1, #HWCAP_TLS @ hardware TLS available? + mrcne p15, 0, \tmp1, c13, c0, 2 @ get user r/w TLS register + strne \tmp1, [\tp, #4]You could factor out some of this hwcap checking now that it's used by both set and get.
Sure, but the code would still run twice (unlike my PATCHv2)
Quoted 3 lines
arch/arm/include/asm/tls.h line 36
Quoted 1 line
arch/arm/include/asm/tls.h +1
-0+ ldrdne \tmp1, \tmp2, [\tp]Does this work for big-endian CPUs?
I'd say yes.
Quoted 22 lines
arch/arm/kernel/process.c copy_thread(unsigned long clone_flags, unsigned long stack_start, · line 400
Quoted 12 lines
arch/arm/kernel/process.c +2
-1@@ -395,7 +395,8 @@ copy_thread(unsigned long clone_flags, unsigned long stack_start,clear_ptrace_hw_breakpoint (p);if (clone_flags &CLONE_SETTLS ) - thread->tp_value = childregs->ARM_r3; + thread->tp_value[0 ] = childregs->ARM_r3; + thread->tp_value[1 ] =current_thread_info ()->tp_value[1 ];This still isn't correct. Imagine the following sequence of events:
- Task foo writes its TPIDRURW register from userspace and then issues a
fork()system call. No context switch occurs between these two events.- We start creating the child task, bar, and end up in
copy_threadwith the `thread' pointing at foo's structthread_info, which contains the *old* TPIDRURW value.
Quoted 6 lines
- We copy out the stale value into bar, which is then scheduled with an old TPIDRURW value.
The solution is to reload the value sitting in the register in
copy_thread, rather than relying on thethread_infobeing up-to-date. That's why I previously suggested not using asm macros for the getters.
Thanks for the informations. Further questions:
Where would i place this functions? In tls.h as inline functions?
How should that function look like? Containing compile-time checks and only using assembler for mrc instructions? Wouldn't that be much overhead in __switch_to?
Quoted 7 lines
Quoted 7 lines
arch/arm/kernel/ptrace.c +0
-0@@ -849,7 +849,7 @@ long arch_ptrace(struct task_struct *child, long request,#endif
Quoted 8 lines
arch/arm/kernel/ptrace.c long arch_ptrace(struct task_struct *child, long request, · line 854
Quoted 5 lines
arch/arm/kernel/ptrace.c +1
-1case PTRACE_GET_THREAD_AREA : - ret =put_user (task_thread_info (child)->tp_value, + ret =put_user (task_thread_info (child)->tp_value[0 ], datap);break ;I'm guessing debuggers don't care about the new TLS value, or do we need a new ptrace request?
I'd say no. In case someone would jump in and say we need it, it'd be a seperate patch anyway.
Best regards.
Am 24.04.2013 11:42, schrieb Will Deacon:
Quoted 11 lines
Hi Andrew,
Quoted 6 lines · Andr? Hentschel
Am 23.04.2013 11:15, schrieb Will Deacon:
Quoted 2 lines
You could introduce `get' tls functions, which don't do anything for CPUs without the relevant registers.
Before i have another round of testing and patch formatting/sending, what about the untested patch below?
Ok. Comments inline.
I answered to that seperatly.
Here is another try based on your comments:
arch/arm/include/asm/thread_info.h +1 -1
@@ -58,7 +58,7 @@ struct thread_info {struct cpu_context_save cpu_context;/* cpu context */ __u32 syscall;/* syscall number */ __u8 used_cp[16 ];/* thread used copro */ -unsigned long tp_value; +unsigned long tp_value[2 ];#ifdef CONFIG_CRUNCH struct crunch_state crunchstate;#endif
arch/arm/include/asm/tls.h +51 -12
99 lines not on this page · raw message
arch/arm/kernel/entry-armv.S +3 -1
@@ -728,7 +728,7 @@ ENTRY(__switch_to) UNWIND(.fnstart ) UNWIND(.cantunwind ) add ip, r1, #TI_CPU_SAVE - ldr r3, [r2, #TI_TP_VALUE] + add r3, r1, #TI_TP_VALUE ARM( stmia ip!, {r4 - sl, fp, sp, lr} ) @ Store most regs on stack THUMB( stmia ip!, {r4 - sl, fp} ) @ Store most regs on stack THUMB( str sp, [ip], #4 )
@@ -736,6 +736,8 @@ ENTRY(__switch_to) #ifdef CONFIG_CPU_USE_DOMAINS ldr r6, [r2, #TI_CPU_DOMAIN] #endif + get_tls r3, r4 + add r3, r2, #TI_TP_VALUE set_tls r3, r4, r5 #if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP) ldr r7, [r2, #TI_TASK]
arch/arm/kernel/process.c +5 -1
@@ -36,6 +36,7 @@#include <asm/cacheflush.h> #include <asm/idmap.h> #include <asm/processor.h> +#include <asm/tls.h> #include <asm/thread_notify.h> #include <asm/stacktrace.h> #include <asm/mach/time.h>
@@ -395,7 +396,10 @@ copy_thread(unsigned long clone_flags, unsigned long stack_start,clear_ptrace_hw_breakpoint (p);if (clone_flags &CLONE_SETTLS ) - thread->tp_value = childregs->ARM_r3; + { + thread->tp_value[0 ] = childregs->ARM_r3; +get_tpidrurw (&thread->tp_value[1 ]); + }thread_notify (THREAD_NOTIFY_COPY , thread);
arch/arm/kernel/ptrace.c +1 -1
@@ -849,7 +849,7 @@ long arch_ptrace(struct task_struct *child, long request,#endif case PTRACE_GET_THREAD_AREA : - ret =put_user (task_thread_info (child)->tp_value, + ret =put_user (task_thread_info (child)->tp_value[0 ], datap);break ;
arch/arm/kernel/traps.c +2 -2
@@ -588,7 +588,7 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)return regs->ARM_r0;case NR (set_tls): - thread->tp_value = regs->ARM_r0; + thread->tp_value[0 ] = regs->ARM_r0;if (tls_emu)return 0 ;if (has_tls_reg) {
@@ -706,7 +706,7 @@ static int get_tp_trap(struct pt_regs *regs, unsigned int instr)int reg = (instr >>12 ) &15 ;if (reg ==15 )return 1 ; - regs->uregs[reg] =current_thread_info ()->tp_value; + regs->uregs[reg] =current_thread_info ()->tp_value[0 ]; regs->ARM_pc +=4 ;return 0 ; }
Hi André,
Will pointed me at this thread and I had a look at fixing this up yesterday by extending his original patch...
There are a few things about this that aren't quite right. Most of the comments are cosmetic but there's an issue in copy_thread that will result in incorrect behaviour, I think.
I've commented below inline and there's a patch at the bottom- can you let me know if it works for you?
Quoted 46 lines · André Hentschel
Am 24.04.2013 11:42, schrieb Will Deacon:
Quoted 11 lines
Hi Andrew,
Quoted 6 lines · André Hentschel
Am 23.04.2013 11:15, schrieb Will Deacon:
Quoted 2 lines
You could introduce `get' tls functions, which don't do anything for CPUs without the relevant registers.
Before i have another round of testing and patch formatting/sending, what about the untested patch below?
Ok. Comments inline.
I answered to that seperatly.
Here is another try based on your comments:arch/arm/include/asm/thread_info.h +1
-1@@ -58,7 +58,7 @@ struct thread_info {struct cpu_context_save cpu_context;/* cpu context */ __u32 syscall;/* syscall number */ __u8 used_cp[16 ];/* thread used copro */ -unsigned long tp_value; +unsigned long tp_value[2 ];#ifdef CONFIG_CRUNCH struct crunch_state crunchstate;#endif arch/arm/include/asm/tls.h +7
-0@@ -2,48 +2,87 @@#define __ASMARM_TLS_H#ifdef __ASSEMBLY__ + .macro check_hwcap_tls, tmp1 + ldr \tmp1, =elf_hwcap + ldr \tmp1, [\tmp1, #0] + tst \tmp1, #HWCAP_TLS @ hardware TLS available? + .endm + +
arch/arm/include/asm/tls.h line 21
Quoted 10 lines
arch/arm/include/asm/tls.h +10
-0+ .macro get_tls_none, tp, tmp1 + .endm + + .macro get_tls_v6k, tp, tmp1 + mrc p15, 0, \tmp1, c13, c0, 2 @ get user r/w TLS register + str \tmp1, [\tp, #4] + .endm + + .macro get_tls_v6, tp, tmp1 + check_hwcap_tls \tmp1
I tend to steer clear of asm that requires certain behaviour wrt the flags, though in this case I think it's probably a sufficiently self contained case to be okay...
Quoted 37 lines
arch/arm/include/asm/tls.h +14
-12+ mrcne p15, 0, \tmp1, c13, c0, 2 @ get user r/w TLS register + strne \tmp1, [\tp, #4] + .endm + + .macro set_tls_none, tp, tmp1, tmp2 .endm .macro set_tls_v6k, tp, tmp1, tmp2 - mcr p15, 0, \tp, c13, c0, 3 @ set TLS register - mov \tmp1, #0 - mcr p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register + ldrd \tmp1, \tmp2, [\tp] + mcr p15, 0, \tmp1, c13, c0, 3 @ set user r/o TLS register + mcr p15, 0, \tmp2, c13, c0, 2 @ set user r/w TLS register .endm .macro set_tls_v6, tp, tmp1, tmp2 - ldr \tmp1, =elf_hwcap - ldr \tmp1, [\tmp1, #0] mov \tmp2, #0xffff0fff - tst \tmp1, #HWCAP_TLS @ hardware TLS available? - mcrne p15, 0, \tp, c13, c0, 3 @ yes, set TLS register - movne \tmp1, #0 - mcrne p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register - streq \tp, [\tmp2, #-15] @ set TLS value at 0xffff0ff0 + check_hwcap_tls \tmp1 + ldrdne \tmp1, \tmp2, [\tp] + ldreq \tmp1, [\tp] + mcrne p15, 0, \tmp1, c13, c0, 3 @ yes, set user r/o TLS register + mcrne p15, 0, \tmp2, c13, c0, 2 @ set user r/w TLS register + streq \tmp1, [\tmp2, #-15] @ set TLS value at 0xffff0ff0 .endm .macro set_tls_software, tp, tmp1, tmp2 - mov \tmp1, #0xffff0fff - str \tp, [\tmp1, #-15] @ set TLS value at 0xffff0ff0
arch/arm/include/asm/tls.h line 56
Quoted 10 lines
arch/arm/include/asm/tls.h +4
-0+ ldr \tmp1, [\tp] + mov \tmp2, #0xffff0fff + str \tmp1, [\tmp2, #-15] @ set TLS value at 0xffff0ff0 .endm #endif #ifdef CONFIG_TLS_REG_EMUL #define tls_emu 1 #define has_tls_reg 1 +#define get_tls get_tls_none
This is different from the set_tls, which deals with both tpidrurw and tpidruro, so the naming is a little inconsistent here...
Quoted 10 lines
arch/arm/include/asm/tls.h +2
-0#define set_tls set_tls_none#elif defined(CONFIG_CPU_V6 )#define tls_emu 0#define has_tls_reg (elf_hwcap & HWCAP_TLS) +#define get_tls get_tls_v6#define set_tls set_tls_v6#elif defined(CONFIG_CPU_32v6K)#define tls_emu 0#define has_tls_reg 1 +#define get_tls get_tls_v6k
arch/arm/include/asm/tls.h line 76
Quoted 10 lines
arch/arm/include/asm/tls.h +3
-0#define set_tls set_tls_v6k#else #define tls_emu 0#define has_tls_reg 0 +#define get_tls get_tls_none#define set_tls set_tls_software#endif +#ifndef __ASSEMBLY__ +static inline void get_tpidrurw (unsigned long *tpidrurw)
A bit weird to have tpidrurw here but tls elsewhere - I settled on tlsuser... (see below)
Quoted 47 lines
arch/arm/include/asm/tls.h +11
-0+{ +unsigned long t; +#ifdef CONFIG_TLS_REG_EMUL +return ; +#endif +if (!has_tls_reg)return ; + __asm__("mcr p15, 0, %0, c13, c0, 2" : :"r" (t)); + *tpidrurw = t; +} +#endif +#endif /* __ASMARM_TLS_H */ arch/arm/kernel/entry-armv.S +3
-1@@ -728,7 +728,7 @@ ENTRY(__switch_to) UNWIND(.fnstart ) UNWIND(.cantunwind ) add ip, r1, #TI_CPU_SAVE - ldr r3, [r2, #TI_TP_VALUE] + add r3, r1, #TI_TP_VALUE ARM( stmia ip!, {r4 - sl, fp, sp, lr} ) @ Store most regs on stack THUMB( stmia ip!, {r4 - sl, fp} ) @ Store most regs on stack THUMB( str sp, [ip], #4 )@@ -736,6 +736,8 @@ ENTRY(__switch_to) #ifdef CONFIG_CPU_USE_DOMAINS ldr r6, [r2, #TI_CPU_DOMAIN] #endif + get_tls r3, r4 + add r3, r2, #TI_TP_VALUE set_tls r3, r4, r5 #if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP) ldr r7, [r2, #TI_TASK]arch/arm/kernel/process.c +1
-0@@ -36,6 +36,7 @@#include <asm/cacheflush.h> #include <asm/idmap.h> #include <asm/processor.h> +#include <asm/tls.h> #include <asm/thread_notify.h> #include <asm/stacktrace.h> #include <asm/mach/time.h>
arch/arm/kernel/process.c copy_thread(unsigned long clone_flags, unsigned long stack_start, · line 402
Quoted 8 lines
arch/arm/kernel/process.c +4
-1@@ -395,7 +396,10 @@ copy_thread(unsigned long clone_flags, unsigned long stack_start,clear_ptrace_hw_breakpoint (p);if (clone_flags &CLONE_SETTLS ) - thread->tp_value = childregs->ARM_r3; + { + thread->tp_value[0 ] = childregs->ARM_r3; +get_tpidrurw (&thread->tp_value[1 ]); + }
This isn't quite right - the re-reading of tpidrurw should be independent of CLONE_SETTLS. We should update tpidrurw from userspace in all cases.
The following is what I've been looking at/testing... It works on V7 and I've build tested it for 1136 - I would've sent it yesterday but was getting things set up for testing on 1136 (v6 not k)
----8<-------
From bd3fe4055777b404a1635f366483637fd0cfa35a Mon Sep 17 00:00:00 2001
From: Jonathan Austin <[email protected]>
Date: Fri, 8 Feb 2013 15:55:12 +0000
Subject: [PATCH] ARM: tls: context switch user writeable TLS register
TPIDRURW
Since commit 6a1c53124aa1 ("ARM: 7403/1: tls: remove covert channel via TPIDRURW") we have zeroed the user writeable TLS register to prevent it from being used as a covert channel between two tasks.
However, it turns out that the wine guys would rather we actually switched the register so that WinRT applications can use it to store a pointer to their `thread environment block (TEB)'.
This patch implements TPIDRURW context-switching for cpus implementing the register. Unlike the TPIDRURO, which is already switched, the TPIDRURW can be updated from userspace so needs careful treatment in the case that we modify TPIDRURW and call fork(). If no context switch occurs between these two events then the thread_info struct that we use to construct the child will have the old, stale, TPIDRURW value. To avoid this we must always read TPIDRURW in copy_thread.
This patch is extended from an earlier version by Will Deacon in order to:
- Save TPIDRURW at context switch
- Deal with the race condition described above, including adding C-getters
--- arch/arm/include/asm/thread_info.h | 2 +- arch/arm/include/asm/tls.h | 51 +++++++++++++++++++++++++++++------- arch/arm/kernel/Makefile | 2 +- arch/arm/kernel/entry-armv.S | 5 +++- arch/arm/kernel/process.c | 4 ++- arch/arm/kernel/ptrace.c | 2 +- arch/arm/kernel/tls.c | 50 +++++++++++++++++++++++++++++++++++ arch/arm/kernel/traps.c | 4 +-- 8 files changed, 104 insertions(+), 16 deletions(-) create mode 100644 arch/arm/kernel/tls.c
arch/arm/include/asm/thread_info.h +1 -1
@@ -58,7 +58,7 @@ struct thread_info {struct cpu_context_save cpu_context;/* cpu context */ __u32 syscall;/* syscall number */ __u8 used_cp[16 ];/* thread used copro */ -unsigned long tp_value; +unsigned long tp_value[2 ];/* TLS registers */ #ifdef CONFIG_CRUNCH struct crunch_state crunchstate;#endif
arch/arm/include/asm/tls.h +42 -9
85 lines not on this page · raw message
arch/arm/kernel/Makefile +1 -1
@@ -17,7 +17,7 @@ CFLAGS_REMOVE_return_address.o = -pg obj-y := elf.o entry-armv.o entry-common.o irq.o opcodes.o \ process.o ptrace.o return_address.o sched_clock.o \ - setup.o signal.o stacktrace.o sys_arm.o time.o traps.o + setup.o signal.o stacktrace.o sys_arm.o time.o tls.o traps.o obj-$(CONFIG_ATAGS) += atags_parse.o obj-$(CONFIG_ATAGS_PROC) += atags_proc.o
arch/arm/kernel/entry-armv.S +4 -1
@@ -728,7 +728,7 @@ ENTRY(__switch_to) UNWIND(.fnstart ) UNWIND(.cantunwind ) add ip, r1, #TI_CPU_SAVE - ldr r3, [r2, #TI_TP_VALUE] + add r3, r1, #TI_TP_VALUE ARM( stmia ip!, {r4 - sl, fp, sp, lr} ) @ Store most regs on stack THUMB( stmia ip!, {r4 - sl, fp} ) @ Store most regs on stack THUMB( str sp, [ip], #4 )
@@ -736,6 +736,9 @@ ENTRY(__switch_to) #ifdef CONFIG_CPU_USE_DOMAINS ldr r6, [r2, #TI_CPU_DOMAIN] #endif + /* Save the user-writeable tls register */ + save_tlsuser r3, r4, r5 + add r3, r2, #TI_TP_VALUE set_tls r3, r4, r5 #if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP) ldr r7, [r2, #TI_TASK]
arch/arm/kernel/process.c +3 -1
@@ -39,6 +39,7 @@#include <asm/thread_notify.h> #include <asm/stacktrace.h> #include <asm/mach/time.h> +#include <asm/tls.h> #ifdef CONFIG_CC_STACKPROTECTOR #include <linux/stackprotector.h>
@@ -395,7 +396,8 @@ copy_thread(unsigned long clone_flags, unsigned long stack_start,clear_ptrace_hw_breakpoint (p);if (clone_flags &CLONE_SETTLS ) - thread->tp_value = childregs->ARM_r3; + thread->tp_value[0 ] = childregs->ARM_r3; + thread->tp_value[1 ] =get_tlsuser ();thread_notify (THREAD_NOTIFY_COPY , thread);
arch/arm/kernel/ptrace.c +1 -1
@@ -849,7 +849,7 @@ long arch_ptrace(struct task_struct *child, long request,#endif case PTRACE_GET_THREAD_AREA : - ret =put_user (task_thread_info (child)->tp_value, + ret =put_user (task_thread_info (child)->tp_value[0 ], datap);break ;
arch/arm/kernel/tls.c new file +50 -0
50 lines not on this page · raw message
arch/arm/kernel/traps.c +2 -2
@@ -588,7 +588,7 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)return regs->ARM_r0;case NR (set_tls): - thread->tp_value = regs->ARM_r0; + thread->tp_value[0 ] = regs->ARM_r0;if (tls_emu)return 0 ;if (has_tls_reg) {
@@ -706,7 +706,7 @@ static int get_tp_trap(struct pt_regs *regs, unsigned int instr)int reg = (instr >>12 ) &15 ;if (reg ==15 )return 1 ; - regs->uregs[reg] =current_thread_info ()->tp_value; + regs->uregs[reg] =current_thread_info ()->tp_value[0 ]; regs->ARM_pc +=4 ;return 0 ; }
Signature · 1 line
1.7.9.5
Review
Quoted 7 lines · Jonathan Austin
(quoted diff) +3
-3.macro set_tls_v6k, tp, tmp1, tmp2 - mcr p15, 0, \tp, c13, c0, 3 @ set TLS register - mov \tmp1, #0 - mcr p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register + ldrd \tmp1, \tmp2, [\tp] + mcr p15, 0, \tmp1, c13, c0, 3 @ set user r/o TLS register + mcr p15, 0, \tmp2, c13, c0, 2 @ set user r/w TLS register
So we're still back at stalling the pipeline with result delays on older CPUs?
Quoted 12 lines
(quoted diff) +6
-0+ .endm + + .macro save_tlsuser_v6k, tp, tmp1, tmp2 + @ TPIDRURW can be updated from userspace, so we have to re-read it + mrc p15, 0, \tmp2, c13, c0, 2 @ load user r/w TLS register + str \tmp2, [\tp, #4] .endm .macro set_tls_v6, tp, tmp1, tmp2@@ -16,15 +25,26 @@ ldr \tmp1, [\tmp1, #0] mov \tmp2, #0xffff0fff
Review line 32
Quoted 10 lines
(quoted diff) +5
-4tst \tmp1, #HWCAP_TLS @ hardware TLS available? - mcrne p15, 0, \tp, c13, c0, 3 @ yes, set TLS register - movne \tmp1, #0 - mcrne p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register - streq \tp, [\tmp2, #-15] @ set TLS value at 0xffff0ff0 + ldrned \tmp1, \tmp2, [\tp] + ldreq \tmp1, [\tp] + mcrne p15, 0, \tmp1, c13, c0, 3 @ yes, set user r/o TLS register + mcrne p15, 0, \tmp2, c13, c0, 2 @ set user r/w TLS register + streq \tmp1, [\tmp2, #-15] @ set TLS value at 0xffff0ff0
This at least is better.
Review line 39
Quoted 7 lines
(quoted diff) +7
-0+ .endm + + .macro save_tlsuser_v6, tp, tmp1, tmp2 + @ TPIDRURW can be updated from userspace, so we have to re-read it + ldr \tmp1, =elf_hwcap + ldr \tmp1, [\tmp1, #0] + tst \tmp1, #HWCAP_TLS @ hardware TLS available?
But this isn't - this involves two delays.
Quoted 28 lines
(quoted diff) +9
-2+ mrcne p15, 0, \tmp2, c13, c0, 2 @ read user r/w TLS register + strne \tmp2, [\tp, #4] @ save in to thread_info .endm .macro set_tls_software, tp, tmp1, tmp2 - mov \tmp1, #0xffff0fff - str \tp, [\tmp1, #-15] @ set TLS value at 0xffff0ff0 + ldr \tmp1, [\tp] + mov \tmp2, #0xffff0fff + str \tmp1, [\tmp2, #-15] @ set TLS value at 0xffff0ff0 .endm #endif@@ -32,18 +52,31 @@ #define tls_emu 1 #define has_tls_reg 1 #define set_tls set_tls_none +#define save_tlsuser save_tlsuser_none +#define get_tlsuser get_tlsuser_none #elif defined(CONFIG_CPU_V6) #define tls_emu 0 #define has_tls_reg (elf_hwcap & HWCAP_TLS) #define set_tls set_tls_v6 +#define save_tlsuser save_tlsuser_v6 +#define get_tlsuser get_tlsuser_v6 #elif defined(CONFIG_CPU_32v6K) #define tls_emu 0 #define has_tls_reg 1
Review line 75
Quoted 10 lines
(quoted diff) +4
-0#define set_tls set_tls_v6k +#define save_tlsuser save_tlsuser_v6k +#define get_tlsuser get_tlsuser_v6k #else #define tls_emu 0 #define has_tls_reg 0 #define set_tls set_tls_software +#define save_tlsuser save_tlsuser_none +#define get_tlsuser get_tlsuser_none #endif
This separation of setting and saving the TLS value is actually quite silly. They're called from the same place, so lets just call it "switch_tls" instead.
Here's just the assembly bits doing that - this is totally untested of course:
arch/arm/include/asm/tls.h | 28 +++++++++++++++------------- arch/arm/kernel/entry-armv.S | 4 ++-- 2 files changed, 17 insertions(+), 15 deletions(-)
arch/arm/include/asm/tls.h +15 -13
@@ -2,27 +2,29 @@#define __ASMARM_TLS_H#ifdef __ASSEMBLY__ - .macro set_tls_none, tp, tmp1, tmp2 + .macro switch_tls_none, base, tp, trw, tmp1, tmp2 .endm - .macro set_tls_v6k, tp, tmp1, tmp2 + .macro switch_tls_v6k, base, tp, trw, 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 - mov \tmp1, #0 - mcr p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register + mcr p15, 0, \trw, c13, c0, 2 @ and the user r/w register + str \tmp2, [\base, #TI_TP_VALUE + 4]@ save it .endm - .macro set_tls_v6, tp, tmp1, tmp2 + .macro switch_tls_v6, base, tp, trw, tmp1, tmp2 ldr \tmp1, =elf_hwcap ldr \tmp1, [\tmp1, #0] mov \tmp2, #0xffff0fff tst \tmp1, #HWCAP_TLS @ hardware TLS available? - mcrne p15, 0, \tp, c13, c0, 3 @ yes, set TLS register - movne \tmp1, #0 - mcrne p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register streq \tp, [\tmp2, #-15] @ set TLS value at 0xffff0ff0 + mrcne p15, 0, \tmp2, c13, c0, 2 @ get the user r/w register + mcrne p15, 0, \tp, c13, c0, 3 @ yes, set TLS register + mcrne p15, 0, \trw, c13, c0, 2 @ set user r/w register + strne \tmp2, [\base, #TI_TP_VALUE + 4]@ save it .endm - .macro set_tls_software, tp, tmp1, tmp2 + .macro switch_tls_software, base, tp, trw, tmp1, tmp2 mov \tmp1, #0xffff0fff str \tp, [\tmp1, #-15] @ set TLS value at 0xffff0ff0 .endm
@@ -31,19 +33,19 @@#ifdef CONFIG_TLS_REG_EMUL #define tls_emu 1#define has_tls_reg 1 -#define set_tls set_tls_none +#define switch_tls switch_tls_none#elif defined(CONFIG_CPU_V6 )#define tls_emu 0#define has_tls_reg (elf_hwcap & HWCAP_TLS) -#define set_tls set_tls_v6 +#define switch_tls switch_tls_v6#elif defined(CONFIG_CPU_32v6K)#define tls_emu 0#define has_tls_reg 1 -#define set_tls set_tls_v6k +#define switch_tls switch_tls_v6k#else #define tls_emu 0#define has_tls_reg 0 -#define set_tls set_tls_software +#define switch_tls switch_tls_software#endif #endif /* __ASMARM_TLS_H */
arch/arm/kernel/entry-armv.S +2 -2
@@ -728,15 +728,15 @@ ENTRY(__switch_to) UNWIND(.fnstart ) UNWIND(.cantunwind ) add ip, r1, #TI_CPU_SAVE - ldr r3, [r2, #TI_TP_VALUE] ARM( stmia ip!, {r4 - sl, fp, sp, lr} ) @ Store most regs on stack THUMB( stmia ip!, {r4 - sl, fp} ) @ Store most regs on stack THUMB( str sp, [ip], #4 ) THUMB( str lr, [ip], #4 ) + ldrd r4, r5, [r2, #TI_TP_VALUE] #ifdef CONFIG_CPU_USE_DOMAINS ldr r6, [r2, #TI_CPU_DOMAIN] #endif - set_tls r3, r4, r5 + switch_tls r2, r4, r5, r3, r7 #if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP) ldr r7, [r2, #TI_TASK] ldr r8, =__stack_chk_guard
Hi Russell,
Thanks for the comments - you're right about the 'switch_tls' being more appropriate - needed to take a step back to see that.
I've got a few questions, added inline.
André, Assuming I've understood things okay, there's a patch that uses Russell's asm stuff (with minor modifications, see the questions) and includes the C-world changes too. Perhaps you could see that it solves your problem?
Quoted 11 lines · Russell King - ARM Linux
Review
Quoted 7 lines · Jonathan Austin
.macro
set_tls_v6k, tp, tmp1, tmp2(quoted diff) +3
-3- mcr p15, 0, \tp, c13, c0, 3 @ set TLS register - mov \tmp1, #0 - mcr p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register + ldrd \tmp1, \tmp2, [\tp] + mcr p15, 0, \tmp1, c13, c0, 3 @ set user r/o TLS register + mcr p15, 0, \tmp2, c13, c0, 2 @ set user r/w TLS registerSo we're still back at stalling the pipeline with result delays on older CPUs?
How much older? This particular bit is v6k specific so I wasn't worrying too much but I guess I'm missing something?
It's an academic question wrt to this patch now, though, as the version you show below re-orders to reduce the stalls...
Quoted 26 lines
Quoted 13 lines
(quoted diff) +6
-0+ .endm + + .macro save_tlsuser_v6k, tp, tmp1, tmp2 + @ TPIDRURW can be updated from userspace, so we have to re-read it + mrc p15, 0, \tmp2, c13, c0, 2 @ load user r/w TLS register + str \tmp2, [\tp, #4] .endm .macro set_tls_v6, tp, tmp1, tmp2@@ -16,15 +25,26 @@ ldr \tmp1, [\tmp1, #0] mov \tmp2, #0xffff0fffReview line 32
Quoted 10 lines
(quoted diff) +5
-4tst \tmp1, #HWCAP_TLS @ hardware TLS available? - mcrne p15, 0, \tp, c13, c0, 3 @ yes, set TLS register - movne \tmp1, #0 - mcrne p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register - streq \tp, [\tmp2, #-15] @ set TLS value at 0xffff0ff0 + ldrned \tmp1, \tmp2, [\tp] + ldreq \tmp1, [\tp] + mcrne p15, 0, \tmp1, c13, c0, 3 @ yes, set user r/o TLS register + mcrne p15, 0, \tmp2, c13, c0, 2 @ set user r/w TLS register + streq \tmp1, [\tmp2, #-15] @ set TLS value at 0xffff0ff0This at least is better.
Quoted 9 lines
Review line 39
Quoted 7 lines
(quoted diff) +7
-0+ .endm + + .macro save_tlsuser_v6, tp, tmp1, tmp2 + @ TPIDRURW can be updated from userspace, so we have to re-read it + ldr \tmp1, =elf_hwcap + ldr \tmp1, [\tmp1, #0] + tst \tmp1, #HWCAP_TLS @ hardware TLS available?But this isn't - this involves two delays.
Indeed. You left this section untouched in your asm below - was that because you didn't look at optimising it, or because you thought there wasn't much better that could be done with it?
As far as I can see, we can't start doing any mcr/mrc operations until we know for sure that the hw implements them, so this is something we're stuck with?
Is V6 but not V6k just 1136?
Quoted 35 lines
Quoted 35 lines
(quoted diff) +11
-2+ mrcne p15, 0, \tmp2, c13, c0, 2 @ read user r/w TLS register + strne \tmp2, [\tp, #4] @ save in to thread_info .endm .macro set_tls_software, tp, tmp1, tmp2 - mov \tmp1, #0xffff0fff - str \tp, [\tmp1, #-15] @ set TLS value at 0xffff0ff0 + ldr \tmp1, [\tp] + mov \tmp2, #0xffff0fff + str \tmp1, [\tmp2, #-15] @ set TLS value at 0xffff0ff0 .endm #endif@@ -32,18 +52,31 @@ #define tls_emu 1 #define has_tls_reg 1 #define set_tls set_tls_none +#define save_tlsuser save_tlsuser_none +#define get_tlsuser get_tlsuser_none #elif defined(CONFIG_CPU_V6) #define tls_emu 0 #define has_tls_reg (elf_hwcap & HWCAP_TLS) #define set_tls set_tls_v6 +#define save_tlsuser save_tlsuser_v6 +#define get_tlsuser get_tlsuser_v6 #elif defined(CONFIG_CPU_32v6K) #define tls_emu 0 #define has_tls_reg 1 #define set_tls set_tls_v6k +#define save_tlsuser save_tlsuser_v6k +#define get_tlsuser get_tlsuser_v6k #else #define tls_emu 0 #define has_tls_reg 0
Quoted 8 lines
Review line 75
Quoted 4 lines
(quoted diff) +2
-0#define set_tls set_tls_software +#define save_tlsuser save_tlsuser_none +#define get_tlsuser get_tlsuser_none #endifThis separation of setting and saving the TLS value is actually quite silly. They're called from the same place, so lets just call it "
switch_tls" instead.
Agreed. Thanks for the suggestion.
Quoted 13 lines
Here's just the assembly bits doing that - this is totally untested of course:
arch/arm/include/asm/tls.h | 28 +++++++++++++++------------- arch/arm/kernel/entry-armv.S | 4 ++-- 2 files changed, 17 insertions(+), 15 deletions(-)arch/arm/include/asm/tls.h +0
-0
arch/arm/include/asm/tls.h line 4
Quoted 3 lines
arch/arm/include/asm/tls.h +0
-0@@ -2,27 +2,29 @@#define __ASMARM_TLS_H#ifdef __ASSEMBLY__
+#include <asm/asm-offsets.h>
required for TI_TP_VALUE
arch/arm/include/asm/tls.h line 8
Quoted 6 lines
arch/arm/include/asm/tls.h +2
-2- .macro set_tls_none, tp, tmp1, tmp2 + .macro switch_tls_none, base, tp, trw, tmp1, tmp2 .endm - .macro set_tls_v6k, tp, tmp1, tmp2 + .macro switch_tls_v6k, base, tp, trw, tmp1, tmp2
How do you feel about calling tp and trw something different? tpidro and tpidrw, or tp and tpuser?
The naming threw me off slightly first time I read this new signature (tp=thread_pointer/tls_pointer/etc).
Quoted 62 lines
arch/arm/include/asm/tls.h +13
-11+ mrc p15, 0, \tmp2, c13, c0, 2 @ get the user r/w register mcr p15, 0, \tp, c13, c0, 3 @ set TLS register - mov \tmp1, #0 - mcr p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register + mcr p15, 0, \trw, c13, c0, 2 @ and the user r/w register + str \tmp2, [\base, #TI_TP_VALUE + 4]@ save it .endm - .macro set_tls_v6, tp, tmp1, tmp2 + .macro switch_tls_v6, base, tp, trw, tmp1, tmp2 ldr \tmp1, =elf_hwcap ldr \tmp1, [\tmp1, #0] mov \tmp2, #0xffff0fff tst \tmp1, #HWCAP_TLS @ hardware TLS available? - mcrne p15, 0, \tp, c13, c0, 3 @ yes, set TLS register - movne \tmp1, #0 - mcrne p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register streq \tp, [\tmp2, #-15] @ set TLS value at 0xffff0ff0 + mrcne p15, 0, \tmp2, c13, c0, 2 @ get the user r/w register + mcrne p15, 0, \tp, c13, c0, 3 @ yes, set TLS register + mcrne p15, 0, \trw, c13, c0, 2 @ set user r/w register + strne \tmp2, [\base, #TI_TP_VALUE + 4]@ save it .endm - .macro set_tls_software, tp, tmp1, tmp2 + .macro switch_tls_software, base, tp, trw, tmp1, tmp2 mov \tmp1, #0xffff0fff str \tp, [\tmp1, #-15] @ set TLS value at 0xffff0ff0 .endm@@ -31,19 +33,19 @@#ifdef CONFIG_TLS_REG_EMUL #define tls_emu 1#define has_tls_reg 1 -#define set_tls set_tls_none +#define switch_tls switch_tls_none#elif defined(CONFIG_CPU_V6 )#define tls_emu 0#define has_tls_reg (elf_hwcap & HWCAP_TLS) -#define set_tls set_tls_v6 +#define switch_tls switch_tls_v6#elif defined(CONFIG_CPU_32v6K)#define tls_emu 0#define has_tls_reg 1 -#define set_tls set_tls_v6k +#define switch_tls switch_tls_v6k#else #define tls_emu 0#define has_tls_reg 0 -#define set_tls set_tls_software +#define switch_tls switch_tls_software#endif #endif /* __ASMARM_TLS_H */ arch/arm/kernel/entry-armv.S +0
-1@@ -728,15 +728,15 @@ ENTRY(__switch_to) UNWIND(.fnstart ) UNWIND(.cantunwind ) add ip, r1, #TI_CPU_SAVE - ldr r3, [r2, #TI_TP_VALUE]
arch/arm/kernel/entry-armv.S ENTRY(__switch_to) · line 739
Quoted 10 lines
arch/arm/kernel/entry-armv.S +2
-1ARM( stmia ip!, {r4 - sl, fp, sp, lr} ) @ Store most regs on stack THUMB( stmia ip!, {r4 - sl, fp} ) @ Store most regs on stack THUMB( str sp, [ip], #4 ) THUMB( str lr, [ip], #4 ) + ldrd r4, r5, [r2, #TI_TP_VALUE] #ifdef CONFIG_CPU_USE_DOMAINS ldr r6, [r2, #TI_CPU_DOMAIN] #endif - set_tls r3, r4, r5 + switch_tls r2, r4, r5, r3, r7
Looking at the implementation above and the way you use 'base', I think that should beswitch_tls r1, r4, r5, r3, r7
notswitch_tls r2, r4, r5, r3, r7
That way we save tpidrurw in to the old thread pointer not the new one.
arch/arm/kernel/entry-armv.S ENTRY(__switch_to) · line 742
Quoted 5 lines
arch/arm/kernel/entry-armv.S +0
-0#if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP) ldr r7, [r2, #TI_TASK] ldr r8, =__stack_chk_guard
Here's a complete patch including Russell's suggested asm. Tested on TC2, build-tested for 1136
I think we could drop the extra .c file too - though it is kinda nice for keeping include/asm/tls.h clean...
----8<------
arch/arm/include/asm/thread_info.h +1 -1
@@ -58,7 +58,7 @@ struct thread_info {struct cpu_context_save cpu_context;/* cpu context */ __u32 syscall;/* syscall number */ __u8 used_cp[16 ];/* thread used copro */ -unsigned long tp_value; +unsigned long tp_value[2 ];/* TLS registers */ #ifdef CONFIG_CRUNCH struct crunch_state crunchstate;#endif
arch/arm/include/asm/tls.h +25 -13
@@ -2,27 +2,30 @@#define __ASMARM_TLS_H#ifdef __ASSEMBLY__ - .macro set_tls_none, tp, tmp1, tmp2 +#include <asm/asm-offsets.h> + .macro switch_tls_none, base, tp, trw, tmp1, tmp2 .endm - .macro set_tls_v6k, tp, tmp1, tmp2 + .macro switch_tls_v6k, base, tp, trw, 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 - mov \tmp1, #0 - mcr p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register + mcr p15, 0, \trw, c13, c0, 2 @ and the user r/w register + strne \tmp2, [\base, #TI_TP_VALUE + 4] @ save it .endm - .macro set_tls_v6, tp, tmp1, tmp2 + .macro switch_tls_v6, base, tp, trw, tmp1, tmp2 ldr \tmp1, =elf_hwcap ldr \tmp1, [\tmp1, #0] mov \tmp2, #0xffff0fff tst \tmp1, #HWCAP_TLS @ hardware TLS available? - mcrne p15, 0, \tp, c13, c0, 3 @ yes, set TLS register - movne \tmp1, #0 - mcrne p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register streq \tp, [\tmp2, #-15] @ set TLS value at 0xffff0ff0 + mrcne p15, 0, \tmp2, c13, c0, 2 @ get the user r/w register + mcrne p15, 0, \tp, c13, c0, 3 @ yes, set TLS register + mcrne p15, 0, \trw, c13, c0, 2 @ set user r/w register + strne \tmp2, [\base, #TI_TP_VALUE + 4] @ save it .endm - .macro set_tls_software, tp, tmp1, tmp2 + .macro switch_tls_software, base, tp, trw, tmp1, tmp2 mov \tmp1, #0xffff0fff str \tp, [\tmp1, #-15] @ set TLS value at 0xffff0ff0 .endm
@@ -31,19 +34,28 @@#ifdef CONFIG_TLS_REG_EMUL #define tls_emu 1#define has_tls_reg 1 -#define set_tls set_tls_none +#define switch_tls switch_tls_none +#define get_tlsuser get_tlsuser_none#elif defined(CONFIG_CPU_V6 )#define tls_emu 0#define has_tls_reg (elf_hwcap & HWCAP_TLS) -#define set_tls set_tls_v6 +#define switch_tls switch_tls_v6 +#define get_tlsuser get_tlsuser_v6#elif defined(CONFIG_CPU_32v6K)#define tls_emu 0#define has_tls_reg 1 -#define set_tls set_tls_v6k +#define switch_tls switch_tls_v6k +#define get_tlsuser get_tlsuser_v6k#else #define tls_emu 0#define has_tls_reg 0 -#define set_tls set_tls_software +#define switch_tls switch_tls_software +#define get_tlsuser get_tlsuser_none#endif +#ifndef __ASSEMBLY__ +extern unsigned long get_tlsuser_none (void ); +extern unsigned long get_tlsuser_v6 (void ); +extern unsigned long get_tlsuser_v6k (void ); +#endif #endif /* __ASMARM_TLS_H */
arch/arm/kernel/Makefile +1 -1
@@ -17,7 +17,7 @@ CFLAGS_REMOVE_return_address.o = -pg obj-y := elf.o entry-armv.o entry-common.o irq.o opcodes.o \ process.o ptrace.o return_address.o sched_clock.o \ - setup.o signal.o stacktrace.o sys_arm.o time.o traps.o + setup.o signal.o stacktrace.o sys_arm.o time.o tls.o traps.o obj-$(CONFIG_ATAGS) += atags_parse.o obj-$(CONFIG_ATAGS_PROC) += atags_proc.o
arch/arm/kernel/entry-armv.S +2 -2
@@ -728,15 +728,15 @@ ENTRY(__switch_to) UNWIND(.fnstart ) UNWIND(.cantunwind ) add ip, r1, #TI_CPU_SAVE - ldr r3, [r2, #TI_TP_VALUE] ARM( stmia ip!, {r4 - sl, fp, sp, lr} ) @ Store most regs on stack THUMB( stmia ip!, {r4 - sl, fp} ) @ Store most regs on stack THUMB( str sp, [ip], #4 ) THUMB( str lr, [ip], #4 ) + ldrd r4, r5, [r2, #TI_TP_VALUE] #ifdef CONFIG_CPU_USE_DOMAINS ldr r6, [r2, #TI_CPU_DOMAIN] #endif - set_tls r3, r4, r5 + switch_tls r1, r4, r5, r3, r7 #if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP) ldr r7, [r2, #TI_TASK] ldr r8, =__stack_chk_guard
arch/arm/kernel/process.c +3 -1
@@ -39,6 +39,7 @@#include <asm/thread_notify.h> #include <asm/stacktrace.h> #include <asm/mach/time.h> +#include <asm/tls.h> #ifdef CONFIG_CC_STACKPROTECTOR #include <linux/stackprotector.h>
@@ -395,7 +396,8 @@ copy_thread(unsigned long clone_flags, unsigned long stack_start,clear_ptrace_hw_breakpoint (p);if (clone_flags &CLONE_SETTLS ) - thread->tp_value = childregs->ARM_r3; + thread->tp_value[0 ] = childregs->ARM_r3; + thread->tp_value[1 ] =get_tlsuser ();thread_notify (THREAD_NOTIFY_COPY , thread);
arch/arm/kernel/ptrace.c +1 -1
@@ -849,7 +849,7 @@ long arch_ptrace(struct task_struct *child, long request,#endif case PTRACE_GET_THREAD_AREA : - ret =put_user (task_thread_info (child)->tp_value, + ret =put_user (task_thread_info (child)->tp_value[0 ], datap);break ;
arch/arm/kernel/tls.c new file +50 -0
50 lines not on this page · raw message
arch/arm/kernel/traps.c +2 -2
@@ -588,7 +588,7 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)return regs->ARM_r0;case NR (set_tls): - thread->tp_value = regs->ARM_r0; + thread->tp_value[0 ] = regs->ARM_r0;if (tls_emu)return 0 ;if (has_tls_reg) {
@@ -706,7 +706,7 @@ static int get_tp_trap(struct pt_regs *regs, unsigned int instr)int reg = (instr >>12 ) &15 ;if (reg ==15 )return 1 ; - regs->uregs[reg] =current_thread_info ()->tp_value; + regs->uregs[reg] =current_thread_info ()->tp_value[0 ]; regs->ARM_pc +=4 ;return 0 ; }
Am 03.05.2013 17:24, schrieb Jonathan Austin:
Quoted 11 lines
Hi Russell,
Thanks for the comments - you're right about the '
switch_tls' being more appropriate - needed to take a step back to see that.I've got a few questions, added inline.
Andr?, Assuming I've understood things okay, there's a patch that uses Russell's asm stuff (with minor modifications, see the questions) and includes the C-world changes too. Perhaps you could see that it solves your problem?
I'll test it tomorrow with my test tool and Wine.
FWIW i hacked together a test tool and it's available at https://github.com/AndreRH/tpidrurw-test
Am 03.05.2013 17:24, schrieb Jonathan Austin:
Quoted 4 lines
Quoted 4 lines
(quoted diff) +1
-1- .macro set_tls_none, tp, tmp1, tmp2 + .macro switch_tls_none, base, tp, trw, tmp1, tmp2 .endm
Quoted 9 lines
Review
Quoted 2 lines
(quoted diff) +1
-1- .macro set_tls_v6k, tp, tmp1, tmp2 + .macro switch_tls_v6k, base, tp, trw, tmp1, tmp2How do you feel about calling tp and trw something different? tpidro and tpidrw, or tp and tpuser?
The naming threw me off slightly first time I read this new signature (tp=thread_pointer/tls_pointer/etc).
FWIW i think tp&tpuser is more consistent.
Quoted 4 lines
André, Assuming I've understood things okay, there's a patch that uses Russell's asm stuff (with minor modifications, see the questions) and includes the C-world changes too. Perhaps you could see that it solves your problem?
It works, but for various reasons i would like to suggest the patch below.
Reasons include: My thoughts about tp&tpuser naming and the helper function for copy_thread, further i'd really like to get a bit credit for spending weeks on getting my second kernel patch in :) If that patch is fine for you and no one object, i'd be happy to test it, adapt the commit message and include:
Not totally sure about the Signed-off-bys. Can i add a Signed-off-by for Russell King? Is it the right mail address for him/you?
arch/arm/include/asm/thread_info.h +1 -1
@@ -58,7 +58,7 @@ struct thread_info {struct cpu_context_save cpu_context;/* cpu context */ __u32 syscall;/* syscall number */ __u8 used_cp[16 ];/* thread used copro */ -unsigned long tp_value; +unsigned long tp_value[2 ];/* TLS registers */ #ifdef CONFIG_CRUNCH struct crunch_state crunchstate;#endif
arch/arm/include/asm/tls.h +28 -13
74 lines not on this page · raw message
arch/arm/kernel/entry-armv.S +2 -2
@@ -728,15 +728,15 @@ ENTRY(__switch_to) UNWIND(.fnstart ) UNWIND(.cantunwind ) add ip, r1, #TI_CPU_SAVE - ldr r3, [r2, #TI_TP_VALUE] ARM( stmia ip!, {r4 - sl, fp, sp, lr} ) @ Store most regs on stack THUMB( stmia ip!, {r4 - sl, fp} ) @ Store most regs on stack THUMB( str sp, [ip], #4 ) THUMB( str lr, [ip], #4 ) + ldrd r4, r5, [r2, #TI_TP_VALUE] #ifdef CONFIG_CPU_USE_DOMAINS ldr r6, [r2, #TI_CPU_DOMAIN] #endif - set_tls r3, r4, r5 + switch_tls r1, r4, r5, r3, r7 #if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP) ldr r7, [r2, #TI_TASK] ldr r8, =__stack_chk_guard
arch/arm/kernel/process.c +3 -1
@@ -39,6 +39,7 @@#include <asm/thread_notify.h> #include <asm/stacktrace.h> #include <asm/mach/time.h> +#include <asm/tls.h> #ifdef CONFIG_CC_STACKPROTECTOR #include <linux/stackprotector.h>
@@ -395,7 +396,8 @@ copy_thread(unsigned long clone_flags, unsigned long stack_start,clear_ptrace_hw_breakpoint (p);if (clone_flags &CLONE_SETTLS ) - thread->tp_value = childregs->ARM_r3; + thread->tp_value[0 ] = childregs->ARM_r3; + thread->tp_value[1 ] =get_tlsuser ();thread_notify (THREAD_NOTIFY_COPY , thread);
arch/arm/kernel/ptrace.c +1 -1
@@ -849,7 +849,7 @@ long arch_ptrace(struct task_struct *child, long request,#endif case PTRACE_GET_THREAD_AREA : - ret =put_user (task_thread_info (child)->tp_value, + ret =put_user (task_thread_info (child)->tp_value[0 ], datap);break ;
arch/arm/kernel/traps.c +2 -2
@@ -588,7 +588,7 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)return regs->ARM_r0;case NR (set_tls): - thread->tp_value = regs->ARM_r0; + thread->tp_value[0 ] = regs->ARM_r0;if (tls_emu)return 0 ;if (has_tls_reg) {
@@ -706,7 +706,7 @@ static int get_tp_trap(struct pt_regs *regs, unsigned int instr)int reg = (instr >>12 ) &15 ;if (reg ==15 )return 1 ; - regs->uregs[reg] =current_thread_info ()->tp_value; + regs->uregs[reg] =current_thread_info ()->tp_value[0 ]; regs->ARM_pc +=4 ;return 0 ; }
_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Hi André,
Quoted 22 lines · André Hentschel
Am 03.05.2013 17:24, schrieb Jonathan Austin:
Quoted 4 lines
Quoted 4 lines
arch/arm/include/asm/tls.h +1
-1- .macro set_tls_none, tp, tmp1, tmp2 + .macro switch_tls_none, base, tp, trw, tmp1, tmp2 .endmQuoted 9 lines
Review
Quoted 2 lines
(quoted diff) +1
-1- .macro set_tls_v6k, tp, tmp1, tmp2 + .macro switch_tls_v6k, base, tp, trw, tmp1, tmp2How do you feel about calling tp and trw something different? tpidro and tpidrw, or tp and tpuser?
The naming threw me off slightly first time I read this new signature (tp=thread_pointer/tls_pointer/etc).
FWIW i think tp&tpuser is more consistent.
Quoted 4 lines
André, Assuming I've understood things okay, there's a patch that uses Russell's asm stuff (with minor modifications, see the questions) and includes the C-world changes too. Perhaps you could see that it solves your problem?
Quoted 8 lines
It works, but for various reasons i would like to suggest the patch below.
Reasons include: My thoughts about tp&tpuser naming and the helper function forcopy_thread, further i'd really like to get a bit credit for spending weeks on getting my second kernel patch in :) If that patch is fine for you and no one object, i'd be happy to test it, adapt the commit message and include:Reported-by André Hentschel <[email protected]>Signed-off-by André Hentschel <[email protected]>Signed-off-by Will Deacon <[email protected]>Signed-off-by Russell King <[email protected]>Signed-off-by Jonathan Austin <[email protected]>
I'm not worried about authorship, so you're welcome to be the author on the patch - assuming Russell's happy with that and with the change of register naming in your patch below.
As this will go through Russell's tree (probably via has patch system), you don't need his Signed-off-by - It'll get added as he takes the patch. As you're signing off, too, you don't need to list yourself as reporting the problem.
It'll look a bit weird to have 3 people signing off on quite a little patch (4 once it goes through Russell's tree) so it's best to make some notes in the commit message about the way this patch was written (IE many authors)
Hope that helps,
Jonny
Quoted 180 lines
Not totally sure about the Signed-off-bys. Can i add a Signed-off-by for Russell King? Is it the right mail address for him/you?
arch/arm/include/asm/thread_info.h +1
-1@@ -58,7 +58,7 @@ struct thread_info {struct cpu_context_save cpu_context;/* cpu context */ __u32 syscall;/* syscall number */ __u8 used_cp[16 ];/* thread used copro */ -unsigned long tp_value; +unsigned long tp_value[2 ];/* TLS registers */ #ifdef CONFIG_CRUNCH struct crunch_state crunchstate;#endif arch/arm/include/asm/tls.h +28
-13@@ -2,27 +2,30 @@#define __ASMARM_TLS_H#ifdef __ASSEMBLY__ - .macro set_tls_none, tp, tmp1, tmp2 +#include <asm/asm-offsets.h> + .macro switch_tls_none, base, tp, tpuser, tmp1, tmp2 .endm - .macro set_tls_v6k, tp, tmp1, tmp2 + .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 - mov \tmp1, #0 - mcr p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register + mcr p15, 0, \tpuser, c13, c0, 2 @ and the user r/w register + strne \tmp2, [\base, #TI_TP_VALUE + 4] @ save it .endm - .macro set_tls_v6, tp, tmp1, tmp2 + .macro switch_tls_v6, base, tp, tpuser, tmp1, tmp2 ldr \tmp1, =elf_hwcap ldr \tmp1, [\tmp1, #0] mov \tmp2, #0xffff0fff tst \tmp1, #HWCAP_TLS @ hardware TLS available? - mcrne p15, 0, \tp, c13, c0, 3 @ yes, set TLS register - movne \tmp1, #0 - mcrne p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register streq \tp, [\tmp2, #-15] @ set TLS value at 0xffff0ff0 + mrcne p15, 0, \tmp2, c13, c0, 2 @ get the user r/w register + mcrne p15, 0, \tp, c13, c0, 3 @ yes, set TLS register + mcrne p15, 0, \tpuser, c13, c0, 2 @ set user r/w register + strne \tmp2, [\base, #TI_TP_VALUE + 4] @ save it .endm - .macro set_tls_software, tp, tmp1, tmp2 + .macro switch_tls_software, base, tp, tpuser, tmp1, tmp2 mov \tmp1, #0xffff0fff str \tp, [\tmp1, #-15] @ set TLS value at 0xffff0ff0 .endm@@ -31,19 +34,31 @@#ifdef CONFIG_TLS_REG_EMUL #define tls_emu 1#define has_tls_reg 1 -#define set_tls set_tls_none +#define switch_tls switch_tls_none#elif defined(CONFIG_CPU_V6 )#define tls_emu 0#define has_tls_reg (elf_hwcap & HWCAP_TLS) -#define set_tls set_tls_v6 +#define switch_tls switch_tls_v6#elif defined(CONFIG_CPU_32v6K)#define tls_emu 0#define has_tls_reg 1 -#define set_tls set_tls_v6k +#define switch_tls switch_tls_v6k#else #define tls_emu 0#define has_tls_reg 0 -#define set_tls set_tls_software +#define switch_tls switch_tls_software#endif +#ifndef __ASSEMBLY__ +static inline unsigned long get_tlsuser (void ) +{ +if (has_tls_reg && !tls_emu) + { +unsigned long t; + __asm__("mcr p15, 0, %0, c13, c0, 2" : :"r" (t)); +return t; + } +return 0 ; +} +#endif #endif /* __ASMARM_TLS_H */ arch/arm/kernel/entry-armv.S +2
-2@@ -728,15 +728,15 @@ ENTRY(__switch_to) UNWIND(.fnstart ) UNWIND(.cantunwind ) add ip, r1, #TI_CPU_SAVE - ldr r3, [r2, #TI_TP_VALUE] ARM( stmia ip!, {r4 - sl, fp, sp, lr} ) @ Store most regs on stack THUMB( stmia ip!, {r4 - sl, fp} ) @ Store most regs on stack THUMB( str sp, [ip], #4 ) THUMB( str lr, [ip], #4 ) + ldrd r4, r5, [r2, #TI_TP_VALUE] #ifdef CONFIG_CPU_USE_DOMAINS ldr r6, [r2, #TI_CPU_DOMAIN] #endif - set_tls r3, r4, r5 + switch_tls r1, r4, r5, r3, r7 #if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP) ldr r7, [r2, #TI_TASK] ldr r8, =__stack_chk_guardarch/arm/kernel/process.c +3
-1@@ -39,6 +39,7 @@#include <asm/thread_notify.h> #include <asm/stacktrace.h> #include <asm/mach/time.h> +#include <asm/tls.h> #ifdef CONFIG_CC_STACKPROTECTOR #include <linux/stackprotector.h> @@ -395,7 +396,8 @@ copy_thread(unsigned long clone_flags, unsigned long stack_start,clear_ptrace_hw_breakpoint (p);if (clone_flags &CLONE_SETTLS ) - thread->tp_value = childregs->ARM_r3; + thread->tp_value[0 ] = childregs->ARM_r3; + thread->tp_value[1 ] =get_tlsuser ();thread_notify (THREAD_NOTIFY_COPY , thread);arch/arm/kernel/ptrace.c +1
-1@@ -849,7 +849,7 @@ long arch_ptrace(struct task_struct *child, long request,#endif case PTRACE_GET_THREAD_AREA : - ret =put_user (task_thread_info (child)->tp_value, + ret =put_user (task_thread_info (child)->tp_value[0 ], datap);break ;arch/arm/kernel/traps.c +2
-2@@ -588,7 +588,7 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)return regs->ARM_r0;case NR (set_tls): - thread->tp_value = regs->ARM_r0; + thread->tp_value[0 ] = regs->ARM_r0;if (tls_emu)return 0 ;if (has_tls_reg) {@@ -706,7 +706,7 @@ static int get_tp_trap(struct pt_regs *regs, unsigned int instr)int reg = (instr >>12 ) &15 ;if (reg ==15 )return 1 ; - regs->uregs[reg] =current_thread_info ()->tp_value; + regs->uregs[reg] =current_thread_info ()->tp_value[0 ]; regs->ARM_pc +=4 ;return 0 ; }