ab92c313eb37ee7f05046ee4671a99728962e60c
[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 #include <vmm/vthread.h>
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 static void vmsetup(void *arg)
27 {
28         struct virtual_machine *vm = (struct virtual_machine *)arg;
29         struct vm_trapframe *vm_tf;
30         int i, ret;
31         uint8_t *p;
32         struct vmm_gpcore_init *gpcis;
33
34         if (vm->nr_gpcs == 0)
35                 vm->nr_gpcs = 1;
36
37         gpcis = calloc(vm->nr_gpcs, sizeof(struct vmm_gpcore_init));
38
39         /* technically, we don't need these pages for the
40          * all guests. Currently, the kernel requires them. */
41         for (i = 0; i < vm->nr_gpcs; i++) {
42                 p = page(NULL, 3);
43                 if (!p)
44                         panic("Can't allocate 3 pages for guest %d: %r", i);
45                 gpcis[i].posted_irq_desc = &p[0];
46                 gpcis[i].vapic_addr = &p[4096];
47                 gpcis[i].apic_addr = &p[8192];
48                 /* TODO: once we are making these GPCs at the same time as vthreads, we
49                  * should set fsbase == the TLS desc of the vthread (if any). */
50                 gpcis[i].fsbase = 0;
51                 gpcis[i].gsbase = 0;
52         }
53
54         /* Set up default page mappings. */
55         setup_paging(vm);
56
57         ret = vmm_init(vm, gpcis, 0);
58         assert(!ret);
59         free(gpcis);
60
61         for (i = 0; i < vm->nr_gpcs; i++) {
62                 vm_tf = gpcid_to_vmtf(vm, i);
63                 vm_tf->tf_cr3 = (uint64_t) vm->root;
64         }
65 }
66
67 struct vthread *vthread_alloc(struct virtual_machine *vm, int guest)
68 {
69         static parlib_once_t once = PARLIB_ONCE_INIT;
70
71         parlib_run_once(&once, vmsetup, vm);
72
73         if (guest > vm->nr_gpcs)
74                 return NULL;
75         return (struct vthread*)gpcid_to_gth(vm, guest);
76 }
77
78 /* TODO: this is arch specific */
79 void vthread_init_ctx(struct vthread *vth, uintptr_t entry_pt, uintptr_t arg,
80                       uintptr_t stacktop)
81 {
82         struct vm_trapframe *vm_tf = vth_to_vmtf(vth);
83
84         vm_tf->tf_rip = entry_pt;
85         vm_tf->tf_rdi = arg;
86         vm_tf->tf_rsp = stacktop;
87 }
88
89 void vthread_run(struct vthread *vthread)
90 {
91         start_guest_thread((struct guest_thread*)vthread);
92 }
93
94 #define DEFAULT_STACK_SIZE 65536
95 static uintptr_t alloc_stacktop(void)
96 {
97         int ret;
98         uintptr_t *stack, *tos;
99
100         ret = posix_memalign((void **)&stack, PGSIZE, DEFAULT_STACK_SIZE);
101         if (ret)
102                 return 0;
103         /* touch the top word on the stack so we don't page fault
104          * on that in the VM. */
105         tos = &stack[DEFAULT_STACK_SIZE / sizeof(uint64_t) - 1];
106         *tos = 0;
107         return (uintptr_t)tos;
108 }
109
110 static uintptr_t vth_get_stack(struct vthread *vth)
111 {
112         struct guest_thread *gth = (struct guest_thread*)vth;
113         struct vthread_info *info = (struct vthread_info*)gth->user_data;
114         uintptr_t stacktop;
115
116         if (info) {
117                 assert(info->stacktop);
118                 return info->stacktop;
119         }
120         stacktop = alloc_stacktop();
121         assert(stacktop);
122         /* Yes, an evil part of me thought of using the top of the stack for this
123          * struct's storage. */
124         gth->user_data = malloc(sizeof(struct vthread_info));
125         assert(gth->user_data);
126         info = (struct vthread_info*)gth->user_data;
127         info->stacktop = stacktop;
128         return stacktop;
129 }
130
131 struct vthread *vthread_create(struct virtual_machine *vm, int guest,
132                                void *entry, void *arg)
133 {
134         struct vthread *vth;
135
136         vth = vthread_alloc(vm, guest);
137         if (!vth)
138                 return NULL;
139         vthread_init_ctx(vth, (uintptr_t)entry, (uintptr_t)arg, vth_get_stack(vth));
140         vthread_run(vth);
141         return vth;
142 }