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