vmm: Added helper functions to user/vmm/memory.c
[akaros.git] / tests / dune / dune.c
1 #include <stdio.h>
2 #include <pthread.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <fcntl.h>
6 #include <parlib/arch/arch.h>
7 #include <parlib/ros_debug.h>
8 #include <unistd.h>
9 #include <gelf.h>
10 #include <errno.h>
11 #include <libelf.h>
12 #include <dirent.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <ros/syscall.h>
16 #include <sys/mman.h>
17 #include <vmm/vmm.h>
18 #include <vmm/acpi/acpi.h>
19 #include <vmm/acpi/vmm_simple_dsdt.h>
20 #include <ros/arch/mmu.h>
21 #include <ros/arch/membar.h>
22 #include <ros/vmm.h>
23 #include <parlib/uthread.h>
24 #include <vmm/linux_bootparam.h>
25 #include <getopt.h>
26 #include <iplib/iplib.h>
27 #include <vmm/sched.h>
28 #include <sys/eventfd.h>
29 #include <sys/uio.h>
30 #include <err.h>
31 #include <vmm/linuxemu.h>
32 #include <vmm/vmm.h>
33
34 struct vmm_gpcore_init gpci;
35 bool linuxemu(struct guest_thread *gth, struct vm_trapframe *tf);
36
37
38 extern char **environ;
39
40 static struct virtual_machine vm = {.halt_exit = true,};
41
42 static unsigned long long memsize = GiB;
43 static uintptr_t memstart = MinMemory;
44
45 static int dune_debug;
46
47 static void hlt(void)
48 {
49         __asm__ __volatile__("\thlt\n\t");
50 }
51
52 static int pc(char *c)
53 {
54         __asm__ __volatile__("movq $1, %%rax\n"
55                              "movq $1, %%rdi\n"
56                              "movq %0, %%rsi\n"
57                              "movq $1, %%rdx\n"
58                              "vmcall\n" ::
59                              "m"(c) : "rdi", "rax", "rsi", "rdx");
60         return 0;
61 }
62
63 static void xnum(uint64_t x)
64 {
65         static char *hex = "0123456789abcdef";
66
67         for (int i = 0; i < 8; i++) {
68                 uint8_t v = ((uint8_t*)&x)[7 - i];
69                 pc(&hex[v >> 4]);
70                 pc(&hex[v & 0xf]);
71         }
72 }
73
74 static void show(char *s)
75 {
76         static char *showedoff = "NULL POINTER: That's bad.\n";
77
78         if (!s) {
79                 show(showedoff);
80                 return;
81         }
82         while (*s) {
83                 pc(s);
84                 s++;
85         }
86 }
87
88 /* This is a small test that runs in gr0 and tests our argument setup.
89  * This test can grow in capability as we find more broken bits in our
90  * dune-like environment. */
91
92 void dune_test(void *stack)
93 {
94         show("Hello this is dune's test\n");
95
96         int argc;
97         char **argv;
98         struct elf_aux *auxv;
99
100         show("dune_test: dumping argv, env, and aux\n");
101
102         argc = *((uint64_t*)stack);
103         argv = &((char**)stack)[1];
104         show("argc: "); xnum(argc); show("\n");
105         show("argv: "); xnum((uint64_t)argv); show("\n");
106
107         for (int i = 0; i < argc; i++, argv++) {
108                 show("arg["); xnum(i); show("]:");
109                 show(argv[0]);
110                 show("\n");
111         }
112         // skip the null and move on to envp.
113         argv++;
114         for (int i = 0; argv[0]; i++, argv++) {
115                 show("env["); xnum(i); show("]:");
116                 show(argv[0]);
117                 show("\n");
118         }
119         // skip the null and move on to auxv.
120         argv++;
121         auxv = (void *)argv;
122         for (int i = 0; auxv[i].v[0]; i++) {
123                 show("auxv["); xnum(i); show("]:");
124                 xnum(auxv[i].v[0]); show(":");
125                 xnum(auxv[i].v[1]); show("\n");
126         }
127         show("Done dumping [argv, env, auxv]\n");
128         show("Testing syscall extensions\n");
129         __asm__ __volatile__("movq $400, %%rax\n"
130                              "vmcall\n" :: );
131         hlt();
132 }
133
134 static struct option long_options[] = {
135         {"aux",           required_argument, 0, 'a'},
136         {"debug",         no_argument,       0, 'd'},
137         {"vmmflags",      required_argument, 0, 'v'},
138         {"memsize",       required_argument, 0, 'm'},
139         {"memstart",      required_argument, 0, 'M'},
140         {"cmdline_extra", required_argument, 0, 'c'},
141         {"greedy",        no_argument,       0, 'g'},
142         {"scp",           no_argument,       0, 's'},
143         {"test",          no_argument,       0, 't'},
144         {"help",          no_argument,       0, 'h'},
145         {0, 0, 0, 0}
146 };
147
148 static void
149 usage(void)
150 {
151         // Sadly, the getopt_long struct does
152         // not have a pointer to help text.
153         fprintf(stderr,
154               "Usage: dune [options] <ELF file] [<ELF file>...]\n");
155         fprintf(stderr,
156               "Or for testing: dune -t [options]\nOptions are:\n");
157         for (int i = 0;
158              i < COUNT_OF(long_options) - 1;
159              i++) {
160                 struct option *l = &long_options[i];
161
162                 fprintf(stderr, "%s or %c%s\n", l->name, l->val,
163                         l->has_arg ? " <arg>" : "");
164         }
165         exit(0);
166 }
167
168 static struct elf_aux *
169 getextra(int *auxc, char *_s)
170 {
171         struct elf_aux *auxv;
172         char *s = strdup(_s);
173         // icky hardcode, but realistic.
174         char *auxpairs[32];
175
176         *auxc = gettokens(s, auxpairs, 32, ",");
177         if (dune_debug)
178                 fprintf(stderr, "Found %d extra aux pairs\n", *auxc);
179         if (*auxc < 1)
180                 return NULL;
181         auxv = malloc(sizeof(*auxv) * *auxc);
182         if (!auxv)
183                 errx(1, "auxv malloc: %r");
184         for (int i = 0; i < *auxc; i++) {
185                 char *aux[2];
186                 int j;
187                 uint32_t t, v;
188
189                 j = gettokens(auxpairs[i], aux, 2, "=");
190                 if (j < 2) {
191                         fprintf(stderr, "%s: should be in the form type=val\n",
192                                 auxpairs[i]);
193                         free(auxv);
194                         return NULL;
195                 }
196                 t = strtoul(aux[0], 0, 0);
197                 v = strtoul(aux[1], 0, 0);
198                 auxv[i].v[0] = t;
199                 auxv[i].v[1] = v;
200                 if (dune_debug)
201                         fprintf(stderr, "Adding aux pair 0x%x:0x%x\n", auxv[i].v[0],
202                                 auxv[i].v[1]);
203         }
204         return auxv;
205
206 }
207
208 static struct elf_aux *
209 buildaux(struct elf_aux *base, int basec, struct elf_aux *extra, int extrac)
210 {
211         int total = basec + extrac;
212         struct elf_aux *ret;
213
214         ret = realloc(extra, total * sizeof(*ret));
215         if (!ret)
216                 return NULL;
217
218         if (dune_debug)
219                 fprintf(stderr, "buildaux: consolidating %d aux and %d extra\n",
220                         basec, extrac);
221         /* TOOD: check for dups. */
222         if (basec)
223                 memmove(&ret[extrac], base, sizeof(*base)*basec);
224         return ret;
225 }
226
227 int main(int argc, char **argv)
228 {
229         void *tos;
230         int envc, auxc, extrac = 0;
231         struct elf_aux *auxv, *extra = NULL;
232         int vmmflags = 0;
233         uint64_t entry = 0;
234         int ret;
235         struct vm_trapframe *vm_tf;
236         int c;
237         int test = 0;
238         int option_index;
239         int ac = argc;
240         char **av = argv;
241
242         fprintf(stderr, "%p %p %p %p\n", PGSIZE, PGSHIFT, PML1_SHIFT,
243                 PML1_PTE_REACH);
244
245         if ((uintptr_t)__procinfo.program_end >= MinMemory) {
246                 fprintf(stderr,
247                         "Panic: vmrunkernel binary extends into guest memory\n");
248                 exit(1);
249         }
250
251         while ((c = getopt_long(argc, argv, "a:dv:m:M:gsth", long_options,
252                                 &option_index)) != -1) {
253                 switch (c) {
254                 case 'a':
255                         extra = getextra(&extrac, optarg);
256                         if (dune_debug)
257                                 fprintf(stderr, "Added %d aux items\n", extrac);
258                         break;
259                 case 'd':
260                         fprintf(stderr, "SET DEBUG\n");
261                         dune_debug++;
262                         break;
263                 case 'v':
264                         vmmflags = strtoull(optarg, 0, 0);
265                         break;
266                 case 'm':
267                         memsize = strtoull(optarg, 0, 0);
268                         break;
269                 case 'M':
270                         memstart = strtoull(optarg, 0, 0);
271                         break;
272                 case 'g':       /* greedy */
273                         parlib_never_yield = TRUE;
274                         break;
275                 case 's':       /* scp */
276                         parlib_wants_to_be_mcp = FALSE;
277                         break;
278                 case 't':
279                         test = 1;
280                         break;
281                 case 'h':
282                 default:
283                         usage();
284                         break;
285                 }
286         }
287         argc -= optind;
288         argv += optind;
289         if ((!test) && (argc < 1)) {
290                 usage();
291         }
292
293         init_lemu_logging(dune_debug);
294         init_syscall_table();
295
296         if ((uintptr_t)(memstart + memsize) >= (uintptr_t)BRK_START) {
297                 fprintf(stderr,
298                         "memstart 0x%lx memsize 0x%lx -> 0x%lx is too large; overlaps BRK_START at %p\n",
299                         memstart, memsize, memstart + memsize, BRK_START);
300                 exit(1);
301         }
302
303         mmap_memory(&vm, memstart, memsize);
304
305         if (dune_debug)
306                 fprintf(stderr, "mmap guest physical memory at %p for 0x%lx bytes\n",
307                         memstart, memsize);
308
309         // TODO: find out why we can't use memstart + memsize as TOS.
310         tos = (void *)(memstart + 0x800000);
311
312         for (envc = 0; environ[envc]; envc++)
313                 ;
314         auxv = (struct elf_aux *)&environ[envc+1];
315         for (auxc = 0; auxv[auxc].v[0]; auxc++)
316                 ;
317         auxv = buildaux(auxv, auxc, extra, extrac);
318         if (!auxv) {
319                 fprintf(stderr, "Can't build auxv: %r");
320                 exit(1);
321         }
322         auxc = auxc + extrac;
323
324         if (!test) {
325                 entry = load_elf(argv[0], MinMemory, NULL, NULL);
326                 if (entry == 0) {
327                         fprintf(stderr, "Unable to load kernel %s\n", argv[0]);
328                         exit(1);
329                 }
330         } else {
331                 fprintf(stderr, "Running dune test\n");
332                 entry = (uintptr_t) dune_test;
333         }
334         if (dune_debug)
335                 fprintf(stderr, "Test: Populate stack at %p\n", tos);
336         tos = populate_stack(tos, ac, av, envc, environ, auxc, auxv);
337         if (dune_debug)
338                 fprintf(stderr, "populated stack at %p; argc %d, envc %d, auxc %d\n",
339                         tos, ac, envc, auxc);
340
341         ret = vthread_attr_init(&vm, vmmflags);
342         if (ret) {
343                 fprintf(stderr, "vmm_init failed: %r\n");
344                 exit(1);
345         }
346
347         vm.gths[0]->vmcall = linuxemu;
348         vm_tf = gth_to_vmtf(vm.gths[0]);
349
350         /* we can't use the default stack since we set one up
351          * ourselves. */
352         vm_tf->tf_rsp = (uint64_t)tos;
353         if (dune_debug)
354                 fprintf(stderr, "stack is %p\n", tos);
355
356         vthread_create(&vm, 0, (void *)entry, tos);
357
358         uthread_sleep_forever();
359         return 0;
360 }