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