AArch64 EL2 hypervisor for QEMU virt that boots at EL2
1#include "hv/allocator.h"
2#include "hv/gicv3.h"
3#include "hv/el2_mmu.h"
4#include "hv/log.h"
5#include "hv/monitor.h"
6#include "hv/mmio_policy.h"
7#include "hv/platform.h"
8#include "hv/policy.h"
9#include "hv/psci.h"
10#include "hv/stage2.h"
11#include "hv/string.h"
12#include "hv/timer.h"
13#include "hv/uart.h"
14
15static char g_line[96];
16static uint32_t g_pos;
17static bool g_paused;
18static struct vcpu g_snapshot;
19static bool g_snapshot_valid;
20
21static void help(void)
22{
23#if CONFIG_MONITOR_MUTATION
24 uart_puts("commands: help status vcpu regs sysregs snapshot restore log log clear policy profile psci mappings mmio el2mmu fdt translate read64 write64 dump unmap irq irq clear resume pause reboot shutdown\n");
25#else
26 uart_puts("commands: help status vcpu regs sysregs snapshot snapshot show log policy psci mappings mmio el2mmu fdt translate read64 dump resume pause\n");
27#endif
28}
29
30static bool is_space(char c)
31{
32 return c == ' ' || c == '\t';
33}
34
35static const char *skip_spaces(const char *s)
36{
37 while (is_space(*s)) {
38 s++;
39 }
40 return s;
41}
42
43static bool starts_with_command(const char *line, const char *cmd)
44{
45 size_t n = hv_strlen(cmd);
46 return hv_strncmp(line, cmd, n) == 0 && (line[n] == '\0' || is_space(line[n]));
47}
48
49static bool digit_value(char c, uint64_t *out)
50{
51 if (c >= '0' && c <= '9') {
52 *out = (uint64_t)(c - '0');
53 return true;
54 }
55 if (c >= 'a' && c <= 'f') {
56 *out = 10u + (uint64_t)(c - 'a');
57 return true;
58 }
59 if (c >= 'A' && c <= 'F') {
60 *out = 10u + (uint64_t)(c - 'A');
61 return true;
62 }
63 return false;
64}
65
66static bool parse_u64(const char **cursor, uint64_t *out)
67{
68 const char *s = skip_spaces(*cursor);
69 uint64_t base = 10u;
70 uint64_t value = 0u;
71 uint64_t digit;
72 bool any = false;
73
74 if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
75 base = 16u;
76 s += 2;
77 }
78 while (digit_value(*s, &digit)) {
79 if (digit >= base) {
80 return false;
81 }
82 if (value > (UINT64_MAX - digit) / base) {
83 return false;
84 }
85 value = (value * base) + digit;
86 any = true;
87 s++;
88 }
89 if (!any) {
90 return false;
91 }
92 *cursor = s;
93 *out = value;
94 return true;
95}
96
97static bool ipa_to_host(uint64_t ipa, uint64_t len, uintptr_t *host)
98{
99 struct stage2_translation tr;
100 uint64_t end;
101 uint64_t tr_end;
102 uint64_t off;
103
104 if (host == (uintptr_t *)0 || len == 0u || hv_add_overflow_u64(ipa, len, &end)) {
105 return false;
106 }
107 if (!stage2_translate(ipa, &tr) ||
108 hv_add_overflow_u64(tr.ipa_base, tr.size, &tr_end) ||
109 !hv_range_contains(tr.ipa_base, tr.size, ipa, len)) {
110 return false;
111 }
112 off = ipa - tr.ipa_base;
113 *host = (uintptr_t)(tr.pa_base + off);
114 return true;
115}
116
117static void monitor_translate(const char *args)
118{
119 uint64_t ipa;
120 struct stage2_translation tr;
121 if (!parse_u64(&args, &ipa)) {
122 uart_puts("usage translate <ipa>\n");
123 return;
124 }
125 if (!stage2_translate(ipa, &tr)) {
126 uart_puts("translate unmapped ipa=");
127 uart_put_hex64(ipa);
128 uart_puts("\n");
129 return;
130 }
131 uart_puts("translate ipa=");
132 uart_put_hex64(ipa);
133 uart_puts(" block=");
134 uart_put_hex64(tr.ipa_base);
135 uart_puts(" pa=");
136 uart_put_hex64(tr.pa_base + (ipa - tr.ipa_base));
137 uart_puts(" perms=");
138 uart_put_hex64(tr.perms);
139 uart_puts(" desc=");
140 uart_put_hex64(tr.desc);
141 uart_puts("\n");
142}
143
144static void monitor_read64(const char *args)
145{
146 uint64_t ipa;
147 uintptr_t host;
148 if (!parse_u64(&args, &ipa) || !hv_is_aligned_u64(ipa, 8u)) {
149 uart_puts("usage read64 <aligned-ipa>\n");
150 return;
151 }
152 if (!ipa_to_host(ipa, 8u, &host)) {
153 uart_puts("read64 unmapped\n");
154 return;
155 }
156 uart_puts("read64 ");
157 uart_put_hex64(ipa);
158 uart_puts("=");
159 uart_put_hex64(*(volatile uint64_t *)host);
160 uart_puts("\n");
161}
162
163static void monitor_write64(const char *args)
164{
165#if CONFIG_MONITOR_MUTATION
166 uint64_t ipa;
167 uint64_t value;
168 uintptr_t host;
169 if (!parse_u64(&args, &ipa) || !parse_u64(&args, &value) || !hv_is_aligned_u64(ipa, 8u)) {
170 uart_puts("usage write64 <aligned-ipa> <value>\n");
171 return;
172 }
173 if (!ipa_to_host(ipa, 8u, &host)) {
174 uart_puts("write64 unmapped\n");
175 return;
176 }
177 *(volatile uint64_t *)host = value;
178 uart_puts("write64 ok\n");
179#else
180 (void)args;
181 uart_puts("write64 disabled\n");
182#endif
183}
184
185static void monitor_dump(const char *args)
186{
187 uint64_t ipa;
188 uint64_t len;
189 uintptr_t host;
190 if (!parse_u64(&args, &ipa) || !parse_u64(&args, &len) || len > 256u) {
191 uart_puts("usage dump <ipa> <len<=256>\n");
192 return;
193 }
194 if (!ipa_to_host(ipa, len, &host)) {
195 uart_puts("dump unmapped\n");
196 return;
197 }
198 for (uint64_t i = 0; i < len; i++) {
199 if ((i & 0x0full) == 0u) {
200 uart_puts("\n");
201 uart_put_hex64(ipa + i);
202 uart_puts(":");
203 }
204 uart_puts(" ");
205 uint8_t b = *(volatile uint8_t *)(host + i);
206 static const char hex[] = "0123456789abcdef";
207 uart_putc(hex[b >> 4u]);
208 uart_putc(hex[b & 0x0fu]);
209 }
210 uart_puts("\n");
211}
212
213static void monitor_unmap(const char *args)
214{
215#if CONFIG_MONITOR_MUTATION
216 uint64_t ipa;
217 if (!parse_u64(&args, &ipa)) {
218 uart_puts("usage unmap <2MiB-aligned-ipa>\n");
219 return;
220 }
221 if (!stage2_unmap_range(HV_ALIGN_DOWN(ipa, 0x200000ull), 0x200000ull)) {
222 uart_puts("unmap failed\n");
223 return;
224 }
225 uart_puts("unmap ok\n");
226#else
227 (void)args;
228 uart_puts("unmap disabled\n");
229#endif
230}
231
232static void monitor_irq(const char *args, struct vcpu *vcpu)
233{
234#if CONFIG_MONITOR_MUTATION
235 uint64_t intid;
236 args = skip_spaces(args);
237 if (hv_strncmp(args, "clear", 5u) == 0 && is_space(args[5])) {
238 args = skip_spaces(args + 5u);
239 if (vcpu == (struct vcpu *)0 || !parse_u64(&args, &intid) || intid >= 64u) {
240 uart_puts("usage irq clear <intid<64>\n");
241 return;
242 }
243 vcpu_clear_pending_irq(vcpu, (uint32_t)intid);
244 gicv3_flush_vcpu_state(vcpu);
245 uart_puts("irq cleared intid=");
246 uart_put_dec64(intid);
247 uart_puts("\n");
248 return;
249 }
250 if (vcpu == (struct vcpu *)0 || !parse_u64(&args, &intid) || intid >= 64u) {
251 uart_puts("usage irq <intid<64> | irq clear <intid<64>\n");
252 return;
253 }
254 vcpu_set_pending_irq(vcpu, (uint32_t)intid);
255 gicv3_flush_vcpu_state(vcpu);
256 uart_puts("irq pending intid=");
257 uart_put_dec64(intid);
258 uart_puts("\n");
259#else
260 (void)args;
261 (void)vcpu;
262 uart_puts("irq disabled\n");
263#endif
264}
265
266static void monitor_profile(const char *args)
267{
268#if CONFIG_MONITOR_MUTATION
269 args = skip_spaces(args);
270 if (*args == '\0') {
271 uart_puts("policy profile=");
272 uart_puts(policy_profile_name(policy_get_profile()));
273 uart_puts("\n");
274 return;
275 }
276 if (!policy_set_profile_name(args)) {
277 uart_puts("usage profile strict|diagnostic|linux-boot|debug\n");
278 return;
279 }
280 uart_puts("policy profile=");
281 uart_puts(policy_profile_name(policy_get_profile()));
282 uart_puts("\n");
283#else
284 (void)args;
285 uart_puts("profile disabled\n");
286#endif
287}
288
289static void monitor_snapshot(struct vcpu *vcpu)
290{
291 if (vcpu == (struct vcpu *)0) {
292 uart_puts("snapshot no-vcpu\n");
293 return;
294 }
295 if (vcpu->state != VCPU_PAUSED) {
296 uart_puts("snapshot requires-paused-vcpu\n");
297 return;
298 }
299 g_snapshot = *vcpu;
300 g_snapshot_valid = true;
301 log_simple(LOG_MONITOR, 610u, vcpu->pc, vcpu->pstate);
302 uart_puts("snapshot saved pc=");
303 uart_put_hex64(g_snapshot.pc);
304 uart_puts(" pstate=");
305 uart_put_hex64(g_snapshot.pstate);
306 uart_puts(" id=");
307 uart_put_dec64(g_snapshot.id);
308 uart_puts("\n");
309}
310
311static void monitor_snapshot_show(void)
312{
313 if (!g_snapshot_valid) {
314 uart_puts("snapshot empty\n");
315 return;
316 }
317 uart_puts("snapshot pc=");
318 uart_put_hex64(g_snapshot.pc);
319 uart_puts(" pstate=");
320 uart_put_hex64(g_snapshot.pstate);
321 uart_puts(" sp_el1=");
322 uart_put_hex64(g_snapshot.sp_el1);
323 uart_puts(" traps=");
324 uart_put_dec64(g_snapshot.stats.traps);
325 uart_puts(" state=");
326 uart_put_dec64((uint64_t)g_snapshot.state);
327 uart_puts("\n");
328}
329
330static void monitor_restore(struct vcpu *vcpu)
331{
332#if CONFIG_MONITOR_MUTATION
333 if (vcpu == (struct vcpu *)0 || !g_snapshot_valid || vcpu->id != g_snapshot.id) {
334 uart_puts("restore no-snapshot\n");
335 return;
336 }
337 *vcpu = g_snapshot;
338 vcpu_set_current(vcpu);
339 log_simple(LOG_MONITOR, 611u, vcpu->pc, vcpu->pstate);
340 uart_puts("restore ok pc=");
341 uart_put_hex64(vcpu->pc);
342 uart_puts("\n");
343#else
344 (void)vcpu;
345 uart_puts("restore disabled\n");
346#endif
347}
348
349static void show_sysregs(const struct vcpu *vcpu)
350{
351 if (vcpu == (const struct vcpu *)0) {
352 return;
353 }
354 uart_puts("sysregs sctlr=");
355 uart_put_hex64(vcpu->sysregs.sctlr_el1);
356 uart_puts(" ttbr0=");
357 uart_put_hex64(vcpu->sysregs.ttbr0_el1);
358 uart_puts(" ttbr1=");
359 uart_put_hex64(vcpu->sysregs.ttbr1_el1);
360 uart_puts(" tcr=");
361 uart_put_hex64(vcpu->sysregs.tcr_el1);
362 uart_puts(" mair=");
363 uart_put_hex64(vcpu->sysregs.mair_el1);
364 uart_puts(" vbar=");
365 uart_put_hex64(vcpu->sysregs.vbar_el1);
366 uart_puts("\n");
367}
368
369static bool command(const char *line, struct vcpu *vcpu)
370{
371 if (hv_strcmp(line, "help") == 0) {
372 help();
373 } else if (hv_strcmp(line, "status") == 0) {
374 vcpu_dump(vcpu);
375 timer_dump();
376 gicv3_dump();
377 } else if (hv_strcmp(line, "vcpu") == 0 || hv_strcmp(line, "regs") == 0) {
378 vcpu_dump(vcpu);
379 } else if (hv_strcmp(line, "snapshot") == 0) {
380 monitor_snapshot(vcpu);
381 } else if (hv_strcmp(line, "snapshot show") == 0) {
382 monitor_snapshot_show();
383 } else if (hv_strcmp(line, "restore") == 0) {
384 monitor_restore(vcpu);
385 } else if (hv_strcmp(line, "sysregs") == 0) {
386 show_sysregs(vcpu);
387 } else if (hv_strcmp(line, "log") == 0) {
388 log_dump(128u);
389 } else if (hv_strcmp(line, "log clear") == 0) {
390#if CONFIG_MONITOR_MUTATION
391 log_clear();
392#else
393 uart_puts("log clear disabled\n");
394#endif
395 } else if (hv_strcmp(line, "policy") == 0) {
396 policy_dump();
397 } else if (starts_with_command(line, "profile")) {
398 monitor_profile(line + hv_strlen("profile"));
399 } else if (hv_strcmp(line, "psci") == 0) {
400 psci_dump();
401 } else if (hv_strcmp(line, "mappings") == 0) {
402 stage2_dump();
403 allocator_dump();
404 } else if (hv_strcmp(line, "mmio") == 0) {
405 mmio_policy_dump();
406 } else if (hv_strcmp(line, "el2mmu") == 0) {
407 el2_mmu_dump();
408 } else if (hv_strcmp(line, "fdt") == 0) {
409 fdt_dump(platform_info());
410 } else if (starts_with_command(line, "translate")) {
411 monitor_translate(line + hv_strlen("translate"));
412 } else if (starts_with_command(line, "read64")) {
413 monitor_read64(line + hv_strlen("read64"));
414 } else if (starts_with_command(line, "write64")) {
415 monitor_write64(line + hv_strlen("write64"));
416 } else if (starts_with_command(line, "dump")) {
417 monitor_dump(line + hv_strlen("dump"));
418 } else if (starts_with_command(line, "unmap")) {
419 monitor_unmap(line + hv_strlen("unmap"));
420 } else if (starts_with_command(line, "irq")) {
421 monitor_irq(line + hv_strlen("irq"), vcpu);
422 } else if (hv_strcmp(line, "resume") == 0) {
423 if (vcpu != (struct vcpu *)0) {
424 vcpu->state = VCPU_RUNNING;
425 }
426 g_paused = false;
427 return true;
428 } else if (hv_strcmp(line, "pause") == 0) {
429 g_paused = true;
430 } else if (hv_strcmp(line, "reboot") == 0 || hv_strcmp(line, "shutdown") == 0) {
431#if CONFIG_MONITOR_MUTATION
432 uart_puts("platform power control is not implemented; halting\n");
433 for (;;) {
434 __asm__ volatile("wfe");
435 }
436#else
437 uart_puts("power control disabled\n");
438#endif
439 } else if (line[0] != '\0') {
440 uart_puts("error unknown-command\n");
441 }
442 return false;
443}
444
445void monitor_init(void)
446{
447 g_pos = 0;
448 g_paused = false;
449 g_snapshot_valid = false;
450}
451
452void monitor_poll(void)
453{
454 char c;
455 while (uart_getc_nonblocking(&c)) {
456 if (c == '\r' || c == '\n') {
457 g_line[g_pos] = '\0';
458 (void)command(g_line, vcpu_current());
459 g_pos = 0;
460 uart_puts("hv> ");
461 } else if ((c == '\b' || c == 0x7f) && g_pos != 0u) {
462 g_pos--;
463 } else if (g_pos + 1u < sizeof(g_line) && c >= ' ' && c <= '~') {
464 g_line[g_pos++] = c;
465 } else {
466 uart_puts("error input-too-long\n");
467 g_pos = 0;
468 }
469 }
470}
471
472void monitor_loop(struct vcpu *vcpu, const char *reason)
473{
474 g_paused = true;
475 if (vcpu != (struct vcpu *)0) {
476 vcpu->state = VCPU_PAUSED;
477 }
478 uart_puts("monitor entered reason=");
479 uart_puts(reason);
480 uart_puts("\nhv> ");
481 while (g_paused) {
482 monitor_poll();
483 __asm__ volatile("wfe");
484 }
485}