vmm: Allow the user to set GUEST_FS/GS_BASE (XCC)
[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  * and vthread_attr_kernel_init. */
28 static int vmsetup(struct virtual_machine *vm, int flags)
29 {
30         struct vm_trapframe *vm_tf;
31         int i, ret;
32         uint8_t *p;
33
34         if (vm->vminit)
35                 return -EBUSY;
36
37         if (vm->nr_gpcs == 0)
38                 vm->nr_gpcs = 1;
39
40         vm->gpcis = calloc(vm->nr_gpcs, sizeof(*vm->gpcis));
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                 vm->gpcis[i].posted_irq_desc = &p[0];
51                 vm->gpcis[i].vapic_addr = &p[4096];
52                 vm->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                 vm->gpcis[i].fsbase = 0;
56                 vm->gpcis[i].gsbase = 0;
57         }
58
59         /* Set up default page mappings. */
60         setup_paging(vm);
61
62         ret = vmm_init(vm, flags);
63         if (ret)
64                 return ret;
65
66         for (i = 0; i < vm->nr_gpcs; i++) {
67                 vm->gths[i]->halt_exit = vm->halt_exit;
68                 vm_tf = gth_to_vmtf(vm->gths[i]);
69                 vm_tf->tf_cr3 = (uint64_t) vm->root;
70         }
71         vm->vminit = 1;
72
73         return 0;
74 }
75
76 /* vthread_addr sets up a virtual_machine struct such that functions
77  * can start up VM guests.  It is like pthread_attr in that it sets up
78  * default attributes and can be used in vthread_create calls. If
79  * vm->nrgpcs is not set then the vm will be set up for 1 guest. */
80 int vthread_attr_init(struct virtual_machine *vm, int vmmflags)
81 {
82         return vmsetup(vm, vmmflags);
83 }
84
85 /* vthread_attr_kernel_init sets up minimum basic attributes for
86  * running a kernel, as opposed to just user mode.  This setup
87  * includes an APIC page at 0xfee00000, to be shared by all cores. */
88 int vthread_attr_kernel_init(struct virtual_machine *vm, int vmmflags)
89 {
90         int ret;
91         int i;
92         uint32_t *apic;
93
94         ret = vmsetup(vm, vmmflags);
95         if (ret)
96                 return ret;
97
98         for (i = 0; i < vm->nr_gpcs; i++) {
99                 apic = vm->gpcis[i].apic_addr;
100                 apic[0x30 / 4] = 0x01060015;
101         }
102         return 0;
103 }
104
105 #define DEFAULT_STACK_SIZE 65536
106 /* vthread_create creates and starts a VM guest. The interface is intended
107  * to be as much like pthread_create as possible. */
108 int vthread_create(struct virtual_machine *vm, int guest, void *rip, void *arg)
109 {
110         struct vm_trapframe *vm_tf;
111         int ret;
112         uint64_t *stack, *tos;
113
114         if (!vm->vminit) {
115                 return -EAGAIN;
116         }
117
118         if (guest > vm->nr_gpcs)
119                 return -ENOENT;
120
121         vm_tf = gth_to_vmtf(vm->gths[guest]);
122
123         /* For now we make the default VM stack pretty small.
124          * We can grow it as needed. */
125         if (!vm_tf->tf_rsp) {
126                 ret = posix_memalign((void **)&stack, 4096, DEFAULT_STACK_SIZE);
127                 if (ret)
128                         return ret;
129                 /* touch the top word on the stack so we don't page fault
130                  * on that in the VM. */
131                 tos = &stack[DEFAULT_STACK_SIZE/sizeof(uint64_t) - 1];
132                 *tos = 0;
133                 vm_tf->tf_rsp = (uint64_t) tos;
134         }
135         vm_tf->tf_rip = (uint64_t)rip;
136         vm_tf->tf_rdi = (uint64_t)arg;
137         start_guest_thread(vm->gths[guest]);
138         return 0;
139 }