]> git.kernelconcepts.de Git - karo-tx-uboot.git/commitdiff
ARM: extend non-secure switch to also go into HYP mode
authorAndre Przywara <andre.przywara@linaro.org>
Thu, 19 Sep 2013 16:06:45 +0000 (18:06 +0200)
committerAlbert ARIBAUD <albert.u.boot@aribaud.net>
Thu, 3 Oct 2013 19:28:55 +0000 (21:28 +0200)
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 calls the new function after the
non-secure switch.

Signed-off-by: Andre Przywara <andre.przywara@linaro.org>
arch/arm/cpu/armv7/Makefile
arch/arm/cpu/armv7/nonsec_virt.S
arch/arm/cpu/armv7/virt-v7.c
arch/arm/include/asm/armv7.h
arch/arm/lib/bootm.c

index 024c28b6e3268fdb2c9e9048e30fd1abe641fb51..ee4b02183a264e829f821ea06856a6227f5cdf83 100644 (file)
@@ -20,7 +20,7 @@ ifneq ($(CONFIG_AM43XX)$(CONFIG_AM33XX)$(CONFIG_OMAP44XX)$(CONFIG_OMAP54XX)$(CON
 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
index cbee8f70a28b4a02443f3c3061ed7b9ad7b52589..358348ffa5fcb9543be4de2816cb7330d28e9bde 100644 (file)
@@ -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@linaro.org>
  *
 #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
 
@@ -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
+       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     r0, _smp_pen                    @ do not use this address again
        b       smp_waitloop                    @ wait for IPIs, board specific
@@ -173,3 +195,14 @@ ENTRY(smp_waitloop)
 ENDPROC(smp_waitloop)
 .weak smp_waitloop
 #endif
+
+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)
index a0b8742464433c22a6a0016bbb08dc2022b61941..6de7fe78135ac51709b2148bab6ba0865aea132e 100644 (file)
@@ -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
 
 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;
index 2efd4bcf424d45c3d8c41386b2fce00bb722caa8..395444ee4f1557cd5a7c88678d9480150f5b1a4d 100644 (file)
@@ -76,14 +76,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__ */
 
index b3a961a825afbcd9834b64974d5d030527594d95..f476a89702262508c2da32a78007ff4056a51050 100644 (file)
@@ -22,7 +22,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
 
@@ -189,8 +189,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 */