Allow uth_disable_notifs without current_uthread
[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->gths[i]->halt_exit = vm->halt_exit;
107                 vm_tf = gth_to_vmtf(vm->gths[i]);
108                 vm_tf->tf_cr3 = (uint64_t)p512;
109         }
110         vm->vminit = 1;
111
112         return 0;
113 }
114
115 /* vthread_addr sets up a virtual_machine struct such that functions
116  * can start up VM guests.  It is like pthread_attr in that it sets up
117  * default attributes and can be used in vthread_create calls. If
118  * vm->nrgpcs is not set then the vm will be set up for 1 guest. */
119 int vthread_attr_init(struct virtual_machine *vm, int vmmflags)
120 {
121         return vmsetup(vm, vmmflags);
122 }
123
124 /* vthread_attr_kernel_init sets up minimum basic attributes for
125  * running a kernel, as opposed to just user mode.  This setup
126  * includes an APIC page at 0xfee00000, to be shared by all cores. */
127 int vthread_attr_kernel_init(struct virtual_machine *vm, int vmmflags)
128 {
129         int ret;
130         int i;
131         uint32_t *apic;
132
133         ret = vmsetup(vm, vmmflags);
134         if (ret)
135                 return ret;
136
137         for (i = 0; i < vm->nr_gpcs; i++) {
138                 apic = vm->gpcis[i].apic_addr;
139                 apic[0x30 / 4] = 0x01060015;
140         }
141         return 0;
142 }
143
144 #define DEFAULT_STACK_SIZE 65536
145 /* vthread_create creates and starts a VM guest. The interface is intended
146  * to be as much like pthread_create as possible. */
147 int vthread_create(struct virtual_machine *vm, int guest, void *rip, void *arg)
148 {
149         struct vm_trapframe *vm_tf;
150         int ret;
151         uint64_t *stack, *tos;
152
153         if (!vm->vminit) {
154                 return -EAGAIN;
155         }
156
157         if (guest > vm->nr_gpcs)
158                 return -ENOENT;
159
160         vm_tf = gth_to_vmtf(vm->gths[guest]);
161
162         /* For now we make the default VM stack pretty small.
163          * We can grow it as needed. */
164         if (!vm_tf->tf_rsp) {
165                 ret = posix_memalign((void **)&stack, 4096, DEFAULT_STACK_SIZE);
166                 if (ret)
167                         return ret;
168                 /* touch the top word on the stack so we don't page fault
169                  * on that in the VM. */
170                 tos = &stack[DEFAULT_STACK_SIZE/sizeof(uint64_t) - 1];
171                 *tos = 0;
172                 vm_tf->tf_rsp = (uint64_t) tos;
173         }
174         vm_tf->tf_rip = (uint64_t)rip;
175         vm_tf->tf_rdi = (uint64_t)arg;
176         start_guest_thread(vm->gths[guest]);
177         return 0;
178 }