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