aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/kernel
diff options
context:
space:
mode:
authorDave Martin <dave.martin@linaro.org>2012-02-09 11:47:17 -0500
committerMarc Zyngier <marc.zyngier@arm.com>2012-09-19 03:32:50 -0400
commit80c59dafb1a9a86fa996e6e34d06b60567c925ca (patch)
tree1f9d36e82f58102722e3c3075fa9f3b73968cf33 /arch/arm/kernel
parentb9a348cb12f3925a27fcf0a38a146b40978588d0 (diff)
ARM: virt: allow the kernel to be entered in HYP mode
This patch does two things: * Ensure that asynchronous aborts are masked at kernel entry. The bootloader should be masking these anyway, but this reduces the damage window just in case it doesn't. * Enter svc mode via exception return to ensure that CPU state is properly serialised. This does not matter when switching from an ordinary privileged mode ("PL1" modes in ARMv7-AR rev C parlance), but it potentially does matter when switching from a another privileged mode such as hyp mode. This should allow the kernel to boot safely either from svc mode or hyp mode, even if no support for use of the ARM Virtualization Extensions is built into the kernel. Signed-off-by: Dave Martin <dave.martin@linaro.org> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Diffstat (limited to 'arch/arm/kernel')
-rw-r--r--arch/arm/kernel/Makefile2
-rw-r--r--arch/arm/kernel/head.S14
-rw-r--r--arch/arm/kernel/hyp-stub.S192
3 files changed, 205 insertions, 3 deletions
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 7ad2d5cf7008..49b61a3f5b29 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -82,4 +82,6 @@ head-y := head$(MMUEXT).o
82obj-$(CONFIG_DEBUG_LL) += debug.o 82obj-$(CONFIG_DEBUG_LL) += debug.o
83obj-$(CONFIG_EARLY_PRINTK) += early_printk.o 83obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
84 84
85obj-$(CONFIG_ARM_VIRT_EXT) += hyp-stub.o
86
85extra-y := $(head-y) vmlinux.lds 87extra-y := $(head-y) vmlinux.lds
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
index 3db960e20cb8..27093e4feef8 100644
--- a/arch/arm/kernel/head.S
+++ b/arch/arm/kernel/head.S
@@ -83,8 +83,12 @@ ENTRY(stext)
83 THUMB( .thumb ) @ switch to Thumb now. 83 THUMB( .thumb ) @ switch to Thumb now.
84 THUMB(1: ) 84 THUMB(1: )
85 85
86 setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode 86#ifdef CONFIG_ARM_VIRT_EXT
87 @ and irqs disabled 87 bl __hyp_stub_install
88#endif
89 @ ensure svc mode and all interrupts masked
90 safe_svcmode_maskall r9
91
88 mrc p15, 0, r9, c0, c0 @ get processor id 92 mrc p15, 0, r9, c0, c0 @ get processor id
89 bl __lookup_processor_type @ r5=procinfo r9=cpuid 93 bl __lookup_processor_type @ r5=procinfo r9=cpuid
90 movs r10, r5 @ invalid processor (r5=0)? 94 movs r10, r5 @ invalid processor (r5=0)?
@@ -326,7 +330,11 @@ ENTRY(secondary_startup)
326 * the processor type - there is no need to check the machine type 330 * the processor type - there is no need to check the machine type
327 * as it has already been validated by the primary processor. 331 * as it has already been validated by the primary processor.
328 */ 332 */
329 setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 333#ifdef CONFIG_ARM_VIRT_EXT
334 bl __hyp_stub_install
335#endif
336 safe_svcmode_maskall r9
337
330 mrc p15, 0, r9, c0, c0 @ get processor id 338 mrc p15, 0, r9, c0, c0 @ get processor id
331 bl __lookup_processor_type 339 bl __lookup_processor_type
332 movs r10, r5 @ invalid processor? 340 movs r10, r5 @ invalid processor?
diff --git a/arch/arm/kernel/hyp-stub.S b/arch/arm/kernel/hyp-stub.S
new file mode 100644
index 000000000000..b03e9244e5ad
--- /dev/null
+++ b/arch/arm/kernel/hyp-stub.S
@@ -0,0 +1,192 @@
1/*
2 * Copyright (c) 2012 Linaro Limited.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19#include <linux/init.h>
20#include <linux/linkage.h>
21#include <asm/assembler.h>
22#include <asm/virt.h>
23
24/*
25 * For the kernel proper, we need to find out the CPU boot mode long after
26 * boot, so we need to store it in a writable variable.
27 *
28 * This is not in .bss, because we set it sufficiently early that the boot-time
29 * zeroing of .bss would clobber it.
30 */
31.data
32ENTRY(__boot_cpu_mode)
33 .long 0
34.text
35
36 /*
37 * Save the primary CPU boot mode. Requires 3 scratch registers.
38 */
39 .macro store_primary_cpu_mode reg1, reg2, reg3
40 mrs \reg1, cpsr
41 and \reg1, \reg1, #MODE_MASK
42 adr \reg2, .L__boot_cpu_mode_offset
43 ldr \reg3, [\reg2]
44 str \reg1, [\reg2, \reg3]
45 .endm
46
47 /*
48 * Compare the current mode with the one saved on the primary CPU.
49 * If they don't match, record that fact. The Z bit indicates
50 * if there's a match or not.
51 * Requires 3 additionnal scratch registers.
52 */
53 .macro compare_cpu_mode_with_primary mode, reg1, reg2, reg3
54 adr \reg2, .L__boot_cpu_mode_offset
55 ldr \reg3, [\reg2]
56 ldr \reg1, [\reg2, \reg3]
57 cmp \mode, \reg1 @ matches primary CPU boot mode?
58 orrne r7, r7, #BOOT_CPU_MODE_MISMATCH
59 strne r7, [r5, r6] @ record what happened and give up
60 .endm
61
62/*
63 * Hypervisor stub installation functions.
64 *
65 * These must be called with the MMU and D-cache off.
66 * They are not ABI compliant and are only intended to be called from the kernel
67 * entry points in head.S.
68 */
69@ Call this from the primary CPU
70ENTRY(__hyp_stub_install)
71 store_primary_cpu_mode r4, r5, r6
72ENDPROC(__hyp_stub_install)
73
74 @ fall through...
75
76@ Secondary CPUs should call here
77ENTRY(__hyp_stub_install_secondary)
78 mrs r4, cpsr
79 and r4, r4, #MODE_MASK
80
81 /*
82 * If the secondary has booted with a different mode, give up
83 * immediately.
84 */
85 compare_cpu_mode_with_primary r4, r5, r6, r7
86 bxne lr
87
88 /*
89 * Once we have given up on one CPU, we do not try to install the
90 * stub hypervisor on the remaining ones: because the saved boot mode
91 * is modified, it can't compare equal to the CPSR mode field any
92 * more.
93 *
94 * Otherwise...
95 */
96
97 cmp r4, #HYP_MODE
98 bxne lr @ give up if the CPU is not in HYP mode
99
100/*
101 * Configure HSCTLR to set correct exception endianness/instruction set
102 * state etc.
103 * Turn off all traps
104 * Eventually, CPU-specific code might be needed -- assume not for now
105 *
106 * This code relies on the "eret" instruction to synchronize the
107 * various coprocessor accesses.
108 */
109 @ Now install the hypervisor stub:
110 adr r7, __hyp_stub_vectors
111 mcr p15, 4, r7, c12, c0, 0 @ set hypervisor vector base (HVBAR)
112
113 @ Disable all traps, so we don't get any nasty surprise
114 mov r7, #0
115 mcr p15, 4, r7, c1, c1, 0 @ HCR
116 mcr p15, 4, r7, c1, c1, 2 @ HCPTR
117 mcr p15, 4, r7, c1, c1, 3 @ HSTR
118
119THUMB( orr r7, #(1 << 30) ) @ HSCTLR.TE
120#ifdef CONFIG_CPU_BIG_ENDIAN
121 orr r7, #(1 << 9) @ HSCTLR.EE
122#endif
123 mcr p15, 4, r7, c1, c0, 0 @ HSCTLR
124
125 mrc p15, 4, r7, c1, c1, 1 @ HDCR
126 and r7, #0x1f @ Preserve HPMN
127 mcr p15, 4, r7, c1, c1, 1 @ HDCR
128
129 bic r7, r4, #MODE_MASK
130 orr r7, r7, #SVC_MODE
131THUMB( orr r7, r7, #PSR_T_BIT )
132 msr spsr_cxsf, r7 @ This is SPSR_hyp.
133
134 __MSR_ELR_HYP(14) @ msr elr_hyp, lr
135 __ERET @ return, switching to SVC mode
136 @ The boot CPU mode is left in r4.
137ENDPROC(__hyp_stub_install_secondary)
138
139__hyp_stub_do_trap:
140 cmp r0, #-1
141 mrceq p15, 4, r0, c12, c0, 0 @ get HVBAR
142 mcrne p15, 4, r0, c12, c0, 0 @ set HVBAR
143 __ERET
144ENDPROC(__hyp_stub_do_trap)
145
146/*
147 * __hyp_set_vectors: Call this after boot to set the initial hypervisor
148 * vectors as part of hypervisor installation. On an SMP system, this should
149 * be called on each CPU.
150 *
151 * r0 must be the physical address of the new vector table (which must lie in
152 * the bottom 4GB of physical address space.
153 *
154 * r0 must be 32-byte aligned.
155 *
156 * Before calling this, you must check that the stub hypervisor is installed
157 * everywhere, by waiting for any secondary CPUs to be brought up and then
158 * checking that BOOT_CPU_MODE_HAVE_HYP(__boot_cpu_mode) is true.
159 *
160 * If not, there is a pre-existing hypervisor, some CPUs failed to boot, or
161 * something else went wrong... in such cases, trying to install a new
162 * hypervisor is unlikely to work as desired.
163 *
164 * When you call into your shiny new hypervisor, sp_hyp will contain junk,
165 * so you will need to set that to something sensible at the new hypervisor's
166 * initialisation entry point.
167 */
168ENTRY(__hyp_get_vectors)
169 mov r0, #-1
170ENDPROC(__hyp_get_vectors)
171 @ fall through
172ENTRY(__hyp_set_vectors)
173 __HVC(0)
174 bx lr
175ENDPROC(__hyp_set_vectors)
176
177.align 2
178.L__boot_cpu_mode_offset:
179 .long __boot_cpu_mode - .
180
181.align 5
182__hyp_stub_vectors:
183__hyp_stub_reset: W(b) .
184__hyp_stub_und: W(b) .
185__hyp_stub_svc: W(b) .
186__hyp_stub_pabort: W(b) .
187__hyp_stub_dabort: W(b) .
188__hyp_stub_trap: W(b) __hyp_stub_do_trap
189__hyp_stub_irq: W(b) .
190__hyp_stub_fiq: W(b) .
191ENDPROC(__hyp_stub_vectors)
192