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