94e0f9054d799681013448b26f20392abb640a02
[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 #include <vmm/vthread.h>
34
35 struct vmm_gpcore_init gpci;
36 bool linuxemu(struct guest_thread *gth, struct vm_trapframe *tf);
37
38
39 extern char **environ;
40
41 static struct virtual_machine vm = {.halt_exit = true,
42                                     .mtx = UTH_MUTEX_INIT,
43                                     .vmcall = linuxemu};
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 static struct option long_options[] = {
138         {"aux",           required_argument, 0, 'a'},
139         {"debug",         no_argument,       0, 'd'},
140         {"memsize",       required_argument, 0, 'm'},
141         {"memstart",      required_argument, 0, 'M'},
142         {"cmdline_extra", required_argument, 0, 'c'},
143         {"greedy",        no_argument,       0, 'g'},
144         {"scp",           no_argument,       0, 's'},
145         {"test",          no_argument,       0, 't'},
146         {"help",          no_argument,       0, 'h'},
147         {0, 0, 0, 0}
148 };
149
150 static void
151 usage(void)
152 {
153         // Sadly, the getopt_long struct does
154         // not have a pointer to help text.
155         fprintf(stderr,
156               "Usage: dune [options] <ELF file] [<ELF file>...]\n");
157         fprintf(stderr,
158               "Or for testing: dune -t [options]\nOptions are:\n");
159         for (int i = 0;
160              i < COUNT_OF(long_options) - 1;
161              i++) {
162                 struct option *l = &long_options[i];
163
164                 fprintf(stderr, "%s or %c%s\n", l->name, l->val,
165                         l->has_arg ? " <arg>" : "");
166         }
167         exit(0);
168 }
169
170 static struct elf_aux *
171 getextra(int *auxc, char *_s)
172 {
173         struct elf_aux *auxv;
174         char *s = strdup(_s);
175         // icky hardcode, but realistic.
176         char *auxpairs[32];
177
178         *auxc = gettokens(s, auxpairs, 32, ",");
179         if (dune_debug)
180                 fprintf(stderr, "Found %d extra aux pairs\n", *auxc);
181         if (*auxc < 1)
182                 return NULL;
183         auxv = malloc(sizeof(*auxv) * *auxc);
184         if (!auxv)
185                 errx(1, "auxv malloc: %r");
186         for (int i = 0; i < *auxc; i++) {
187                 char *aux[2];
188                 int j;
189                 uint32_t t, v;
190
191                 j = gettokens(auxpairs[i], aux, 2, "=");
192                 if (j < 2) {
193                         fprintf(stderr, "%s: should be in the form type=val\n",
194                                 auxpairs[i]);
195                         free(auxv);
196                         return NULL;
197                 }
198                 t = strtoul(aux[0], 0, 0);
199                 v = strtoul(aux[1], 0, 0);
200                 auxv[i].v[0] = t;
201                 auxv[i].v[1] = v;
202                 if (dune_debug)
203                         fprintf(stderr, "Adding aux pair 0x%x:0x%x\n", auxv[i].v[0],
204                                 auxv[i].v[1]);
205         }
206         return auxv;
207
208 }
209
210 static struct elf_aux *
211 buildaux(struct elf_aux *base, int basec, struct elf_aux *extra, int extrac)
212 {
213         int total = basec + extrac;
214         struct elf_aux *ret;
215
216         ret = realloc(extra, total * sizeof(*ret));
217         if (!ret)
218                 return NULL;
219
220         if (dune_debug)
221                 fprintf(stderr, "buildaux: consolidating %d aux and %d extra\n",
222                         basec, extrac);
223         /* TOOD: check for dups. */
224         if (basec)
225                 memmove(&ret[extrac], base, sizeof(*base)*basec);
226         return ret;
227 }
228
229 int main(int argc, char **argv)
230 {
231         void *tos;
232         int envc, auxc, extrac = 0;
233         struct elf_aux *auxv, *extra = NULL;
234         uint64_t entry = 0;
235         struct vthread *vth;
236         struct vmm_gpcore_init gpci[1];
237         int c;
238         int test = 0;
239         int option_index;
240         int ac = argc;
241         char **av = argv;
242
243         fprintf(stderr, "%p %p %p %p\n", PGSIZE, PGSHIFT, PML1_SHIFT,
244                 PML1_PTE_REACH);
245
246         if ((uintptr_t)__procinfo.program_end >= MinMemory) {
247                 fprintf(stderr,
248                         "Panic: vmrunkernel binary extends into guest memory\n");
249                 exit(1);
250         }
251
252         while ((c = getopt_long(argc, argv, "a:dv:m:M:gsth", long_options,
253                                 &option_index)) != -1) {
254                 switch (c) {
255                 case 'a':
256                         extra = getextra(&extrac, optarg);
257                         if (dune_debug)
258                                 fprintf(stderr, "Added %d aux items\n", extrac);
259                         break;
260                 case 'd':
261                         fprintf(stderr, "SET DEBUG\n");
262                         dune_debug++;
263                         break;
264                 case 'm':
265                         memsize = strtoull(optarg, 0, 0);
266                         break;
267                 case 'M':
268                         memstart = strtoull(optarg, 0, 0);
269                         break;
270                 case 'g':       /* greedy */
271                         parlib_never_yield = TRUE;
272                         break;
273                 case 's':       /* scp */
274                         parlib_wants_to_be_mcp = FALSE;
275                         break;
276                 case 't':
277                         test = 1;
278                         break;
279                 case 'h':
280                 default:
281                         usage();
282                         break;
283                 }
284         }
285         argc -= optind;
286         argv += optind;
287         if ((!test) && (argc < 1)) {
288                 usage();
289         }
290
291         init_lemu_logging(dune_debug);
292         init_linuxemu();
293
294         if ((uintptr_t)(memstart + memsize) >= (uintptr_t)BRK_START) {
295                 fprintf(stderr,
296                         "memstart 0x%lx memsize 0x%lx -> 0x%lx is too large; overlaps BRK_START at %p\n",
297                         memstart, memsize, memstart + memsize, BRK_START);
298                 exit(1);
299         }
300
301         mmap_memory(&vm, memstart, memsize);
302
303         if (dune_debug)
304                 fprintf(stderr, "mmap guest physical memory at %p for 0x%lx bytes\n",
305                         memstart, memsize);
306
307         // TODO: find out why we can't use memstart + memsize as TOS.
308         tos = (void *)(memstart + 0x800000);
309
310         for (envc = 0; environ[envc]; envc++)
311                 ;
312         auxv = (struct elf_aux *)&environ[envc+1];
313         for (auxc = 0; auxv[auxc].v[0]; auxc++)
314                 ;
315         auxv = buildaux(auxv, auxc, extra, extrac);
316         if (!auxv) {
317                 fprintf(stderr, "Can't build auxv: %r");
318                 exit(1);
319         }
320         auxc = auxc + extrac;
321
322         if (!test) {
323                 entry = load_elf(argv[0], MinMemory, NULL, NULL);
324                 if (entry == 0) {
325                         fprintf(stderr, "Unable to load kernel %s\n", argv[0]);
326                         exit(1);
327                 }
328         } else {
329                 fprintf(stderr, "Running dune test\n");
330                 entry = (uintptr_t) dune_test;
331         }
332         if (dune_debug)
333                 fprintf(stderr, "Test: Populate stack at %p\n", tos);
334         tos = populate_stack(tos, ac, av, envc, environ, auxc, auxv);
335         if (dune_debug)
336                 fprintf(stderr, "populated stack at %p; argc %d, envc %d, auxc %d\n",
337                         tos, ac, envc, auxc);
338
339         if (dune_debug)
340                 fprintf(stderr, "stack is %p\n", tos);
341
342         gpci_init(gpci);
343         vth = vthread_alloc(&vm, gpci);
344         vthread_init_ctx(vth, entry, (uintptr_t)tos, (uintptr_t)tos);
345         vthread_run(vth);
346
347         uthread_sleep_forever();
348         return 0;
349 }