On Fri, Aug 09, 2013 at 05:03:11PM +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 | 37 ++++++++++++++++++++++++++++++++++ > arch/arm/include/asm/armv7.h | 6 ++++-- > arch/arm/lib/bootm.c | 7 ++++++- > 5 files changed, 86 insertions(+), 9 deletions(-) > > diff --git a/arch/arm/cpu/armv7/Makefile b/arch/arm/cpu/armv7/Makefile > index 5813e87..c20df3d 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 a88fa6b..fb1651d 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 */ > @@ -54,10 +55,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 I see you kep this as is, oh well. > + 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 > @@ -72,9 +90,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 r0, _smp_pen @ do not use this address again > b smp_waitloop @ wait for IPIs, board specific > @@ -161,3 +183,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 > + hvc #0 @ for older asm: .byte 0x70, 0x00, 0x40, 0xe1 > + 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 50f0a3a..4dc8c45 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 > @@ -31,6 +32,14 @@ > > unsigned long gic_dist_addr; > > +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; > @@ -90,6 +99,34 @@ void __weak smp_kick_all_cpus(void) > kick_secondary_cpus_gic(gic_dist_addr); > } > > +int armv7_switch_hyp(void) > +{ > + unsigned int reg; > + > + /* check whether we are in HYP mode already */ > + if ((read_cpsr() & 0x1f) == 0x1a) { > + debug("CPU already in HYP mode\n"); > + return 0; > + } > + > + /* check whether the CPU supports the virtualization extensions */ > + reg = read_id_pfr1(); > + if ((reg & CPUID_ARM_VIRT_MASK) != 1 << CPUID_ARM_VIRT_SHIFT) { > + printf("HYP mode: Virtualization extensions not implemented.\n"); > + return -1; > + } > + > + /* call the HYP switching code on this CPU also */ > + _switch_to_hyp(); > + > + if ((read_cpsr() & 0x1F) != 0x1a) { > + printf("HYP mode: switch not successful.\n"); > + return -1; > + } > + > + return 0; > +} > + > int armv7_switch_nonsec(void) > { > unsigned int reg; > diff --git a/arch/arm/include/asm/armv7.h b/arch/arm/include/asm/armv7.h > index 06657fe..1a7b150 100644 > --- a/arch/arm/include/asm/armv7.h > +++ b/arch/arm/include/asm/armv7.h > @@ -92,14 +92,16 @@ 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) > > int armv7_switch_nonsec(void); > +int 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 6470eac..ff36319 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 > > @@ -201,8 +201,13 @@ static void do_nonsec_virt_switch(void) > { > #if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT) > if (armv7_switch_nonsec() == 0) > +#ifdef CONFIG_ARMV7_VIRT > + if (armv7_switch_hyp() == 0) > + debug("entered HYP mode\n"); > +#else > debug("entered non-secure state\n"); > #endif > +#endif > } > > /* Subcommand: PREP */ > -- > 1.7.12.1 > _______________________________________________ kvmarm mailing list kvmarm@xxxxxxxxxxxxxxxxxxxxx https://lists.cs.columbia.edu/cucslists/listinfo/kvmarm