Handle multiple virtio mmio devices.
[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         io(gth);
78         return TRUE;
79 }
80
81 static bool handle_msr(struct guest_thread *gth)
82 {
83         struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
84
85         /* TODO: consider pushing the gth into msrio */
86         if (msrio(gth, gth_to_gpci(gth), vm_tf->tf_exit_reason)) {
87                 /* Use event injection through vmctl to send a general protection fault
88                  * vmctl.interrupt gets written to the VM-Entry Interruption-Information
89                  * Field by vmx */
90                 vm_tf->tf_trap_inject = VM_TRAP_VALID
91                                       | VM_TRAP_ERROR_CODE
92                                       | VM_TRAP_HARDWARE
93                                       | HW_TRAP_GP_FAULT;
94         } else {
95                 vm_tf->tf_rip += 2;
96         }
97         return TRUE;
98 }
99
100 static bool handle_apic_access(struct guest_thread *gth)
101 {
102         uint64_t gpa, *regp;
103         uint8_t regx;
104         int store, size;
105         int advance;
106         struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
107
108         if (decode(gth, &gpa, &regx, &regp, &store, &size, &advance))
109                 return FALSE;
110         if (__apic_access(gth, gpa, regx, regp, store))
111                 return FALSE;
112         vm_tf->tf_rip += advance;
113         return TRUE;
114 }
115
116 static bool handle_halt(struct guest_thread *gth)
117 {
118         struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
119
120         while (!consdata)
121                 ;
122         vm_tf->tf_rip += 1;
123         return TRUE;
124 }
125
126 static bool handle_mwait(struct guest_thread *gth)
127 {
128         struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
129
130         while (!consdata)
131                 ;
132         vm_tf->tf_rip += 3;
133         return TRUE;
134 }
135
136 /* Is this a vmm specific thing?  or generic?
137  *
138  * what do we do when we want to kill the vm?  what are our other options? */
139 bool handle_vmexit(struct guest_thread *gth)
140 {
141         struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
142
143         switch (vm_tf->tf_exit_reason) {
144         case EXIT_REASON_EPT_VIOLATION:
145                 return handle_ept_fault(gth);
146         case EXIT_REASON_VMCALL:
147                 return handle_vmcall(gth);
148         case EXIT_REASON_IO_INSTRUCTION:
149                 return handle_io(gth);
150         case EXIT_REASON_MSR_WRITE:
151         case EXIT_REASON_MSR_READ:
152                 return handle_msr(gth);
153         case EXIT_REASON_APIC_ACCESS:
154                 return handle_apic_access(gth);
155         case EXIT_REASON_HLT:
156                 return handle_halt(gth);
157         case EXIT_REASON_MWAIT_INSTRUCTION:
158                 return handle_mwait(gth);
159         case EXIT_REASON_EXTERNAL_INTERRUPT:
160         case EXIT_REASON_APIC_WRITE:
161                 /* TODO: just ignore these? */
162                 return TRUE;
163         default:
164                 fprintf(stderr, "Don't know how to handle exit %d\n",
165                         vm_tf->tf_exit_reason);
166                 fprintf(stderr, "RIP %p, shutdown 0x%x\n", vm_tf->tf_rip,
167                         vm_tf->tf_exit_reason);
168                 return FALSE;
169         }
170 }