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

Andre Przywara andre.przywara at linaro.org
Tue Jul 30 13:59:29 CEST 2013


On 07/30/2013 12:02 AM, Christoffer Dall wrote:
> On Wed, Jul 10, 2013 at 01:54:18AM +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/Makefile      |  2 +-
>>   arch/arm/cpu/armv7/nonsec_virt.S | 43 +++++++++++++++++++++++++++++++++++-----
>>   arch/arm/cpu/armv7/virt-v7.c     | 31 +++++++++++++++++++++++++++++
>>   arch/arm/include/asm/armv7.h     |  9 +++++++--
>>   arch/arm/lib/bootm.c             | 19 +++++++++++++++---
>>   5 files changed, 93 insertions(+), 11 deletions(-)
>>
>> diff --git a/arch/arm/cpu/armv7/Makefile b/arch/arm/cpu/armv7/Makefile
>> index b59f59e..e5eaa56 100644
>> --- a/arch/arm/cpu/armv7/Makefile
>> +++ b/arch/arm/cpu/armv7/Makefile
>> @@ -36,7 +36,7 @@ ifneq ($(CONFIG_AM33XX)$(CONFIG_OMAP44XX)$(CONFIG_OMAP54XX)$(CONFIG_TEGRA)$(CONF
>>   SOBJS	+= lowlevel_init.o
>>   endif
>>
>> -ifneq ($(CONFIG_ARMV7_NONSEC),)
>> +ifneq ($(CONFIG_ARMV7_NONSEC)$(CONFIG_ARMV7_VIRT),)
>>   SOBJS   += nonsec_virt.o
>>   COBJS	+= virt-v7.o
>>   endif
>> diff --git a/arch/arm/cpu/armv7/nonsec_virt.S b/arch/arm/cpu/armv7/nonsec_virt.S
>> index f9b6b39..895c3b0 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>
>>    *
>> @@ -28,15 +28,16 @@
>>   #include <asm/armv7.h>
>>
>>   .arch_extension sec
>> +.arch_extension virt
>>
>> -/* the vector table for secure state */
>> +/* the vector table for secure state and HYP mode */
>>   _monitor_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 */
>> @@ -53,10 +54,27 @@ _secure_monitor:
>>   	bic	r1, r1, #0x4e			@ clear IRQ, FIQ, EA, nET bits
>>   	orr	r1, r1, #0x31			@ enable NS, AW, FW bits
>>
>> +#ifdef CONFIG_ARMV7_VIRT
>> +	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
>> +#endif
>> +
>>   	mcr	p15, 0, r1, c1, c1, 0		@ write SCR (with NS bit set)
>>
>> +#ifdef CONFIG_ARMV7_VIRT
>> +	mrceq	p15, 0, r0, c12, c0, 1		@ get MVBAR value
>> +	mcreq	p15, 4, r0, c12, c0, 0		@ write HVBAR
>> +#endif
>> +
>>   	movs	pc, lr				@ return to non-secure SVC
>>
>> +_hyp_trap:
>> +	mrs	lr, elr_hyp	@ for older asm: .byte 0x00, 0xe3, 0x0e, 0xe1
>
> this comment just confuses: either make it intelligent to support an
> older compiler or just get rid of these byte encodings.  You can always
> disassemble the file and lookup the byte code with a modern compiler to
> get back to the byte encoding.

Well, I used a Debian 6 cross compiler before, which didn't support 
these instructions. After your remark I updated the system to Debian 7, 
but found it not appropriate to ask any user to do the same just to use 
a fixed, non-parametrized assembly instruction. I have the feeling that 
there are quite some users out there who cannot and don't want to easily 
update their compiler.
So I decided to leave the workaround in the comment to give a hint to a 
quick fix.

By "making it intelligent" you mean a macro which does some version 
checking and inserts the .byte sequence if needed? Are there any 
archetypes of such code?

Regards,
Andre.

>> +	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
>> @@ -71,9 +89,13 @@ ENTRY(_smp_pen)
>>   	mcr	p15, 0, r1, c12, c0, 0		@ set VBAR
>>
>>   	bl	_nonsec_init
>> +	mov	r12, r0				@ save GICC address
>> +#ifdef CONFIG_ARMV7_VIRT
>> +	bl	_switch_to_hyp
>> +#endif
>>
>> -	ldr	r1, [r0, #GICC_IAR]		@ acknowledge IPI
>> -	str	r1, [r0, #GICC_EOIR]		@ signal end of interrupt
>> +	ldr	r1, [r12, #GICC_IAR]		@ acknowledge IPI
>> +	str	r1, [r12, #GICC_EOIR]		@ signal end of interrupt
>>   	adr	r1, _smp_pen
>>   waitloop:
>>   	wfi
>> @@ -164,3 +186,14 @@ ENTRY(_nonsec_init)
>>
>>   	bx	lr
>>   ENDPROC(_nonsec_init)
>> +
>> +ENTRY(_switch_to_hyp)
>> +	mov	r0, lr
>> +	mov	r1, sp				@ save SVC copy of LR and SP
>> +	isb
>
> did you find out that this isb is indeed needed? if so, why?
>
>> +	hvc #0			 @ for older asm: .byte 0x70, 0x00, 0x40, 0xe1
>
> same comment as above?
>
>> +	mov	sp, r1
>> +	mov	lr, r0				@ restore SVC copy of LR and SP
>> +
>> +	bx	lr
>> +ENDPROC(_switch_to_hyp)
>> diff --git a/arch/arm/cpu/armv7/virt-v7.c b/arch/arm/cpu/armv7/virt-v7.c
>> index a0d0b34..3645572 100644
>> --- a/arch/arm/cpu/armv7/virt-v7.c
>> +++ b/arch/arm/cpu/armv7/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;
>> @@ -92,6 +101,28 @@ static void kick_secondary_cpus(unsigned int gicdaddr)
>>   	writel(1U << 24, gicdaddr + GICD_SGIR);
>>   }
>>
>> +enum nonsec_virt_errors armv7_switch_hyp(void)
>> +{
>> +	unsigned int reg;
>> +
>> +	/* check whether we are in HYP mode already */
>> +	if ((read_cpsr() & 0x1f) == 0x1a)
>> +		return VIRT_ALREADY_HYP_MODE;
>> +
>> +	/* check whether the CPU supports the virtualization extensions */
>> +	reg = read_id_pfr1();
>> +	if ((reg & CPUID_ARM_VIRT_MASK) != 1 << CPUID_ARM_VIRT_SHIFT)
>> +		return VIRT_ERR_NO_VIRT_EXT;
>> +
>> +	/* call the HYP switching code on this CPU also */
>> +	_switch_to_hyp();
>> +
>> +	if ((read_cpsr() & 0x1F) != 0x1a)
>> +		return VIRT_ERR_NOT_HYP_MODE;
>> +
>> +	return NONSEC_VIRT_SUCCESS;
>> +}
>> +
>>   enum nonsec_virt_errors armv7_switch_nonsec(void)
>>   {
>>   	unsigned int reg, ret;
>> diff --git a/arch/arm/include/asm/armv7.h b/arch/arm/include/asm/armv7.h
>> index f6582a1..baa22fe 100644
>> --- a/arch/arm/include/asm/armv7.h
>> +++ b/arch/arm/include/asm/armv7.h
>> @@ -89,21 +89,26 @@ void v7_outer_cache_inval_all(void);
>>   void v7_outer_cache_flush_range(u32 start, u32 end);
>>   void v7_outer_cache_inval_range(u32 start, u32 end);
>>
>> -#ifdef CONFIG_ARMV7_NONSEC
>> +#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT)
>>
>>   enum nonsec_virt_errors {
>>   	NONSEC_VIRT_SUCCESS,
>>   	NONSEC_ERR_NO_SEC_EXT,
>>   	NONSEC_ERR_NO_GIC_ADDRESS,
>>   	NONSEC_ERR_GIC_ADDRESS_ABOVE_4GB,
>> +	VIRT_ALREADY_HYP_MODE,
>> +	VIRT_ERR_NO_VIRT_EXT,
>> +	VIRT_ERR_NOT_HYP_MODE
>>   };
>>
>>   enum nonsec_virt_errors armv7_switch_nonsec(void);
>> +enum nonsec_virt_errors armv7_switch_hyp(void);
>>
>>   /* defined in assembly file */
>>   unsigned int _nonsec_init(void);
>>   void _smp_pen(void);
>> -#endif /* CONFIG_ARMV7_NONSEC */
>> +void _switch_to_hyp(void);
>> +#endif /* CONFIG_ARMV7_NONSEC || CONFIG_ARMV7_VIRT */
>>
>>   #endif /* ! __ASSEMBLY__ */
>>
>> diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c
>> index 7b0619e..90875b3 100644
>> --- a/arch/arm/lib/bootm.c
>> +++ b/arch/arm/lib/bootm.c
>> @@ -34,7 +34,7 @@
>>   #include <asm/bootm.h>
>>   #include <linux/compiler.h>
>>
>> -#ifdef CONFIG_ARMV7_NONSEC
>> +#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT)
>>   #include <asm/armv7.h>
>>   #endif
>>
>> @@ -192,13 +192,17 @@ __weak void setup_board_tags(struct tag **in_params) {}
>>
>>   static void do_nonsec_virt_switch(void)
>>   {
>> -#ifdef CONFIG_ARMV7_NONSEC
>> +#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT)
>>   	int ret;
>>
>>   	ret = armv7_switch_nonsec();
>> +#ifdef CONFIG_ARMV7_VIRT
>> +	if (ret == NONSEC_VIRT_SUCCESS)
>> +		ret = armv7_switch_hyp();
>> +#endif
>>   	switch (ret) {
>>   	case NONSEC_VIRT_SUCCESS:
>> -		debug("entered non-secure state\n");
>> +		debug("entered non-secure state or HYP mode\n");
>>   		break;
>>   	case NONSEC_ERR_NO_SEC_EXT:
>>   		printf("nonsec: Security extensions not implemented.\n");
>> @@ -209,6 +213,15 @@ static void do_nonsec_virt_switch(void)
>>   	case NONSEC_ERR_GIC_ADDRESS_ABOVE_4GB:
>>   		printf("nonsec: PERIPHBASE is above 4 GB, no access.\n");
>>   		break;
>> +	case VIRT_ERR_NO_VIRT_EXT:
>> +		printf("HYP mode: Virtualization extensions not implemented.\n");
>> +		break;
>> +	case VIRT_ALREADY_HYP_MODE:
>> +		debug("CPU already in HYP mode\n");
>> +		break;
>> +	case VIRT_ERR_NOT_HYP_MODE:
>> +		printf("HYP mode: switch not successful.\n");
>> +		break;
>>   	}
>>   #endif
>>   }
>> --
>> 1.7.12.1
>>



More information about the U-Boot mailing list