arena: Connecting importers with sources
[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
27 #include <vmm/sched.h>
28 #include <sys/eventfd.h>
29 #include <sys/uio.h>
30
31 #define MiB 0x100000ull
32 #define GiB (1ull << 30)
33 #define MinMemory (16*MiB)
34
35 static struct virtual_machine local_vm, *vm = &local_vm;
36 struct vmm_gpcore_init gpci;
37 static void *ram;
38 static unsigned long long memsize = GiB;
39 static uintptr_t memstart = MinMemory;
40 static uintptr_t stack;
41 static unsigned long long *p512, *p1, *p2m;
42
43 static int debug = 0;
44
45 /* load_kernel loads an ELF file as a kernel. */
46 uintptr_t
47 load_kernel(char *filename)
48 {
49         Elf64_Ehdr *ehdr;
50         Elf *elf;
51         size_t phnum = 0;
52         Elf64_Phdr *hdrs;
53         int fd;
54
55         elf_version(EV_CURRENT);
56         fd = open(filename, O_RDONLY);
57         if (fd < 0) {
58                 fprintf(stderr, "Can't open %s: %r\n", filename);
59                 return 0;
60         }
61
62         elf = elf_begin(fd, ELF_C_READ, NULL);
63         if (elf == NULL) {
64                 fprintf(stderr, "%s: cannot read %s ELF file.\n", __func__, filename);
65                 close(fd);
66                 return 0;
67         }
68
69         ehdr = elf64_getehdr(elf);
70         if (ehdr == NULL) {
71                 fprintf(stderr, "%s: cannot get exec header of %s.\n",
72                         __func__, filename);
73                 goto fail;
74         }
75         fprintf(stderr, "%s ELF entry point is %p\n", filename,
76                 (void *)ehdr->e_entry);
77
78         if (elf_getphdrnum(elf, &phnum) < 0) {
79                 fprintf(stderr, "%s: cannot get program header num of %s.\n",
80                         __func__, filename);
81                 goto fail;
82         }
83         fprintf(stderr, "%s has %p program headers\n", filename, phnum);
84
85         hdrs = elf64_getphdr(elf);
86         if (hdrs == NULL) {
87                 fprintf(stderr, "%s: cannot get program headers of %s.\n",
88                         __func__, filename);
89                 goto fail;
90         }
91
92         for (int i = 0; i < phnum; i++) {
93                 size_t tot;
94                 Elf64_Phdr *h = &hdrs[i];
95                 uintptr_t pa;
96
97                 fprintf(stderr,
98                         "%d: type 0x%lx flags 0x%lx  offset 0x%lx vaddr 0x%lx paddr 0x%lx size 0x%lx  memsz 0x%lx align 0x%lx\n",
99                         i,
100                         h->p_type,              /* Segment type */
101                         h->p_flags,             /* Segment flags */
102                         h->p_offset,            /* Segment file offset */
103                         h->p_vaddr,             /* Segment virtual address */
104                         h->p_paddr,             /* Segment physical address */
105                         h->p_filesz,            /* Segment size in file */
106                         h->p_memsz,             /* Segment size in memory */
107                         h->p_align              /* Segment alignment */);
108                 if (h->p_type != PT_LOAD)
109                         continue;
110                 if ((h->p_flags & (PF_R | PF_W | PF_X)) == 0)
111                         continue;
112
113                 pa = h->p_paddr;
114                 fprintf(stderr,
115                         "Read header %d @offset %p to %p (elf PA is %p) %d bytes:",
116                         i, h->p_offset, pa, h->p_paddr, h->p_filesz);
117                 tot = 0;
118                 while (tot < h->p_filesz) {
119                         int amt = pread(fd, (void *)(pa + tot), h->p_filesz - tot,
120                                         h->p_offset + tot);
121                         if (amt < 1)
122                                 break;
123                         tot += amt;
124                 }
125                 fprintf(stderr, "read a total of %d bytes\n", tot);
126                 if (tot < h->p_filesz) {
127                         fprintf(stderr, "%s: got %d bytes, wanted %d bytes\n",
128                                 filename, tot, h->p_filesz);
129                         goto fail;
130                 }
131         }
132
133         close(fd);
134         elf_end(elf);
135         return ehdr->e_entry;
136  fail:
137         close(fd);
138         elf_end(elf);
139         return 0;
140 }
141
142 int main(int argc, char **argv)
143 {
144         int vmmflags = VMM_VMCALL_PRINTF;
145         uint64_t entry = 0;
146         int ret;
147         struct vm_trapframe *vm_tf;
148         int c;
149         int option_index;
150         static struct option long_options[] = {
151                 {"debug",         no_argument,       0, 'd'},
152                 {"vmmflags",      required_argument, 0, 'v'},
153                 {"memsize",       required_argument, 0, 'm'},
154                 {"memstart",      required_argument, 0, 'M'},
155                 {"stack",         required_argument, 0, 'S'},
156                 {"cmdline_extra", required_argument, 0, 'c'},
157                 {"greedy",        no_argument,       0, 'g'},
158                 {"scp",           no_argument,       0, 's'},
159                 {"help",          no_argument,       0, 'h'},
160                 {0, 0, 0, 0}
161         };
162
163         fprintf(stderr, "%p %p %p %p\n", PGSIZE, PGSHIFT, PML1_SHIFT,
164                         PML1_PTE_REACH);
165
166         if ((uintptr_t)__procinfo.program_end >= MinMemory) {
167                 fprintf(stderr,
168                         "Panic: vmrunkernel binary extends into guest memory\n");
169                 exit(1);
170         }
171
172         while ((c = getopt_long(argc, argv, "dv:m:M:S:gsh", long_options,
173                                 &option_index)) != -1) {
174                 switch (c) {
175                         case 'd':
176                                 debug++;
177                                 break;
178                         case 'v':
179                                 vmmflags = strtoull(optarg, 0, 0);
180                                 break;
181                         case 'm':
182                                 memsize = strtoull(optarg, 0, 0);
183                                 break;
184                         case 'M':
185                                 memstart = strtoull(optarg, 0, 0);
186                                 break;
187                         case 'S':
188                                 stack = strtoull(optarg, 0, 0);
189                                 break;
190                         case 'g':       /* greedy */
191                                 parlib_never_yield = TRUE;
192                                 break;
193                         case 's':       /* scp */
194                                 parlib_wants_to_be_mcp = FALSE;
195                                 break;
196                         case 'h':
197                         default:
198                                 // Sadly, the getopt_long struct does
199                                 // not have a pointer to help text.
200                                 for (int i = 0;
201                                     i < sizeof(long_options)/sizeof(long_options[0]) - 1;
202                                     i++) {
203                                         struct option *l = &long_options[i];
204
205                                         fprintf(stderr, "%s or %c%s\n", l->name, l->val,
206                                                 l->has_arg ? " <arg>" : "");
207                                 }
208                                 exit(0);
209                 }
210         }
211         argc -= optind;
212         argv += optind;
213         if (argc < 1) {
214                 fprintf(stderr, "Usage: %s vmimage [-n (no vmcall printf)]\n", argv[0]);
215                 exit(1);
216         }
217
218         if ((uintptr_t)(memstart + memsize) >= (uintptr_t)BRK_START) {
219                 fprintf(stderr,
220                         "memstart 0x%lx memsize 0x%lx -> 0x%lx is too large; overlaps BRK_START at %p\n",
221                         memstart, memsize, memstart + memsize, BRK_START);
222                 exit(1);
223         }
224
225         ram = mmap((void *)memstart, memsize,
226                    PROT_READ | PROT_WRITE | PROT_EXEC,
227                    MAP_POPULATE | MAP_ANONYMOUS, -1, 0);
228         if (ram != (void *)memstart) {
229                 fprintf(stderr, "Could not mmap 0x%lx bytes at 0x%lx\n",
230                         memsize, memstart);
231                 exit(1);
232         }
233
234         entry = load_kernel(argv[0]);
235         if (entry == 0) {
236                 fprintf(stderr, "Unable to load kernel %s\n", argv[0]);
237                 exit(1);
238         }
239
240         vm->nr_gpcs = 1;
241         vm->gpcis = &gpci;
242         ret = vmm_init(vm, vmmflags);
243         if (ret) {
244                 fprintf(stderr, "vmm_init failed: %r\n");
245                 exit(1);
246         }
247
248         /* Allocate 3 pages for page table pages: a page of 512 GiB
249          * PTEs with only one entry filled to point to a page of 1 GiB
250          * PTEs; a page of 1 GiB PTEs with only one entry filled to
251          * point to a page of 2 MiB PTEs; and a page of 2 MiB PTEs,
252          * all of which may be filled. For now, we don't handle
253          * starting addresses not aligned on 512 GiB boundaries or
254          * sizes > GiB */
255         ret = posix_memalign((void **)&p512, PGSIZE, 3 * PGSIZE);
256         if (ret) {
257                 perror("ptp alloc");
258                 exit(1);
259         }
260
261         /* Set up a 1:1 ("identity") page mapping from guest virtual
262          * to guest physical using the (host virtual)
263          * `kerneladdress`. This mapping may be used for only a short
264          * time, until the guest sets up its own page tables. Be aware
265          * that the values stored in the table are physical addresses.
266          * This is subtle and mistakes are easily disguised due to the
267          * identity mapping, so take care when manipulating these
268          * mappings. */
269         p1 = &p512[NPTENTRIES];
270         p2m = &p512[2 * NPTENTRIES];
271
272         fprintf(stderr, "Map %p for %zu bytes\n", memstart, memsize);
273         /* TODO: fix this nested loop so it's correct for more than
274          * one GiB. */
275         for(uintptr_t p4 = memstart; p4 < memstart + memsize;
276             p4 += PML4_PTE_REACH) {
277                 p512[PML4(p4)] = (uint64_t)p1 | PTE_KERN_RW;
278                 for (uintptr_t p3 = p4; p3 < memstart + memsize;
279                      p3 += PML3_PTE_REACH) {
280                         p1[PML3(p3)] = (uint64_t)p2m | PTE_KERN_RW;
281                         for (uintptr_t p2 = p3; p2 < memstart + memsize; p2 += PML2_PTE_REACH) {
282                                 p2m[PML2(p2)] =
283                                         (uint64_t)(p2) | PTE_KERN_RW | PTE_PS;
284                         }
285                 }
286         }
287
288         fprintf(stderr, "p512 %p p512[0] is 0x%lx p1 %p p1[0] is 0x%x\n", p512, p512[0], p1, p1[0]);
289
290         vm_tf = gth_to_vmtf(vm->gths[0]);
291         vm_tf->tf_cr3 = (uint64_t) p512;
292         vm_tf->tf_rip = entry;
293         vm_tf->tf_rsp = stack;
294         vm_tf->tf_rsi = (uint64_t) 0;
295         start_guest_thread(vm->gths[0]);
296
297         uthread_sleep_forever();
298         return 0;
299 }