[PATCH 1/2] arm: Replace CONFIG_HAS_TLS_REG with HWCAP_TLS and check for it on V6

Nicolas Pitre nico at fluxnic.net
Tue Jun 29 15:20:33 EDT 2010


On Tue, 29 Jun 2010, Tony Lindgren wrote:

> OK. Sorry for the delay again. Here's an updated version that sets
> __kuser_get_tls instruction dynamically. Does this do what you were
> thinking, or did I miss something?

See my comments below.

> Also, can we detect somehow the hardware that uses CONFIG_TLS_REG_EMUL?
> Might be possible to remove that Kconfig option too later on..

Well... I don't think we _should_ try to detect it as it is not widely 
available if at all.

And there is actually a "bug" with __kuser_get_tls because if ever 
CONFIG_TLS_REG_EMUL is selected then a call to __kuser_get_tls is 
currently doing nothing while it should actually use the mrc 
instruction.

> diff --git a/arch/arm/include/asm/hwcap.h b/arch/arm/include/asm/hwcap.h
> index f7bd52b..c1062c3 100644
> --- a/arch/arm/include/asm/hwcap.h
> +++ b/arch/arm/include/asm/hwcap.h
> @@ -19,6 +19,7 @@
>  #define HWCAP_NEON	4096
>  #define HWCAP_VFPv3	8192
>  #define HWCAP_VFPv3D16	16384
> +#define HWCAP_TLS	32768
>  
>  #if defined(__KERNEL__) && !defined(__ASSEMBLY__)
>  /*
> diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
> index 7ee48e7..949df9b 100644
> --- a/arch/arm/kernel/entry-armv.S
> +++ b/arch/arm/kernel/entry-armv.S
> @@ -739,11 +739,13 @@ ENTRY(__switch_to)
>  #ifdef CONFIG_MMU
>  	ldr	r6, [r2, #TI_CPU_DOMAIN]
>  #endif
> -#if defined(CONFIG_HAS_TLS_REG)
> -	mcr	p15, 0, r3, c13, c0, 3		@ set TLS register
> -#elif !defined(CONFIG_TLS_REG_EMUL)
> -	mov	r4, #0xffff0fff
> -	str	r3, [r4, #-15]			@ TLS val at 0xffff0ff0
> +#if !defined(CONFIG_TLS_REG_EMUL)
> +	ldr	r4, =elf_hwcap
> +	ldr	r4, [r4, #0]
> +	mov	r5, #0xffff0fff
> +	tst	r4, #HWCAP_TLS			@ hardware TLS available?
> +	mcrne	p15, 0, r3, c13, c0, 3		@ yes, set TLS register
> +	streq	r3, [r5, #-15]			@ set TLS value at 0xffff0ff0
>  #endif

Could this be a tristate instead?  There are actually 3 cases:

1) We know the TLS reg is _always_ available i.e. ARMv6K, ARMV7 and 
   above.  In that case the test on elf_hwcap is redundant and wasteful.

2) We know the TLS reg is _never_ available i.e. pre-ARMv6.  The test on 
   elf_hwcap is again redundant and wasteful.

3) We're unsure as the actual CPU is known only at run time.

Case #1 will become the common case in the future.  Case #2 is still 
widely relevant for deployed systems in the field, and some popular 
ARMv5TE based SOCs are still produced right now.  So instead of 
replacing #1 and #2 with #3, I think you should add #3 to the other 
cases instead.

>  #ifdef CONFIG_MMU
>  	mcr	p15, 0, r6, c3, c0, 0		@ Set domain register
> @@ -1009,16 +1011,13 @@ kuser_cmpxchg_fixup:
>   */
>  
>  __kuser_get_tls:				@ 0xffff0fe0
> -
> -#if !defined(CONFIG_HAS_TLS_REG) && !defined(CONFIG_TLS_REG_EMUL)
> -	ldr	r0, [pc, #(16 - 8)]		@ TLS stored at 0xffff0ff0
> -#else
> -	mrc	p15, 0, r0, c13, c0, 3		@ read TLS register
> -#endif
> +	nop				@ read TLS, set in kuser_get_tls_init
>  	usr_ret	lr
> -
> -	.rep	5
> -	.word	0			@ pad up to __kuser_helper_version
> +	mrc	p15, 0, r0, c13, c0, 3	@ 0xffff0fe8 hardware TLS code
> +	ldr	r0, [pc, #(16 - 8)]	@ 0xffff0fec software TLS code
> +	.word	0			@ 0xffff0ff0 software TLS value
> +	nop				@ pad up to __kuser_helper_version
> +	nop
>  	.endr

This looks obscur.  The idea of patching the appropriate instruction at 
runtime here is a good one.  However I'd simply keep the ldr version in 
place otherwise the pc displacement doesn't match anymore when 
disassembling.  And simply overwrite it with the mrc version when 
necessary.

BTW you left a stray .endr here.

>  /*
> diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
> index 122d999..a675260 100644
> --- a/arch/arm/kernel/setup.c
> +++ b/arch/arm/kernel/setup.c
> @@ -269,6 +269,27 @@ static void __init cacheid_init(void)
>  extern struct proc_info_list *lookup_processor_type(unsigned int);
>  extern struct machine_desc *lookup_machine_type(unsigned int);
>  
> +#ifdef CONFIG_CPU_V6
> +static void __init feat_v6_fixup(void)
> +{
> +	int id = read_cpuid_id();
> +
> +	if (id & 0x000f0000 != 0x00070000)
> +		return;
> +
> +	/*
> +	 * HWCAP_TLS is available only on 1136 r1p0 and later,
> +	 * see also kuser_get_tls_init.
> +	 */
> +	if ((((id >> 4) & 0xfff) == 0xb36) && (((id >> 20) & 3) == 0))
> +		elf_hwcap &= ~HWCAP_TLS;

You should probably test for the vendor ID as well (ARM in this case).

> +}
> +#else
> +static inline void feat_v6_fixup(void)
> +{
> +}
> +#endif

I think the #ifdef is unnecessary here.  This is __init code anyway, so 
this could as well be always compiled in.

> +
>  static void __init setup_processor(void)
>  {
>  	struct proc_info_list *list;
> @@ -311,6 +332,8 @@ static void __init setup_processor(void)
>  	elf_hwcap &= ~HWCAP_THUMB;
>  #endif
>  
> +	feat_v6_fixup();
> +
>  	cacheid_init();
>  	cpu_proc_init();
>  }
> diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
> index 1621e53..85dd001 100644
> --- a/arch/arm/kernel/traps.c
> +++ b/arch/arm/kernel/traps.c
> @@ -518,16 +518,19 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)
>  
>  	case NR(set_tls):
>  		thread->tp_value = regs->ARM_r0;
> -#if defined(CONFIG_HAS_TLS_REG)
> -		asm ("mcr p15, 0, %0, c13, c0, 3" : : "r" (regs->ARM_r0) );
> -#elif !defined(CONFIG_TLS_REG_EMUL)
> -		/*
> -		 * User space must never try to access this directly.
> -		 * Expect your app to break eventually if you do so.
> -		 * The user helper at 0xffff0fe0 must be used instead.
> -		 * (see entry-armv.S for details)
> -		 */
> -		*((unsigned int *)0xffff0ff0) = regs->ARM_r0;
> +#if !defined(CONFIG_TLS_REG_EMUL)
> +		if (elf_hwcap & HWCAP_TLS) {
> +			asm ("mcr p15, 0, %0, c13, c0, 3"
> +				: : "r" (regs->ARM_r0));
> +		} else {
> +			/*
> +			 * User space must never try to access this directly.
> +			 * Expect your app to break eventually if you do so.
> +			 * The user helper at 0xffff0fe0 must be used instead.
> +			 * (see entry-armv.S for details)
> +			 */
> +			*((unsigned int *)0xffff0ff0) = regs->ARM_r0;
> +		}
>  #endif

The same comment as for __kuser_get_tls would apply here.  However, 
instead of duplicating the code block, you could define a macro, such as 
has_tls_reg(), that would be either 0, 1, or ((elf_hwcap & HWCAP_TLS).

>  		return 0;
>  
> @@ -743,6 +746,21 @@ void __init trap_init(void)
>  	return;
>  }
>  
> +#if defined(CONFIG_TLS_REG_EMUL)
> +static void __init kuser_get_tls_init(unsigned long vectors)
> +{
> +	memcpy((void *)vectors + 0xfe0, (void *)vectors + 0xfe8, 4);
> +}
> +#else
> +static void __init kuser_get_tls_init(unsigned long vectors)
> +{
> +	if (elf_hwcap & HWCAP_TLS)
> +		memcpy((void *)vectors + 0xfe0, (void *)vectors + 0xfe8, 4);
> +	else
> +		memcpy((void *)vectors + 0xfe0, (void *)vectors + 0xfec, 4);
> +}
> +#endif

Please move the #ifdef within the function body.  Also it would be nice 
to add comments about what those magic offsets are.


Nicolas



More information about the linux-arm-kernel mailing list