VMM: Add helpers for loading guest pcores
[akaros.git] / kern / arch / x86 / vmm / vmm.c
1 /* Copyright 2015 Google Inc.
2  * 
3  * See LICENSE for details.
4  */
5
6 /* We're not going to falll into the trap of only compiling support
7  * for AMD OR Intel for an image. It all gets compiled in, and which
8  * one you use depends on on cpuinfo, not a compile-time
9  * switch. That's proven to be the best strategy.  Conditionally
10  * compiling in support is the path to hell.
11  */
12 #include <assert.h>
13 #include <pmap.h>
14 #include <smp.h>
15 #include <kmalloc.h>
16
17 #include <ros/vmm.h>
18 #include "intel/vmx.h"
19 #include "vmm.h"
20 #include <trap.h>
21 #include <umem.h>
22
23 /* TODO: have better cpuid info storage and checks */
24 bool x86_supports_vmx = FALSE;
25
26 static void vmmcp_posted_handler(struct hw_trapframe *hw_tf, void *data);
27
28 /* Figure out what kind of CPU we are on, and if it supports any reasonable
29  * virtualization. For now, if we're not some sort of newer intel, don't
30  * bother. This does all cores. Again, note, we make these decisions at runtime,
31  * to avoid getting into the problems that compile-time decisions can cause. 
32  * At this point, of course, it's still all intel.
33  */
34 void vmm_init(void)
35 {
36         int ret;
37         /* Check first for intel capabilities. This is hence two back-to-back
38          * implementationd-dependent checks. That's ok, it's all msr dependent.
39          */
40         ret = intel_vmm_init();
41         if (! ret) {
42                 printd("intel_vmm_init worked\n");
43
44                 //Register I_VMMCP_POSTED IRQ
45                 //register_irq(I_VMMCP_POSTED, vmmcp_posted_handler, NULL,
46                 //              MKBUS(BusLAPIC, 0, 0, 0));
47                 x86_supports_vmx = TRUE;
48                 return;
49         }
50
51         /* TODO: AMD. Will we ever care? It's not clear. */
52         printk("vmm_init failed, ret %d\n", ret);
53         return;
54 }
55
56 static void vmmcp_posted_handler(struct hw_trapframe *hw_tf, void *data)
57 {
58         printk("%s\n", __func__);
59 }
60
61 void vmm_pcpu_init(void)
62 {
63         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
64
65         pcpui->guest_pcoreid = -1;
66         if (!x86_supports_vmx)
67                 return;
68         if (! intel_vmm_pcpu_init()) {
69                 printd("vmm_pcpu_init worked\n");
70                 return;
71         }
72         /* TODO: AMD. Will we ever care? It's not clear. */
73         printk("vmm_pcpu_init failed\n");
74 }
75
76 int vm_post_interrupt(struct vmctl *v)
77 {
78         int vmx_interrupt_notify(struct vmctl *v);
79         if (current->vmm.amd) {
80                 return -1;
81         } else {
82                 return vmx_interrupt_notify(v);
83         }
84         return -1;
85 }
86
87 int vm_run(struct vmctl *v)
88 {
89         int vmx_launch(struct vmctl *v);
90         if (current->vmm.amd) {
91                 return -1;
92         } else {
93                 return vmx_launch(v);
94         }
95         return -1;
96 }
97
98 /* Initializes a process to run virtual machine contexts, returning the number
99  * initialized, optionally setting errno */
100 int vmm_struct_init(struct proc *p, unsigned int nr_guest_pcores,
101                     struct vmm_gpcore_init *u_gpcis, int flags)
102 {
103         struct vmm *vmm = &p->vmm;
104         unsigned int i;
105         struct vmm_gpcore_init gpci;
106
107         if (flags & ~VMM_ALL_FLAGS) {
108                 set_errstr("%s: flags is 0x%lx, VMM_ALL_FLAGS is 0x%lx\n", __func__,
109                            flags, VMM_ALL_FLAGS);
110                 set_errno(EINVAL);
111                 return 0;
112         }
113         vmm->flags = flags;
114         if (!x86_supports_vmx) {
115                 set_errno(ENODEV);
116                 return 0;
117         }
118         qlock(&vmm->qlock);
119         if (vmm->vmmcp) {
120                 set_errno(EINVAL);
121                 qunlock(&vmm->qlock);
122                 return 0;
123         }
124         /* Set this early, so cleanup checks the gpc array */
125         vmm->vmmcp = TRUE;
126         nr_guest_pcores = MIN(nr_guest_pcores, num_cores);
127         vmm->amd = 0;
128         vmm->guest_pcores = kzmalloc(sizeof(void*) * nr_guest_pcores, KMALLOC_WAIT);
129         for (i = 0; i < nr_guest_pcores; i++) {
130                 if (copy_from_user(&gpci, &u_gpcis[i],
131                                    sizeof(struct vmm_gpcore_init))) {
132                         set_error(EINVAL, "Bad pointer %p for gps", u_gpcis);
133                         break;
134                 }
135                 vmm->guest_pcores[i] = vmx_create_vcpu(p, &gpci);
136                 /* If we failed, we'll clean it up when the process dies */
137                 if (!vmm->guest_pcores[i]) {
138                         set_errno(ENOMEM);
139                         break;
140                 }
141         }
142         vmm->nr_guest_pcores = i;
143         for (int i = 0; i < VMM_VMEXIT_NR_TYPES; i++)
144                 vmm->vmexits[i] = 0;
145         qunlock(&vmm->qlock);
146         return i;
147 }
148
149 /* Has no concurrency protection - only call this when you know you have the
150  * only ref to vmm.  For instance, from __proc_free, where there is only one ref
151  * to the proc (and thus proc.vmm). */
152 void __vmm_struct_cleanup(struct proc *p)
153 {
154         struct vmm *vmm = &p->vmm;
155         if (!vmm->vmmcp)
156                 return;
157         for (int i = 0; i < vmm->nr_guest_pcores; i++) {
158                 if (vmm->guest_pcores[i])
159                         vmx_destroy_vcpu(vmm->guest_pcores[i]);
160         }
161         kfree(vmm->guest_pcores);
162         ept_flush(p->env_pgdir.eptp);
163         vmm->vmmcp = FALSE;
164 }
165
166 struct vmx_vcpu *lookup_guest_pcore(struct proc *p, int guest_pcoreid)
167 {
168         /* nr_guest_pcores is written once at setup and never changed */
169         if (guest_pcoreid >= p->vmm.nr_guest_pcores)
170                 return 0;
171         return p->vmm.guest_pcores[guest_pcoreid];
172 }
173
174 struct vmx_vcpu *load_guest_pcore(struct proc *p, int guest_pcoreid)
175 {
176         struct vmx_vcpu *gpc;
177         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
178
179         gpc = lookup_guest_pcore(p, guest_pcoreid);
180         if (!gpc)
181                 return 0;
182         assert(pcpui->guest_pcoreid == -1);
183         spin_lock(&p->vmm.lock);
184         if (gpc->cpu != -1) {
185                 spin_unlock(&p->vmm.lock);
186                 return 0;
187         }
188         gpc->cpu = core_id();
189         spin_unlock(&p->vmm.lock);
190         /* We've got dibs on the gpc; we don't need to hold the lock any longer. */
191         pcpui->guest_pcoreid = guest_pcoreid;
192         ept_sync_context(vcpu_get_eptp(gpc));
193         vmx_load_guest_pcore(gpc);
194         return gpc;
195 }
196
197 void unload_guest_pcore(struct proc *p, int guest_pcoreid)
198 {
199         struct vmx_vcpu *gpc;
200         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
201
202         gpc = lookup_guest_pcore(p, guest_pcoreid);
203         assert(gpc);
204         spin_lock(&p->vmm.lock);
205         assert(gpc->cpu != -1);
206         ept_sync_context(vcpu_get_eptp(gpc));
207         vmx_unload_guest_pcore(gpc);
208         gpc->cpu = -1;
209         /* As soon as we unlock, this gpc can be started on another core */
210         spin_unlock(&p->vmm.lock);
211         pcpui->guest_pcoreid = -1;
212 }