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. > + 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