[U-Boot] [PATCH v2 6/7] ARM: extend non-secure switch to also go into HYP mode

Christoffer Dall christoffer.dall at linaro.org
Thu Jun 20 01:40:23 CEST 2013


On Thu, Jun 13, 2013 at 01:01:12PM +0200, Andre Przywara wrote:
> For the KVM and XEN hypervisors to be usable, we need to enter the
> kernel in HYP mode. Now that we already are in non-secure state,
> HYP mode switching is within short reach.
> 
> While doing the non-secure switch, we have to enable the HVC
> instruction and setup the HYP mode HVBAR (while still secure).
> 
> The actual switch is done by dropping back from a HYP mode handler
> without actually leaving HYP mode, so we introduce a new handler
> routine in our new secure exception vector table.
> 
> In the assembly switching routine we save and restore the banked LR
> and SP registers around the hypercall to do the actual HYP mode
> switch.
> 
> The C routine first checks whether we are in HYP mode already and
> also whether the virtualization extensions are available. It also
> checks whether the HYP mode switch was finally successful.
> The bootm command part only adds and adjusts some error reporting.
> 
> Signed-off-by: Andre Przywara <andre.przywara at linaro.org>
> ---
>  arch/arm/cpu/armv7/nonsec_virt.S | 31 ++++++++++++++++++++++++++++---
>  arch/arm/include/asm/armv7.h     |  7 +++++--
>  arch/arm/lib/bootm.c             | 14 ++++++++++----
>  arch/arm/lib/virt-v7.c           | 27 ++++++++++++++++++++++-----
>  4 files changed, 65 insertions(+), 14 deletions(-)
> 
> diff --git a/arch/arm/cpu/armv7/nonsec_virt.S b/arch/arm/cpu/armv7/nonsec_virt.S
> index 919f6e9..950da6f 100644
> --- a/arch/arm/cpu/armv7/nonsec_virt.S
> +++ b/arch/arm/cpu/armv7/nonsec_virt.S
> @@ -1,5 +1,5 @@
>  /*
> - * code for switching cores into non-secure state
> + * code for switching cores into non-secure state and into HYP mode
>   *
>   * Copyright (c) 2013	Andre Przywara <andre.przywara at linaro.org>
>   *
> @@ -26,14 +26,14 @@
>  #include <asm/gic.h>
>  #include <asm/armv7.h>
>  
> -/* the vector table for secure state */
> +/* the vector table for secure state and HYP mode */
>  _secure_vectors:
>  	.word 0	/* reset */
>  	.word 0 /* undef */
>  	adr pc, _secure_monitor
>  	.word 0
>  	.word 0
> -	.word 0
> +	adr pc, _hyp_trap
>  	.word 0
>  	.word 0
>  	.word 0	/* pad */
> @@ -50,10 +50,23 @@ _secure_monitor:
>  	bic	r1, r1, #0x4e			@ clear IRQ, FIQ, EA, nET bits
>  	orr	r1, r1, #0x31			@ enable NS, AW, FW bits
>  
> +	mrc	p15, 0, r0, c0, c1, 1		@ read ID_PFR1
> +	and	r0, r0, #CPUID_ARM_VIRT_MASK	@ mask virtualization bits
> +	cmp	r0, #(1 << CPUID_ARM_VIRT_SHIFT)
> +	orreq	r1, r1, #0x100			@ allow HVC instruction
> +
>  	mcr	p15, 0, r1, c1, c1, 0		@ write SCR (with NS bit set)
>  
> +	mrceq	p15, 0, r0, c12, c0, 1		@ get MVBAR value

Why not just do load the address of _secure_vectors directly?  I think it
makes it more clear what happens.

> +	mcreq	p15, 4, r0, c12, c0, 0		@ write HVBAR
> +
>  	movs	pc, lr				@ return to non-secure SVC
>  
> +_hyp_trap:
> +	.byte 0x00, 0xe3, 0x0e, 0xe1		@ mrs lr, elr_hyp

Again, why not just add the necessary .arch_extension or assembler
directive in the makefile and use the instructions directly.

The only reason I would see for performing this obscurity would be to
support really old compilers, which I doubt we fill for future boards?

