[U-Boot] [PATCH v2 00/13] ARMv7: add PSCI support to u-boot

PSCI is an ARM standard that provides a generic interface that supervisory software can use to manage power in the following situations: - Core idle management - CPU hotplug - big.LITTLE migration models - System shutdown and reset
It basically allows the kernel to offload these tasks to the firmware, and rely on common kernel side code.
More importantly, it gives a way to ensure that CPUs enter the kernel at the appropriate exception level (ie HYP mode, to allow the use of the virtualization extensions), even across events like CPUs being powered off/on or suspended.
The main idea here is to turn some of the existing u-boot code into a separate section that can live in secure RAM (or a reserved page of memory), containing a secure monitor that will implement the PSCI operations. This code will still be alive when u-boot is long gone, hence the need for a piece of memory that will not be touched by the OS.
This patch series contains 4 parts: - the first four patches are just bug fixes - the next three refactor the HYP/non-secure code to allow relocation in secure memory - the next three contain the generic PSCI code and DT infrastructure - the last three implement the CPU_ON method of the Allwinner A20 (aka sun7i).
I realize the A20 u-boot code is not upstream yet (BTW is anyone actively working on that?), but hopefully that should give a good idea of how things are structured so far. The patches are against the sunxi u-boot tree as of today, and the first 10 patches will directly apply to mainline u-boot.
As for using this code, it goes like this: sun7i# ext2load mmc 0:1 0x40008000 zImage ; ext2load mmc 0:1 0x60000000 sun7i-a20-cubietruck.dtb 2270120 bytes read in 117 ms (18.5 MiB/s) 9138 bytes read in 3 ms (2.9 MiB/s) sun7i# fdt addr 0x60000000 ; fdt resize ; fdt set ethernet0 mac-address "[5a fe b0 07 b0 07]" sun7i# setenv bootargs console=ttyS0,115200 earlyprintk ip=dhcp root=/dev/nfs nfsroot=/backup/a20_root,tcp sun7i# bootz 0x40008000 - 0x60000000
The kernel now boots in HYP mode, finds its secondary CPU without any SMP code present in the kernel, and runs KVM out of the box. Hopefully, the Xen/ARM guys can do the same fairly easily.
This code has also been tested on a VExpress TC2, running KVM with all 5 CPUs, in order to make sure there was no obvious regression.
I'm wildly cross-posting this patch series, including to lists I'm not subscribed to. Please keep me on Cc for any comment you may have.
Cheers,
M.
Marc Zyngier (13): ARM: HYP/non-sec: fix alignment requirements for vectors ARM: HYP/non-sec: move switch to non-sec to the last boot phase ARM: HYP/non-sec: add a barrier after setting SCR.NS==1 ARM: non-sec: reset CNTVOFF to zero ARM: add missing HYP mode constant ARM: HYP/non-sec: add separate section for secure code ARM: HYP/non-sec: allow relocation to secure RAM ARM: HYP/non-sec: add generic ARMv7 PSCI code ARM: HYP/non-sec: add the option for a second-stage monitor ARM: HYP/non-sec/PSCI: emit DT nodes sunxi: fix SRAM_B/SRAM_D memory map sunxi: HYP/non-sec: add sun7i PSCI backend sunxi: HYP/non-sec: configure CNTFRQ on all CPUs
arch/arm/cpu/armv7/Makefile | 5 + arch/arm/cpu/armv7/nonsec_virt.S | 168 ++++++++++++++++--------------- arch/arm/cpu/armv7/psci.S | 113 +++++++++++++++++++++ arch/arm/cpu/armv7/sunxi/Makefile | 3 + arch/arm/cpu/armv7/sunxi/psci.S | 124 +++++++++++++++++++++++ arch/arm/cpu/armv7/sunxi/u-boot-psci.lds | 63 ++++++++++++ arch/arm/cpu/armv7/virt-dt.c | 102 +++++++++++++++++++ arch/arm/cpu/armv7/virt-v7.c | 59 ++++------- arch/arm/cpu/u-boot.lds | 30 ++++++ arch/arm/include/asm/arch-sunxi/cpu.h | 4 +- arch/arm/include/asm/armv7.h | 11 +- arch/arm/include/asm/proc-armv/ptrace.h | 2 + arch/arm/include/asm/psci.h | 35 +++++++ arch/arm/include/asm/secure.h | 26 +++++ arch/arm/lib/bootm.c | 26 ++--- arch/arm/lib/interrupts.c | 2 +- arch/arm/lib/sections.c | 2 + include/configs/sun7i.h | 7 ++ 18 files changed, 641 insertions(+), 141 deletions(-) create mode 100644 arch/arm/cpu/armv7/psci.S create mode 100644 arch/arm/cpu/armv7/sunxi/psci.S create mode 100644 arch/arm/cpu/armv7/sunxi/u-boot-psci.lds create mode 100644 arch/arm/cpu/armv7/virt-dt.c create mode 100644 arch/arm/include/asm/psci.h create mode 100644 arch/arm/include/asm/secure.h

Make sure the vectors are aligned on a 32 byte boundary, not the code that deals with it...
Signed-off-by: Marc Zyngier marc.zyngier@arm.com --- arch/arm/cpu/armv7/nonsec_virt.S | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/arm/cpu/armv7/nonsec_virt.S b/arch/arm/cpu/armv7/nonsec_virt.S index 24b4c18..29987cd 100644 --- a/arch/arm/cpu/armv7/nonsec_virt.S +++ b/arch/arm/cpu/armv7/nonsec_virt.S @@ -14,6 +14,8 @@ .arch_extension sec .arch_extension virt
+ .align 5 @ Minimal alignment for vectors + /* the vector table for secure state and HYP mode */ _monitor_vectors: .word 0 /* reset */ @@ -32,7 +34,6 @@ _monitor_vectors: * to non-secure state. * We use only r0 and r1 here, due to constraints in the caller. */ - .align 5 _secure_monitor: mrc p15, 0, r1, c1, c1, 0 @ read SCR bic r1, r1, #0x4e @ clear IRQ, FIQ, EA, nET bits

