VMM: Make new virtio implementation work with new vmm 2LS
[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                 if (size < 0) {
32                         // TODO: It would be preferable for the decoder to return an
33                         //       unsigned value, so that we don't have to worry
34                         //       about this. I don't know if it's even possible for
35                         //       the width to be negative;
36                         VIRTIO_DRI_ERRX(vm->cons_mmio_dev->vqdev,
37                             "Driver tried to access the device with a negative access width in the instruction?");
38                 }
39                 //fprintf(stderr, "RIP is 0x%x\n", vm_tf->tf_rip);
40                 if (store)
41                         virtio_mmio_wr(vm, vm->cons_mmio_dev, gpa, size, (uint32_t *)regp);
42                 else
43                         *regp = virtio_mmio_rd(vm, vm->cons_mmio_dev, gpa, size);
44
45         } else if (PG_ADDR(gpa) == 0xfec00000) {
46                 do_ioapic(gth, gpa, regx, regp, store);
47         } else if (PG_ADDR(gpa) == 0) {
48                 memmove(regp, &vm->low4k[gpa], size);
49         } else {
50                 fprintf(stderr, "EPT violation: can't handle %p\n", gpa);
51                 fprintf(stderr, "RIP %p, exit reason 0x%x\n", vm_tf->tf_rip,
52                                 vm_tf->tf_exit_reason);
53                 fprintf(stderr, "Returning 0xffffffff\n");
54                 showstatus(stderr, gth);
55                 // Just fill the whole register for now.
56                 *regp = (uint64_t) -1;
57                 return FALSE;
58         }
59         vm_tf->tf_rip += advance;
60         return TRUE;
61 }
62
63 static bool handle_vmcall(struct guest_thread *gth)
64 {
65         struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
66         uint8_t byte;
67
68         byte = vm_tf->tf_rdi;
69         printf("%c", byte);
70         if (byte == '\n')
71                 printf("%c", '%');
72         vm_tf->tf_rip += 3;
73         return TRUE;
74 }
75
76 static bool handle_io(struct guest_thread *gth)
77 {
78         io(gth);
79         return TRUE;
80 }
81
82 static bool handle_msr(struct guest_thread *gth)
83 {
84         struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
85
86         /* TODO: consider pushing the gth into msrio */
87         if (msrio(gth, gth_to_gpci(gth), vm_tf->tf_exit_reason)) {
88                 /* Use event injection through vmctl to send a general protection fault
89                  * vmctl.interrupt gets written to the VM-Entry Interruption-Information
90                  * Field by vmx */
91                 vm_tf->tf_trap_inject = VM_TRAP_VALID
92                                       | VM_TRAP_ERROR_CODE
93                                       | VM_TRAP_HARDWARE
94                                       | HW_TRAP_GP_FAULT;
95         } else {
96                 vm_tf->tf_rip += 2;
97         }
98         return TRUE;
99 }
100
101 static bool handle_apic_access(struct guest_thread *gth)
102 {
103         uint64_t gpa, *regp;
104         uint8_t regx;
105         int store, size;
106         int advance;
107         struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
108
109         if (decode(gth, &gpa, &regx, &regp, &store, &size, &advance))
110                 return FALSE;
111         if (__apic_access(gth, gpa, regx, regp, store))
112                 return FALSE;
113         vm_tf->tf_rip += advance;
114         return TRUE;
115 }
116
117 static bool handle_halt(struct guest_thread *gth)
118 {
119         struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
120
121         while (!consdata)
122                 ;
123         vm_tf->tf_rip += 1;
124         return TRUE;
125 }
126
127 static bool handle_mwait(struct guest_thread *gth)
128 {
129         struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
130
131         while (!consdata)
132                 ;
133         vm_tf->tf_rip += 3;
134         return TRUE;
135 }
136
137 /* Is this a vmm specific thing?  or generic?
138  *
139  * what do we do when we want to kill the vm?  what are our other options? */
140 bool handle_vmexit(struct guest_thread *gth)
141 {
142         struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
143
144         switch (vm_tf->tf_exit_reason) {
145         case EXIT_REASON_EPT_VIOLATION:
146                 return handle_ept_fault(gth);
147         case EXIT_REASON_VMCALL:
148                 return handle_vmcall(gth);
149         case EXIT_REASON_IO_INSTRUCTION:
150                 return handle_io(gth);
151         case EXIT_REASON_MSR_WRITE:
152         case EXIT_REASON_MSR_READ:
153                 return handle_msr(gth);
154         case EXIT_REASON_APIC_ACCESS:
155                 return handle_apic_access(gth);
156         case EXIT_REASON_HLT:
157                 return handle_halt(gth);
158         case EXIT_REASON_MWAIT_INSTRUCTION:
159                 return handle_mwait(gth);
160         case EXIT_REASON_EXTERNAL_INTERRUPT:
161         case EXIT_REASON_APIC_WRITE:
162                 /* TODO: just ignore these? */
163                 return TRUE;
164         default:
165                 fprintf(stderr, "Don't know how to handle exit %d\n",
166                         vm_tf->tf_exit_reason);
167                 fprintf(stderr, "RIP %p, shutdown 0x%x\n", vm_tf->tf_rip,
168                         vm_tf->tf_exit_reason);
169                 return FALSE;
170         }
171 }