2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (C) 2017 Alexandru Elisei <alexandru.elisei@gmail.com>
5 * Copyright (c) 2021 Andrew Turner
7 * This software was developed by Alexandru Elisei under sponsorship
8 * from the FreeBSD Foundation.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 #include <machine/asm.h>
34 #include <machine/hypervisor.h>
39 .macro save_host_registers
40 /* TODO: Only store callee saved registers */
42 str x30, [sp, #(30 * 8)]
43 stp x28, x29, [sp, #(28 * 8)]
44 stp x26, x27, [sp, #(26 * 8)]
45 stp x24, x25, [sp, #(24 * 8)]
46 stp x22, x23, [sp, #(22 * 8)]
47 stp x20, x21, [sp, #(20 * 8)]
48 stp x18, x19, [sp, #(18 * 8)]
49 stp x16, x17, [sp, #(16 * 8)]
50 stp x14, x15, [sp, #(14 * 8)]
51 stp x12, x13, [sp, #(12 * 8)]
52 stp x10, x11, [sp, #(10 * 8)]
53 stp x8, x9, [sp, #(8 * 8)]
54 stp x6, x7, [sp, #(6 * 8)]
55 stp x4, x5, [sp, #(4 * 8)]
56 stp x2, x3, [sp, #(2 * 8)]
57 stp x0, x1, [sp, #(0 * 8)]
60 .macro restore_host_registers
61 /* TODO: Only restore callee saved registers */
62 ldp x0, x1, [sp, #(0 * 8)]
63 ldp x2, x3, [sp, #(2 * 8)]
64 ldp x4, x5, [sp, #(4 * 8)]
65 ldp x6, x7, [sp, #(6 * 8)]
66 ldp x8, x9, [sp, #(8 * 8)]
67 ldp x10, x11, [sp, #(10 * 8)]
68 ldp x12, x13, [sp, #(12 * 8)]
69 ldp x14, x15, [sp, #(14 * 8)]
70 ldp x16, x17, [sp, #(16 * 8)]
71 ldp x18, x19, [sp, #(18 * 8)]
72 ldp x20, x21, [sp, #(20 * 8)]
73 ldp x22, x23, [sp, #(22 * 8)]
74 ldp x24, x25, [sp, #(24 * 8)]
75 ldp x26, x27, [sp, #(26 * 8)]
76 ldp x28, x29, [sp, #(28 * 8)]
77 ldr x30, [sp, #(30 * 8)]
81 .macro save_guest_registers
82 /* Back up x0 so we can use it as a temporary register */
83 stp x0, x1, [sp, #-(2 * 8)]!
85 /* Restore the hypctx pointer */
88 stp x2, x3, [x0, #(TF_X + 2 * 8)]
89 stp x4, x5, [x0, #(TF_X + 4 * 8)]
90 stp x6, x7, [x0, #(TF_X + 6 * 8)]
91 stp x8, x9, [x0, #(TF_X + 8 * 8)]
92 stp x10, x11, [x0, #(TF_X + 10 * 8)]
93 stp x12, x13, [x0, #(TF_X + 12 * 8)]
94 stp x14, x15, [x0, #(TF_X + 14 * 8)]
95 stp x16, x17, [x0, #(TF_X + 16 * 8)]
96 stp x18, x19, [x0, #(TF_X + 18 * 8)]
97 stp x20, x21, [x0, #(TF_X + 20 * 8)]
98 stp x22, x23, [x0, #(TF_X + 22 * 8)]
99 stp x24, x25, [x0, #(TF_X + 24 * 8)]
100 stp x26, x27, [x0, #(TF_X + 26 * 8)]
101 stp x28, x29, [x0, #(TF_X + 28 * 8)]
103 str lr, [x0, #(TF_LR)]
105 /* Restore the saved x0 & x1 and save them */
106 ldp x2, x3, [sp], #(2 * 8)
107 stp x2, x3, [x0, #(TF_X + 0 * 8)]
110 .macro restore_guest_registers
112 * Copy the guest x0 and x1 to the stack so we can restore them
113 * after loading the other registers.
115 ldp x2, x3, [x0, #(TF_X + 0 * 8)]
116 stp x2, x3, [sp, #-(2 * 8)]!
118 ldr lr, [x0, #(TF_LR)]
120 ldp x28, x29, [x0, #(TF_X + 28 * 8)]
121 ldp x26, x27, [x0, #(TF_X + 26 * 8)]
122 ldp x24, x25, [x0, #(TF_X + 24 * 8)]
123 ldp x22, x23, [x0, #(TF_X + 22 * 8)]
124 ldp x20, x21, [x0, #(TF_X + 20 * 8)]
125 ldp x18, x19, [x0, #(TF_X + 18 * 8)]
126 ldp x16, x17, [x0, #(TF_X + 16 * 8)]
127 ldp x14, x15, [x0, #(TF_X + 14 * 8)]
128 ldp x12, x13, [x0, #(TF_X + 12 * 8)]
129 ldp x10, x11, [x0, #(TF_X + 10 * 8)]
130 ldp x8, x9, [x0, #(TF_X + 8 * 8)]
131 ldp x6, x7, [x0, #(TF_X + 6 * 8)]
132 ldp x4, x5, [x0, #(TF_X + 4 * 8)]
133 ldp x2, x3, [x0, #(TF_X + 2 * 8)]
135 ldp x0, x1, [sp], #(2 * 8)
148 .section ".vmm_vectors","ax"
151 vempty /* Synchronous EL2t */
152 vempty /* IRQ EL2t */
153 vempty /* FIQ EL2t */
154 vempty /* Error EL2t */
156 vempty /* Synchronous EL2h */
157 vempty /* IRQ EL2h */
158 vempty /* FIQ EL2h */
159 vempty /* Error EL2h */
161 vector hyp_init /* Synchronous 64-bit EL1 */
162 vempty /* IRQ 64-bit EL1 */
163 vempty /* FIQ 64-bit EL1 */
164 vempty /* Error 64-bit EL1 */
166 vempty /* Synchronous 32-bit EL1 */
167 vempty /* IRQ 32-bit EL1 */
168 vempty /* FIQ 32-bit EL1 */
169 vempty /* Error 32-bit EL1 */
174 vempty /* Synchronous EL2t */
175 vempty /* IRQ EL2t */
176 vempty /* FIQ EL2t */
177 vempty /* Error EL2t */
179 vector el2_el2h_sync /* Synchronous EL2h */
180 vector el2_el2h_irq /* IRQ EL2h */
181 vector el2_el2h_fiq /* FIQ EL2h */
182 vector el2_el2h_error /* Error EL2h */
184 vector el2_el1_sync64 /* Synchronous 64-bit EL1 */
185 vector el2_el1_irq64 /* IRQ 64-bit EL1 */
186 vector el2_el1_fiq64 /* FIQ 64-bit EL1 */
187 vector el2_el1_error64 /* Error 64-bit EL1 */
189 vempty /* Synchronous 32-bit EL1 */
190 vempty /* IRQ 32-bit EL1 */
191 vempty /* FIQ 32-bit EL1 */
192 vempty /* Error 32-bit EL1 */
195 * Initialize the hypervisor mode with a new exception vector table, translation
199 * x0 - translation tables physical address
200 * x1 - stack top virtual address
202 * x3 - SCTLR_EL2 value
203 * x4 - VTCR_EL2 value
205 LENTRY(handle_hyp_init)
206 /* Install the new exception vectors */
208 add x6, x6, :lo12:hyp_vectors
210 /* Set the stack top address */
212 /* Use the host VTTBR_EL2 to tell the host and the guests apart */
215 /* Load the base address for the translation tables */
217 /* Invalidate the TLB */
222 /* Use the same memory attributes as EL1 */
225 /* Configure address translation */
228 /* Set the system control register for EL2 */
230 /* Set the Stage 2 translation control register */
234 /* MMU is up and running */
236 LEND(handle_hyp_init)
238 .macro do_world_switch_to_host
240 restore_host_registers
242 /* Restore host VTTBR */
248 .macro handle_el2_excp type
249 /* Save registers before modifying so we can restore them */
252 /* Test if the exception happened when the host was running */
257 /* We got the exception while the guest was running */
259 do_world_switch_to_host
264 /* We got the exception while the host was running */
271 LENTRY(handle_el2_el2h_sync)
272 handle_el2_excp #EXCP_TYPE_EL2_SYNC
273 LEND(handle_el2_el2h_sync)
275 LENTRY(handle_el2_el2h_irq)
276 handle_el2_excp #EXCP_TYPE_EL2_IRQ
277 LEND(handle_el2_el2h_irq)
279 LENTRY(handle_el2_el2h_fiq)
280 handle_el2_excp #EXCP_TYPE_EL2_FIQ
281 LEND(handle_el2_el2h_fiq)
283 LENTRY(handle_el2_el2h_error)
284 handle_el2_excp #EXCP_TYPE_EL2_ERROR
285 LEND(handle_el2_el2h_error)
288 LENTRY(handle_el2_el1_sync64)
289 /* Save registers before modifying so we can restore them */
292 /* Check for host hypervisor call */
295 ldr x9, [sp], #16 /* Restore the temp register */
299 * Called from the host
302 /* Check if this is a cleanup call and handle in a controlled state */
303 cmp x0, #(HYP_CLEANUP)
311 1: /* Guest exception taken to EL2 */
312 do_world_switch_to_host
313 mov x0, #EXCP_TYPE_EL1_SYNC
315 LEND(handle_el2_el1_sync64)
318 * We only trap IRQ, FIQ and SError exceptions when a guest is running. Do a
319 * world switch to host to handle these exceptions.
322 LENTRY(handle_el2_el1_irq64)
323 do_world_switch_to_host
328 mov x0, #EXCP_TYPE_MAINT_IRQ
331 mov x0, #EXCP_TYPE_EL1_IRQ
335 LEND(handle_el2_el1_irq)
337 LENTRY(handle_el2_el1_fiq64)
338 do_world_switch_to_host
339 mov x0, #EXCP_TYPE_EL1_FIQ
341 LEND(handle_el2_el1_fiq64)
343 LENTRY(handle_el2_el1_error64)
344 do_world_switch_to_host
345 mov x0, #EXCP_TYPE_EL1_ERROR
347 LEND(handle_el2_el1_error64)
352 * uint64_t vmm_enter_guest(struct hypctx *hypctx)
355 * x0 - hypctx address
357 ENTRY(vmm_enter_guest)
358 /* Save hypctx address */
362 restore_guest_registers
370 * void vmm_cleanup(uint64_t handle, void *hyp_stub_vectors)
373 * x1 - physical address of hyp_stub_vectors
376 /* Restore the stub vectors */
379 /* Disable the MMU */
382 bic x2, x2, #SCTLR_EL2_M