u-boot/arch/arm/cpu/armv7/nonsec_virt.S

203 lines
5.1 KiB
ArmAsm
Raw Normal View History

/*
* code for switching cores into non-secure state and into HYP mode
*
* Copyright (c) 2013 Andre Przywara <andre.przywara@linaro.org>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
ARM: add assembly routine to switch to non-secure state While actually switching to non-secure state is one thing, another part of this process is to make sure that we still have full access to the interrupt controller (GIC). The GIC is fully aware of secure vs. non-secure state, some registers are banked, others may be configured to be accessible from secure state only. To be as generic as possible, we get the GIC memory mapped address based on the PERIPHBASE value in the CBAR register. Since this register is not architecturally defined, we check the MIDR before to be from an A15 or A7. For CPUs not having the CBAR or boards with wrong information herein we allow providing the base address as a configuration variable. Now that we know the GIC address, we: a) allow private interrupts to be delivered to the core (GICD_IGROUPR0 = 0xFFFFFFFF) b) enable the CPU interface (GICC_CTLR[0] = 1) c) set the priority filter to allow non-secure interrupts (GICC_PMR = 0xFF) Also we allow access to all coprocessor interfaces from non-secure state by writing the appropriate bits in the NSACR register. The generic timer base frequency register is only accessible from secure state, so we have to program it now. Actually this should be done from primary firmware before, but some boards seems to omit this, so if needed we do this here with a board specific value. The Versatile Express board does not need this, so we remove the frequency from the configuration file here. After having switched to non-secure state, we also enable the non-secure GIC CPU interface, since this register is banked. Since we need to call this routine also directly from the smp_pen later (where we don't have any stack), we can only use caller saved registers r0-r3 and r12 to not mess with the compiler. Signed-off-by: Andre Przywara <andre.przywara@linaro.org>
2013-09-20 00:06:41 +08:00
#include <linux/linkage.h>
#include <asm/gic.h>
#include <asm/armv7.h>
#include <asm/proc-armv/ptrace.h>
ARM: add assembly routine to switch to non-secure state While actually switching to non-secure state is one thing, another part of this process is to make sure that we still have full access to the interrupt controller (GIC). The GIC is fully aware of secure vs. non-secure state, some registers are banked, others may be configured to be accessible from secure state only. To be as generic as possible, we get the GIC memory mapped address based on the PERIPHBASE value in the CBAR register. Since this register is not architecturally defined, we check the MIDR before to be from an A15 or A7. For CPUs not having the CBAR or boards with wrong information herein we allow providing the base address as a configuration variable. Now that we know the GIC address, we: a) allow private interrupts to be delivered to the core (GICD_IGROUPR0 = 0xFFFFFFFF) b) enable the CPU interface (GICC_CTLR[0] = 1) c) set the priority filter to allow non-secure interrupts (GICC_PMR = 0xFF) Also we allow access to all coprocessor interfaces from non-secure state by writing the appropriate bits in the NSACR register. The generic timer base frequency register is only accessible from secure state, so we have to program it now. Actually this should be done from primary firmware before, but some boards seems to omit this, so if needed we do this here with a board specific value. The Versatile Express board does not need this, so we remove the frequency from the configuration file here. After having switched to non-secure state, we also enable the non-secure GIC CPU interface, since this register is banked. Since we need to call this routine also directly from the smp_pen later (where we don't have any stack), we can only use caller saved registers r0-r3 and r12 to not mess with the compiler. Signed-off-by: Andre Przywara <andre.przywara@linaro.org>
2013-09-20 00:06:41 +08:00
.arch_extension sec
.arch_extension virt
.pushsection ._secure.text, "ax"
.align 5
/* 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
.word 0
.word 0
.macro is_cpu_virt_capable tmp
mrc p15, 0, \tmp, c0, c1, 1 @ read ID_PFR1
and \tmp, \tmp, #CPUID_ARM_VIRT_MASK @ mask virtualization bits
cmp \tmp, #(1 << CPUID_ARM_VIRT_SHIFT)
.endm
/*
* secure monitor handler
* U-boot calls this "software interrupt" in start.S
* This is executed on a "smc" instruction, we use a "smc #0" to switch
* to non-secure state.
* r0, r1, r2: passed to the callee
* ip: target PC
*/
_secure_monitor:
#ifdef CONFIG_ARMV7_PSCI
ldr r5, =_psci_vectors @ Switch to the next monitor
mcr p15, 0, r5, c12, c0, 1
isb
@ Obtain a secure stack, and configure the PSCI backend
bl psci_arch_init
#endif
mrc p15, 0, r5, c1, c1, 0 @ read SCR
bic r5, r5, #0x4a @ clear IRQ, EA, nET bits
orr r5, r5, #0x31 @ enable NS, AW, FW bits
@ FIQ preserved for secure mode
mov r6, #SVC_MODE @ default mode is SVC
is_cpu_virt_capable r4
#ifdef CONFIG_ARMV7_VIRT
orreq r5, r5, #0x100 @ allow HVC instruction
moveq r6, #HYP_MODE @ Enter the kernel as HYP
#endif
mcr p15, 0, r5, c1, c1, 0 @ write SCR (with NS bit set)
isb
bne 1f
@ Reset CNTVOFF to 0 before leaving monitor mode
mrc p15, 0, r4, c0, c1, 1 @ read ID_PFR1
ands r4, r4, #CPUID_ARM_GENTIMER_MASK @ test arch timer bits
movne r4, #0
mcrrne p15, 4, r4, r4, c14 @ Reset CNTVOFF to zero
1:
mov lr, ip
mov ip, #(F_BIT | I_BIT | A_BIT) @ Set A, I and F
tst lr, #1 @ Check for Thumb PC
orrne ip, ip, #T_BIT @ Set T if Thumb
orr ip, ip, r6 @ Slot target mode in
msr spsr_cxfs, ip @ Set full SPSR
movs pc, lr @ ERET to non-secure
ENTRY(_do_nonsec_entry)
mov ip, r0
mov r0, r1
mov r1, r2
mov r2, r3
smc #0
ENDPROC(_do_nonsec_entry)
.macro get_cbar_addr addr
#ifdef CONFIG_ARM_GIC_BASE_ADDRESS
ldr \addr, =CONFIG_ARM_GIC_BASE_ADDRESS
#else
mrc p15, 4, \addr, c15, c0, 0 @ read CBAR
bfc \addr, #0, #15 @ clear reserved bits
#endif
.endm
.macro get_gicd_addr addr
get_cbar_addr \addr
add \addr, \addr, #GIC_DIST_OFFSET @ GIC dist i/f offset
.endm
.macro get_gicc_addr addr, tmp
get_cbar_addr \addr
is_cpu_virt_capable \tmp
movne \tmp, #GIC_CPU_OFFSET_A9 @ GIC CPU offset for A9
moveq \tmp, #GIC_CPU_OFFSET_A15 @ GIC CPU offset for A15/A7
add \addr, \addr, \tmp
.endm
#ifndef CONFIG_ARMV7_PSCI
/*
* 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
* code has already been executed by a C function before.
* Then they go back to wfi and wait to be woken up by the kernel again.
*/
ENTRY(_smp_pen)
cpsid i
cpsid f
bl _nonsec_init
adr r0, _smp_pen @ do not use this address again
b smp_waitloop @ wait for IPIs, board specific
ENDPROC(_smp_pen)
#endif
ARM: add assembly routine to switch to non-secure state While actually switching to non-secure state is one thing, another part of this process is to make sure that we still have full access to the interrupt controller (GIC). The GIC is fully aware of secure vs. non-secure state, some registers are banked, others may be configured to be accessible from secure state only. To be as generic as possible, we get the GIC memory mapped address based on the PERIPHBASE value in the CBAR register. Since this register is not architecturally defined, we check the MIDR before to be from an A15 or A7. For CPUs not having the CBAR or boards with wrong information herein we allow providing the base address as a configuration variable. Now that we know the GIC address, we: a) allow private interrupts to be delivered to the core (GICD_IGROUPR0 = 0xFFFFFFFF) b) enable the CPU interface (GICC_CTLR[0] = 1) c) set the priority filter to allow non-secure interrupts (GICC_PMR = 0xFF) Also we allow access to all coprocessor interfaces from non-secure state by writing the appropriate bits in the NSACR register. The generic timer base frequency register is only accessible from secure state, so we have to program it now. Actually this should be done from primary firmware before, but some boards seems to omit this, so if needed we do this here with a board specific value. The Versatile Express board does not need this, so we remove the frequency from the configuration file here. After having switched to non-secure state, we also enable the non-secure GIC CPU interface, since this register is banked. Since we need to call this routine also directly from the smp_pen later (where we don't have any stack), we can only use caller saved registers r0-r3 and r12 to not mess with the compiler. Signed-off-by: Andre Przywara <andre.przywara@linaro.org>
2013-09-20 00:06:41 +08:00
/*
* Switch a core to non-secure state.
*
* 1. initialize the GIC per-core interface
* 2. allow coprocessor access in non-secure modes
*
* Called from smp_pen by secondary cores and directly by the BSP.
* Do not assume that the stack is available and only use registers
* r0-r3 and r12.
*
* PERIPHBASE is used to get the GIC address. This could be 40 bits long,
* though, but we check this in C before calling this function.
*/
ENTRY(_nonsec_init)
get_gicd_addr r3
ARM: add assembly routine to switch to non-secure state While actually switching to non-secure state is one thing, another part of this process is to make sure that we still have full access to the interrupt controller (GIC). The GIC is fully aware of secure vs. non-secure state, some registers are banked, others may be configured to be accessible from secure state only. To be as generic as possible, we get the GIC memory mapped address based on the PERIPHBASE value in the CBAR register. Since this register is not architecturally defined, we check the MIDR before to be from an A15 or A7. For CPUs not having the CBAR or boards with wrong information herein we allow providing the base address as a configuration variable. Now that we know the GIC address, we: a) allow private interrupts to be delivered to the core (GICD_IGROUPR0 = 0xFFFFFFFF) b) enable the CPU interface (GICC_CTLR[0] = 1) c) set the priority filter to allow non-secure interrupts (GICC_PMR = 0xFF) Also we allow access to all coprocessor interfaces from non-secure state by writing the appropriate bits in the NSACR register. The generic timer base frequency register is only accessible from secure state, so we have to program it now. Actually this should be done from primary firmware before, but some boards seems to omit this, so if needed we do this here with a board specific value. The Versatile Express board does not need this, so we remove the frequency from the configuration file here. After having switched to non-secure state, we also enable the non-secure GIC CPU interface, since this register is banked. Since we need to call this routine also directly from the smp_pen later (where we don't have any stack), we can only use caller saved registers r0-r3 and r12 to not mess with the compiler. Signed-off-by: Andre Przywara <andre.przywara@linaro.org>
2013-09-20 00:06:41 +08:00
mvn r1, #0 @ all bits to 1
str r1, [r3, #GICD_IGROUPRn] @ allow private interrupts
get_gicc_addr r3, r1
ARM: add assembly routine to switch to non-secure state While actually switching to non-secure state is one thing, another part of this process is to make sure that we still have full access to the interrupt controller (GIC). The GIC is fully aware of secure vs. non-secure state, some registers are banked, others may be configured to be accessible from secure state only. To be as generic as possible, we get the GIC memory mapped address based on the PERIPHBASE value in the CBAR register. Since this register is not architecturally defined, we check the MIDR before to be from an A15 or A7. For CPUs not having the CBAR or boards with wrong information herein we allow providing the base address as a configuration variable. Now that we know the GIC address, we: a) allow private interrupts to be delivered to the core (GICD_IGROUPR0 = 0xFFFFFFFF) b) enable the CPU interface (GICC_CTLR[0] = 1) c) set the priority filter to allow non-secure interrupts (GICC_PMR = 0xFF) Also we allow access to all coprocessor interfaces from non-secure state by writing the appropriate bits in the NSACR register. The generic timer base frequency register is only accessible from secure state, so we have to program it now. Actually this should be done from primary firmware before, but some boards seems to omit this, so if needed we do this here with a board specific value. The Versatile Express board does not need this, so we remove the frequency from the configuration file here. After having switched to non-secure state, we also enable the non-secure GIC CPU interface, since this register is banked. Since we need to call this routine also directly from the smp_pen later (where we don't have any stack), we can only use caller saved registers r0-r3 and r12 to not mess with the compiler. Signed-off-by: Andre Przywara <andre.przywara@linaro.org>
2013-09-20 00:06:41 +08:00
mov r1, #3 @ Enable both groups
ARM: add assembly routine to switch to non-secure state While actually switching to non-secure state is one thing, another part of this process is to make sure that we still have full access to the interrupt controller (GIC). The GIC is fully aware of secure vs. non-secure state, some registers are banked, others may be configured to be accessible from secure state only. To be as generic as possible, we get the GIC memory mapped address based on the PERIPHBASE value in the CBAR register. Since this register is not architecturally defined, we check the MIDR before to be from an A15 or A7. For CPUs not having the CBAR or boards with wrong information herein we allow providing the base address as a configuration variable. Now that we know the GIC address, we: a) allow private interrupts to be delivered to the core (GICD_IGROUPR0 = 0xFFFFFFFF) b) enable the CPU interface (GICC_CTLR[0] = 1) c) set the priority filter to allow non-secure interrupts (GICC_PMR = 0xFF) Also we allow access to all coprocessor interfaces from non-secure state by writing the appropriate bits in the NSACR register. The generic timer base frequency register is only accessible from secure state, so we have to program it now. Actually this should be done from primary firmware before, but some boards seems to omit this, so if needed we do this here with a board specific value. The Versatile Express board does not need this, so we remove the frequency from the configuration file here. After having switched to non-secure state, we also enable the non-secure GIC CPU interface, since this register is banked. Since we need to call this routine also directly from the smp_pen later (where we don't have any stack), we can only use caller saved registers r0-r3 and r12 to not mess with the compiler. Signed-off-by: Andre Przywara <andre.przywara@linaro.org>
2013-09-20 00:06:41 +08:00
str r1, [r3, #GICC_CTLR] @ and clear all other bits
mov r1, #0xff
str r1, [r3, #GICC_PMR] @ set priority mask register
mrc p15, 0, r0, c1, c1, 2
ARM: add assembly routine to switch to non-secure state While actually switching to non-secure state is one thing, another part of this process is to make sure that we still have full access to the interrupt controller (GIC). The GIC is fully aware of secure vs. non-secure state, some registers are banked, others may be configured to be accessible from secure state only. To be as generic as possible, we get the GIC memory mapped address based on the PERIPHBASE value in the CBAR register. Since this register is not architecturally defined, we check the MIDR before to be from an A15 or A7. For CPUs not having the CBAR or boards with wrong information herein we allow providing the base address as a configuration variable. Now that we know the GIC address, we: a) allow private interrupts to be delivered to the core (GICD_IGROUPR0 = 0xFFFFFFFF) b) enable the CPU interface (GICC_CTLR[0] = 1) c) set the priority filter to allow non-secure interrupts (GICC_PMR = 0xFF) Also we allow access to all coprocessor interfaces from non-secure state by writing the appropriate bits in the NSACR register. The generic timer base frequency register is only accessible from secure state, so we have to program it now. Actually this should be done from primary firmware before, but some boards seems to omit this, so if needed we do this here with a board specific value. The Versatile Express board does not need this, so we remove the frequency from the configuration file here. After having switched to non-secure state, we also enable the non-secure GIC CPU interface, since this register is banked. Since we need to call this routine also directly from the smp_pen later (where we don't have any stack), we can only use caller saved registers r0-r3 and r12 to not mess with the compiler. Signed-off-by: Andre Przywara <andre.przywara@linaro.org>
2013-09-20 00:06:41 +08:00
movw r1, #0x3fff
movt r1, #0x0004
orr r0, r0, r1
mcr p15, 0, r0, c1, c1, 2 @ NSACR = all copros to non-sec
ARM: add assembly routine to switch to non-secure state While actually switching to non-secure state is one thing, another part of this process is to make sure that we still have full access to the interrupt controller (GIC). The GIC is fully aware of secure vs. non-secure state, some registers are banked, others may be configured to be accessible from secure state only. To be as generic as possible, we get the GIC memory mapped address based on the PERIPHBASE value in the CBAR register. Since this register is not architecturally defined, we check the MIDR before to be from an A15 or A7. For CPUs not having the CBAR or boards with wrong information herein we allow providing the base address as a configuration variable. Now that we know the GIC address, we: a) allow private interrupts to be delivered to the core (GICD_IGROUPR0 = 0xFFFFFFFF) b) enable the CPU interface (GICC_CTLR[0] = 1) c) set the priority filter to allow non-secure interrupts (GICC_PMR = 0xFF) Also we allow access to all coprocessor interfaces from non-secure state by writing the appropriate bits in the NSACR register. The generic timer base frequency register is only accessible from secure state, so we have to program it now. Actually this should be done from primary firmware before, but some boards seems to omit this, so if needed we do this here with a board specific value. The Versatile Express board does not need this, so we remove the frequency from the configuration file here. After having switched to non-secure state, we also enable the non-secure GIC CPU interface, since this register is banked. Since we need to call this routine also directly from the smp_pen later (where we don't have any stack), we can only use caller saved registers r0-r3 and r12 to not mess with the compiler. Signed-off-by: Andre Przywara <andre.przywara@linaro.org>
2013-09-20 00:06:41 +08:00
/* The CNTFRQ register of the generic timer needs to be
* programmed in secure state. Some primary bootloaders / firmware
* omit this, so if the frequency is provided in the configuration,
* we do this here instead.
* But first check if we have the generic timer.
*/
#ifdef CONFIG_SYS_CLK_FREQ
mrc p15, 0, r0, c0, c1, 1 @ read ID_PFR1
and r0, r0, #CPUID_ARM_GENTIMER_MASK @ mask arch timer bits
cmp r0, #(1 << CPUID_ARM_GENTIMER_SHIFT)
ldreq r1, =CONFIG_SYS_CLK_FREQ
mcreq p15, 0, r1, c14, c0, 0 @ write CNTFRQ
#endif
adr r1, _monitor_vectors
mcr p15, 0, r1, c12, c0, 1 @ set MVBAR to secure vectors
isb
mov r0, r3 @ return GICC address
bx lr
ENDPROC(_nonsec_init)
#ifdef CONFIG_SMP_PEN_ADDR
/* void __weak smp_waitloop(unsigned previous_address); */
ENTRY(smp_waitloop)
wfi
ldr r1, =CONFIG_SMP_PEN_ADDR @ load start address
ldr r1, [r1]
cmp r0, r1 @ make sure we dont execute this code
beq smp_waitloop @ again (due to a spurious wakeup)
mov r0, r1
b _do_nonsec_entry
ENDPROC(smp_waitloop)
.weak smp_waitloop
#endif
.popsection