diff mbox

[v3,3/7] ARM: add assembly routine to switch to non-secure state

Message ID 1373414059-22779-4-git-send-email-andre.przywara@linaro.org
State Superseded
Headers show

Commit Message

Andre Przywara July 9, 2013, 11:54 p.m. UTC
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>
---
 arch/arm/cpu/armv7/nonsec_virt.S    | 85 +++++++++++++++++++++++++++++++++++++
 arch/arm/include/asm/armv7.h        | 18 ++++++++
 arch/arm/include/asm/gic.h          | 17 ++++++++
 include/configs/vexpress_ca15_tc2.h |  2 -
 4 files changed, 120 insertions(+), 2 deletions(-)
 create mode 100644 arch/arm/include/asm/gic.h

Comments

Nikolay Nikolaev July 10, 2013, 12:47 p.m. UTC | #1
Hello Andre,


On Wed, Jul 10, 2013 at 2:54 AM, Andre Przywara
<andre.przywara@linaro.org>wrote:

> 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>
> ---
>  arch/arm/cpu/armv7/nonsec_virt.S    | 85
> +++++++++++++++++++++++++++++++++++++
>  arch/arm/include/asm/armv7.h        | 18 ++++++++
>  arch/arm/include/asm/gic.h          | 17 ++++++++
>  include/configs/vexpress_ca15_tc2.h |  2 -
>  4 files changed, 120 insertions(+), 2 deletions(-)
>  create mode 100644 arch/arm/include/asm/gic.h
>
> diff --git a/arch/arm/cpu/armv7/nonsec_virt.S
> b/arch/arm/cpu/armv7/nonsec_virt.S
> index 68a6b38..e9ee831 100644
> --- a/arch/arm/cpu/armv7/nonsec_virt.S
> +++ b/arch/arm/cpu/armv7/nonsec_virt.S
> @@ -23,6 +23,11 @@
>   */
>
>  #include <config.h>
> +#include <linux/linkage.h>
> +#include <asm/gic.h>
> +#include <asm/armv7.h>
> +
> +.arch_extension sec
>
>  /* the vector table for secure state */
>  _monitor_vectors:
> @@ -52,3 +57,83 @@ _secure_monitor:
>
>         movs    pc, lr                          @ return to non-secure SVC
>
> +/*
> + * 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
> + * 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)
> +#ifdef CONFIG_ARM_GIC_BASE_ADDRESS
> +       ldr     r2, =CONFIG_ARM_GIC_BASE_ADDRESS
> +#else
> +       mrc     p15, 4, r2, c15, c0, 0          @ read CBAR
>
PERIPHBASE[39:32] is CBAR[7:0] which we ignore here - OK.
PERIPHBASE[31:15] is CBAR[31:15] - OK
The rest of the bits is UNK/SBZP - we should not rely they are 0s.

I'd suggest to mask r2 with 0xFFFF8000 here (figure out some name like
PERIPHBASE_MASK)


> +#endif
> +       add     r3, r2, #GIC_DIST_OFFSET        @ GIC dist i/f offset
> +       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
> +
> +       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
> +
> +       movw    r1, #0x3fff
> +       movt    r1, #0x0006
> +       mcr     p15, 0, r1, 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.
> + */
> +#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
> +
> +       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)
> diff --git a/arch/arm/include/asm/armv7.h b/arch/arm/include/asm/armv7.h
> index 20caa7c..ab9fa58 100644
> --- a/arch/arm/include/asm/armv7.h
> +++ b/arch/arm/include/asm/armv7.h
> @@ -34,6 +34,19 @@
>  #define MIDR_CORTEX_A15_R0P0   0x410FC0F0
>  #define MIDR_CORTEX_A15_R2P2   0x412FC0F2
>
> +/* Cortex-A7 revisions */
> +#define MIDR_CORTEX_A7_R0P0    0x410FC070
> +
> +#define MIDR_PRIMARY_PART_MASK 0xFF0FFFF0
> +
> +/* ID_PFR1 feature fields */
> +#define CPUID_ARM_SEC_SHIFT            4
> +#define CPUID_ARM_SEC_MASK             (0xF << CPUID_ARM_SEC_SHIFT)
> +#define CPUID_ARM_VIRT_SHIFT           12
> +#define CPUID_ARM_VIRT_MASK            (0xF << CPUID_ARM_VIRT_SHIFT)
> +#define CPUID_ARM_GENTIMER_SHIFT       16
> +#define CPUID_ARM_GENTIMER_MASK                (0xF <<
> CPUID_ARM_GENTIMER_SHIFT)
> +
>  /* CCSIDR */
>  #define CCSIDR_LINE_SIZE_OFFSET                0
>  #define CCSIDR_LINE_SIZE_MASK          0x7
> @@ -76,6 +89,11 @@ void v7_outer_cache_inval_all(void);
>  void v7_outer_cache_flush_range(u32 start, u32 end);
>  void v7_outer_cache_inval_range(u32 start, u32 end);
>
> +#ifdef CONFIG_ARMV7_NONSEC
> +/* defined in assembly file */
> +unsigned int _nonsec_init(void);
> +#endif /* CONFIG_ARMV7_NONSEC */
> +
>  #endif /* ! __ASSEMBLY__ */
>
>  #endif
> diff --git a/arch/arm/include/asm/gic.h b/arch/arm/include/asm/gic.h
> new file mode 100644
> index 0000000..c2b1e28
> --- /dev/null
> +++ b/arch/arm/include/asm/gic.h
> @@ -0,0 +1,17 @@
> +#ifndef __GIC_V2_H__
> +#define __GIC_V2_H__
> +
> +/* register offsets for the ARM generic interrupt controller (GIC) */
> +
> +#define GIC_DIST_OFFSET                0x1000
> +#define GICD_CTLR              0x0000
> +#define GICD_TYPER             0x0004
> +#define GICD_IGROUPRn          0x0080
> +#define GICD_SGIR              0x0F00
> +
> +#define GIC_CPU_OFFSET_A9      0x0100
> +#define GIC_CPU_OFFSET_A15     0x2000
> +#define GICC_CTLR              0x0000
> +#define GICC_PMR               0x0004
> +
> +#endif
> diff --git a/include/configs/vexpress_ca15_tc2.h
> b/include/configs/vexpress_ca15_tc2.h
> index 9e230ad..4f425ac 100644
> --- a/include/configs/vexpress_ca15_tc2.h
> +++ b/include/configs/vexpress_ca15_tc2.h
> @@ -31,6 +31,4 @@
>  #include "vexpress_common.h"
>  #define CONFIG_BOOTP_VCI_STRING     "U-boot.armv7.vexpress_ca15x2_tc2"
>
> -#define CONFIG_SYS_CLK_FREQ 24000000
> -
>  #endif
> --
> 1.7.12.1
>
>
regards,
Nikolay Nikolaev
diff mbox

