AArch64 EL2 hypervisor for QEMU virt that boots at EL2
1#include "hv/arch.h"
2#include "hv/config.h"
3#include "hv/mmio.h"
4#include "hv/panic.h"
5#include "hv/uart.h"
6#include "hv/vcpu.h"
7
8uint64_t arch_current_el(void)
9{
10 uint64_t v;
11 __asm__ volatile("mrs %0, CurrentEL" : "=r"(v));
12 return (v >> 2u) & 0x3u;
13}
14
15uint64_t arch_mpidr_el1(void)
16{
17 uint64_t v;
18 __asm__ volatile("mrs %0, mpidr_el1" : "=r"(v));
19 return v;
20}
21
22uint64_t arch_cntpct_el0(void)
23{
24 uint64_t v;
25 __asm__ volatile("mrs %0, cntpct_el0" : "=r"(v));
26 return v;
27}
28
29uint64_t arch_cntfrq_el0(void)
30{
31 uint64_t v;
32 __asm__ volatile("mrs %0, cntfrq_el0" : "=r"(v));
33 return v;
34}
35
36uint64_t arch_vectors_base(void)
37{
38 return (uint64_t)(uintptr_t)__vectors_el2;
39}
40
41bool arch_vectors_aligned(void)
42{
43 return hv_is_aligned_u64(arch_vectors_base(), 2048u);
44}
45
46uint64_t arch_irq_save(void)
47{
48 uint64_t flags;
49 __asm__ volatile("mrs %0, daif\nmsr daifset, #0xf\nisb" : "=r"(flags) :: "memory");
50 return flags;
51}
52
53void arch_irq_restore(uint64_t flags)
54{
55 __asm__ volatile("msr daif, %0\nisb" :: "r"(flags) : "memory");
56}
57
58void arch_dump_el2_state(void)
59{
60 uint64_t current_el;
61 uint64_t hcr;
62 uint64_t vtcr;
63 uint64_t vttbr;
64 uint64_t vbar;
65 uint64_t sctlr;
66 uint64_t mair;
67 uint64_t sp;
68
69 __asm__ volatile(
70 "mrs %0, CurrentEL\n"
71 "mrs %1, hcr_el2\n"
72 "mrs %2, vtcr_el2\n"
73 "mrs %3, vttbr_el2\n"
74 "mrs %4, vbar_el2\n"
75 "mrs %5, sctlr_el2\n"
76 "mrs %6, mair_el2\n"
77 "mov %7, sp\n"
78 : "=r"(current_el), "=r"(hcr), "=r"(vtcr), "=r"(vttbr),
79 "=r"(vbar), "=r"(sctlr), "=r"(mair), "=r"(sp));
80
81 uart_puts("cpu current_el=");
82 uart_put_hex64(current_el);
83 uart_puts(" hcr_el2=");
84 uart_put_hex64(hcr);
85 uart_puts(" vtcr_el2=");
86 uart_put_hex64(vtcr);
87 uart_puts(" vttbr_el2=");
88 uart_put_hex64(vttbr);
89 uart_puts("\n");
90 uart_puts("cpu vbar_el2=");
91 uart_put_hex64(vbar);
92 uart_puts(" sctlr_el2=");
93 uart_put_hex64(sctlr);
94 uart_puts(" mair_el2=");
95 uart_put_hex64(mair);
96 uart_puts(" sp=");
97 uart_put_hex64(sp);
98 uart_puts("\n");
99}
100
101void arch_install_vectors(void)
102{
103 if (!arch_vectors_aligned()) {
104 panic("VBAR_EL2 vector table is not 2KB aligned");
105 }
106 __asm__ volatile("msr vbar_el2, %0\nisb" :: "r"(arch_vectors_base()) : "memory");
107}
108
109void arch_tlbi_vmalle1is(void)
110{
111 __asm__ volatile("dsb ish\n tlbi vmalle1is\n dsb ish\n isb" ::: "memory");
112}
113
114void arch_enable_el2_mmu(uint64_t ttbr0, uint64_t tcr, uint64_t mair)
115{
116 uint64_t sctlr;
117
118 __asm__ volatile(
119 "dsb sy\n"
120 "msr ttbr0_el2, %0\n"
121 "msr tcr_el2, %1\n"
122 "msr mair_el2, %2\n"
123 "isb\n"
124 "tlbi alle2\n"
125 "dsb sy\n"
126 "isb\n"
127 :: "r"(ttbr0), "r"(tcr), "r"(mair)
128 : "memory");
129
130 __asm__ volatile("mrs %0, sctlr_el2" : "=r"(sctlr));
131 sctlr |= HV_BIT(0) | HV_BIT(2) | HV_BIT(12);
132 __asm__ volatile(
133 "msr sctlr_el2, %0\n"
134 "isb\n"
135 :: "r"(sctlr)
136 : "memory");
137}
138
139void arch_el2_configure(uint64_t vttbr, uint64_t vtcr)
140{
141 const uint64_t mair = 0x000000000000ff00ull | 0x0000000000000004ull;
142 uint64_t hcr = 0;
143
144 hcr |= HV_BIT(31);
145 hcr |= HV_BIT(0);
146#if !CONFIG_BOOT_LINUX
147 hcr |= HV_BIT(26);
148#endif
149 hcr |= HV_BIT(22);
150 hcr |= HV_BIT(21);
151 hcr |= HV_BIT(19);
152#if !CONFIG_BOOT_LINUX
153 hcr |= HV_BIT(20);
154 hcr |= HV_BIT(18) | HV_BIT(17) | HV_BIT(16) | HV_BIT(15);
155#endif
156#if CONFIG_ENABLE_WFI_TRAPS
157 hcr |= HV_BIT(14) | HV_BIT(13);
158#endif
159
160 __asm__ volatile(
161 "msr mair_el2, %0\n"
162 "msr vtcr_el2, %1\n"
163 "msr vttbr_el2, %2\n"
164 "msr mdcr_el2, %3\n"
165 "msr cptr_el2, %4\n"
166 "msr cnthctl_el2, %5\n"
167 "msr hcr_el2, %6\n"
168 "isb\n"
169 :: "r"(mair), "r"(vtcr), "r"(vttbr),
170 "r"(0ull), "r"(0x33ffull), "r"(3ull), "r"(hcr)
171 : "memory");
172}
173
174void arch_prepare_guest_entry(struct vcpu *vcpu)
175{
176 BUG_ON(vcpu == (struct vcpu *)0);
177 __asm__ volatile(
178 "msr sctlr_el1, %0\n"
179 "msr ttbr0_el1, %1\n"
180 "msr ttbr1_el1, %2\n"
181 "msr tcr_el1, %3\n"
182 "msr mair_el1, %4\n"
183 "msr vbar_el1, %5\n"
184 "msr cntkctl_el1, %6\n"
185 "isb\n"
186 :: "r"(vcpu->sysregs.sctlr_el1), "r"(vcpu->sysregs.ttbr0_el1),
187 "r"(vcpu->sysregs.ttbr1_el1), "r"(vcpu->sysregs.tcr_el1),
188 "r"(vcpu->sysregs.mair_el1), "r"(vcpu->sysregs.vbar_el1),
189 "r"(vcpu->sysregs.cntkctl_el1)
190 : "memory");
191}
192
193void arch_capture_el1_sysregs(struct vcpu *vcpu)
194{
195 BUG_ON(vcpu == (struct vcpu *)0);
196 __asm__ volatile(
197 "mrs %0, vbar_el1\n"
198 "mrs %1, elr_el1\n"
199 "mrs %2, spsr_el1\n"
200 "mrs %3, esr_el1\n"
201 "mrs %4, far_el1\n"
202 : "=r"(vcpu->sysregs.vbar_el1), "=r"(vcpu->sysregs.elr_el1),
203 "=r"(vcpu->sysregs.spsr_el1), "=r"(vcpu->sysregs.esr_el1),
204 "=r"(vcpu->sysregs.far_el1));
205}
206
207void arch_sync_el1_sysregs(const struct vcpu *vcpu)
208{
209 BUG_ON(vcpu == (const struct vcpu *)0);
210 __asm__ volatile(
211 "dsb sy\n"
212 "msr mair_el1, %0\n"
213 "msr tcr_el1, %1\n"
214 "msr ttbr0_el1, %2\n"
215 "msr ttbr1_el1, %3\n"
216 "msr vbar_el1, %4\n"
217 "msr cntkctl_el1, %5\n"
218 "msr cpacr_el1, %6\n"
219 "isb\n"
220 "msr sctlr_el1, %7\n"
221 "isb\n"
222 :: "r"(vcpu->sysregs.mair_el1), "r"(vcpu->sysregs.tcr_el1),
223 "r"(vcpu->sysregs.ttbr0_el1), "r"(vcpu->sysregs.ttbr1_el1),
224 "r"(vcpu->sysregs.vbar_el1), "r"(vcpu->sysregs.cntkctl_el1),
225 "r"(vcpu->sysregs.cpacr_el1), "r"(vcpu->sysregs.sctlr_el1)
226 : "memory");
227}
228
229void arch_advance_guest_pc(struct vcpu *vcpu)
230{
231 BUG_ON(vcpu == (struct vcpu *)0);
232 vcpu->pc += 4u;
233}
234
235void arch_inject_guest_undef(struct vcpu *vcpu)
236{
237 const uint64_t current_el_spx_sync = 0x200u;
238 const uint64_t pstate_el1h_daif = 0x3c5u;
239
240 BUG_ON(vcpu == (struct vcpu *)0);
241 arch_capture_el1_sysregs(vcpu);
242 if (!hv_is_aligned_u64(vcpu->sysregs.vbar_el1, 2048u)) {
243 vcpu->state = VCPU_PAUSED;
244 return;
245 }
246
247 vcpu->sysregs.elr_el1 = vcpu->pc;
248 vcpu->sysregs.spsr_el1 = vcpu->pstate;
249 vcpu->sysregs.esr_el1 = 0u;
250 vcpu->sysregs.far_el1 = 0u;
251 vcpu->pc = vcpu->sysregs.vbar_el1 + current_el_spx_sync;
252 vcpu->pstate = pstate_el1h_daif;
253 vcpu->stats.injected_undefs++;
254
255 __asm__ volatile(
256 "msr elr_el1, %0\n"
257 "msr spsr_el1, %1\n"
258 "msr esr_el1, %2\n"
259 "msr far_el1, %3\n"
260 "isb\n"
261 :: "r"(vcpu->sysregs.elr_el1), "r"(vcpu->sysregs.spsr_el1),
262 "r"(vcpu->sysregs.esr_el1), "r"(vcpu->sysregs.far_el1)
263 : "memory");
264}
265
266void arch_halt(void)
267{
268 for (;;) {
269 __asm__ volatile("wfe");
270 }
271}