Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | /* SPDX-License-Identifier: GPL-2.0+ */ /* * code for switching cores into non-secure state and into HYP mode * * Copyright (c) 2013 Andre Przywara <andre.przywara@linaro.org> */ #include <config.h> #include <linux/linkage.h> #include <asm/gic.h> #include <asm/armv7.h> #include <asm/proc-armv/ptrace.h> .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 bl psci_stack_setup @ Configure the PSCI backend push {r0, r1, r2, ip} bl psci_arch_init pop {r0, r1, r2, ip} #endif #ifdef CONFIG_ARM_ERRATA_773022 mrc p15, 0, r5, c1, c0, 1 orr r5, r5, #(1 << 1) mcr p15, 0, r5, c1, c0, 1 isb #endif #ifdef CONFIG_ARM_ERRATA_774769 mrc p15, 0, r5, c1, c0, 1 orr r5, r5, #(1 << 25) mcr p15, 0, r5, c1, c0, 1 isb #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 mrseq r3, sp_svc msreq sp_hyp, r3 @ migrate SP #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 /* * 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 mvn r1, #0 @ all bits to 1 str r1, [r3, #GICD_IGROUPRn] @ allow private interrupts get_gicc_addr r3, r1 mov r1, #3 @ Enable both groups 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 movw r1, #0x3fff movt r1, #0x0004 orr r0, r0, r1 mcr p15, 0, r0, c1, c1, 2 @ NSACR = all copros to non-sec /* 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. */ #if CONFIG_COUNTER_FREQUENCY 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_COUNTER_FREQUENCY 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 CFG_SMP_PEN_ADDR /* void __weak smp_waitloop(unsigned previous_address); */ WEAK(smp_waitloop) wfi ldr r1, =CFG_SMP_PEN_ADDR @ load start address ldr r1, [r1] #ifdef CONFIG_PEN_ADDR_BIG_ENDIAN rev r1, r1 #endif 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) #endif .popsection |