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