Skip to content
Snippets Groups Projects
Commit d4296887 authored by Andre Przywara's avatar Andre Przywara Committed by Albert ARIBAUD
Browse files

ARM: extend non-secure switch to also go into HYP mode


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: default avatarAndre Przywara <andre.przywara@linaro.org>
parent ba6a1698
No related branches found
No related tags found
No related merge requests found
...@@ -20,7 +20,7 @@ ifneq ($(CONFIG_AM43XX)$(CONFIG_AM33XX)$(CONFIG_OMAP44XX)$(CONFIG_OMAP54XX)$(CON ...@@ -20,7 +20,7 @@ ifneq ($(CONFIG_AM43XX)$(CONFIG_AM33XX)$(CONFIG_OMAP44XX)$(CONFIG_OMAP54XX)$(CON
SOBJS += lowlevel_init.o SOBJS += lowlevel_init.o
endif endif
ifneq ($(CONFIG_ARMV7_NONSEC),) ifneq ($(CONFIG_ARMV7_NONSEC)$(CONFIG_ARMV7_VIRT),)
SOBJS += nonsec_virt.o SOBJS += nonsec_virt.o
COBJS += virt-v7.o COBJS += virt-v7.o
endif endif
......
/* /*
* 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> * Copyright (c) 2013 Andre Przywara <andre.przywara@linaro.org>
* *
...@@ -28,15 +28,16 @@ ...@@ -28,15 +28,16 @@
#include <asm/armv7.h> #include <asm/armv7.h>
.arch_extension sec .arch_extension sec
.arch_extension virt
/* the vector table for secure state */ /* the vector table for secure state and HYP mode */
_monitor_vectors: _monitor_vectors:
.word 0 /* reset */ .word 0 /* reset */
.word 0 /* undef */ .word 0 /* undef */
adr pc, _secure_monitor adr pc, _secure_monitor
.word 0 .word 0
.word 0 .word 0
.word 0 adr pc, _hyp_trap
.word 0 .word 0
.word 0 .word 0
...@@ -53,10 +54,27 @@ _secure_monitor: ...@@ -53,10 +54,27 @@ _secure_monitor:
bic r1, r1, #0x4e @ clear IRQ, FIQ, EA, nET bits bic r1, r1, #0x4e @ clear IRQ, FIQ, EA, nET bits
orr r1, r1, #0x31 @ enable NS, AW, FW 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) 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 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 * 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 * of the non-secure and HYP mode transition. The GIC distributor specific
...@@ -71,9 +89,13 @@ ENTRY(_smp_pen) ...@@ -71,9 +89,13 @@ ENTRY(_smp_pen)
mcr p15, 0, r1, c12, c0, 0 @ set VBAR mcr p15, 0, r1, c12, c0, 0 @ set VBAR
bl _nonsec_init 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 ldr r1, [r12, #GICC_IAR] @ acknowledge IPI
str r1, [r0, #GICC_EOIR] @ signal end of interrupt str r1, [r12, #GICC_EOIR] @ signal end of interrupt
adr r0, _smp_pen @ do not use this address again adr r0, _smp_pen @ do not use this address again
b smp_waitloop @ wait for IPIs, board specific b smp_waitloop @ wait for IPIs, board specific
...@@ -173,3 +195,14 @@ ENTRY(smp_waitloop) ...@@ -173,3 +195,14 @@ ENTRY(smp_waitloop)
ENDPROC(smp_waitloop) ENDPROC(smp_waitloop)
.weak smp_waitloop .weak smp_waitloop
#endif #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)
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* Andre Przywara, Linaro * Andre Przywara, Linaro
* *
* Routines to transition ARMv7 processors from secure into non-secure state * 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 * needed to enable ARMv7 virtualization for current hypervisors
* *
* See file CREDITS for list of people who contributed to this * See file CREDITS for list of people who contributed to this
...@@ -31,6 +32,14 @@ ...@@ -31,6 +32,14 @@
unsigned long gic_dist_addr; 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) static unsigned int read_id_pfr1(void)
{ {
unsigned int reg; unsigned int reg;
...@@ -90,6 +99,34 @@ void __weak smp_kick_all_cpus(void) ...@@ -90,6 +99,34 @@ void __weak smp_kick_all_cpus(void)
kick_secondary_cpus_gic(gic_dist_addr); 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) int armv7_switch_nonsec(void)
{ {
unsigned int reg; unsigned int reg;
......
...@@ -76,14 +76,16 @@ void v7_outer_cache_inval_all(void); ...@@ -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_flush_range(u32 start, u32 end);
void v7_outer_cache_inval_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_nonsec(void);
int armv7_switch_hyp(void);
/* defined in assembly file */ /* defined in assembly file */
unsigned int _nonsec_init(void); unsigned int _nonsec_init(void);
void _smp_pen(void); void _smp_pen(void);
#endif /* CONFIG_ARMV7_NONSEC */ void _switch_to_hyp(void);
#endif /* CONFIG_ARMV7_NONSEC || CONFIG_ARMV7_VIRT */
#endif /* ! __ASSEMBLY__ */ #endif /* ! __ASSEMBLY__ */
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
#include <asm/bootm.h> #include <asm/bootm.h>
#include <linux/compiler.h> #include <linux/compiler.h>
#ifdef CONFIG_ARMV7_NONSEC #if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT)
#include <asm/armv7.h> #include <asm/armv7.h>
#endif #endif
...@@ -189,8 +189,13 @@ static void do_nonsec_virt_switch(void) ...@@ -189,8 +189,13 @@ static void do_nonsec_virt_switch(void)
{ {
#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT) #if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT)
if (armv7_switch_nonsec() == 0) 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"); debug("entered non-secure state\n");
#endif #endif
#endif
} }
/* Subcommand: PREP */ /* Subcommand: PREP */
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment