AArch64 EL2 hypervisor for QEMU virt that boots at EL2
1#include "hv/arch.h"
2#include "hv/config.h"
3#include "hv/gicv3.h"
4#include "hv/log.h"
5#include "hv/monitor.h"
6#include "hv/panic.h"
7#include "hv/policy.h"
8#include "hv/psci.h"
9#include "hv/stage2.h"
10#include "hv/string.h"
11#include "hv/sysreg.h"
12#include "hv/timer.h"
13#include "hv/trap.h"
14#include "hv/uart.h"
15
16#define HVC_GET_HV_ID 0u
17#define HVC_DUMP_LOG 1u
18#define HVC_GET_STATUS 2u
19#define HVC_PAUSE 3u
20#define HVC_GUEST_CONSOLE_WRITE 4u
21#define HVC_REPORT_EXCEPTION 5u
22#define HVC_ERR_UNKNOWN UINT64_MAX
23
24static uint32_t action_to_log(enum policy_action action)
25{
26 switch (action) {
27 case POLICY_EMULATE_READ: return LOG_ACT_EMULATE_READ;
28 case POLICY_EMULATE_WRITE: return LOG_ACT_EMULATE_WRITE;
29 case POLICY_WRITE_MASK: return LOG_ACT_WRITE_MASK;
30 case POLICY_DENY_SKIP: return LOG_ACT_DENY_SKIP;
31 case POLICY_DENY_UNDEF: return LOG_ACT_DENY_UNDEF;
32 case POLICY_PAUSE: return LOG_ACT_PAUSE;
33 case POLICY_PANIC: return LOG_ACT_PANIC;
34 case POLICY_ALLOW_NATIVE:
35 default: return LOG_ACT_NONE;
36 }
37}
38
39static void log_trap_event(const struct vcpu *vcpu, const struct trap_frame *frame,
40 const struct sysreg_access *access,
41 const struct policy_decision *decision,
42 uint64_t operand)
43{
44 struct log_event ev;
45 hv_memset(&ev, 0, sizeof(ev));
46 ev.kind = LOG_TRAP;
47 ev.vcpu = vcpu->id;
48 ev.ec = esr_ec(frame->esr_el2);
49 ev.iss = frame->esr_el2 & 0x01ffffffu;
50 ev.elr = frame->elr_el2;
51 ev.far = frame->far_el2;
52 ev.hpfar = frame->hpfar_el2;
53 ev.pstate = frame->spsr_el2;
54 if (access != (const struct sysreg_access *)0) {
55 ev.sysreg_key = access->key;
56 ev.is_read = access->is_read ? 1u : 0u;
57 ev.rt = access->rt;
58 ev.name = decision->entry != (const struct policy_entry *)0 ?
59 decision->entry->name : sysreg_key_name(access->key);
60 }
61 if (decision != (const struct policy_decision *)0) {
62 ev.action = action_to_log(decision->action);
63 ev.result = decision->value;
64 ev.reason = decision->reason;
65 if (decision->entry != (const struct policy_entry *)0) {
66 ev.policy_class = (uint32_t)decision->entry->access_class;
67 }
68 }
69 ev.operand = operand;
70 log_event_commit(&ev);
71}
72
73static bool guest_ptr_to_host(uint64_t ipa, uint64_t len, const char **out)
74{
75 uint64_t end;
76 if (out == (const char **)0 || hv_add_overflow_u64(ipa, len, &end)) {
77 return false;
78 }
79 if (ipa < CONFIG_GUEST_IPA_BASE ||
80 end > (CONFIG_GUEST_IPA_BASE + CONFIG_GUEST_RAM_SIZE)) {
81 return false;
82 }
83 *out = (const char *)(uintptr_t)(CONFIG_GUEST_RAM_BASE + (ipa - CONFIG_GUEST_IPA_BASE));
84 return true;
85}
86
87static void handle_hvc(struct vcpu *vcpu)
88{
89 uint64_t num = vcpu_read_gpr(vcpu, 0u);
90 vcpu->stats.hvc_calls++;
91
92 struct log_event ev;
93 hv_memset(&ev, 0, sizeof(ev));
94 ev.kind = LOG_HVC;
95 ev.vcpu = vcpu->id;
96 ev.ec = ESR_EC_HVC64;
97 ev.iss = vcpu->last_esr & 0x01ffffffu;
98 ev.elr = vcpu->pc;
99 ev.far = vcpu->last_far;
100 ev.hpfar = vcpu->last_hpfar;
101 ev.pstate = vcpu->pstate;
102 ev.operand = num;
103 ev.action = LOG_ACT_NONE;
104 ev.reason = 300u;
105
106 switch (num) {
107 case HVC_GET_HV_ID:
108 vcpu_write_gpr(vcpu, 0u, 0x415249454cull);
109 ev.result = 0x415249454cull;
110 break;
111 case HVC_DUMP_LOG:
112 log_dump(64u);
113 vcpu_write_gpr(vcpu, 0u, 0u);
114 break;
115 case HVC_GET_STATUS:
116 vcpu_write_gpr(vcpu, 0u, (uint64_t)vcpu->state);
117 ev.result = (uint64_t)vcpu->state;
118 break;
119 case HVC_PAUSE:
120 vcpu->state = VCPU_PAUSED;
121 vcpu_write_gpr(vcpu, 0u, 0u);
122 log_event_commit(&ev);
123 monitor_loop(vcpu, "guest requested pause");
124 return;
125 case HVC_GUEST_CONSOLE_WRITE: {
126 const char *s = (const char *)0;
127 uint64_t ptr = vcpu_read_gpr(vcpu, 1u);
128 uint64_t len = vcpu_read_gpr(vcpu, 2u);
129 if (len > 256u || !guest_ptr_to_host(ptr, len, &s)) {
130 vcpu_write_gpr(vcpu, 0u, HVC_ERR_UNKNOWN);
131 ev.reason = 301u;
132 break;
133 }
134 uart_puts("guest: ");
135 for (uint64_t i = 0; i < len; i++) {
136 uart_putc(s[i]);
137 }
138 uart_puts("\n");
139 vcpu_write_gpr(vcpu, 0u, 0u);
140 break;
141 }
142 case HVC_REPORT_EXCEPTION: {
143 uint64_t guest_esr = vcpu_read_gpr(vcpu, 1u);
144 uint64_t guest_elr = vcpu_read_gpr(vcpu, 2u);
145 uint64_t guest_far = vcpu_read_gpr(vcpu, 3u);
146 struct log_event ge;
147 hv_memset(&ge, 0, sizeof(ge));
148 ge.kind = LOG_ABORT;
149 ge.vcpu = vcpu->id;
150 ge.ec = esr_ec(guest_esr);
151 ge.iss = guest_esr & 0x01ffffffu;
152 ge.elr = guest_elr;
153 ge.far = guest_far;
154 ge.action = LOG_ACT_NONE;
155 ge.reason = 303u;
156 ge.pstate = vcpu->pstate;
157 ge.name = "guest_exception_report";
158 log_event_commit(&ge);
159 vcpu_write_gpr(vcpu, 0u, 0u);
160 break;
161 }
162 default:
163 if ((num & 0xffff0000ull) == 0x84000000ull ||
164 (num & 0xffff0000ull) == 0xc4000000ull) {
165 log_event_commit(&ev);
166 (void)psci_handle_call(vcpu, num);
167 return;
168 }
169 vcpu_write_gpr(vcpu, 0u, HVC_ERR_UNKNOWN);
170 ev.reason = 302u;
171 break;
172 }
173
174 log_event_commit(&ev);
175}
176
177static void handle_sysreg(struct vcpu *vcpu, const struct trap_frame *frame)
178{
179 struct sysreg_access access;
180 struct policy_decision decision;
181 uint64_t operand = 0;
182
183 if (!sysreg_decode_from_esr_iss(frame->esr_el2 & 0x01ffffffu, &access)) {
184 panic("failed to decode sysreg iss");
185 }
186
187 if (!access.is_read) {
188 operand = vcpu_read_gpr(vcpu, access.rt);
189 }
190
191 if (!policy_apply_sysreg(vcpu, &access, operand, &decision)) {
192 panic("policy engine failed");
193 }
194
195 if (access.is_read && decision.action == POLICY_EMULATE_READ) {
196 vcpu_write_gpr(vcpu, access.rt, decision.value);
197 }
198
199 log_trap_event(vcpu, frame, &access, &decision, operand);
200 vcpu->stats.sysreg_traps++;
201
202 switch (decision.action) {
203 case POLICY_EMULATE_READ:
204 case POLICY_EMULATE_WRITE:
205 case POLICY_WRITE_MASK:
206 case POLICY_DENY_SKIP:
207 arch_advance_guest_pc(vcpu);
208 break;
209 case POLICY_DENY_UNDEF:
210 arch_inject_guest_undef(vcpu);
211 if (vcpu->state == VCPU_PAUSED) {
212 monitor_loop(vcpu, "policy could not inject undefined instruction");
213 }
214 break;
215 case POLICY_PAUSE:
216 vcpu->state = VCPU_PAUSED;
217 monitor_loop(vcpu, "policy pause");
218 break;
219 case POLICY_PANIC:
220 panic("policy panic action");
221 case POLICY_ALLOW_NATIVE:
222 default:
223 panic("unsafe native sysreg policy");
224 }
225}
226
227void trap_handle_lower_sync(struct trap_frame *frame)
228{
229 struct vcpu *vcpu = vcpu_current();
230 if (vcpu == (struct vcpu *)0) {
231 panic("trap without current vcpu");
232 }
233 vcpu_load_frame(vcpu, frame);
234 gicv3_sync_vcpu_state(vcpu);
235 vcpu->stats.traps++;
236 vcpu->state = VCPU_RUNNING;
237
238 uint32_t ec = esr_ec(frame->esr_el2);
239 switch (ec) {
240 case ESR_EC_SYSREG:
241 handle_sysreg(vcpu, frame);
242 break;
243 case ESR_EC_HVC64:
244 handle_hvc(vcpu);
245 break;
246 case ESR_EC_SMC64: {
247 uint64_t function_id = vcpu_read_gpr(vcpu, 0u);
248 if ((function_id & 0xffff0000ull) == 0x84000000ull ||
249 (function_id & 0xffff0000ull) == 0xc4000000ull) {
250 (void)psci_handle_call(vcpu, function_id);
251 } else {
252 vcpu->stats.smc_denied++;
253 struct policy_decision d;
254 hv_memset(&d, 0, sizeof(d));
255 d.action = POLICY_DENY_UNDEF;
256 d.reason = 400u;
257 log_trap_event(vcpu, frame, (const struct sysreg_access *)0, &d, 0u);
258 arch_inject_guest_undef(vcpu);
259 if (vcpu->state == VCPU_PAUSED) {
260 monitor_loop(vcpu, "guest smc denied");
261 }
262 }
263 break;
264 }
265 case ESR_EC_WFI_WFE:
266 vcpu->stats.wfx_traps++;
267 log_simple(LOG_TRAP, 401u, frame->esr_el2, frame->elr_el2);
268 arch_advance_guest_pc(vcpu);
269 break;
270 case ESR_EC_IABT_LOWER:
271 vcpu->stats.instruction_aborts++;
272 if (stage2_handle_abort(vcpu, frame, true)) {
273 break;
274 }
275 monitor_loop(vcpu, "guest instruction abort");
276 break;
277 case ESR_EC_DABT_LOWER:
278 vcpu->stats.data_aborts++;
279 if (stage2_handle_abort(vcpu, frame, false)) {
280 break;
281 }
282 monitor_loop(vcpu, "guest data abort");
283 break;
284 case ESR_EC_BRK64:
285 case ESR_EC_UNKNOWN:
286 default:
287 vcpu->stats.unknown_exceptions++;
288 log_simple(LOG_TRAP, 404u, frame->esr_el2, frame->elr_el2);
289 monitor_loop(vcpu, "guest unknown or brk exception");
290 break;
291 }
292
293 timer_vcpu_sync(vcpu);
294 gicv3_flush_vcpu_state(vcpu);
295 arch_context_barrier();
296 vcpu_store_frame(vcpu, frame);
297}
298
299void trap_handle_current_sync(struct trap_frame *frame)
300{
301 panic_with_code("unexpected EL2 synchronous exception", frame != (struct trap_frame *)0 ? frame->esr_el2 : 0u);
302}
303
304void trap_handle_irq(struct trap_frame *frame)
305{
306 uint32_t irq = gicv3_ack_irq();
307 struct log_event ev;
308 hv_memset(&ev, 0, sizeof(ev));
309 ev.kind = LOG_TRAP;
310 ev.vcpu = vcpu_current() != (struct vcpu *)0 ? vcpu_current()->id : UINT64_MAX;
311 ev.ec = 0xffu;
312 ev.iss = irq;
313 ev.elr = frame != (struct trap_frame *)0 ? frame->elr_el2 : 0u;
314 ev.pstate = frame != (struct trap_frame *)0 ? frame->spsr_el2 : 0u;
315 ev.reason = irq >= 1020u ? 501u : 500u;
316 log_event_commit(&ev);
317 if (irq < 1020u) {
318 gicv3_eoi(irq);
319 }
320}