AArch64 EL2 hypervisor for QEMU virt that boots at EL2
1

Configure Feed

Select the types of activity you want to include in your feed.

ariel / core / trap.c
9.8 kB 320 lines
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}