| #include <stdio.h> |
| #include <pthread.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <parlib/arch/arch.h> |
| #include <parlib/ros_debug.h> |
| #include <unistd.h> |
| #include <gelf.h> |
| #include <errno.h> |
| #include <libelf.h> |
| #include <dirent.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <ros/syscall.h> |
| #include <sys/mman.h> |
| #include <vmm/vmm.h> |
| #include <vmm/acpi/acpi.h> |
| #include <vmm/acpi/vmm_simple_dsdt.h> |
| #include <ros/arch/mmu.h> |
| #include <ros/arch/membar.h> |
| #include <ros/vmm.h> |
| #include <parlib/uthread.h> |
| #include <vmm/linux_bootparam.h> |
| #include <getopt.h> |
| #include <iplib/iplib.h> |
| #include <vmm/sched.h> |
| #include <sys/eventfd.h> |
| #include <sys/uio.h> |
| #include <err.h> |
| #include <vmm/linuxemu.h> |
| #include <vmm/vmm.h> |
| #include <vmm/vthread.h> |
| |
| struct vmm_gpcore_init gpci; |
| bool linuxemu(struct guest_thread *gth, struct vm_trapframe *tf); |
| |
| |
| extern char **environ; |
| |
| static struct virtual_machine vm = {.halt_exit = true, |
| .mtx = UTH_MUTEX_INIT, |
| .vmcall = linuxemu}; |
| |
| static unsigned long long memsize = GiB; |
| static uintptr_t memstart = MinMemory; |
| |
| static int dune_debug; |
| |
| static void hlt(void) |
| { |
| __asm__ __volatile__("\thlt\n\t"); |
| } |
| |
| static int pc(char *c) |
| { |
| __asm__ __volatile__("movq $1, %%rax\n" |
| "movq $1, %%rdi\n" |
| "movq %0, %%rsi\n" |
| "movq $1, %%rdx\n" |
| "vmcall\n" :: |
| "m"(c) : "rdi", "rax", "rsi", "rdx"); |
| return 0; |
| } |
| |
| static void xnum(uint64_t x) |
| { |
| static char *hex = "0123456789abcdef"; |
| |
| for (int i = 0; i < 8; i++) { |
| uint8_t v = ((uint8_t*)&x)[7 - i]; |
| pc(&hex[v >> 4]); |
| pc(&hex[v & 0xf]); |
| } |
| } |
| |
| static void show(char *s) |
| { |
| static char *showedoff = "NULL POINTER: That's bad.\n"; |
| |
| if (!s) { |
| show(showedoff); |
| return; |
| } |
| while (*s) { |
| pc(s); |
| s++; |
| } |
| } |
| |
| /* This is a small test that runs in gr0 and tests our argument setup. |
| * This test can grow in capability as we find more broken bits in our |
| * dune-like environment. */ |
| |
| void dune_test(void *stack) |
| { |
| show("Hello this is dune's test\n"); |
| |
| int argc; |
| char **argv; |
| struct elf_aux *auxv; |
| |
| show("dune_test: dumping argv, env, and aux\n"); |
| |
| argc = *((uint64_t*)stack); |
| argv = &((char**)stack)[1]; |
| show("argc: "); xnum(argc); show("\n"); |
| show("argv: "); xnum((uint64_t)argv); show("\n"); |
| |
| for (int i = 0; i < argc; i++, argv++) { |
| show("arg["); xnum(i); show("]:"); |
| show(argv[0]); |
| show("\n"); |
| } |
| // skip the null and move on to envp. |
| argv++; |
| for (int i = 0; argv[0]; i++, argv++) { |
| show("env["); xnum(i); show("]:"); |
| show(argv[0]); |
| show("\n"); |
| } |
| // skip the null and move on to auxv. |
| argv++; |
| auxv = (void *)argv; |
| for (int i = 0; auxv[i].v[0]; i++) { |
| show("auxv["); xnum(i); show("]:"); |
| xnum(auxv[i].v[0]); show(":"); |
| xnum(auxv[i].v[1]); show("\n"); |
| } |
| show("Done dumping [argv, env, auxv]\n"); |
| show("Testing syscall extensions\n"); |
| __asm__ __volatile__("movq $400, %%rax\n" |
| "vmcall\n" :: ); |
| hlt(); |
| } |
| |
| static struct option long_options[] = { |
| {"aux", required_argument, 0, 'a'}, |
| {"debug", no_argument, 0, 'd'}, |
| {"memsize", required_argument, 0, 'm'}, |
| {"memstart", required_argument, 0, 'M'}, |
| {"cmdline_extra", required_argument, 0, 'c'}, |
| {"greedy", no_argument, 0, 'g'}, |
| {"scp", no_argument, 0, 's'}, |
| {"test", no_argument, 0, 't'}, |
| {"help", no_argument, 0, 'h'}, |
| {0, 0, 0, 0} |
| }; |
| |
| static void usage(void) |
| { |
| // Sadly, the getopt_long struct does |
| // not have a pointer to help text. |
| fprintf(stderr, |
| "Usage: dune [options] <ELF file] [<ELF file>...]\n"); |
| fprintf(stderr, |
| "Or for testing: dune -t [options]\nOptions are:\n"); |
| for (int i = 0; |
| i < COUNT_OF(long_options) - 1; |
| i++) { |
| struct option *l = &long_options[i]; |
| |
| fprintf(stderr, "%s or %c%s\n", l->name, l->val, |
| l->has_arg ? " <arg>" : ""); |
| } |
| exit(0); |
| } |
| |
| static struct elf_aux *getextra(int *auxc, char *_s) |
| { |
| struct elf_aux *auxv; |
| char *s = strdup(_s); |
| // icky hardcode, but realistic. |
| char *auxpairs[32]; |
| |
| *auxc = gettokens(s, auxpairs, 32, ","); |
| if (dune_debug) |
| fprintf(stderr, "Found %d extra aux pairs\n", *auxc); |
| if (*auxc < 1) |
| return NULL; |
| auxv = malloc(sizeof(*auxv) * *auxc); |
| if (!auxv) |
| errx(1, "auxv malloc: %r"); |
| for (int i = 0; i < *auxc; i++) { |
| char *aux[2]; |
| int j; |
| uint32_t t, v; |
| |
| j = gettokens(auxpairs[i], aux, 2, "="); |
| if (j < 2) { |
| fprintf(stderr, "%s: should be in the form type=val\n", |
| auxpairs[i]); |
| free(auxv); |
| return NULL; |
| } |
| t = strtoul(aux[0], 0, 0); |
| v = strtoul(aux[1], 0, 0); |
| auxv[i].v[0] = t; |
| auxv[i].v[1] = v; |
| if (dune_debug) |
| fprintf(stderr, "Adding aux pair 0x%x:0x%x\n", |
| auxv[i].v[0], auxv[i].v[1]); |
| } |
| return auxv; |
| |
| } |
| |
| static struct elf_aux *buildaux(struct elf_aux *base, int basec, |
| struct elf_aux *extra, int extrac) |
| { |
| int total = basec + extrac; |
| struct elf_aux *ret; |
| |
| ret = realloc(extra, total * sizeof(*ret)); |
| if (!ret) |
| return NULL; |
| |
| if (dune_debug) |
| fprintf(stderr, "buildaux: consolidating %d aux and %d extra\n", |
| basec, extrac); |
| /* TOOD: check for dups. */ |
| if (basec) |
| memmove(&ret[extrac], base, sizeof(*base)*basec); |
| return ret; |
| } |
| |
| int main(int argc, char **argv) |
| { |
| void *tos; |
| int envc, auxc, extrac = 0; |
| struct elf_aux *auxv, *extra = NULL; |
| uint64_t entry = 0; |
| struct vthread *vth; |
| struct vmm_gpcore_init gpci[1]; |
| int c; |
| int test = 0; |
| int option_index; |
| int ac = argc; |
| char **av = argv; |
| |
| fprintf(stderr, "%p %p %p %p\n", PGSIZE, PGSHIFT, PML1_SHIFT, |
| PML1_PTE_REACH); |
| |
| if ((uintptr_t)__procinfo.program_end >= MinMemory) { |
| fprintf(stderr, |
| "Panic: vmrunkernel binary extends into guest memory\n"); |
| exit(1); |
| } |
| |
| while ((c = getopt_long(argc, argv, "a:dv:m:M:gsth", long_options, |
| &option_index)) != -1) { |
| switch (c) { |
| case 'a': |
| extra = getextra(&extrac, optarg); |
| if (dune_debug) |
| fprintf(stderr, "Added %d aux items\n", extrac); |
| break; |
| case 'd': |
| fprintf(stderr, "SET DEBUG\n"); |
| dune_debug++; |
| break; |
| case 'm': |
| memsize = strtoull(optarg, 0, 0); |
| break; |
| case 'M': |
| memstart = strtoull(optarg, 0, 0); |
| break; |
| case 'g': /* greedy */ |
| parlib_never_yield = TRUE; |
| break; |
| case 's': /* scp */ |
| parlib_wants_to_be_mcp = FALSE; |
| break; |
| case 't': |
| test = 1; |
| break; |
| case 'h': |
| default: |
| usage(); |
| break; |
| } |
| } |
| argc -= optind; |
| argv += optind; |
| if ((!test) && (argc < 1)) { |
| usage(); |
| } |
| |
| init_lemu_logging(dune_debug); |
| init_linuxemu(); |
| |
| if ((uintptr_t)(memstart + memsize) >= (uintptr_t)BRK_START) { |
| fprintf(stderr, |
| "memstart 0x%lx memsize 0x%lx -> 0x%lx is too large; overlaps BRK_START at %p\n", |
| memstart, memsize, memstart + memsize, BRK_START); |
| exit(1); |
| } |
| |
| mmap_memory(&vm, memstart, memsize); |
| |
| if (dune_debug) |
| fprintf(stderr, |
| "mmap guest physical memory at %p for 0x%lx bytes\n", |
| memstart, memsize); |
| |
| // TODO: find out why we can't use memstart + memsize as TOS. |
| tos = (void *)(memstart + 0x800000); |
| |
| for (envc = 0; environ[envc]; envc++) |
| ; |
| auxv = (struct elf_aux *)&environ[envc+1]; |
| for (auxc = 0; auxv[auxc].v[0]; auxc++) |
| ; |
| auxv = buildaux(auxv, auxc, extra, extrac); |
| if (!auxv) { |
| fprintf(stderr, "Can't build auxv: %r"); |
| exit(1); |
| } |
| auxc = auxc + extrac; |
| |
| if (!test) { |
| entry = load_elf(argv[0], MinMemory, NULL, NULL); |
| if (entry == 0) { |
| fprintf(stderr, "Unable to load kernel %s\n", argv[0]); |
| exit(1); |
| } |
| } else { |
| fprintf(stderr, "Running dune test\n"); |
| entry = (uintptr_t) dune_test; |
| } |
| if (dune_debug) |
| fprintf(stderr, "Test: Populate stack at %p\n", tos); |
| tos = populate_stack(tos, ac, av, envc, environ, auxc, auxv); |
| if (dune_debug) |
| fprintf(stderr, |
| "populated stack at %p; argc %d, envc %d, auxc %d\n", |
| tos, ac, envc, auxc); |
| |
| if (dune_debug) |
| fprintf(stderr, "stack is %p\n", tos); |
| |
| gpci_init(gpci); |
| vth = vthread_alloc(&vm, gpci); |
| vthread_init_ctx(vth, entry, (uintptr_t)tos, (uintptr_t)tos); |
| vthread_run(vth); |
| |
| uthread_sleep_forever(); |
| return 0; |
| } |