1/* 2 * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <autoconf.h> 8#include <stdio.h> 9#include <stdlib.h> 10 11#include <sel4/sel4.h> 12#include <sel4/messages.h> 13 14#include <sel4vm/guest_vm.h> 15#include <sel4vm/guest_vm_util.h> 16#include <sel4vm/boot.h> 17#include <sel4vm/guest_vm_exits.h> 18#include <sel4vm/guest_irq_controller.h> 19#include <sel4vm/sel4_arch/processor.h> 20#include <sel4vm/arch/guest_arm_context.h> 21 22#include "vm.h" 23#include "arm_vm.h" 24#include "arm_vm_exits.h" 25#include "fault.h" 26 27#include "vgic/vgic.h" 28#include "syscalls.h" 29#include "mem_abort.h" 30 31static int vm_user_exception_handler(vm_vcpu_t *vcpu); 32static int vm_vcpu_handler(vm_vcpu_t *vcpu); 33static int vm_unknown_exit_handler(vm_vcpu_t *vcpu); 34static int vm_vppi_event_handler(vm_vcpu_t *vcpu); 35 36static vm_exit_handler_fn_t arm_exit_handlers[] = { 37 [VM_GUEST_ABORT_EXIT] = vm_guest_mem_abort_handler, 38 [VM_SYSCALL_EXIT] = vm_syscall_handler, 39 [VM_USER_EXCEPTION_EXIT] = vm_user_exception_handler, 40 [VM_VGIC_MAINTENANCE_EXIT] = vm_vgic_maintenance_handler, 41 [VM_VCPU_EXIT] = vm_vcpu_handler, 42 [VM_VPPI_EXIT] = vm_vppi_event_handler, 43 [VM_UNKNOWN_EXIT] = vm_unknown_exit_handler 44}; 45 46static int vm_decode_exit(seL4_Word label) 47{ 48 int exit_reason = VM_UNKNOWN_EXIT; 49 50 switch (label) { 51 case seL4_Fault_VMFault: 52 exit_reason = VM_GUEST_ABORT_EXIT; 53 break; 54 case seL4_Fault_UnknownSyscall: 55 exit_reason = VM_SYSCALL_EXIT; 56 break; 57 case seL4_Fault_UserException: 58 exit_reason = VM_USER_EXCEPTION_EXIT; 59 break; 60 case seL4_Fault_VGICMaintenance: 61 exit_reason = VM_VGIC_MAINTENANCE_EXIT; 62 break; 63 case seL4_Fault_VCPUFault: 64 exit_reason = VM_VCPU_EXIT; 65 break; 66 case seL4_Fault_VPPIEvent: 67 exit_reason = VM_VPPI_EXIT; 68 break; 69 default: 70 exit_reason = VM_UNKNOWN_EXIT; 71 } 72 return exit_reason; 73} 74 75static int handle_exception(vm_vcpu_t *vcpu, seL4_Word ip) 76{ 77 seL4_UserContext regs; 78 seL4_CPtr tcb = vm_get_vcpu_tcb(vcpu); 79 int err; 80 ZF_LOGE("%sInvalid instruction from [%s] at PC: 0x"XFMT"%s\n", 81 ANSI_COLOR(RED, BOLD), vcpu->vm->vm_name, seL4_GetMR(0), ANSI_COLOR(RESET)); 82 err = seL4_TCB_ReadRegisters(tcb, false, 0, sizeof(regs) / sizeof(regs.pc), ®s); 83 assert(!err); 84 print_ctx_regs(®s); 85 return VM_EXIT_HANDLED; 86} 87 88static int vm_vppi_event_handler(vm_vcpu_t *vcpu) 89{ 90 int err; 91 seL4_Word ppi_irq; 92 ppi_irq = seL4_GetMR(0); 93 /* We directly inject the interrupt assuming it has been previously registered 94 * If not the interrupt will dropped by the VM */ 95 err = vm_inject_irq(vcpu, ppi_irq); 96 if (err) { 97 ZF_LOGE("VPPI IRQ %d dropped on vcpu %d", ppi_irq, vcpu->vcpu_id); 98 /* Acknowledge to unmask it as our guest will not use the interrupt */ 99 seL4_Error ack_err = seL4_ARM_VCPU_AckVPPI(vcpu->vcpu.cptr, ppi_irq); 100 if (ack_err) { 101 ZF_LOGE("Failed to ACK VPPI: VPPI Ack invocation failed"); 102 return -1; 103 } 104 } 105 seL4_MessageInfo_t reply; 106 reply = seL4_MessageInfo_new(0, 0, 0, 0); 107 seL4_Reply(reply); 108 return 0; 109} 110 111static int vm_user_exception_handler(vm_vcpu_t *vcpu) 112{ 113 seL4_Word ip; 114 int err; 115 ip = seL4_GetMR(0); 116 err = handle_exception(vcpu, ip); 117 assert(!err); 118 if (!err) { 119 seL4_MessageInfo_t reply; 120 reply = seL4_MessageInfo_new(0, 0, 0, 0); 121 seL4_Reply(reply); 122 } 123 return VM_EXIT_HANDLED; 124} 125 126static void print_unhandled_vcpu_hsr(vm_vcpu_t *vcpu, uint32_t hsr) 127{ 128 printf("======= Unhandled VCPU fault from [%s] =======\n", vcpu->vm->vm_name); 129 printf("HSR Value: 0x%08x\n", hsr); 130 printf("HSR Exception Class: %s [0x%x]\n", hsr_reasons[HSR_EXCEPTION_CLASS(hsr)], HSR_EXCEPTION_CLASS(hsr)); 131 printf("Instruction Length: %d\n", HSR_IL(hsr)); 132 printf("ISS Value: 0x%x\n", hsr & HSR_ISS_MASK); 133 printf("==============================================\n"); 134} 135 136static int vm_vcpu_handler(vm_vcpu_t *vcpu) 137{ 138 uint32_t hsr; 139 int err; 140 fault_t *fault; 141 fault = vcpu->vcpu_arch.fault; 142 hsr = seL4_GetMR(seL4_UnknownSyscall_ARG0); 143 if (vcpu->vcpu_arch.unhandled_vcpu_callback) { 144 /* Pass the vcpu fault to library user in case they can handle it */ 145 err = new_vcpu_fault(fault, hsr); 146 if (err) { 147 ZF_LOGE("Failed to create new fault"); 148 return VM_EXIT_HANDLE_ERROR; 149 } 150 err = vcpu->vcpu_arch.unhandled_vcpu_callback(vcpu, hsr, vcpu->vcpu_arch.unhandled_vcpu_callback_cookie); 151 if (!err) { 152 return VM_EXIT_HANDLED; 153 } 154 } 155 print_unhandled_vcpu_hsr(vcpu, hsr); 156 return VM_EXIT_HANDLE_ERROR; 157} 158 159static int vm_unknown_exit_handler(vm_vcpu_t *vcpu) 160{ 161 /* What? Why are we here? What just happened? */ 162 ZF_LOGE("Unknown fault from [%s]", vcpu->vm->vm_name); 163 vcpu->vm->run.exit_reason = VM_GUEST_UNKNOWN_EXIT; 164 return VM_EXIT_HANDLE_ERROR; 165} 166 167static int vcpu_stop(vm_vcpu_t *vcpu) 168{ 169 vcpu->vcpu_online = false; 170 return seL4_TCB_Suspend(vm_get_vcpu_tcb(vcpu)); 171} 172 173int vcpu_start(vm_vcpu_t *vcpu) 174{ 175 int err; 176 vcpu->vcpu_online = true; 177 seL4_Word vmpidr_val; 178 seL4_Word vmpidr_reg; 179 180#if CONFIG_MAX_NUM_NODES > 1 181#ifdef CONFIG_ARCH_AARCH64 182 vmpidr_reg = seL4_VCPUReg_VMPIDR_EL2; 183#else 184 vmpidr_reg = seL4_VCPUReg_VMPIDR; 185#endif 186 if (vcpu->vcpu_id == BOOT_VCPU) { 187 /* VMPIDR Bit Assignments [G8.2.167, Arm Architecture Reference Manual Armv8] 188 * - BIT(24): Performance of PEs (processing element) at the lowest affinity level is very interdependent 189 * - BIT(31): This implementation includes the ARMv7 Multiprocessing Extensions functionality 190 */ 191 vmpidr_val = BIT(24) | BIT(31); 192 } else { 193 vmpidr_val = vcpu->target_cpu; 194 } 195 err = vm_set_arm_vcpu_reg(vcpu, vmpidr_reg, vmpidr_val); 196 if (err) { 197 ZF_LOGE("Failed to set VMPIDR register"); 198 return -1; 199 } 200#endif 201 return seL4_TCB_Resume(vm_get_vcpu_tcb(vcpu)); 202} 203 204int vm_register_unhandled_vcpu_fault_callback(vm_vcpu_t *vcpu, unhandled_vcpu_fault_callback_fn vcpu_fault_callback, 205 void *cookie) 206{ 207 if (!vcpu) { 208 ZF_LOGE("Failed to register fault callback: Invalid VCPU handle"); 209 return -1; 210 } 211 212 if (!vcpu_fault_callback) { 213 ZF_LOGE("Failed to register vcpu fault callback: Invalid callback"); 214 return -1; 215 } 216 vcpu->vcpu_arch.unhandled_vcpu_callback = vcpu_fault_callback; 217 vcpu->vcpu_arch.unhandled_vcpu_callback_cookie = cookie; 218 return 0; 219 220} 221 222int vm_run_arch(vm_t *vm) 223{ 224 int err; 225 int ret; 226 227 ret = 1; 228 /* Loop, handling events */ 229 while (ret > 0) { 230 seL4_MessageInfo_t tag; 231 seL4_Word sender_badge; 232 seL4_Word label; 233 int vm_exit_reason; 234 235 tag = seL4_Recv(vm->host_endpoint, &sender_badge); 236 label = seL4_MessageInfo_get_label(tag); 237 if (sender_badge >= MIN_VCPU_BADGE && sender_badge <= MAX_VCPU_BADGE) { 238 seL4_Word vcpu_idx = VCPU_BADGE_IDX(sender_badge); 239 if (vcpu_idx >= vm->num_vcpus) { 240 ZF_LOGE("Invalid VCPU index. Exiting"); 241 ret = -1; 242 } else { 243 vm_exit_reason = vm_decode_exit(label); 244 ret = arm_exit_handlers[vm_exit_reason](vm->vcpus[vcpu_idx]); 245 if (ret == VM_EXIT_HANDLE_ERROR) { 246 vm->run.exit_reason = VM_GUEST_ERROR_EXIT; 247 } 248 } 249 } else { 250 if (vm->run.notification_callback) { 251 err = vm->run.notification_callback(vm, sender_badge, tag, 252 vm->run.notification_callback_cookie); 253 } else { 254 ZF_LOGE("Unable to handle VM notification. Exiting"); 255 err = -1; 256 } 257 if (err) { 258 ret = -1; 259 vm->run.exit_reason = VM_GUEST_ERROR_EXIT; 260 } 261 } 262 } 263 264 return ret; 265} 266