Patch

diff --git a/arch/arm/cpu/armv7/nonsec_virt.S b/arch/arm/cpu/armv7/nonsec_virt.S
index 68a6b38..e9ee831 100644
--- a/arch/arm/cpu/armv7/nonsec_virt.S
+++ b/arch/arm/cpu/armv7/nonsec_virt.S
@@ -23,6 +23,11 @@ 
  */
 
 #include <config.h>
+#include <linux/linkage.h>
+#include <asm/gic.h>
+#include <asm/armv7.h>
+
+.arch_extension sec
 
 /* the vector table for secure state */
 _monitor_vectors:
@@ -52,3 +57,83 @@  _secure_monitor:
 
 	movs	pc, lr				@ return to non-secure SVC
 
+/*
+ * 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
+ * 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)
+#ifdef CONFIG_ARM_GIC_BASE_ADDRESS
+	ldr	r2, =CONFIG_ARM_GIC_BASE_ADDRESS
+#else
+	mrc	p15, 4, r2, c15, c0, 0		@ read CBAR
+#endif
+	add	r3, r2, #GIC_DIST_OFFSET	@ GIC dist i/f offset
+	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
+
+	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
+
+	movw	r1, #0x3fff
+	movt	r1, #0x0006
+	mcr	p15, 0, r1, 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.
+ */
+#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
+
+	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)
diff --git a/arch/arm/include/asm/armv7.h b/arch/arm/include/asm/armv7.h
index 20caa7c..ab9fa58 100644
--- a/arch/arm/include/asm/armv7.h
+++ b/arch/arm/include/asm/armv7.h
@@ -34,6 +34,19 @@ 
 #define MIDR_CORTEX_A15_R0P0	0x410FC0F0
 #define MIDR_CORTEX_A15_R2P2	0x412FC0F2
 
