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