@@ -128,7 +128,7 @@ KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS))
endif
ccflags-y := -fpic -fno-builtin -I$(obj)
-asflags-y := -Wa,-march=all
+asflags-y := -Wa,-march=all -DZIMAGE
# Supply kernel BSS size to the decompressor via a linker symbol.
KBSS_SZ = $(shell $(CROSS_COMPILE)size $(obj)/../../../../vmlinux | \
@@ -137,7 +137,7 @@ start:
#ifdef CONFIG_ARM_VIRT
bl __hyp_stub_install @ get into SVC mode, reversibly
#endif
- safe_svcmode_maskall r4
+ safe_svcmode_maskall r7, r8
mov r7, r1 @ save architecture ID
mov r8, r2 @ save atags pointer
@@ -469,7 +469,7 @@ not_relocated: mov r0, #0
mov r2, r8 @ restore atags pointer
#ifdef CONFIG_ARM_VIRT
- ldr r0, =__boot_cpu_mode
+ bl __get_boot_cpu_mode
and r0, r0, #MODE_MASK
cmp r0, #HYP_MODE @ if not booted in HYP mode...
bne __enter_kernel @ boot kernel directly
@@ -479,7 +479,7 @@ not_relocated: mov r0, #0
add r0, r0, r12
bl __hyp_set_vectors
- hvc #0 @ otherwise bounce via HVC call
+ hvc 0 @ otherwise bounce via HVC call
b . @ should never be reached
@@ -233,10 +233,17 @@
#endif
/*
- * Helper macro to enter SVC mode cleanly and mask interrupts. reg is a
- * scratch register available for the macro to overwrite.
+ * Helper macro to enter SVC mode cleanly and mask interrupts. reg and reg2
+ * are scratch registers for the macro to overwrite.
+ *
+ * This macro is intended for forcing the CPU into SVC mode at boot time.
+ * you cannot return to the original mode.
+ *
+ * The old mode's SPSR is transferred to SPSR_svc, in case it is important
+ * (the zImage loader currently uses this).
*/
-.macro safe_svcmode_maskall reg:req
+.macro safe_svcmode_maskall reg:req, reg2:req
+ mrs \reg2 , spsr
mrs \reg , cpsr
orr \reg , \reg , #PSR_A_BIT | PSR_I_BIT | PSR_F_BIT
bic \reg , \reg , #MODE_MASK
@@ -244,7 +251,7 @@
msr spsr_cxsf, \reg
adr \reg , BSYM(1f)
movs pc, \reg
-1:
+1: msr spsr_cxsf, \reg2
.endm
/*
@@ -45,6 +45,7 @@
extern int __boot_cpu_mode;
void __hyp_set_vectors(unsigned long phys_vector_base);
+unsigned long __get_boot_cpu_mode(void);
#endif /* __ASSEMBLY__ */
@@ -53,6 +54,12 @@ void __hyp_set_vectors(unsigned long phys_vector_base);
* __boot_cpu_mode:
*/
#define BOOT_CPU_MODE_PRIMARY(x) (x & MODE_MASK)
+
+/*
+ * Flag indicating that the kernel was not entered in the same mode on every
+ * CPU. The zImage loader stashes this value in an SPSR, so we need an
+ * architecturally defined flag bit here (the N flag, as it happens)
+ */
#define BOOT_CPU_MODE_MISMATCH (1<<31)
#define BOOT_CPU_MODE_HAVE_HYP(x) \
@@ -95,7 +95,7 @@ ENTRY(stext)
bl __hyp_stub_install
#endif
@ ensure svc mode and all interrupts masked
- safe_svcmode_maskall r4
+ safe_svcmode_maskall r9, r10
mrc p15, 0, r9, c0, c0 @ get processor id
bl __lookup_processor_type @ r5=procinfo r9=cpuid
@@ -353,7 +353,7 @@ ENTRY(secondary_startup)
#ifdef CONFIG_ARM_VIRT
bl __hyp_stub_install
#endif
- safe_svcmode_maskall r4
+ safe_svcmode_maskall r9, r10
mrc p15, 0, r9, c0, c0 @ get processor id
bl __lookup_processor_type
@@ -32,6 +32,48 @@
#include <asm/assembler.h>
#include <asm/virt.h>
+#ifdef ZIMAGE
+
+/*
+ * For the zImage loader, we have no writable storage until the kernel has
+ * been relocated. However, we never change mode or take exceptions, so we
+ * can save the initial mode in SPSR_svc.
+ */
+.macro save_boot_mode Rvalue:req, Rtemp:req, Rtemp2
+ msr SPSR_cxsf, \Rvalue
+.endm
+
+.macro retrieve_boot_mode Rd:req, Rtemp:req
+ mrs \Rd , SPSR
+.endm
+
+#else /* ! ZIMAGE */
+
+/*
+ * For the kernel proper, we need to find out the CPU boot mode long after
+ * boot, so we need to store it in a writable variable.
+ *
+ * This is not in .bss, because we set it sufficiently early that the boot-time
+ * zeroing of .bss would clobber it.
+ */
+.data
+ENTRY(__boot_cpu_mode)
+ .long 0
+.text
+
+.macro save_boot_mode Rvalue:req, Rtemp:req, Rtemp2
+ adr \Rtemp , .L__boot_cpu_mode_offset
+ ldr \Rtemp2 , [ \Rtemp ]
+ str \Rvalue , [ \Rtemp , \Rtemp2 ]
+.endm
+
+.macro retrieve_boot_mode Rd:req, Rtemp:req
+ adr \Rd , .L__boot_cpu_mode_offset
+ ldr \Rtemp , [ \Rd ]
+ ldr \Rd , [ \Rd , \Rtemp ]
+.endm
+#endif /* ! ZIMAGE */
+
/*
* Hypervisor stub installation functions.
*
@@ -41,32 +83,35 @@
*/
@ Call this from the primary CPU
ENTRY(__hyp_stub_install)
- adr r4, 1f
- ldr r5, .L__boot_cpu_mode_offset
mrs r6, cpsr
and r6, r6, #MODE_MASK
- str r6, [r4, r5] @ record the CPU mode we were booted in
+ save_boot_mode r6, r4, r5
ENDPROC(__hyp_stub_install)
@ fall through...
@ Secondary CPUs should call here
ENTRY(__hyp_stub_install_secondary)
- adr r4, 1f
- ldr r5, .L__boot_cpu_mode_offset
mrs r6, cpsr
and r6, r6, #MODE_MASK
- ldr r7, [r4, r5]
+ retrieve_boot_mode r7, r4
cmp r6, r7
- beq 2f @ matches primary CPU boot mode?
+ beq 1f @ matches primary CPU boot mode?
orr r7, r7, #BOOT_CPU_MODE_MISMATCH
- str r7, [r4, r5]
+ save_boot_mode r7, r4, r5
bx lr @ record what happened and give up
- @ otherwise ...
+ /*
+ * Once we have given up on one CPU, we do not try to install the
+ * stub hypervisor on the remaining ones: because the saved boot mode
+ * is modified, it can't compare equal to the CPSR mode field any
+ * more.
+ *
+ * Otherwise...
+ */
-2: cmp r6, #HYP_MODE
+1: cmp r6, #HYP_MODE
bxne lr @ give up if the CPU is not in HYP mode
/*
@@ -85,12 +130,12 @@ ENTRY(__hyp_stub_install_secondary)
adr r7, __hyp_stub_vectors
mcr p15, 4, r7, c12, c0, 0 @ set hypervisor vector base (HVBAR)
- adr r4, 1f
- ldr r5, .L__boot_cpu_mode_offset
-1: str r6, [r4, r5] @ Store the boot mode
bic r7, r6, #MODE_MASK
orr r7, r7, #SVC_MODE
- msr spsr_cxsf, r7
+ msr spsr_cxsf, r7 @ This is SPSR_hyp.
+
+ save_boot_mode r6, r4, r5 @ Store the boot mode
+
msr_elr_hyp 14 @ msr elr_hyp, lr
eret @ return, switching to SVC mode
ENDPROC(__hyp_stub_install_secondary)
@@ -127,9 +172,16 @@ ENTRY(__hyp_set_vectors)
bx lr
ENDPROC(__hyp_set_vectors)
+ENTRY(__get_boot_cpu_mode)
+ retrieve_boot_mode r0, r1
+ bx lr
+ENDPROC(__get_boot_cpu_mode)
+
+#ifndef ZIMAGE
.align 2
.L__boot_cpu_mode_offset:
- .long __boot_cpu_mode - 1b
+ .long __boot_cpu_mode - .
+#endif
.align 5
__hyp_stub_vectors:
@@ -143,7 +195,3 @@ __hyp_stub_irq: W(b) .
__hyp_stub_fiq: W(b) .
ENDPROC(__hyp_stub_vectors)
-.bss
-
-ENTRY(__boot_cpu_mode)
- .long 0
The zImage loader doesn't have any usable RAM until the kernel is relocated. To support this, the hypervisor stub is abstracted to allow the boot CPU mode to be stored into an SPSR when building the zImage loader. When building the kernel proper, we store the value into .data (moved from .bss, because zeroing out of the .bss at boot time will likely clobber it). A helper function __get_boot_cpu_mode() is provided to read back the stored value. This is mostly to help keep the zImage loader code clean, since in the kernel proper, the __boot_cpu_mode variable can straightforwardly be relied upon instead. However, __get_boot_cpu_mode() will still work. The safe_svcmode_maskall macro is modified to transfer the old mode's SPSR to the new mode. For the main kernel entry point this will result in a couple of redundant instructions, since the SPSR is not significant -- however, this allows us to continue to use the same macro in the kernel and in the zImage loader. Signed-off-by: Dave Martin <dave.martin@linaro.org> --- arch/arm/boot/compressed/Makefile | 2 +- arch/arm/boot/compressed/head.S | 6 +- arch/arm/include/asm/assembler.h | 15 +++++-- arch/arm/include/asm/virt.h | 7 +++ arch/arm/kernel/head.S | 4 +- arch/arm/kernel/hyp-stub.S | 86 ++++++++++++++++++++++++++++-------- 6 files changed, 91 insertions(+), 29 deletions(-)