VMM: moves various helpers to vmx.h
[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 "intel/vmx.h"
18 #include "vmm.h"
19
20 /* TODO: have better cpuid info storage and checks */
21 bool x86_supports_vmx = FALSE;
22
23 /* Figure out what kind of CPU we are on, and if it supports any reasonable
24  * virtualization. For now, if we're not some sort of newer intel, don't
25  * bother. This does all cores. Again, note, we make these decisions at runtime,
26  * to avoid getting into the problems that compile-time decisions can cause. 
27  * At this point, of course, it's still all intel.
28  */
29 void vmm_init(void)
30 {
31         int ret;
32         /* Check first for intel capabilities. This is hence two back-to-back
33          * implementationd-dependent checks. That's ok, it's all msr dependent.
34          */
35         ret = intel_vmm_init();
36         if (! ret) {
37                 printd("intel_vmm_init worked\n");
38                 x86_supports_vmx = TRUE;
39                 return;
40         }
41
42         /* TODO: AMD. Will we ever care? It's not clear. */
43         printk("vmm_init failed, ret %d\n", ret);
44         return;
45 }
46
47 void vmm_pcpu_init(void)
48 {
49         if (!x86_supports_vmx)
50                 return;
51         if (! intel_vmm_pcpu_init()) {
52                 printd("vmm_pcpu_init worked\n");
53                 return;
54         }
55         /* TODO: AMD. Will we ever care? It's not clear. */
56         printk("vmm_pcpu_init failed\n");
57 }
58
59 int vm_run(uint64_t rip, uint64_t rsp, uint64_t cr3)
60 {
61         int vmx_launch(uint64_t rip, uint64_t rsp, uint64_t cr3);
62         if (current->vmm.amd) {
63                 return -1;
64         } else {
65                 return vmx_launch(rip, rsp, cr3);
66         }
67         return -1;
68 }
69
70 /* Initializes a process to run virtual machine contexts, returning the number
71  * initialized, optionally setting errno */
72 int vmm_struct_init(struct proc *p, unsigned int nr_guest_pcores)
73 {
74         struct vmm *vmm = &p->vmm;
75         unsigned int i;
76         epte_t *ept_pml4;
77         if (!x86_supports_vmx) {
78                 set_errno(ENODEV);
79                 return 0;
80         }
81         qlock(&vmm->qlock);
82         if (vmm->vmmcp) {
83                 set_errno(EINVAL);
84                 qunlock(&vmm->qlock);
85                 return 0;
86         }
87         /* Set this early, so cleanup checks the gpc array */
88         vmm->vmmcp = TRUE;
89         ept_pml4 = kpage_zalloc_addr();
90         if (!ept_pml4) {
91                 set_errno(ENOMEM);
92                 return 0;
93         }
94         p->env_pgdir.epte = ept_pml4;
95         p->env_pgdir.eptp = construct_eptp(PADDR(ept_pml4));
96         nr_guest_pcores = MIN(nr_guest_pcores, num_cpus);
97         vmm->amd = 0;
98         vmm->guest_pcores = kzmalloc(sizeof(void*) * nr_guest_pcores, KMALLOC_WAIT);
99         for (i = 0; i < nr_guest_pcores; i++) {
100                 vmm->guest_pcores[i] = vmx_create_vcpu(p);
101                 /* If we failed, we'll clean it up when the process dies */
102                 if (!vmm->guest_pcores[i]) {
103                         set_errno(ENOMEM);
104                         break;
105                 }
106         }
107         vmm->nr_guest_pcores = i;
108         qunlock(&vmm->qlock);
109         return i;
110 }
111
112 /* Has no concurrency protection - only call this when you know you have the
113  * only ref to vmm.  For instance, from __proc_free, where there is only one ref
114  * to the proc (and thus proc.vmm). */
115 void __vmm_struct_cleanup(struct proc *p)
116 {
117         struct vmm *vmm = &p->vmm;
118         if (!vmm->vmmcp)
119                 return;
120         for (int i = 0; i < vmm->nr_guest_pcores; i++) {
121                 if (vmm->guest_pcores[i])
122                         vmx_destroy_vcpu(vmm->guest_pcores[i]);
123         }
124         kfree(vmm->guest_pcores);
125         ept_flush(p->env_pgdir.eptp);
126         page_decref(kva2page(p->env_pgdir.epte));
127         vmm->vmmcp = FALSE;
128 }