+/* Cortex-A7 revisions */
+#define MIDR_CORTEX_A7_R0P0	0x410FC070
+
+#define MIDR_PRIMARY_PART_MASK	0xFF0FFFF0
+
+/* ID_PFR1 feature fields */
+#define CPUID_ARM_SEC_SHIFT		4
+#define CPUID_ARM_SEC_MASK		(0xF << CPUID_ARM_SEC_SHIFT)
+#define CPUID_ARM_VIRT_SHIFT		12
+#define CPUID_ARM_VIRT_MASK		(0xF << CPUID_ARM_VIRT_SHIFT)
+#define CPUID_ARM_GENTIMER_SHIFT	16
+#define CPUID_ARM_GENTIMER_MASK		(0xF << CPUID_ARM_GENTIMER_SHIFT)
+
 /* CCSIDR */
 #define CCSIDR_LINE_SIZE_OFFSET		0
 #define CCSIDR_LINE_SIZE_MASK		0x7
@@ -76,6 +89,11 @@  void v7_outer_cache_inval_all(void);
 void v7_outer_cache_flush_range(u32 start, u32 end);
 void v7_outer_cache_inval_range(u32 start, u32 end);
 
+#ifdef CONFIG_ARMV7_NONSEC
+/* defined in assembly file */
+unsigned int _nonsec_init(void);
+#endif /* CONFIG_ARMV7_NONSEC */
+
 #endif /* ! __ASSEMBLY__ */
 
 #endif
diff --git a/arch/arm/include/asm/gic.h b/arch/arm/include/asm/gic.h
new file mode 100644
index 0000000..c2b1e28
--- /dev/null
+++ b/arch/arm/include/asm/gic.h
@@ -0,0 +1,17 @@ 
+#ifndef __GIC_V2_H__
+#define __GIC_V2_H__
+
+/* register offsets for the ARM generic interrupt controller (GIC) */
+
+#define GIC_DIST_OFFSET		0x1000
+#define GICD_CTLR		0x0000
+#define GICD_TYPER		0x0004
+#define GICD_IGROUPRn		0x0080
+#define GICD_SGIR		0x0F00
+
+#define GIC_CPU_OFFSET_A9	0x0100
+#define GIC_CPU_OFFSET_A15	0x2000
+#define GICC_CTLR		0x0000
+#define GICC_PMR		0x0004
+
+#endif
diff --git a/include/configs/vexpress_ca15_tc2.h b/include/configs/vexpress_ca15_tc2.h
index 9e230ad..4f425ac 100644
--- a/include/configs/vexpress_ca15_tc2.h
+++ b/include/configs/vexpress_ca15_tc2.h
@@ -31,6 +31,4 @@ 
 #include "vexpress_common.h"
 #define CONFIG_BOOTP_VCI_STRING     "U-boot.armv7.vexpress_ca15x2_tc2"
 
-#define CONFIG_SYS_CLK_FREQ 24000000
-
 #endif