vmm: Moves gpci into guest_thread
[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 | MAP_PRIVATE;
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 static int vmsetup(struct virtual_machine *vm, int flags)
28 {
29         struct vm_trapframe *vm_tf;
30         int i, ret;
31         uint8_t *p;
32         struct vmm_gpcore_init *gpcis;
33
34         if (vm->vminit)
35                 return -EBUSY;
36
37         if (vm->nr_gpcs == 0)
38                 vm->nr_gpcs = 1;
39
40         gpcis = calloc(vm->nr_gpcs, sizeof(struct vmm_gpcore_init));
41
42         /* technically, we don't need these pages for the
43          * all guests. Currently, the kernel requires them. */
44         for (i = 0; i < vm->nr_gpcs; i++) {
45                 p = page(NULL, 3);
46                 if (!p) {
47                         werrstr("Can't allocate 3 pages for guest %d: %r", i);
48                         return -1;
49                 }
50                 gpcis[i].posted_irq_desc = &p[0];
51                 gpcis[i].vapic_addr = &p[4096];
52                 gpcis[i].apic_addr = &p[8192];
53                 /* TODO: once we are making these GPCs at the same time as vthreads, we
54                  * should set fsbase == the TLS desc of the vthread (if any). */
55                 gpcis[i].fsbase = 0;
56                 gpcis[i].gsbase = 0;
57         }
58
59         /* Set up default page mappings. */
60         setup_paging(vm);
61
62         ret = vmm_init(vm, gpcis, flags);
63         if (ret)
64                 return ret;
65         free(gpcis);
66
67         for (i = 0; i < vm->nr_gpcs; i++) {
68                 vm->gths[i]->halt_exit = vm->halt_exit;
69                 vm_tf = gth_to_vmtf(vm->gths[i]);
70                 vm_tf->tf_cr3 = (uint64_t) vm->root;
71         }
72         vm->vminit = 1;
73
74         return 0;
75 }
76
77 /* vthread_addr sets up a virtual_machine struct such that functions
78  * can start up VM guests.  It is like pthread_attr in that it sets up
79  * default attributes and can be used in vthread_create calls. If
80  * vm->nrgpcs is not set then the vm will be set up for 1 guest. */
81 int vthread_attr_init(struct virtual_machine *vm, int vmmflags)
82 {
83         return vmsetup(vm, vmmflags);
84 }
85
86 #define DEFAULT_STACK_SIZE 65536
87 /* vthread_create creates and starts a VM guest. The interface is intended
88  * to be as much like pthread_create as possible. */
89 int vthread_create(struct virtual_machine *vm, int guest, void *rip, void *arg)
90 {
91         struct vm_trapframe *vm_tf;
92         int ret;
93         uint64_t *stack, *tos;
94
95         if (!vm->vminit) {
96                 return -EAGAIN;
97         }
98
99         if (guest > vm->nr_gpcs)
100                 return -ENOENT;
101
102         vm_tf = gth_to_vmtf(vm->gths[guest]);
103
104         /* For now we make the default VM stack pretty small.
105          * We can grow it as needed. */
106         if (!vm_tf->tf_rsp) {
107                 ret = posix_memalign((void **)&stack, 4096, DEFAULT_STACK_SIZE);
108                 if (ret)
109                         return ret;
110                 /* touch the top word on the stack so we don't page fault
111                  * on that in the VM. */
112                 tos = &stack[DEFAULT_STACK_SIZE/sizeof(uint64_t) - 1];
113                 *tos = 0;
114                 vm_tf->tf_rsp = (uint64_t) tos;
115         }
116         vm_tf->tf_rip = (uint64_t)rip;
117         vm_tf->tf_rdi = (uint64_t)arg;
118         start_guest_thread(vm->gths[guest]);
119         return 0;
120 }