> +	mov pc, lr				@ do no switch modes, but
> +						@ return to caller
> +
>  /*
>   * Secondary CPUs start here and call the code for the core specific parts
>   * of the non-secure and HYP mode transition. The GIC distributor specific
> @@ -69,6 +82,7 @@ _smp_pen:
>  	mcr	p15, 0, r1, c12, c0, 0		@ set VBAR
>  
>  	bl	_nonsec_init
> +	bl	_hyp_init
>  
>  	ldr	r1, [r3, #0x0c]			@ read GICD acknowledge
>  	str	r1, [r3, #0x10]			@ write GICD EOI
> @@ -145,3 +159,14 @@ _nonsec_init:
>  	str	r1, [r2]			@ allow private interrupts
>  
>  	bx	lr
> +
> +.globl _hyp_init
> +_hyp_init:

nit: the naming here is a little misleading for someone not knowing
what's going on. You're not really initializing the mode, but switching
to it:

_switch_to_hyp: ???

> +	mov	r2, lr
> +	mov	r3, sp				@ save SVC copy of LR and SP
> +	isb

I don't think this isb is necessary.

> +	.byte 0x70, 0x00, 0x40, 0xe1		@ hvc #0

again, this is really funky.  If it's really necessary, can we please
have a define somewhere so you can just do HVC(0) instead?

> +	mov	sp, r3
> +	mov	lr, r2				@ fix HYP mode banked LR and SP

nit: s/fix HYP mode/restore S-SVC mode/

> +
> +	bx	lr
> diff --git a/arch/arm/include/asm/armv7.h b/arch/arm/include/asm/armv7.h
> index 04545b9..8c3a85e 100644
> --- a/arch/arm/include/asm/armv7.h
> +++ b/arch/arm/include/asm/armv7.h
> @@ -89,15 +89,18 @@ void v7_outer_cache_inval_range(u32 start, u32 end);
>  
>  #ifdef CONFIG_ARMV7_VIRT
>  
> -#define HYP_ERR_NO_SEC_EXT		2
> +#define HYP_ERR_ALREADY_HYP_MODE	1
> +#define HYP_ERR_NO_VIRT_EXT		2
>  #define HYP_ERR_NO_GIC_ADDRESS		3
>  #define HYP_ERR_GIC_ADDRESS_ABOVE_4GB	4
> +#define HYP_ERR_NOT_HYP_MODE		5

see, this is just messy. I really don't think you need to propogate
error values like this.

>  
> -int armv7_switch_nonsec(void);
> +int armv7_switch_hyp(void);
>  
>  /* defined in cpu/armv7/nonsec_virt.S */
>  void _nonsec_init(void);
>  void _smp_pen(void);
> +void _hyp_init(void);
>  #endif /* CONFIG_ARMV7_VIRT */
>  
>  #endif /* ! __ASSEMBLY__ */
> diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c
> index 8251a89..7edd84d 100644
> --- a/arch/arm/lib/bootm.c
> +++ b/arch/arm/lib/bootm.c
> @@ -227,12 +227,15 @@ static void boot_prep_linux(bootm_headers_t *images)
>  		hang();
>  	}
>  #ifdef CONFIG_ARMV7_VIRT
> -	switch (armv7_switch_nonsec()) {
> +	switch (armv7_switch_hyp()) {
>  	case 0:
> -		debug("entered non-secure state\n");
> +		debug("entered HYP mode\n");
>  		break;
> -	case HYP_ERR_NO_SEC_EXT:
> -		printf("HYP mode: Security extensions not implemented.\n");
> +	case HYP_ERR_ALREADY_HYP_MODE:
> +		debug("CPU already in HYP mode\n");
> +		break;
> +	case HYP_ERR_NO_VIRT_EXT:
> +		printf("HYP mode: Virtualization extensions not implemented.\n");
>  		break;
>  	case HYP_ERR_NO_GIC_ADDRESS:
>  		printf("HYP mode: could not determine GIC address.\n");
> @@ -240,6 +243,9 @@ static void boot_prep_linux(bootm_headers_t *images)
>  	case HYP_ERR_GIC_ADDRESS_ABOVE_4GB:
>  		printf("HYP mode: PERIPHBASE is above 4 GB, cannot access this.\n");
>  		break;
> +	case HYP_ERR_NOT_HYP_MODE:
> +		printf("HYP mode: switch not successful.\n");
> +		break;
>  	}
>  #endif
>  }
> diff --git a/arch/arm/lib/virt-v7.c b/arch/arm/lib/virt-v7.c
> index 6946e4d..1e206b9 100644
> --- a/arch/arm/lib/virt-v7.c
> +++ b/arch/arm/lib/virt-v7.c
> @@ -3,6 +3,7 @@
>   * Andre Przywara, Linaro
>   *
>   * Routines to transition ARMv7 processors from secure into non-secure state
> + * and from non-secure SVC into HYP mode
>   * needed to enable ARMv7 virtualization for current hypervisors
>   *
>   * See file CREDITS for list of people who contributed to this
> @@ -29,6 +30,14 @@
>  #include <asm/gic.h>
>  #include <asm/io.h>
>  
> +static unsigned int read_cpsr(void)
> +{
> +	unsigned int reg;
> +
> +	asm volatile ("mrs %0, cpsr\n" : "=r" (reg));
> +	return reg;
> +}
> +
>  static unsigned int read_id_pfr1(void)
>  {
>  	unsigned int reg;
> @@ -110,16 +119,20 @@ static void kick_secondary_cpus(char *gicdptr)
>  	writel(1U << 24, &gicdptr[GICD_SGIR]);
>  }
>  
> -int armv7_switch_nonsec(void)
> +int armv7_switch_hyp(void)
>  {
>  	unsigned int reg, ret;
>  	char *gicdptr;
>  	unsigned itlinesnr, i;
>  
> -	/* check whether the CPU supports the security extensions */
> +	/* check whether we are in HYP mode already */
> +	if ((read_cpsr() & 0x1f) == 0x1a)
> +		return HYP_ERR_ALREADY_HYP_MODE;
> +
> +	/* check whether the CPU supports the virtualization extensions */
>  	reg = read_id_pfr1();
> -	if ((reg & 0xF0) == 0)
> -		return HYP_ERR_NO_SEC_EXT;
> +	if ((reg & CPUID_ARM_VIRT_MASK) != 1 << CPUID_ARM_VIRT_SHIFT)
> +		return HYP_ERR_NO_VIRT_EXT;
>  
>  	set_generic_timer_frequency();
>  
> @@ -147,8 +160,12 @@ int armv7_switch_nonsec(void)
>  
>  	kick_secondary_cpus(gicdptr);
>  
> -	/* call the non-sec switching code on this CPU also */
> +	/* call the HYP switching code on this CPU also */
>  	_nonsec_init();
> +	_hyp_init();
> +
> +	if ((read_cpsr() & 0x1F) != 0x1a)
> +		return HYP_ERR_NOT_HYP_MODE;
>  
>  	return 0;
>  }
> -- 
> 1.7.12.1
> 


More information about the U-Boot mailing list