dune: working test with emulated syscalls
[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
32 struct vmm_gpcore_init gpci;
33 bool linuxemu(struct guest_thread *gth, struct vm_trapframe *tf);
34
35 /* ah, elf. */
36 struct elf_aux {
37         unsigned long v[2];
38 };
39
40 extern char **environ;
41
42 static struct virtual_machine vm = {.halt_exit = true,};
43
44 static unsigned long long memsize = GiB;
45 static uintptr_t memstart = MinMemory;
46
47 static int debug;
48
49 static void hlt(void)
50 {
51         __asm__ __volatile__("\thlt\n\t");
52 }
53
54 static int pc(uint16_t c)
55 {
56         __asm__ __volatile__("movw $0, %%rax\nmovw %0, %%di\n\tvmcall\n" ::
57                              "m"(c) : "rdi", "rax");
58         return 0;
59 }
60
61 static void xnum(uint64_t x)
62 {
63         static char *hex = "0123456789abcdef";
64
65         for (int i = 0; i < 8; i++) {
66                 uint8_t v = ((uint8_t*)&x)[7 - i];
67
68                 pc(hex[v >> 4]);
69                 pc(hex[v & 0xf]);
70         }
71 }
72
73 static void show(char *s)
74 {
75         static char *showedoff = "NULL POINTER: That's bad.\n";
76
77         if (!s) {
78                 show(showedoff);
79                 return;
80         }
81         while (*s) {
82         //      xnum((uint64_t)s);pc(';');
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 void dune_test(void *stack)
92 {
93         int argc;
94         char **argv;
95         struct elf_aux *auxv;
96
97         show("dune_test: dumping argv, env, and aux\n");
98
99         argc = *((uint64_t*)stack);
100         argv = &((char**)stack)[1];
101         show("argc: "); xnum(argc); show("\n");
102         show("argv: "); xnum((uint64_t)argv); show("\n");
103
104         for (int i = 0; i < argc; i++, argv++) {
105                 show("arg["); xnum(i); show("]:");
106                 show(argv[0]);
107                 show("\n");
108         }
109         // skip the null and move on to envp.
110         argv++;
111         for (int i = 0; argv[0]; i++, argv++) {
112                 show("env["); xnum(i); show("]:");
113                 show(argv[0]);
114                 show("\n");
115         }
116         // skip the null and move on to auxv.
117         argv++;
118         auxv = (void *)argv;
119         for (int i = 0; auxv[i].v[0]; i++) {
120                 show("auxv["); xnum(i); show("]:");
121                 xnum(auxv[i].v[0]); show(":");
122                 xnum(auxv[i].v[1]); show("\n");
123         }
124         show("Done dumping [argv, env, auxv]\n");
125         __asm__ __volatile__("movl $0xdeadbeef, %eax\nmovq $1, %rdi\nmov $2,"
126                              "%rsi\nmovq $3, %rdx\n"
127                              "movq $4,%rcx\nmovq $5, %r8\nmovq $6,%r9\nvmcall\n");
128         hlt();
129 }
130
131 /* populate_stack fills the stack with an argv, envp, and auxv.
132  * We assume the stack pointer is backed by real memory.
133  * It will go hard with you if it does not. For your own health,
134  * stack should be 16-byte aligned. */
135 void *populate_stack(uintptr_t *stack, int argc, char *argv[],
136                          int envc, char *envp[],
137                          int auxc, struct elf_aux auxv[])
138 {
139         /* Function to get the lengths of the argument and environment strings. */
140         int get_lens(int argc, char *argv[], int arg_lens[])
141         {
142                 int total = 0;
143
144                 if (!argc)
145                         return 0;
146                 for (int i = 0; i < argc; i++) {
147                         arg_lens[i] = strlen(argv[i]) + 1;
148                         total += arg_lens[i];
149                 }
150                 return total;
151         }
152
153         /* Function to help map the argument and environment strings, to their
154          * final location. */
155         int remap(int argc, char *argv[], char *new_argv[],
156               char new_argbuf[], int arg_lens[])
157         {
158                 int offset = 0;
159
160                 if (!argc)
161                         return 0;
162                 for (int i = 0; i < argc; i++) {
163                         memcpy(new_argbuf + offset, argv[i], arg_lens[i]);
164                         if (debug) {
165                                 fprintf(stderr, "data: memcpy(%p, %p, %ld)\n",
166                                         new_argbuf + offset, argv[i], arg_lens[i]);
167                                 fprintf(stderr, "arg: set arg %d, @%p, to %p\n", i,
168                                         &new_argv[i], new_argbuf + offset);
169                         }
170                         new_argv[i] = new_argbuf + offset;
171                         offset += arg_lens[i];
172                 }
173                 new_argv[argc] = NULL;
174                 return offset;
175         }
176
177         /* Start tracking the size of the buffer necessary to hold all of our data
178          * on the stack. Preallocate space for argc, argv, envp, and auxv in this
179          * buffer. */
180         int bufsize = 0;
181
182         bufsize += 1 * sizeof(size_t);
183         bufsize += (auxc + 1) * sizeof(struct elf_aux);
184         bufsize += (envc + 1) * sizeof(char**);
185         bufsize += (argc + 1) * sizeof(char**);
186         if (debug)
187                 fprintf(stderr, "Bufsize for pointers and argc is %d\n", bufsize);
188
189         /* Add in the size of the env and arg strings. */
190         int arg_lens[argc];
191         int env_lens[envc];
192
193         bufsize += get_lens(argc, argv, arg_lens);
194         bufsize += get_lens(envc, envp, env_lens);
195         if (debug)
196                 fprintf(stderr, "Bufsize for pointers, argc, and strings is %d\n",
197                         bufsize);
198
199         /* Adjust bufsize so that our buffer will ultimately be 16 byte aligned. */
200         bufsize = (bufsize + 15) & ~0xf;
201         if (debug)
202                 fprintf(stderr,
203                         "Bufsize for pointers, argc, and strings is rounded is %d\n",
204                         bufsize);
205
206         /* Set up pointers to all of the appropriate data regions we map to. */
207         size_t *new_argc = (size_t*)((uint8_t*)stack - bufsize);
208         char **new_argv = (char**)(new_argc + 1);
209         char **new_envp = new_argv + argc + 1;
210         struct elf_aux *new_auxv = (struct elf_aux*)(new_envp + envc + 1);
211         char *new_argbuf = (char*)(new_auxv + auxc + 1);
212
213         if (debug) {
214                 fprintf(stderr, "There are %d args, %d env, and %d aux\n", new_argc,
215                         envc, auxc);
216                 fprintf(stderr, "Locations: argc: %p, argv: %p, envp: %p, auxv: %p\n",
217                                 new_argc, new_argv, new_envp, new_auxv);
218                 fprintf(stderr, "Locations: argbuf: %p, ", new_argbuf);
219                 fprintf(stderr, "Sizeof argc is %d\n", sizeof(size_t));
220         }
221         /* Map argc into its final location. */
222         *new_argc = argc;
223
224         /* Map all data for argv and envp into its final location. */
225         int offset = 0;
226
227         offset = remap(argc, argv, new_argv, new_argbuf, arg_lens);
228         if (offset == -1)
229                 return 0;
230         if (debug) {
231                 fprintf(stderr, "Locations: argbuf: %p, envbuf: %p, ", new_argbuf,
232                         new_argbuf + offset);
233
234         }
235         offset = remap(envc, envp, new_envp, new_argbuf + offset, env_lens);
236         if (offset == -1)
237                 return 0;
238
239         /* Map auxv into its final location. */
240         struct elf_aux null_aux = {0, 0};
241
242         memcpy(new_auxv, auxv, auxc * sizeof(struct elf_aux));
243         memcpy(new_auxv + auxc, &null_aux, sizeof(struct elf_aux));
244         if (debug) {
245                 fprintf(stderr, "auxbuf: %p\n", new_auxv);
246                 hexdump(stdout, new_auxv, auxc * sizeof(struct elf_aux));
247         }
248         return (uint8_t*)stack - bufsize;
249 }
250
251 static struct option long_options[] = {
252         {"aux",           required_argument, 0, 'a'},
253         {"debug",         no_argument,       0, 'd'},
254         {"vmmflags",      required_argument, 0, 'v'},
255         {"memsize",       required_argument, 0, 'm'},
256         {"memstart",      required_argument, 0, 'M'},
257         {"cmdline_extra", required_argument, 0, 'c'},
258         {"greedy",        no_argument,       0, 'g'},
259         {"scp",           no_argument,       0, 's'},
260         {"test",          no_argument,       0, 't'},
261         {"help",          no_argument,       0, 'h'},
262         {0, 0, 0, 0}
263 };
264
265 static void
266 usage(void)
267 {
268         // Sadly, the getopt_long struct does
269         // not have a pointer to help text.
270         fprintf(stderr,
271               "Usage: dune [options] <ELF file] [<ELF file>...]\n");
272         fprintf(stderr,
273               "Or for testing: dune -t [options]\nOptions are:\n");
274         for (int i = 0;
275              i < COUNT_OF(long_options) - 1;
276              i++) {
277                 struct option *l = &long_options[i];
278
279                 fprintf(stderr, "%s or %c%s\n", l->name, l->val,
280                         l->has_arg ? " <arg>" : "");
281         }
282         exit(0);
283 }
284
285 static struct elf_aux *
286 getextra(int *auxc, char *_s)
287 {
288         struct elf_aux *auxv;
289         char *s = strdup(_s);
290         // icky hardcode, but realistic.
291         char *auxpairs[32];
292
293         *auxc = gettokens(s, auxpairs, 32, ",");
294         if (debug)
295                 fprintf(stderr, "Found %d extra aux pairs\n", *auxc);
296         if (*auxc < 1)
297                 return NULL;
298         auxv = malloc(sizeof(*auxv) * *auxc);
299         if (!auxv)
300                 errx(1, "auxv malloc: %r");
301         for (int i = 0; i < *auxc; i++) {
302                 char *aux[2];
303                 int j;
304                 uint32_t t, v;
305
306                 j = gettokens(auxpairs[i], aux, 2, "=");
307                 if (j < 2) {
308                         fprintf(stderr, "%s: should be in the form type=val\n",
309                                 auxpairs[i]);
310                         free(auxv);
311                         return NULL;
312                 }
313                 t = strtoul(aux[0], 0, 0);
314                 v = strtoul(aux[1], 0, 0);
315                 auxv[i].v[0] = t;
316                 auxv[i].v[1] = v;
317                 if (debug)
318                         fprintf(stderr, "Adding aux pair 0x%x:0x%x\n", auxv[i].v[0],
319                                 auxv[i].v[1]);
320         }
321         return auxv;
322
323 }
324
325 static struct elf_aux *
326 buildaux(struct elf_aux *base, int basec, struct elf_aux *extra, int extrac)
327 {
328         int total = basec + extrac;
329         struct elf_aux *ret;
330
331         ret = realloc(extra, total * sizeof(*ret));
332         if (!ret)
333                 return NULL;
334
335         if (debug)
336                 fprintf(stderr, "buildaux: consolidating %d aux and %d extra\n",
337                         basec, extrac);
338         /* TOOD: check for dups. */
339         if (basec)
340                 memmove(&ret[extrac], base, sizeof(*base)*basec);
341         return ret;
342 }
343
344 static bool
345 testvmcall(struct guest_thread *gth, struct vm_trapframe *tf)
346 {
347         uintptr_t rax = tf->tf_rax;
348
349         switch (rax) {
350         default:
351                 fprintf(stderr, "testvmcall: gth %p tf %p\n", gth, tf);
352                 fprintf(stderr, "args should be 1, 2, 3, 4, 5, 6:\n");
353                 fprintf(stderr, "%p %p %p %p %p %p\n",
354                         tf->tf_rdi, tf->tf_rsi, tf->tf_rdx,
355                         tf->tf_rcx, tf->tf_r8, tf->tf_r9);
356                 fprintf(stderr, "RAX should be deadbeef: 0x%x\n", rax);
357                 break;
358         case 0:
359                 write(1, &tf->tf_rdi, 1);
360                 break;
361         }
362         tf->tf_rip += 3;
363         return true;
364 }
365
366 int main(int argc, char **argv)
367 {
368         void *tos;
369         int envc, auxc, extrac = 0;
370         struct elf_aux *auxv, *extra = NULL;
371         int vmmflags = 0;
372         uint64_t entry = 0;
373         int ret;
374         struct vm_trapframe *vm_tf;
375         int c;
376         int test = 0;
377         int option_index;
378         int ac = argc;
379         char **av = argv;
380
381         fprintf(stderr, "%p %p %p %p\n", PGSIZE, PGSHIFT, PML1_SHIFT,
382                 PML1_PTE_REACH);
383
384         if ((uintptr_t)__procinfo.program_end >= MinMemory) {
385                 fprintf(stderr,
386                         "Panic: vmrunkernel binary extends into guest memory\n");
387                 exit(1);
388         }
389
390         while ((c = getopt_long(argc, argv, "a:dv:m:M:gsth", long_options,
391                                 &option_index)) != -1) {
392                 switch (c) {
393                 case 'a':
394                         extra = getextra(&extrac, optarg);
395                         if (debug)
396                                 fprintf(stderr, "Added %d aux items\n", extrac);
397                         break;
398                 case 'd':
399                         fprintf(stderr, "SET DEBUG\n");
400                         debug++;
401                         break;
402                 case 'v':
403                         vmmflags = strtoull(optarg, 0, 0);
404                         break;
405                 case 'm':
406                         memsize = strtoull(optarg, 0, 0);
407                         break;
408                 case 'M':
409                         memstart = strtoull(optarg, 0, 0);
410                         break;
411                 case 'g':       /* greedy */
412                         parlib_never_yield = TRUE;
413                         break;
414                 case 's':       /* scp */
415                         parlib_wants_to_be_mcp = FALSE;
416                         break;
417                 case 't':
418                         test = 1;
419                         break;
420                 case 'h':
421                 default:
422                         usage();
423                         break;
424                 }
425         }
426         argc -= optind;
427         argv += optind;
428         if ((!test) && (argc < 1)) {
429                 usage();
430         }
431
432         if ((uintptr_t)(memstart + memsize) >= (uintptr_t)BRK_START) {
433                 fprintf(stderr,
434                         "memstart 0x%lx memsize 0x%lx -> 0x%lx is too large; overlaps BRK_START at %p\n",
435                         memstart, memsize, memstart + memsize, BRK_START);
436                 exit(1);
437         }
438
439         mmap_memory(memstart, memsize);
440
441         if (debug)
442                 fprintf(stderr, "mmap guest physical memory at %p for 0x%lx bytes\n",
443                         memstart, memsize);
444
445         // TODO: find out why we can't use memstart + memsize as TOS.
446         tos = (void *)(memstart + 0x800000);
447
448         for (envc = 0; environ[envc]; envc++)
449                 ;
450         auxv = (struct elf_aux *)&environ[envc+1];
451         for (auxc = 0; auxv[auxc].v[0]; auxc++)
452                 ;
453         auxv = buildaux(auxv, auxc, extra, extrac);
454         if (!auxv) {
455                 fprintf(stderr, "Can't build auxv: %r");
456                 exit(1);
457         }
458         auxc = auxc + extrac;
459
460         if (!test) {
461                 entry = load_elf(argv[0]);
462                 if (entry == 0) {
463                         fprintf(stderr, "Unable to load kernel %s\n", argv[0]);
464                         exit(1);
465                 }
466         } else {
467                 fprintf(stderr, "Running dune test\n");
468                 entry = (uintptr_t) dune_test;
469         }
470         if (debug)
471                 fprintf(stderr, "Test: Populate stack at %p\n", tos);
472         tos = populate_stack(tos, ac, av, envc, environ, auxc, auxv);
473         if (debug)
474                 fprintf(stderr, "populated stack at %p; argc %d, envc %d, auxc %d\n",
475                         tos, ac, envc, auxc);
476
477         ret = vthread_attr_init(&vm, vmmflags);
478         if (ret) {
479                 fprintf(stderr, "vmm_init failed: %r\n");
480                 exit(1);
481         }
482
483         vm.gths[0]->vmcall = test ? testvmcall : linuxemu;
484         vm_tf = gth_to_vmtf(vm.gths[0]);
485
486         /* we can't use the default stack since we set one up
487          * ourselves. */
488         vm_tf->tf_rsp = (uint64_t)tos;
489         if (debug)
490                 fprintf(stderr, "stack is %p\n", tos);
491
492         vthread_create(&vm, 0, (void *)entry, tos);
493
494         uthread_sleep_forever();
495         return 0;
496 }