AArch64 EL2 hypervisor for QEMU virt that boots at EL2
1#include <stdint.h>
2#include <stddef.h>
3
4void guest_main(void);
5uint64_t guest_hvc_call(uint64_t num, uint64_t a1, uint64_t a2);
6
7static size_t guest_strlen(const char *s)
8{
9 size_t n = 0;
10 while (s[n] != '\0') {
11 n++;
12 }
13 return n;
14}
15
16static uint64_t hvc_call(uint64_t num, uint64_t a1, uint64_t a2)
17{
18 return guest_hvc_call(num, a1, a2);
19}
20
21static void console(const char *s)
22{
23 (void)hvc_call(4u, (uint64_t)(uintptr_t)s, guest_strlen(s));
24}
25
26static uint64_t read_midr(void)
27{
28 uint64_t v;
29 __asm__ volatile("mrs %0, midr_el1" : "=r"(v));
30 return v;
31}
32
33static uint64_t read_pfr0(void)
34{
35 uint64_t v;
36 __asm__ volatile("mrs %0, id_aa64pfr0_el1" : "=r"(v));
37 return v;
38}
39
40static uint64_t read_cntv_ctl(void)
41{
42 uint64_t v;
43 __asm__ volatile("mrs %0, cntv_ctl_el0" : "=r"(v));
44 return v;
45}
46
47static void write_sctlr(uint64_t v)
48{
49 __asm__ volatile("msr sctlr_el1, %0" :: "r"(v) : "memory");
50}
51
52static void write_vbar(uint64_t v)
53{
54 __asm__ volatile("msr vbar_el1, %0" :: "r"(v) : "memory");
55}
56
57static void write_ttbr0(uint64_t v)
58{
59 __asm__ volatile("msr ttbr0_el1, %0" :: "r"(v) : "memory");
60}
61
62static void write_tcr(uint64_t v)
63{
64 __asm__ volatile("msr tcr_el1, %0" :: "r"(v) : "memory");
65}
66
67static void write_actlr(uint64_t v)
68{
69 __asm__ volatile("msr actlr_el1, %0" :: "r"(v) : "memory");
70}
71
72static uint64_t attempt_hcr_el2_read(void)
73{
74 uint64_t v;
75 __asm__ volatile("mrs %0, hcr_el2" : "=r"(v));
76 return v;
77}
78
79static uint64_t read_unknown_sysreg(void)
80{
81 uint64_t v;
82 __asm__ volatile("mrs %0, S3_7_C15_C15_7" : "=r"(v));
83 return v;
84}
85
86void guest_main(void)
87{
88 console("diagnostic guest: start");
89 uint64_t midr = read_midr();
90 (void)midr;
91 console("diagnostic guest: read midr_el1");
92
93 uint64_t pfr0 = read_pfr0();
94 (void)pfr0;
95 console("diagnostic guest: read id_aa64pfr0_el1");
96
97 write_sctlr(0x30d00800u);
98 console("diagnostic guest: wrote sctlr_el1 shadow");
99
100 write_vbar(0x40000800u);
101 console("diagnostic guest: wrote vbar_el1 vector base");
102
103 write_ttbr0(0u);
104 console("diagnostic guest: wrote ttbr0_el1 shadow");
105
106 write_tcr(0u);
107 console("diagnostic guest: wrote tcr_el1 shadow");
108
109 uint64_t cntv = read_cntv_ctl();
110 (void)cntv;
111 console("diagnostic guest: read cntv_ctl_el0");
112
113 (void)hvc_call(0u, 0u, 0u);
114 console("diagnostic guest: requested hv id");
115
116 (void)hvc_call(1u, 0u, 0u);
117 console("diagnostic guest: requested log dump");
118
119 console("diagnostic guest: attempting denied hcr_el2 read");
120 (void)attempt_hcr_el2_read();
121 console("diagnostic guest: returned through guest undefined vector");
122
123 __asm__ volatile("brk #0");
124 console("diagnostic guest: returned through guest brk vector");
125
126 console("diagnostic guest: attempting unknown sysreg read");
127 (void)read_unknown_sysreg();
128 console("diagnostic guest: returned from unknown sysreg read");
129
130 console("diagnostic guest: attempting denied actlr_el1 write");
131 write_actlr(1u);
132 console("diagnostic guest: unexpected return from denied actlr_el1");
133 (void)hvc_call(3u, 0u, 0u);
134}