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.7 kB 317 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 } 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}