On Sat, 2013-12-07 at 11:19 +0000, Marc Zyngier wrote:
Make sure the vectors are aligned on a 32 byte boundary, not the code that deals with it...
Signed-off-by: Marc Zyngier marc.zyngier@arm.com
Looks like Masahiro Yamada's equivalent patch is now in the u-boot-arm tree.
Ian.

Having the switch to non-secure in the "prep" phase is causing all kind of troubles, as that stage can be called multiple times.
Instead, move the switch to non-secure to the last possible phase, when there is no turning back anymore.
Signed-off-by: Marc Zyngier marc.zyngier@arm.com --- arch/arm/lib/bootm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c index f476a89..f3da634 100644 --- a/arch/arm/lib/bootm.c +++ b/arch/arm/lib/bootm.c @@ -234,7 +234,6 @@ static void boot_prep_linux(bootm_headers_t *images) printf("FDT and ATAGS support not compiled in - hanging\n"); hang(); } - do_nonsec_virt_switch(); }
/* Subcommand: GO */ @@ -264,8 +263,10 @@ static void boot_jump_linux(bootm_headers_t *images, int flag) else r2 = gd->bd->bi_boot_params;
- if (!fake) + if (!fake) { + do_nonsec_virt_switch(); kernel_entry(0, machid, r2); + } }
/* Main Entry point for arm bootm implementation

A CP15 instruction execution can be reordered, requiring an isb to be sure it is executed in program order.
Signed-off-by: Marc Zyngier marc.zyngier@arm.com --- arch/arm/cpu/armv7/nonsec_virt.S | 1 + 1 file changed, 1 insertion(+)
diff --git a/arch/arm/cpu/armv7/nonsec_virt.S b/arch/arm/cpu/armv7/nonsec_virt.S index 29987cd..648066f 100644 --- a/arch/arm/cpu/armv7/nonsec_virt.S +++ b/arch/arm/cpu/armv7/nonsec_virt.S @@ -47,6 +47,7 @@ _secure_monitor: #endif
mcr p15, 0, r1, c1, c1, 0 @ write SCR (with NS bit set) + isb
#ifdef CONFIG_ARMV7_VIRT mrceq p15, 0, r0, c12, c0, 1 @ get MVBAR value

Before switching to non-secure, make sure that CNTVOFF is set to zero on all CPUs. Otherwise, kernel running in non-secure without HYP enabled (hence using virtual timers) may observe timers that are not synchronized, effectively seeing time going backward...
Signed-off-by: Marc Zyngier marc.zyngier@arm.com --- arch/arm/cpu/armv7/nonsec_virt.S | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/arch/arm/cpu/armv7/nonsec_virt.S b/arch/arm/cpu/armv7/nonsec_virt.S index 648066f..5f81a4d 100644 --- a/arch/arm/cpu/armv7/nonsec_virt.S +++ b/arch/arm/cpu/armv7/nonsec_virt.S @@ -39,10 +39,10 @@ _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) +#ifdef CONFIG_ARMV7_VIRT orreq r1, r1, #0x100 @ allow HVC instruction #endif
@@ -53,7 +53,14 @@ _secure_monitor: 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 +1: movs pc, lr @ return to non-secure SVC
_hyp_trap:

In order to be able to use the various mode constants (far more readable than random hex values), add the missing HYP and A values.
Also update arm/lib/interrupts.c to display HYP instead of an unknown value.
Signed-off-by: Marc Zyngier marc.zyngier@arm.com --- arch/arm/include/asm/proc-armv/ptrace.h | 2 ++ arch/arm/lib/interrupts.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/arch/arm/include/asm/proc-armv/ptrace.h b/arch/arm/include/asm/proc-armv/ptrace.h index a060ee6..e66c381 100644 --- a/arch/arm/include/asm/proc-armv/ptrace.h +++ b/arch/arm/include/asm/proc-armv/ptrace.h @@ -19,12 +19,14 @@ #define IRQ_MODE 0x12 #define SVC_MODE 0x13 #define ABT_MODE 0x17 +#define HYP_MODE 0x1a #define UND_MODE 0x1b #define SYSTEM_MODE 0x1f #define MODE_MASK 0x1f #define T_BIT 0x20 #define F_BIT 0x40 #define I_BIT 0x80 +#define A_BIT 0x100 #define CC_V_BIT (1 << 28) #define CC_C_BIT (1 << 29) #define CC_Z_BIT (1 << 30) diff --git a/arch/arm/lib/interrupts.c b/arch/arm/lib/interrupts.c index 603bf14..fa553c4 100644 --- a/arch/arm/lib/interrupts.c +++ b/arch/arm/lib/interrupts.c @@ -103,7 +103,7 @@ void show_regs (struct pt_regs *regs) "UK12_26", "UK13_26", "UK14_26", "UK15_26", "USER_32", "FIQ_32", "IRQ_32", "SVC_32", "UK4_32", "UK5_32", "UK6_32", "ABT_32", - "UK8_32", "UK9_32", "UK10_32", "UND_32", + "UK8_32", "UK9_32", "HYP_32", "UND_32", "UK12_32", "UK13_32", "UK14_32", "SYS_32", };

In anticipation of refactoring the HYP/non-secure code to run from secure RAM, add a new linker section that will contain that code.
Nothing is using it just yet.
Signed-off-by: Marc Zyngier marc.zyngier@arm.com --- arch/arm/cpu/u-boot.lds | 30 ++++++++++++++++++++++++++++++ arch/arm/lib/sections.c | 2 ++ 2 files changed, 32 insertions(+)
diff --git a/arch/arm/cpu/u-boot.lds b/arch/arm/cpu/u-boot.lds index 23bf030..8096efe 100644 --- a/arch/arm/cpu/u-boot.lds +++ b/arch/arm/cpu/u-boot.lds @@ -7,6 +7,8 @@ * SPDX-License-Identifier: GPL-2.0+ */
+#include <config.h> + OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") OUTPUT_ARCH(arm) ENTRY(_start) @@ -22,6 +24,34 @@ SECTIONS *(.text*) }
+#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT) || defined(CONFIG_ARMV7_PSCI) + +#ifndef CONFIG_ARMV7_SECURE_BASE +#define CONFIG_ARMV7_SECURE_BASE +#endif + + .__secure_start : { + . = ALIGN(0x1000); + *(.__secure_start) + } + + .secure_text CONFIG_ARMV7_SECURE_BASE : + AT(ADDR(.__secure_start) + SIZEOF(.__secure_start)) + { + *(._secure.text) + } + + . = LOADADDR(.__secure_start) + + SIZEOF(.__secure_start) + + SIZEOF(.secure_text); + + __secure_end_lma = .; + .__secure_end : AT(__secure_end_lma) { + *(.__secure_end) + LONG(0x1d1071c); /* Must output something to reset LMA */ + } +#endif + . = ALIGN(4); .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
diff --git a/arch/arm/lib/sections.c b/arch/arm/lib/sections.c index e35687c..c141923 100644 --- a/arch/arm/lib/sections.c +++ b/arch/arm/lib/sections.c @@ -25,3 +25,5 @@ char __image_copy_start[0] __attribute__((section(".__image_copy_start"))); char __image_copy_end[0] __attribute__((section(".__image_copy_end"))); char __rel_dyn_start[0] __attribute__((section(".__rel_dyn_start"))); char __rel_dyn_end[0] __attribute__((section(".__rel_dyn_end"))); +char __secure_start[0] __attribute__((section(".__secure_start"))); +char __secure_end[0] __attribute__((section(".__secure_end")));

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 | 21 ++--- 5 files changed, 136 insertions(+), 141 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 5f81a4d..35f5463 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 @ Minimal alignment for vectors
/* the vector table for secure state and HYP mode */ @@ -23,51 +26,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 @@ -75,31 +113,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. * * 1. initialize the GIC per-core interface * 2. allow coprocessor access in non-secure modes - * 3. 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 @@ -109,38 +137,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 - - 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 + get_gicc_addr r3, r1
mov r1, #1 @ set GICC_CTLR[enable] 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 @@ -158,44 +171,24 @@ 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)
#ifdef CONFIG_SMP_PEN_ADDR /* void __weak smp_waitloop(unsigned previous_address); */ ENTRY(smp_waitloop) - wfi + wfe 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 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 f3da634..408a774 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) @@ -185,19 +186,6 @@ static void setup_end_tag(bd_t *bd)
__weak void setup_board_tags(struct tag **in_params) {}
-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 */ static void boot_prep_linux(bootm_headers_t *images) { @@ -264,8 +252,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 } }

