e7963e5b22f5413ff5f2a7bffc2d205e5f79a167
[akaros.git] / user / vmm / vthread.c
1 /* Copyright (c) 2016 Google Inc.
2  *
3  * See LICENSE for details.
4  *
5  * Helper functions for virtual machines */
6
7 #include <errno.h>
8 #include <stdlib.h>
9 #include <parlib/bitmask.h>
10 #include <parlib/uthread.h>
11 #include <sys/mman.h>
12 #include <sys/syscall.h>
13 #include <vmm/vmm.h>
14
15
16 static void *page(void *addr, int count)
17 {
18         void *v;
19         unsigned long flags = MAP_POPULATE | MAP_ANONYMOUS;
20
21         if (addr)
22                 flags |= MAP_FIXED;
23         return mmap(addr, count * 4096, PROT_READ | PROT_WRITE, flags, -1, 0);
24 }
25
26 /* vmsetup is a basic helper function used by vthread_attr_init
27  * and vthread_attr_kernel_init. */
28 static int vmsetup(struct virtual_machine *vm, int flags)
29 {
30         unsigned long long *p512, *p1;
31         struct vm_trapframe *vm_tf;
32         int i, ret;
33         uint8_t *p;
34
35         if (vm->vminit)
36                 return -EBUSY;
37
38         if (vm->nr_gpcs == 0)
39                 vm->nr_gpcs = 1;
40
41         vm->gpcis = calloc(vm->nr_gpcs, sizeof(*vm->gpcis));
42
43         /* Set up default page mappings. The common case,
44          * for user VM threads and kernel VM threads, is that
45          * they need some kind of initial page tables. The kernels
46          * will almost always throw them away; the user VM threads
47          * will almost always continue to use them. Using two
48          * pages and setting up an initial page table is
49          * cheap and makes users lives easier. This initial
50          * page table can grow to 512 GiB, which should be enough
51          * for now.
52          *
53          * At the same time, we allow users to select other
54          * arrangements if they wish.  Here's a simple example: is it
55          * possible someone will want a different guest page table for
56          * every guest? Yes.
57          *
58          * We lock the page table to 0x1000000 for now. We can't just
59          * let it pick anything as it may pick something the guest
60          * can't address (i.e. outside EPT range). */
61
62         /* Allocate 2 pages for page table pages: a page of
63          * 512 GiB PTEs with only one entry filled to point to
64          * a page of 1 GiB PTEs; a page of 1 GiB PTEs with
65          * only one entry filled. */
66
67         p512 = page((void *)0x1000000, 2);
68         if (!p512) {
69                 werrstr("page table allocation failed: %r\n");
70                 return -1;
71         }
72         p1 = &p512[512];
73         vm->root = p512;
74
75         /* Set up a 1:1 ("identity") page mapping from host
76          * virtual to guest physical for 1 GiB.  This mapping
77          * is used unless the guest (e.g. Linux) sets up its
78          * own page tables. Be aware that the values stored in
79          * the table are physical addresses.  This is subtle
80          * and mistakes are easily disguised due to the
81          * identity mapping, so take care when manipulating
82          * these mappings. Note: we don't yet have symbols for
83          * "start of virtual address common to host and guest"
84          * so we just use  the first GiB for now. */
85         p512[PML4(0x400000)] = (uint64_t)p1 | PTE_KERN_RW;
86         p1[PML3(0x400000)] = PTE_PS | PTE_KERN_RW;
87
88         /* technically, we don't need these pages for the
89          * all guests. Currently, the kernel requires them. */
90         for (i = 0; i < vm->nr_gpcs; i++) {
91                 p = page(NULL, 3);
92                 if (!p) {
93                         werrstr("Can't allocate 3 pages for guest %d: %r", i);
94                         return -1;
95                 }
96                 vm->gpcis[i].posted_irq_desc = &p[0];
97                 vm->gpcis[i].vapic_addr = &p[4096];
98                 vm->gpcis[i].apic_addr = &p[8192];
99         }
100
101         ret = vmm_init(vm, flags);
102         if (ret)
103                 return ret;
104
105         for (i = 0; i < vm->nr_gpcs; i++) {
106                 vm_tf = gth_to_vmtf(vm->gths[i]);
107                 vm_tf->tf_cr3 = (uint64_t)p512;
108         }
109         vm->vminit = 1;
110
111         return 0;
112 }
113
114 /* vthread_addr sets up a virtual_machine struct such that functions
115  * can start up VM guests.  It is like pthread_attr in that it sets up
116  * default attributes and can be used in vthread_create calls. If
117  * vm->nrgpcs is not set then the vm will be set up for 1 guest. */
118 int vthread_attr_init(struct virtual_machine *vm, int vmmflags)
119 {
120         return vmsetup(vm, vmmflags);
121 }
122
123 /* vthread_attr_kernel_init sets up minimum basic attributes for
124  * running a kernel, as opposed to just user mode.  This setup
125  * includes an APIC page at 0xfee00000, to be shared by all cores. */
126 int vthread_attr_kernel_init(struct virtual_machine *vm, int vmmflags)
127 {
128         int ret;
129         int i;
130         uint32_t *apic;
131
132         ret = vmsetup(vm, vmmflags);
133         if (ret)
134                 return ret;
135
136         for (i = 0; i < vm->nr_gpcs; i++) {
137                 apic = vm->gpcis[i].apic_addr;
138                 apic[0x30 / 4] = 0x01060015;
139         }
140         return 0;
141 }
142
143 #define DEFAULT_STACK_SIZE 65536
144 /* vthread_create creates and starts a VM guest. The interface is intended
145  * to be as much like pthread_create as possible. */
146 int vthread_create(struct virtual_machine *vm, int guest, void *rip, void *arg)
147 {
148         struct vm_trapframe *vm_tf;
149         int ret;
150         uint64_t *stack, *tos;
151
152         if (!vm->vminit) {
153                 return -EAGAIN;
154         }
155
156         if (guest > vm->nr_gpcs)
157                 return -ENOENT;
158
159         vm_tf = gth_to_vmtf(vm->gths[guest]);
160
161         /* For now we make the default VM stack pretty small.
162          * We can grow it as needed. */
163         if (!vm_tf->tf_rsp) {
164                 ret = posix_memalign((void **)&stack, 4096, DEFAULT_STACK_SIZE);
165                 if (ret)
166                         return ret;
167                 /* touch the top word on the stack so we don't page fault
168                  * on that in the VM. */
169                 tos = &stack[DEFAULT_STACK_SIZE/sizeof(uint64_t) - 1];
170                 *tos = 0;
171                 vm_tf->tf_rsp = (uint64_t) tos;
172         }
173         vm_tf->tf_rip = (uint64_t)rip;
174         vm_tf->tf_rdi = (uint64_t)arg;
175         start_guest_thread(vm->gths[guest]);
176         return 0;
177 }