VMM: Replace the old virtio in vmrunkernel with the new virtio
[akaros.git] / user / vmm / vmexit.c
1 /* Copyright (c) 2015-2016 Google Inc.
2  * See LICENSE for details. */
3
4 #include <parlib/common.h>
5 #include <vmm/virtio.h>
6 #include <vmm/virtio_mmio.h>
7 #include <vmm/virtio_ids.h>
8 #include <vmm/virtio_config.h>
9 #include <vmm/vmm.h>
10 #include <parlib/arch/trap.h>
11 #include <stdio.h>
12
13 /* TODO: need infrastructure to handle GPC wakeup properly */
14 static bool consdata = FALSE;
15
16 static bool handle_ept_fault(struct guest_thread *gth)
17 {
18         struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
19         struct virtual_machine *vm = gth_to_vm(gth);
20         uint64_t gpa, *regp;
21         uint8_t regx;
22         int store, size;
23         int advance;
24
25         if (decode(gth, &gpa, &regx, &regp, &store, &size, &advance))
26                 return FALSE;
27         /* TODO use helpers for some of these addr checks.  the fee/fec ones might
28          * be wrong too. */
29         if (PG_ADDR(gpa) == vm->virtio_mmio_base) {
30                 /* TODO: can the guest cause us to spawn off infinite threads? */
31                 virtio_mmio(gth, gpa, regx, regp, store);
32                 if (size < 0) {
33                         // TODO: It would be preferable for the decoder to return an
34                         //       unsigned value, so that we don't have to worry
35                         //       about this. I don't know if it's even possible for
36                         //       the width to be negative;
37                         VIRTIO_DRI_ERRX(cons_mmio_dev.vqdev,
38                             "Driver tried to access the device with a negative access width in the instruction?");
39                 }
40                 //fprintf(stderr, "RIP is 0x%x\n", vm_tf->tf_rip);
41                 if (store)
42                         virtio_mmio_wr(&cons_mmio_dev, gpa, size, (uint32_t *)regp);
43                 else
44                         *regp = virtio_mmio_rd(&cons_mmio_dev, gpa, size);
45
46         } else if (PG_ADDR(gpa) == 0xfec00000) {
47                 do_ioapic(gth, gpa, regx, regp, store);
48         } else if (PG_ADDR(gpa) == 0) {
49                 memmove(regp, &vm->low4k[gpa], size);
50         } else {
51                 fprintf(stderr, "EPT violation: can't handle %p\n", gpa);
52                 fprintf(stderr, "RIP %p, exit reason 0x%x\n", vm_tf->tf_rip,
53                                 vm_tf->tf_exit_reason);
54                 fprintf(stderr, "Returning 0xffffffff\n");
55                 showstatus(stderr, gth);
56                 // Just fill the whole register for now.
57                 *regp = (uint64_t) -1;
58                 return FALSE;
59         }
60         vm_tf->tf_rip += advance;
61         return TRUE;
62 }
63
64 static bool handle_vmcall(struct guest_thread *gth)
65 {
66         struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
67         uint8_t byte;
68
69         byte = vm_tf->tf_rdi;
70         printf("%c", byte);
71         if (byte == '\n')
72                 printf("%c", '%');
73         vm_tf->tf_rip += 3;
74         return TRUE;
75 }
76
77 static bool handle_io(struct guest_thread *gth)
78 {
79         io(gth);
80         return TRUE;
81 }
82
83 static bool handle_msr(struct guest_thread *gth)
84 {
85         struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
86
87         /* TODO: consider pushing the gth into msrio */
88         if (msrio(gth, gth_to_gpci(gth), vm_tf->tf_exit_reason)) {
89                 /* Use event injection through vmctl to send a general protection fault
90                  * vmctl.interrupt gets written to the VM-Entry Interruption-Information
91                  * Field by vmx */
92                 vm_tf->tf_trap_inject = VM_TRAP_VALID
93                                       | VM_TRAP_ERROR_CODE
94                                       | VM_TRAP_HARDWARE
95                                       | HW_TRAP_GP_FAULT;
96         } else {
97                 vm_tf->tf_rip += 2;
98         }
99         return TRUE;
100 }
101
102 static bool handle_apic_access(struct guest_thread *gth)
103 {
104         uint64_t gpa, *regp;
105         uint8_t regx;
106         int store, size;
107         int advance;
108         struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
109
110         if (decode(gth, &gpa, &regx, &regp, &store, &size, &advance))
111                 return FALSE;
112         if (__apic_access(gth, gpa, regx, regp, store))
113                 return FALSE;
114         vm_tf->tf_rip += advance;
115         return TRUE;
116 }
117
118 static bool handle_halt(struct guest_thread *gth)
119 {
120         struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
121
122         while (!consdata)
123                 ;
124         vm_tf->tf_rip += 1;
125         return TRUE;
126 }
127
128 static bool handle_mwait(struct guest_thread *gth)
129 {
130         struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
131
132         while (!consdata)
133                 ;
134         vm_tf->tf_rip += 3;
135         return TRUE;
136 }
137
138 /* Is this a vmm specific thing?  or generic?
139  *
140  * what do we do when we want to kill the vm?  what are our other options? */
141 bool handle_vmexit(struct guest_thread *gth)
142 {
143         struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
144
145         switch (vm_tf->tf_exit_reason) {
146         case EXIT_REASON_EPT_VIOLATION:
147                 return handle_ept_fault(gth);
148         case EXIT_REASON_VMCALL:
149                 return handle_vmcall(gth);
150         case EXIT_REASON_IO_INSTRUCTION:
151                 return handle_io(gth);
152         case EXIT_REASON_MSR_WRITE:
153         case EXIT_REASON_MSR_READ:
154                 return handle_msr(gth);
155         case EXIT_REASON_APIC_ACCESS:
156                 return handle_apic_access(gth);
157         case EXIT_REASON_HLT:
158                 return handle_halt(gth);
159         case EXIT_REASON_MWAIT_INSTRUCTION:
160                 return handle_mwait(gth);
161         case EXIT_REASON_EXTERNAL_INTERRUPT:
162         case EXIT_REASON_APIC_WRITE:
163                 /* TODO: just ignore these? */
164                 return TRUE;
165         default:
166                 fprintf(stderr, "Don't know how to handle exit %d\n",
167                         vm_tf->tf_exit_reason);
168                 fprintf(stderr, "RIP %p, shutdown 0x%x\n", vm_tf->tf_rip,
169                         vm_tf->tf_exit_reason);
170                 return FALSE;
171         }
172 }