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@xxxxxxxxxx> >> --- >> 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@xxxxxxxxxx> >> * >> @@ -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 >> _______________________________________________ kvmarm mailing list kvmarm@xxxxxxxxxxxxxxxxxxxxx https://lists.cs.columbia.edu/cucslists/listinfo/kvmarm