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), &regs);
83    assert(!err);
84    print_ctx_regs(&regs);
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