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