Hi Marc,
On Sat, 7 Dec 2013 11:19:12 +0000, 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 | 21 ++--- 5 files changed, 136 insertions(+), 141 deletions(-) create mode 100644 arch/arm/include/asm/secure.h
It seems like patch 07/13 does not apply properly on top of current ARM. Can you have a look? If a rebased V3 is needed, you can remove patch 01 from the list (or if it is simpler to you, keep it in and I'll just ignore it when applying).
Amicalement,

Hi Albert,
On Thu, 12 Dec 2013 11:47:31 +0100, Albert ARIBAUD albert.u.boot@aribaud.net wrote:
Hi Marc,
On Sat, 7 Dec 2013 11:19:12 +0000, 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 | 21 ++--- 5 files changed, 136 insertions(+), 141 deletions(-) create mode 100644 arch/arm/include/asm/secure.h
It seems like patch 07/13 does not apply properly on top of current ARM. Can you have a look? If a rebased V3 is needed, you can remove patch 01 from the list (or if it is simpler to you, keep it in and I'll just ignore it when applying).
I don't think there was a V3 for this so far; correct?
Amicalement,
Amicalement,

On 12/02/14 08:36, Albert ARIBAUD wrote:
Hi Albert,
On Thu, 12 Dec 2013 11:47:31 +0100, Albert ARIBAUD albert.u.boot@aribaud.net wrote:
Hi Marc,
On Sat, 7 Dec 2013 11:19:12 +0000, 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 | 21 ++--- 5 files changed, 136 insertions(+), 141 deletions(-) create mode 100644 arch/arm/include/asm/secure.h
It seems like patch 07/13 does not apply properly on top of current ARM. Can you have a look? If a rebased V3 is needed, you can remove patch 01 from the list (or if it is simpler to you, keep it in and I'll just ignore it when applying).
I don't think there was a V3 for this so far; correct?
Correct. I've been lazy. I'll try to repost something by the end of the week.
Thanks,
M.

Implement core support for PSCI. As this is generic code, it doesn't implement anything really useful (all the functions are returning Not Implemented).
Signed-off-by: Marc Zyngier marc.zyngier@arm.com --- arch/arm/cpu/armv7/Makefile | 4 ++ arch/arm/cpu/armv7/psci.S | 113 ++++++++++++++++++++++++++++++++++++++++++++ arch/arm/include/asm/psci.h | 35 ++++++++++++++ 3 files changed, 152 insertions(+) create mode 100644 arch/arm/cpu/armv7/psci.S create mode 100644 arch/arm/include/asm/psci.h
diff --git a/arch/arm/cpu/armv7/Makefile b/arch/arm/cpu/armv7/Makefile index 4069aa4..8027385 100644 --- a/arch/arm/cpu/armv7/Makefile +++ b/arch/arm/cpu/armv7/Makefile @@ -24,6 +24,10 @@ obj-y += nonsec_virt.o obj-y += virt-v7.o endif
+ifneq ($(CONFIG_ARMV7_PSCI),) +obj-y += psci.o +endif + obj-$(CONFIG_OMAP_COMMON) += omap-common/ obj-$(CONFIG_TEGRA) += tegra-common/
diff --git a/arch/arm/cpu/armv7/psci.S b/arch/arm/cpu/armv7/psci.S new file mode 100644 index 0000000..3c70b9a --- /dev/null +++ b/arch/arm/cpu/armv7/psci.S @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2013 - ARM Ltd + * Author: Marc Zyngier marc.zyngier@arm.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +#include <config.h> +#include <linux/linkage.h> +#include <asm/psci.h> + + .pushsection ._secure.text, "ax" + + .arch_extension sec + + .align 5 + .globl _psci_vectors +_psci_vectors: + adr pc, . @ reset + adr pc, . @ undef + adr pc, _smc_psci @ smc + adr pc, . @ pabort + adr pc, . @ dabort + adr pc, . @ hyp + adr pc, . @ irq + adr pc, . @ fiq + +ENTRY(psci_cpu_suspend) +ENTRY(psci_cpu_off) +ENTRY(psci_cpu_on) +ENTRY(psci_migrate) + mov r0, #ARM_PSCI_RET_NI @ Return -1 (Not Implemented) + mov pc, lr +ENDPROC(psci_migrate) +ENDPROC(psci_cpu_on) +ENDPROC(psci_cpu_off) +ENDPROC(psci_cpu_suspend) +.weak psci_cpu_suspend +.weak psci_cpu_off +.weak psci_cpu_on +.weak psci_migrate + +_psci_table: + .word ARM_PSCI_FN_CPU_SUSPEND + .word psci_cpu_suspend + .word ARM_PSCI_FN_CPU_OFF + .word psci_cpu_off + .word ARM_PSCI_FN_CPU_ON + .word psci_cpu_on + .word ARM_PSCI_FN_MIGRATE + .word psci_migrate + .word 0 + .word 0 + +_secure_stacks: @ Enough to save 16 registers per CPU + .skip 16*4*CONFIG_ARMV7_PSCI_NR_CPUS +_secure_stack_base: + +_smc_psci: + @ Switch to secure mode + mrc p15, 0, sp, c1, c1, 0 + bic sp, sp, #1 + mcr p15, 0, sp, c1, c1, 0 + + adr sp, _secure_stack_base + mcr p15, 0, r0, c13, c0, 4 @ use TPIDRPRW as a tmp reg + mcr p15, 0, r1, c13, c0, 3 @ use TPIDRURO as a tmp reg + mrc p15, 0, r0, c0, c0, 5 @ MPIDR + and r1, r0, #3 @ cpu number in cluster + lsr r0, r0, #8 + and r0, r0, #3 @ cluster number + mul r1, r1, r0 @ absolute cpu nr + sbc sp, sp, r1, lsl #6 @ sp = sp_base - 64*cpunr + + mrc p15, 0, r0, c13, c0, 4 @ restore r0 + mrc p15, 0, r1, c13, c0, 3 @ restore r1 + + push {r4-r12,lr} + + adr r4, _psci_table +1: ldr r5, [r4] @ Load PSCI function ID + ldr r6, [r4, #4] @ Load target PC + cmp r5, #0 @ If reach the end, bail out + mvneq r0, #0 @ Return -1 (Not Implemented) + beq 2f + cmp r0, r5 @ If not matching, try next entry + addne r4, r4, #8 + bne 1b + cmp r6, #0 @ Not implemented + moveq r0, #ARM_PSCI_RET_NI + beq 2f + + blx r6 @ Execute PSCI function + +2: pop {r4-r12, lr} + + @ Back to non-secure + mrc p15, 0, sp, c1, c1, 0 + orr sp, sp, #1 + mcr p15, 0, sp, c1, c1, 0 + movs pc, lr @ Return to the kernel + + .popsection diff --git a/arch/arm/include/asm/psci.h b/arch/arm/include/asm/psci.h new file mode 100644 index 0000000..704b4b0 --- /dev/null +++ b/arch/arm/include/asm/psci.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2013 - ARM Ltd + * Author: Marc Zyngier marc.zyngier@arm.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +#ifndef __ARM_PSCI_H__ +#define __ARM_PSCI_H__ + +/* PSCI interface */ +#define ARM_PSCI_FN_BASE 0x95c1ba5e +#define ARM_PSCI_FN(n) (ARM_PSCI_FN_BASE + (n)) + +#define ARM_PSCI_FN_CPU_SUSPEND ARM_PSCI_FN(0) +#define ARM_PSCI_FN_CPU_OFF ARM_PSCI_FN(1) +#define ARM_PSCI_FN_CPU_ON ARM_PSCI_FN(2) +#define ARM_PSCI_FN_MIGRATE ARM_PSCI_FN(3) + +#define ARM_PSCI_RET_SUCCESS 0 +#define ARM_PSCI_RET_NI (-1) +#define ARM_PSCI_RET_INVAL (-2) +#define ARM_PSCI_RET_DENIED (-3) + +#endif /* __ARM_PSCI_H__ */

Allow the switch to a second stage secure monitor just before switching to non-secure.
This allows a resident piece of firmware to be active once the kernel has been entered (the u-boot monitor is dead anyway, its pages being reused).
Signed-off-by: Marc Zyngier marc.zyngier@arm.com --- arch/arm/cpu/armv7/nonsec_virt.S | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/arch/arm/cpu/armv7/nonsec_virt.S b/arch/arm/cpu/armv7/nonsec_virt.S index 35f5463..6130a37 100644 --- a/arch/arm/cpu/armv7/nonsec_virt.S +++ b/arch/arm/cpu/armv7/nonsec_virt.S @@ -45,6 +45,12 @@ _monitor_vectors: * 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 +#endif + 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

Generate the PSCI node in the device tree.
Also add a reserve section for the "secure" code that lives in in normal RAM, so that the kernel knows it'd better not trip on it.
Signed-off-by: Marc Zyngier marc.zyngier@arm.com --- arch/arm/cpu/armv7/Makefile | 1 + arch/arm/cpu/armv7/virt-dt.c | 102 +++++++++++++++++++++++++++++++++++++++++++ arch/arm/include/asm/armv7.h | 1 + arch/arm/lib/bootm.c | 2 + 4 files changed, 106 insertions(+) create mode 100644 arch/arm/cpu/armv7/virt-dt.c
diff --git a/arch/arm/cpu/armv7/Makefile b/arch/arm/cpu/armv7/Makefile index 8027385..93a5242 100644 --- a/arch/arm/cpu/armv7/Makefile +++ b/arch/arm/cpu/armv7/Makefile @@ -22,6 +22,7 @@ endif ifneq ($(CONFIG_ARMV7_NONSEC)$(CONFIG_ARMV7_VIRT),) obj-y += nonsec_virt.o obj-y += virt-v7.o +obj-y += virt-dt.o endif
ifneq ($(CONFIG_ARMV7_PSCI),) diff --git a/arch/arm/cpu/armv7/virt-dt.c b/arch/arm/cpu/armv7/virt-dt.c new file mode 100644 index 0000000..730ede2 --- /dev/null +++ b/arch/arm/cpu/armv7/virt-dt.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2013 - ARM Ltd + * Author: Marc Zyngier marc.zyngier@arm.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +#include <common.h> +#include <stdio_dev.h> +#include <linux/ctype.h> +#include <linux/types.h> +#include <asm/global_data.h> +#include <libfdt.h> +#include <fdt_support.h> +#include <asm/armv7.h> +#include <asm/psci.h> + +static int fdt_psci(void *fdt) +{ +#ifdef CONFIG_ARMV7_PSCI + int nodeoff; + int tmp; + + nodeoff = fdt_path_offset(fdt, "/cpus"); + if (nodeoff < 0) { + printf("couldn't find /cpus\n"); + return nodeoff; + } + + /* add 'enable-method = "psci"' to each cpu node */ + for (tmp = fdt_first_subnode(fdt, nodeoff); + tmp >= 0; + tmp = fdt_next_subnode(fdt, tmp)) { + const struct fdt_property *prop; + int len; + + prop = fdt_get_property(fdt, tmp, "device_type", &len); + if (!prop) + continue; + if (len < 4) + continue; + if (strcmp(prop->data, "cpu")) + continue; + + fdt_setprop_string(fdt, tmp, "enable-method", "psci"); + } + + nodeoff = fdt_path_offset(fdt, "/psci"); + if (nodeoff < 0) { + nodeoff = fdt_path_offset(fdt, "/"); + if (nodeoff < 0) + return nodeoff; + + nodeoff = fdt_add_subnode(fdt, nodeoff, "psci"); + if (nodeoff < 0) + return nodeoff; + } + + tmp = fdt_setprop_string(fdt, nodeoff, "compatible", "arm,psci"); + if (tmp) + return tmp; + tmp = fdt_setprop_string(fdt, nodeoff, "method", "smc"); + if (tmp) + return tmp; + tmp = fdt_setprop_u32(fdt, nodeoff, "cpu_suspend", ARM_PSCI_FN_CPU_SUSPEND); + if (tmp) + return tmp; + tmp = fdt_setprop_u32(fdt, nodeoff, "cpu_off", ARM_PSCI_FN_CPU_OFF); + if (tmp) + return tmp; + tmp = fdt_setprop_u32(fdt, nodeoff, "cpu_on", ARM_PSCI_FN_CPU_ON); + if (tmp) + return tmp; + tmp = fdt_setprop_u32(fdt, nodeoff, "migrate", ARM_PSCI_FN_MIGRATE); + if (tmp) + return tmp; +#endif + return 0; +} + +int armv7_update_dt(void *fdt) +{ + fdt_resize(fdt); + +#ifndef CONFIG_ARMV7_SECURE_BASE + /* secure code lives in RAM, keep it alive */ + fdt_add_mem_rsv(fdt, (unsigned long)__secure_start, + __secure_end - __secure_start); +#endif + + return fdt_psci(fdt); +} diff --git a/arch/arm/include/asm/armv7.h b/arch/arm/include/asm/armv7.h index 11476dd..323f282 100644 --- a/arch/arm/include/asm/armv7.h +++ b/arch/arm/include/asm/armv7.h @@ -79,6 +79,7 @@ void v7_outer_cache_inval_range(u32 start, u32 end); #if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT)
int armv7_init_nonsec(void); +int armv7_update_dt(void *fdt);
/* defined in assembly file */ unsigned int _nonsec_init(void); diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c index 408a774..1709eec 100644 --- a/arch/arm/lib/bootm.c +++ b/arch/arm/lib/bootm.c @@ -253,6 +253,8 @@ static void boot_jump_linux(bootm_headers_t *images, int flag)
if (!fake) { #if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT) + if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) + armv7_update_dt(images->ft_addr); armv7_init_nonsec(); secure_ram_addr(_do_nonsec_entry)(kernel_entry, 0, machid, r2);

Move the B and D SRAM bank to their actual location (or at least where the documentation pretends they are).
Signed-off-by: Marc Zyngier marc.zyngier@arm.com --- arch/arm/include/asm/arch-sunxi/cpu.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm/include/asm/arch-sunxi/cpu.h b/arch/arm/include/asm/arch-sunxi/cpu.h index 378989c..41fb8dc 100644 --- a/arch/arm/include/asm/arch-sunxi/cpu.h +++ b/arch/arm/include/asm/arch-sunxi/cpu.h @@ -31,8 +31,8 @@ #define SUNXI_SRAM_A2_BASE 0x00004000 /* 16 kiB */ #define SUNXI_SRAM_A3_BASE 0x00008000 /* 13 kiB */ #define SUNXI_SRAM_A4_BASE 0x0000b400 /* 3 kiB */ -#define SUNXI_SRAM_D_BASE 0x01c00000 -#define SUNXI_SRAM_B_BASE 0x01c00000 /* 64 kiB (secure) */ +#define SUNXI_SRAM_D_BASE 0x00010000 /* 4 kiB */ +#define SUNXI_SRAM_B_BASE 0x00020000 /* 64 kiB (secure) */
#define SUNXI_SRAMC_BASE 0x01c00000 #define SUNXI_DRAMC_BASE 0x01c01000

So far, only supporting the CPU_ON method. Other functions can be added later.
Signed-off-by: Marc Zyngier marc.zyngier@arm.com --- arch/arm/cpu/armv7/sunxi/Makefile | 3 + arch/arm/cpu/armv7/sunxi/psci.S | 124 +++++++++++++++++++++++++++++++ arch/arm/cpu/armv7/sunxi/u-boot-psci.lds | 63 ++++++++++++++++ include/configs/sun7i.h | 6 ++ 4 files changed, 196 insertions(+) create mode 100644 arch/arm/cpu/armv7/sunxi/psci.S create mode 100644 arch/arm/cpu/armv7/sunxi/u-boot-psci.lds
diff --git a/arch/arm/cpu/armv7/sunxi/Makefile b/arch/arm/cpu/armv7/sunxi/Makefile index f71a26d..0369049 100644 --- a/arch/arm/cpu/armv7/sunxi/Makefile +++ b/arch/arm/cpu/armv7/sunxi/Makefile @@ -43,6 +43,9 @@ obj-y += cpu_info.o ifdef CONFIG_CMD_WATCHDOG obj-$(CONFIG_CMD_WATCHDOG) += cmd_watchdog.o endif +ifdef CONFIG_ARMV7_PSCI +obj-y += psci.o +endif endif
ifdef CONFIG_SPL_BUILD diff --git a/arch/arm/cpu/armv7/sunxi/psci.S b/arch/arm/cpu/armv7/sunxi/psci.S new file mode 100644 index 0000000..eb11883 --- /dev/null +++ b/arch/arm/cpu/armv7/sunxi/psci.S @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2013 - ARM Ltd + * Author: Marc Zyngier marc.zyngier@arm.com + * + * Based on code by Carl van Schaik carl@ok-labs.com. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +#include <config.h> +#include <asm/psci.h> +#include <asm/arch/cpu.h> + + .pushsection ._secure.text, "ax" + + .arch_extension sec + +#define TEN_MS (10 * CONFIG_SYS_CLK_FREQ / 1000) + + @ r1 = target CPU + @ r2 = target PC +.globl psci_cpu_on +psci_cpu_on: + adr r0, _target_pc + str r2, [r0] + dsb + + movw r0, #(SUNXI_CPUCFG_BASE & 0xffff) + movt r0, #(SUNXI_CPUCFG_BASE >> 16) + + @ CPU mask + and r1, r1, #3 @ only care about first cluster + mov r4, #1 + lsl r4, r4, r1 + + adr r6, _sunxi_cpu_entry + str r6, [r0, #0x1a4] @ PRIVATE_REG (boot vector) + + @ Assert reset on target CPU + mov r6, #0 + lsl r5, r1, #6 @ 64 bytes per CPU + add r5, r5, #0x40 @ Offset from base + add r5, r5, r0 @ CPU control block + str r6, [r5] @ Reset CPU + + @ l1 invalidate + ldr r6, [r0, #0x184] + bic r6, r6, r4 + str r6, [r0, #0x184] + + @ Lock CPU + ldr r6, [r0, #0x1e4] + bic r6, r6, r4 + str r6, [r0, #0x1e4] + + @ Release power clamp + movw r6, #0x1ff + movt r6, #0 +1: lsrs r6, r6, #1 + str r6, [r0, #0x1b0] + bne 1b + + @ Write CNTP_TVAL : 10ms @ 24MHz (240000 cycles) + movw r1, #(TEN_MS & 0xffff) + movt r1, #(TEN_MS >> 16) + mcr p15, 0, r1, c14, c2, 0 + isb + @ Enable physical timer, mask interrupt + mov r1, #3 + mcr p15, 0, r1, c14, c2, 1 + @ Poll physical timer until ISTATUS is on +1: isb + mrc p15, 0, r1, c14, c2, 1 + ands r1, r1, #4 + bne 1b + @ Disable timer + mov r1, #0 + mcr p15, 0, r1, c14, c2, 1 + isb + + @ Clear power gating + ldr r6, [r0, #0x1b4] + bic r6, r6, #1 + str r6, [r0, #0x1b4] + + @ Deassert reset on target CPU + mov r6, #3 + str r6, [r5] + + @ Unlock CPU + ldr r6, [r0, #0x1e4] + orr r6, r6, r4 + str r6, [r0, #0x1e4] + + mov r0, #ARM_PSCI_RET_SUCCESS @ Return PSCI_RET_SUCCESS + mov pc, lr + +_target_pc: + .word 0 + +_sunxi_cpu_entry: + @ Set SMP bit + mrc p15, 0, r0, c1, c0, 1 + orr r0, r0, #0x40 + mcr p15, 0, r0, c1, c0, 1 + isb + + bl _nonsec_init + + adr r0, _target_pc + ldr r0, [r0] + b _do_nonsec_entry + + .popsection diff --git a/arch/arm/cpu/armv7/sunxi/u-boot-psci.lds b/arch/arm/cpu/armv7/sunxi/u-boot-psci.lds new file mode 100644 index 0000000..a0f166e --- /dev/null +++ b/arch/arm/cpu/armv7/sunxi/u-boot-psci.lds @@ -0,0 +1,63 @@ +/* + * (C) Copyright 2013 ARM Ltd + * Marc Zyngier marc.zyngier@arm.com + * + * Based on sunxi/u-boot-spl.lds: + * + * (C) Copyright 2012 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Tom Cubie tangliang@allwinnertech.com + * + * Based on omap-common/u-boot-spl.lds: + * + * (C) Copyright 2002 + * Gary Jennejohn, DENX Software Engineering, garyj@denx.de + * + * (C) Copyright 2010 + * Texas Instruments, <www.ti.com> + * Aneesh V aneesh@ti.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +MEMORY { sram : ORIGIN = CONFIG_ARMV7_PSCI_BASE, LENGTH = 0x1000 } +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(_start) +SECTIONS +{ + .text : + { + _start = .; + *(.text*) + } > sram + + . = ALIGN(4); + .rodata : { *(SORT_BY_ALIGNMENT(.rodata*)) } > sram + + . = ALIGN(4); + .data : { *(SORT_BY_ALIGNMENT(.data*)) } > sram + + . = ALIGN(4); + _end = .; + + /DISCARD/ : { + *(.bss*) + } +} diff --git a/include/configs/sun7i.h b/include/configs/sun7i.h index a6ede2a..c3c24fb 100644 --- a/include/configs/sun7i.h +++ b/include/configs/sun7i.h @@ -38,6 +38,12 @@ #define CONFIG_BOARD_POSTCLK_INIT 1 #endif
+#define CONFIG_ARMV7_VIRT 1 +#define CONFIG_ARMV7_NONSEC 1 +#define CONFIG_ARMV7_PSCI 1 +#define CONFIG_ARMV7_PSCI_NR_CPUS 2 +#define CONFIG_ARMV7_SECURE_BASE SUNXI_SRAM_B_BASE + /* * Include common sunxi configuration where most the settings are */

CNTFRQ needs to be properly configured on all CPUs. Otherwise, virtual machines hoping to find valuable information on secondary CPUs will be disapointed...
Signed-off-by: Marc Zyngier marc.zyngier@arm.com --- include/configs/sun7i.h | 1 + 1 file changed, 1 insertion(+)
diff --git a/include/configs/sun7i.h b/include/configs/sun7i.h index c3c24fb..9098541 100644 --- a/include/configs/sun7i.h +++ b/include/configs/sun7i.h @@ -43,6 +43,7 @@ #define CONFIG_ARMV7_PSCI 1 #define CONFIG_ARMV7_PSCI_NR_CPUS 2 #define CONFIG_ARMV7_SECURE_BASE SUNXI_SRAM_B_BASE +#define CONFIG_SYS_CLK_FREQ 24000000
/* * Include common sunxi configuration where most the settings are

On Sat, 2013-12-07 at 11:19 +0000, Marc Zyngier wrote:
The kernel now boots in HYP mode, finds its secondary CPU without any SMP code present in the kernel, and runs KVM out of the box. Hopefully, the Xen/ARM guys can do the same fairly easily.
Indeed, I booted with this series on a cb2 and Xen SMP Just Worked.
Thanks!
Ian.

On 09/12/13 10:51, Ian Campbell wrote:
On Sat, 2013-12-07 at 11:19 +0000, Marc Zyngier wrote:
The kernel now boots in HYP mode, finds its secondary CPU without any SMP code present in the kernel, and runs KVM out of the box. Hopefully, the Xen/ARM guys can do the same fairly easily.
Indeed, I booted with this series on a cb2 and Xen SMP Just Worked.
Profit! ;-)
Thanks for testing!
M.

Hi,
On 12/09/2013 11:51 AM, Ian Campbell wrote:
On Sat, 2013-12-07 at 11:19 +0000, Marc Zyngier wrote:
The kernel now boots in HYP mode, finds its secondary CPU without any SMP code present in the kernel, and runs KVM out of the box. Hopefully, the Xen/ARM guys can do the same fairly easily.
Indeed, I booted with this series on a cb2 and Xen SMP Just Worked.
Cool! Can you push the u-boot code you're using to your u-boot repo: http://xenbits.xen.org/gitweb/?p=people/ianc/u-boot.git;a=summary
? that will save me reproducing your work.
Thanks & Regards,
Hans

On 09/12/13 11:29, Hans de Goede wrote:
Hi,
On 12/09/2013 11:51 AM, Ian Campbell wrote:
On Sat, 2013-12-07 at 11:19 +0000, Marc Zyngier wrote:
The kernel now boots in HYP mode, finds its secondary CPU without any SMP code present in the kernel, and runs KVM out of the box. Hopefully, the Xen/ARM guys can do the same fairly easily.
Indeed, I booted with this series on a cb2 and Xen SMP Just Worked.
Cool! Can you push the u-boot code you're using to your u-boot repo: http://xenbits.xen.org/gitweb/?p=people/ianc/u-boot.git;a=summary
? that will save me reproducing your work.
I've just created git://git.kernel.org/pub/scm/linux/kernel/git/maz/u-boot.git wip/psci
The mirrors seem a bit slow at the moment, but they should hopefully catch up.
M.

On Mon, 2013-12-09 at 12:29 +0100, Hans de Goede wrote:
Hi,
On 12/09/2013 11:51 AM, Ian Campbell wrote:
On Sat, 2013-12-07 at 11:19 +0000, Marc Zyngier wrote:
The kernel now boots in HYP mode, finds its secondary CPU without any SMP code present in the kernel, and runs KVM out of the box. Hopefully, the Xen/ARM guys can do the same fairly easily.
Indeed, I booted with this series on a cb2 and Xen SMP Just Worked.
Cool! Can you push the u-boot code you're using to your u-boot repo: http://xenbits.xen.org/gitweb/?p=people/ianc/u-boot.git;a=summary
? that will save me reproducing your work.
Done, see the devel/sunxi-psci branch.
Cheers, Ian.

Hi Marc,
On Sat, 7 Dec 2013 11:19:05 +0000, Marc Zyngier marc.zyngier@arm.com wrote:
PSCI is an ARM standard that provides a generic interface that supervisory software can use to manage power in the following situations:
- Core idle management
- CPU hotplug
- big.LITTLE migration models
- System shutdown and reset
It basically allows the kernel to offload these tasks to the firmware, and rely on common kernel side code.
More importantly, it gives a way to ensure that CPUs enter the kernel at the appropriate exception level (ie HYP mode, to allow the use of the virtualization extensions), even across events like CPUs being powered off/on or suspended.
The main idea here is to turn some of the existing u-boot code into a separate section that can live in secure RAM (or a reserved page of memory), containing a secure monitor that will implement the PSCI operations. This code will still be alive when u-boot is long gone, hence the need for a piece of memory that will not be touched by the OS.
This patch series contains 4 parts:
- the first four patches are just bug fixes
- the next three refactor the HYP/non-secure code to allow relocation in secure memory
- the next three contain the generic PSCI code and DT infrastructure
- the last three implement the CPU_ON method of the Allwinner A20 (aka sun7i).
I realize the A20 u-boot code is not upstream yet (BTW is anyone actively working on that?), but hopefully that should give a good idea of how things are structured so far. The patches are against the sunxi u-boot tree as of today, and the first 10 patches will directly apply to mainline u-boot.
As for using this code, it goes like this: sun7i# ext2load mmc 0:1 0x40008000 zImage ; ext2load mmc 0:1 0x60000000 sun7i-a20-cubietruck.dtb 2270120 bytes read in 117 ms (18.5 MiB/s) 9138 bytes read in 3 ms (2.9 MiB/s) sun7i# fdt addr 0x60000000 ; fdt resize ; fdt set ethernet0 mac-address "[5a fe b0 07 b0 07]" sun7i# setenv bootargs console=ttyS0,115200 earlyprintk ip=dhcp root=/dev/nfs nfsroot=/backup/a20_root,tcp sun7i# bootz 0x40008000 - 0x60000000
The kernel now boots in HYP mode, finds its secondary CPU without any SMP code present in the kernel, and runs KVM out of the box. Hopefully, the Xen/ARM guys can do the same fairly easily.
This code has also been tested on a VExpress TC2, running KVM with all 5 CPUs, in order to make sure there was no obvious regression.
I'm wildly cross-posting this patch series, including to lists I'm not subscribed to. Please keep me on Cc for any comment you may have.
Cheers,
M.
There are comments re 01/13 an 07/13. Should I expect a V3 on the U-Boot ML?
Amicalement,

Hi Albert,
On 13/02/14 10:11, Albert ARIBAUD wrote:
Hi Marc,
On Sat, 7 Dec 2013 11:19:05 +0000, Marc Zyngier marc.zyngier@arm.com wrote:
PSCI is an ARM standard that provides a generic interface that supervisory software can use to manage power in the following situations:
- Core idle management
- CPU hotplug
- big.LITTLE migration models
- System shutdown and reset
It basically allows the kernel to offload these tasks to the firmware, and rely on common kernel side code.
More importantly, it gives a way to ensure that CPUs enter the kernel at the appropriate exception level (ie HYP mode, to allow the use of the virtualization extensions), even across events like CPUs being powered off/on or suspended.
The main idea here is to turn some of the existing u-boot code into a separate section that can live in secure RAM (or a reserved page of memory), containing a secure monitor that will implement the PSCI operations. This code will still be alive when u-boot is long gone, hence the need for a piece of memory that will not be touched by the OS.
This patch series contains 4 parts:
- the first four patches are just bug fixes
- the next three refactor the HYP/non-secure code to allow relocation in secure memory
- the next three contain the generic PSCI code and DT infrastructure
- the last three implement the CPU_ON method of the Allwinner A20 (aka sun7i).
I realize the A20 u-boot code is not upstream yet (BTW is anyone actively working on that?), but hopefully that should give a good idea of how things are structured so far. The patches are against the sunxi u-boot tree as of today, and the first 10 patches will directly apply to mainline u-boot.
As for using this code, it goes like this: sun7i# ext2load mmc 0:1 0x40008000 zImage ; ext2load mmc 0:1 0x60000000 sun7i-a20-cubietruck.dtb 2270120 bytes read in 117 ms (18.5 MiB/s) 9138 bytes read in 3 ms (2.9 MiB/s) sun7i# fdt addr 0x60000000 ; fdt resize ; fdt set ethernet0 mac-address "[5a fe b0 07 b0 07]" sun7i# setenv bootargs console=ttyS0,115200 earlyprintk ip=dhcp root=/dev/nfs nfsroot=/backup/a20_root,tcp sun7i# bootz 0x40008000 - 0x60000000
The kernel now boots in HYP mode, finds its secondary CPU without any SMP code present in the kernel, and runs KVM out of the box. Hopefully, the Xen/ARM guys can do the same fairly easily.
This code has also been tested on a VExpress TC2, running KVM with all 5 CPUs, in order to make sure there was no obvious regression.
I'm wildly cross-posting this patch series, including to lists I'm not subscribed to. Please keep me on Cc for any comment you may have.
Cheers,
M.
There are comments re 01/13 an 07/13. Should I expect a V3 on the U-Boot ML?
Yes. As I said yesterday, I plan to address the comments pretty quickly (during the weekend anyway), and repost a new version on the same mailing lists.
Thanks,
M.

Hi Marc Zyngier
Where could obtain this repository with these alterations to be able to test virtualization with KVM on my cubieboard2'm using Kernel 3.14.rc4
I appreciate if you help
Thank you

Hi,
On 2014-02-25 13:38, Ezaul Zillmer wrote:
Hi Marc Zyngier
Where could obtain this repository with these alterations to be able to test virtualization with KVM on my cubieboard2'm using Kernel 3.14.rc4
I appreciate if you help
You're looking at an older patch series. The new one can be found there: http://permalink.gmane.org/gmane.comp.hardware.netbook.arm.sunxi/7151
I also have a repository with this code at: git://git.kernel.org/pub/scm/linux/kernel/git/maz/u-boot.git wip/psci
Cheers,
M.

Thank´s Marc
Error Compile
How could solve this compilation problem? What am I doing wrong?
Have since I'm Very Grateful!
make Cubieboard2 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
arm-linux-gnueabihf-gcc -M -g -Os -ffunction-sections -fdata-sections -fno-common -ffixed-r9 -msoft-float -D__KERNEL__ -DCONFIG_SYS_TEXT_BASE=0x4a000000 -I/root/cubie-test/util/u-boot-maz-wip-psci/include -I/root/cubie-test/util/u-boot-maz-wip-psci/arch/arm/include -fno-builtin -ffreestanding -nostdinc -isystem /usr/lib/gcc/arm-linux-gnueabihf/4.7/include -pipe -DCONFIG_ARM -D__ARM__ -marm -mno-thumb-interwork -mabi=aapcs-linux -mword-relocations -march=armv7-a -MQ designware.o designware.c >.depend.designware designware.c:21:3: error: #error "DesignWare Ether MAC requires PHYLIB - missing CONFIG_PHYLIB" make[2]: *** Sem regra para processar o alvo `.depend', necessário por `built-in.o'. Pare. make[2]: Saindo do diretório `/root/cubie-test/util/u-boot-maz-wip-psci/drivers/net' make[1]: ** [drivers/net/built-in.o] Erro 2 make[1]: Saindo do diretório `/root/cubie-test/util/u-boot-maz-wip-psci' make: ** [Cubieboard2] Erro 2 root@vbi7:~/cubie-test/util/u-boot-maz-wip-psci#
_____________________________________________________________
Em terça-feira, 25 de fevereiro de 2014 11h04min55s UTC-3, maz escreveu:
Hi,
On 2014-02-25 13:38, Ezaul Zillmer wrote:
Hi Marc Zyngier
Where could obtain this repository with these alterations to be able to test virtualization with KVM on my cubieboard2'm using Kernel 3.14.rc4
I appreciate if you help
You're looking at an older patch series. The new one can be found there: http://permalink.gmane.org/gmane.comp.hardware.netbook.arm.sunxi/7151
I also have a repository with this code at: git://git.kernel.org/pub/scm/linux/kernel/git/maz/u-boot.git wip/psci
Cheers,
M.
-- Who you jivin' with that Cosmik Debris?
participants (6)
-
Albert ARIBAUD
-
Ezaul Zillmer
-
Hans de Goede
-
Ian Campbell
-
Marc Zyngier
-
Marc Zyngier