
Mark,
In your nonsec_init code, you suggest this change:
+ mrc p15, 0, r0, c1, c1, 2 movw r1, #0x3fff - movt r1, #0x0006 - mcr p15, 0, r1, c1, c1, 2 @ NSACR = all copros to non-sec + movt r1, #0x0004 + orr r0, r0, r1 + mcr p15, 0, r0, c1, c1, 2 @ NSACR = all copros to non-sec
Leaving:
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
That sets all the co-processor bits, but the man page suggests that only copros with bits 10 and 11 should be modified. It also seems that if the PLE is enabled, we should mark it NS-enabled at bit 16 also:. Perhaps:
mrc p15, 0, r0, c1, c1, 2 movw r1, #0x0c00 movt r1, #0x0005 orr r0, r0, r1 mcr p15, 0, r0, c1, c1, 2 @ NSACR = all copros to non-sec
HTH, jdl
On Fri, May 2, 2014 at 3:30 PM, Jon Loeliger loeliger@gmail.com wrote:
Mark,
I finally have all this working for me on an A9 system too!
However, there were a few things that I had to change a bit. For example, by CPUs will always come out of reset at 0x0 and I do not have the ability to set their first-fetch address to anything else. To accommodate this, I need to ensure that the _monitor_vectors are loaded at address 0x0, and that the first entry in the exception vector (for reset) jumped to some notion of "secure_reset code". So I changed this code:
diff --git a/arch/arm/cpu/armv7/nonsec_virt.S b/arch/arm/cpu/armv7/nonsec_virt.S index b5c946f..2a43e3c 100644 --- a/arch/arm/cpu/armv7/nonsec_virt.S +++ b/arch/arm/cpu/armv7/nonsec_virt.S @@ -10,10 +10,13 @@ #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: @@ -22,51 +25,86 @@ _monitor_vectors: adr pc, _secure_monitor .word 0 .word 0
adr pc, _hyp_trap
.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
So that it did this too:
@@ -20,15 +20,23 @@ .align 5 /* the vector table for secure state and HYP mode */ _monitor_vectors:
.word 0 /* reset */
.word 0 /* undef */
adr pc, _secure_monitor
ldr pc, _secure_reset /* reset */
.word 0 /* undef */
adr pc, _secure_monitor /* SMC */ .word 0 .word 0 .word 0 .word 0 .word 0
+_secure_reset: +#ifdef CONFIG_SECURE_MONITOR_RESET_FUNCTION
.word CONFIG_SECURE_MONITOR_RESET_FUNCTION
+#else
.word 0
+#endif
.macro is_cpu_virt_capable tmp
That enabled me to define CONFIG_SECURE_MONITOR_RESET_FUNCTION in my config header file:
/*
- With the Secure Monitor at 0x0, its reset vector must also
- then point off to the correct "out-of-reset entry function."
*/ #define CONFIG_SECURE_MONITOR_RESET_FUNCTION _myplatform_cpu_entry #define CONFIG_ARMV7_SECURE_BASE 0x0
That _myplatform_cpu_entry corresponds to your sunxi_cpu_entry code.
So, yeah, I know that isn't a proper patch and all. :-) I'm just sending you more information to ponder for this patch series! If you would like to generalize your patch this way, please feel free to do so. If not, I can send a proper patch after this hits mainline or so.
HTH, jdl
On Sat, Apr 26, 2014 at 7:17 AM, Marc Zyngier marc.zyngier@arm.com wrote:
The current non-sec switching code suffers from one major issue: it cannot run in secure RAM, as a large part of u-boot still needs to be run while we're switched to non-secure.
This patch reworks the whole HYP/non-secure strategy by:
- making sure the secure code is the *last* thing u-boot executes before entering the payload
- performing an exception return from secure mode directly into the payload
- allowing the code to be dynamically relocated to secure RAM before switching to non-secure.
This involves quite a bit of horrible code, specially as u-boot relocation is quite primitive.
Signed-off-by: Marc Zyngier marc.zyngier@arm.com
arch/arm/cpu/armv7/nonsec_virt.S | 161 +++++++++++++++++++-------------------- arch/arm/cpu/armv7/virt-v7.c | 59 +++++--------- arch/arm/include/asm/armv7.h | 10 ++- arch/arm/include/asm/secure.h | 26 +++++++ arch/arm/lib/bootm.c | 22 +++--- 5 files changed, 138 insertions(+), 140 deletions(-) create mode 100644 arch/arm/include/asm/secure.h
diff --git a/arch/arm/cpu/armv7/nonsec_virt.S b/arch/arm/cpu/armv7/nonsec_virt.S index b5c946f..2a43e3c 100644 --- a/arch/arm/cpu/armv7/nonsec_virt.S +++ b/arch/arm/cpu/armv7/nonsec_virt.S @@ -10,10 +10,13 @@ #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: @@ -22,51 +25,86 @@ _monitor_vectors: adr pc, _secure_monitor .word 0 .word 0
adr pc, _hyp_trap
.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.
- We use only r0 and r1 here, due to constraints in the caller.
- r0, r1, r2: passed to the callee
*/
- ip: target PC
_secure_monitor:
mrc p15, 0, r1, c1, c1, 0 @ read SCR
bic r1, r1, #0x4e @ clear IRQ, FIQ, EA, nET bits
orr r1, r1, #0x31 @ enable NS, AW, FW bits
mrc p15, 0, r5, c1, c1, 0 @ read SCR
bic r5, r5, #0x4e @ clear IRQ, FIQ, EA, nET bits
orr r5, r5, #0x31 @ enable NS, AW, FW bits
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)
mov r6, #SVC_MODE @ default mode is SVC
is_cpu_virt_capable r4
#ifdef CONFIG_ARMV7_VIRT
orreq r1, r1, #0x100 @ allow HVC instruction
orreq r5, r5, #0x100 @ allow HVC instruction
moveq r6, #HYP_MODE @ Enter the kernel as HYP
#endif
mcr p15, 0, r1, c1, c1, 0 @ write SCR (with NS bit set)
mcr p15, 0, r5, c1, c1, 0 @ write SCR (with NS bit set) isb
-#ifdef CONFIG_ARMV7_VIRT
mrceq p15, 0, r0, c12, c0, 1 @ get MVBAR value
mcreq p15, 4, r0, c12, c0, 0 @ write HVBAR
-#endif bne 1f
@ Reset CNTVOFF to 0 before leaving monitor mode
mrc p15, 0, r0, c0, c1, 1 @ read ID_PFR1
ands r0, r0, #CPUID_ARM_GENTIMER_MASK @ test arch timer bits
movne r0, #0
mcrrne p15, 4, r0, r0, c14 @ Reset CNTVOFF to zero
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:
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
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
@@ -74,31 +112,21 @@ _hyp_trap:
- Then they go back to wfi and wait to be woken up by the kernel again.
*/ ENTRY(_smp_pen)
mrs r0, cpsr
orr r0, r0, #0xc0
msr cpsr, r0 @ disable interrupts
ldr r1, =_start
mcr p15, 0, r1, c12, c0, 0 @ set VBAR
cpsid i
cpsid f bl _nonsec_init
mov r12, r0 @ save GICC address
-#ifdef CONFIG_ARMV7_VIRT
bl _switch_to_hyp
-#endif
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
ENDPROC(_smp_pen) +#endif
/*
- Switch a core to non-secure state.
- initialize the GIC per-core interface
- allow coprocessor access in non-secure modes
- switch the cpu mode (by calling "smc #0")
- Called from smp_pen by secondary cores and directly by the BSP.
- Do not assume that the stack is available and only use registers
@@ -108,38 +136,23 @@ ENDPROC(_smp_pen)
- though, but we check this in C before calling this function.
*/ ENTRY(_nonsec_init) -#ifdef CONFIG_ARM_GIC_BASE_ADDRESS
ldr r2, =CONFIG_ARM_GIC_BASE_ADDRESS
-#else
mrc p15, 4, r2, c15, c0, 0 @ read CBAR
bfc r2, #0, #15 @ clear reserved bits
-#endif
add r3, r2, #GIC_DIST_OFFSET @ GIC dist i/f offset
get_gicd_addr r3
mvn r1, #0 @ all bits to 1 str r1, [r3, #GICD_IGROUPRn] @ allow private interrupts
mrc p15, 0, r0, c0, c0, 0 @ read MIDR
ldr r1, =MIDR_PRIMARY_PART_MASK
and r0, r0, r1 @ mask out variant and revision
get_gicc_addr r3, r1
ldr r1, =MIDR_CORTEX_A7_R0P0 & MIDR_PRIMARY_PART_MASK
cmp r0, r1 @ check for Cortex-A7
ldr r1, =MIDR_CORTEX_A15_R0P0 & MIDR_PRIMARY_PART_MASK
cmpne r0, r1 @ check for Cortex-A15
movne r1, #GIC_CPU_OFFSET_A9 @ GIC CPU offset for A9
moveq r1, #GIC_CPU_OFFSET_A15 @ GIC CPU offset for A15/A7
add r3, r2, r1 @ r3 = GIC CPU i/f addr
mov r1, #1 @ set GICC_CTLR[enable]
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, #0x0006
mcr p15, 0, r1, c1, c1, 2 @ NSACR = all copros to non-sec
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
@@ -157,21 +170,9 @@ ENTRY(_nonsec_init)
adr r1, _monitor_vectors mcr p15, 0, r1, c12, c0, 1 @ set MVBAR to secure vectors
mrc p15, 0, ip, c12, c0, 0 @ save secure copy of VBAR
isb
smc #0 @ call into MONITOR mode
mcr p15, 0, ip, c12, c0, 0 @ write non-secure copy of VBAR
mov r1, #1
str r1, [r3, #GICC_CTLR] @ enable non-secure CPU i/f
add r2, r2, #GIC_DIST_OFFSET
str r1, [r2, #GICD_CTLR] @ allow private interrupts mov r0, r3 @ return GICC address
bx lr
ENDPROC(_nonsec_init)
@@ -183,18 +184,10 @@ ENTRY(smp_waitloop) ldr r1, [r1] cmp r0, r1 @ make sure we dont execute this code beq smp_waitloop @ again (due to a spurious wakeup)
mov pc, r1
mov r0, r1
b _do_nonsec_entry
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)
.popsection
diff --git a/arch/arm/cpu/armv7/virt-v7.c b/arch/arm/cpu/armv7/virt-v7.c index 2cd604f..6500030 100644 --- a/arch/arm/cpu/armv7/virt-v7.c +++ b/arch/arm/cpu/armv7/virt-v7.c @@ -13,17 +13,10 @@ #include <asm/armv7.h> #include <asm/gic.h> #include <asm/io.h> +#include <asm/secure.h>
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; @@ -72,6 +65,18 @@ static unsigned long get_gicd_base_address(void) #endif }
+static void relocate_secure_section(void) +{ +#ifdef CONFIG_ARMV7_SECURE_BASE
size_t sz = __secure_end - __secure_start;
memcpy((void *)CONFIG_ARMV7_SECURE_BASE, __secure_start, sz);
flush_dcache_range(CONFIG_ARMV7_SECURE_BASE,
CONFIG_ARMV7_SECURE_BASE + sz + 1);
invalidate_icache_all();
+#endif +}
static void kick_secondary_cpus_gic(unsigned long gicdaddr) { /* kick all CPUs (except this one) by writing to GICD_SGIR */ @@ -83,35 +88,7 @@ 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) +int armv7_init_nonsec(void) { unsigned int reg; unsigned itlinesnr, i; @@ -147,11 +124,13 @@ int armv7_switch_nonsec(void) for (i = 1; i <= itlinesnr; i++) writel((unsigned)-1, gic_dist_addr + GICD_IGROUPRn + 4 * i);
smp_set_core_boot_addr((unsigned long)_smp_pen, -1);
+#ifndef CONFIG_ARMV7_PSCI
smp_set_core_boot_addr((unsigned long)secure_ram_addr(_smp_pen), -1); smp_kick_all_cpus();
+#endif
/* call the non-sec switching code on this CPU also */
_nonsec_init();
relocate_secure_section();
secure_ram_addr(_nonsec_init)(); return 0;
} diff --git a/arch/arm/include/asm/armv7.h b/arch/arm/include/asm/armv7.h index 395444e..11476dd 100644 --- a/arch/arm/include/asm/armv7.h +++ b/arch/arm/include/asm/armv7.h @@ -78,13 +78,17 @@ void v7_outer_cache_inval_range(u32 start, u32 end);
#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT)
-int armv7_switch_nonsec(void); -int armv7_switch_hyp(void); +int armv7_init_nonsec(void);
/* defined in assembly file */ unsigned int _nonsec_init(void); +void _do_nonsec_entry(void *target_pc, unsigned long r0,
unsigned long r1, unsigned long r2);
void _smp_pen(void); -void _switch_to_hyp(void);
+extern char __secure_start[]; +extern char __secure_end[];
#endif /* CONFIG_ARMV7_NONSEC || CONFIG_ARMV7_VIRT */
#endif /* ! __ASSEMBLY__ */ diff --git a/arch/arm/include/asm/secure.h b/arch/arm/include/asm/secure.h new file mode 100644 index 0000000..effdb18 --- /dev/null +++ b/arch/arm/include/asm/secure.h @@ -0,0 +1,26 @@ +#ifndef __ASM_SECURE_H +#define __ASM_SECURE_H
+#include <config.h>
+#ifdef CONFIG_ARMV7_SECURE_BASE +/*
- Warning, horror ahead.
- The target code lives in our "secure ram", but u-boot doesn't know
- that, and has blindly added reloc_off to every relocation
- entry. Gahh. Do the opposite conversion. This hack also prevents
- GCC from generating code veeners, which u-boot doesn't relocate at
- all...
- */
+#define secure_ram_addr(_fn) ({ \
DECLARE_GLOBAL_DATA_PTR; \
void *__fn = _fn; \
typeof(_fn) *__tmp = (__fn - gd->reloc_off); \
__tmp; \
})
+#else +#define secure_ram_addr(_fn) (_fn) +#endif
+#endif diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c index 10634a4..61aa14e 100644 --- a/arch/arm/lib/bootm.c +++ b/arch/arm/lib/bootm.c @@ -20,6 +20,7 @@ #include <libfdt.h> #include <fdt_support.h> #include <asm/bootm.h> +#include <asm/secure.h> #include <linux/compiler.h>
#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT) @@ -184,27 +185,17 @@ static void setup_end_tag(bd_t *bd)
__weak void setup_board_tags(struct tag **in_params) {}
+#ifdef CONFIG_ARM64 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
-#ifdef CONFIG_ARM64 smp_kick_all_cpus(); flush_dcache_all(); /* flush cache before swtiching to EL2 */ armv8_switch_to_el2(); #ifdef CONFIG_ARMV8_SWITCH_TO_EL1 armv8_switch_to_el1(); #endif -#endif } +#endif
/* Subcommand: PREP */ static void boot_prep_linux(bootm_headers_t *images) @@ -287,8 +278,13 @@ static void boot_jump_linux(bootm_headers_t *images, int flag) r2 = gd->bd->bi_boot_params;
if (!fake) {
do_nonsec_virt_switch();
+#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT)
armv7_init_nonsec();
secure_ram_addr(_do_nonsec_entry)(kernel_entry,
0, machid, r2);
+#else kernel_entry(0, machid, r2); +#endif } #endif } -- 1.9.2
U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot