abortive try at setting up tables. Failed miserably.
[akaros.git] / tests / vmm / vmrunkernel.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 <errno.h>
10 #include <dirent.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <ros/syscall.h>
14 #include <sys/mman.h>
15 #include <vmm/coreboot_tables.h>
16 #include <vmm/vmm.h>
17 #include <ros/arch/mmu.h>
18 #include <ros/vmx.h>
19 #include <parlib/uthread.h>
20 #include <vmm/virtio.h>
21 #include <vmm/virtio_mmio.h>
22 #include <vmm/virtio_ids.h>
23 #include <vmm/virtio_config.h>
24
25 /* Kind of sad what a total clusterf the pc world is. By 1999, you could just scan the hardware 
26  * and work it out. But 2005, that was no longer possible. How sad. 
27  * so we have to fake acpi to make it all work. !@#$!@#$#.
28  * This will be copied to memory at 0xe0000, so the kernel can find it.
29  */
30 /* assume they're all 256 bytes long just to make it easy. Just have pointers that point to aligned things. */
31
32 struct Rsdp rsdp = {
33         .signature = "RSDP PTR ",
34         .rchecksum = 0,
35         .oemid = "AKAROS",
36         .raddr = [0x00, 0x01, 0xe0, 0x00], // 0x00e00100
37         .revision = 2,
38         .length = 36,
39 };
40
41 /* This has to be dropped into memory, then the other crap just follows it.
42  * this starts at 0xe00100
43  */
44 struct Sdthdr fmadt = {
45         .sig = "MADT",
46         .length = 0,
47         .rev = 0,
48         .csum = 0,
49         .oemid = "AKAROS",
50         .oemtblid = "GOOGGOOD",
51         .oemrev = "WORK",
52         .creatorid = "NAN ",
53         .creatorrev = "WAN "
54 };
55
56 struct Madt madt = {
57         .lapicpa = 0xfee00000ULL,
58         .pcat = 0,
59         // Intel screwed this up. They put a pointer here, but it seems to imply an array? Who knows? 
60         .st = 0x00e00200;
61 };
62
63 struct Apicst Apic0 = {.type = 0, .next = 0x00e00300, .pid = 0, .id = 0};
64 struct Apicst Apic1 = {.type = 1, .next = 0x00000000, .id = 1, .ibase = 0xfec00000, .ibase 0};
65         
66 /* the array of things. These get copied to consecutive 256-byte boundary areas starting at 0xe0000 */
67 void *apicarray[] = { &rsdp, &fmadt, &madt, &Apic0, &Apic1};
68 /* this test will run the "kernel" in the negative address space. We hope. */
69 void *low1m;
70 uint8_t low4k[4096];
71 unsigned long long stack[1024];
72 volatile int shared = 0;
73 volatile int quit = 0;
74 int mcp = 1;
75
76 /* total hack. If the vm runs away we want to get control again. */
77 unsigned int maxresume = (unsigned int) -1;
78
79 #define MiB 0x100000u
80 #define GiB (1u<<30)
81 #define GKERNBASE (16*MiB)
82 #define KERNSIZE (128*MiB+GKERNBASE)
83 uint8_t _kernel[KERNSIZE];
84
85 unsigned long long *p512, *p1, *p2m;
86
87 void **my_retvals;
88 int nr_threads = 3;
89 int debug = 0;
90 int resumeprompt = 0;
91 /* unlike Linux, this shared struct is for both host and guest. */
92 //      struct virtqueue *constoguest = 
93 //              vring_new_virtqueue(0, 512, 8192, 0, inpages, NULL, NULL, "test");
94 uint64_t virtio_mmio_base = 0x100000000;
95
96 void *consout(void *arg)
97 {
98         char *line, *consline, *outline;
99         static struct scatterlist out[] = { {NULL, sizeof(outline)}, };
100         static struct scatterlist in[] = { {NULL, sizeof(line)}, };
101         static struct scatterlist iov[32];
102         struct virtio_threadarg *a = arg;
103         static unsigned int inlen, outlen, conslen;
104         struct virtqueue *v = a->arg->virtio;
105         fprintf(stderr, "talk thread ..\n");
106         uint16_t head, gaveit = 0, gotitback = 0;
107         uint32_t vv;
108         int i;
109         int num;
110         if (debug) {
111                 printf("----------------------- TT a %p\n", a);
112                 printf("talk thread ttargs %x v %x\n", a, v);
113         }
114         
115         for(num = 0;;num++) {
116                 /* host: use any buffers we should have been sent. */
117                 head = wait_for_vq_desc(v, iov, &outlen, &inlen);
118                 if (debug)
119                         printf("CCC: vq desc head %d, gaveit %d gotitback %d\n", head, gaveit, gotitback);
120                 for(i = 0; debug && i < outlen + inlen; i++)
121                         printf("CCC: v[%d/%d] v %p len %d\n", i, outlen + inlen, iov[i].v, iov[i].length);
122                 /* host: if we got an output buffer, just output it. */
123                 for(i = 0; i < outlen; i++) {
124                         num++;
125                         int j;
126                         for (j = 0; j < iov[i].length; j++)
127                                 printf("%c", ((char *)iov[i].v)[j]);
128                 }
129                 
130                 if (debug)
131                         printf("CCC: outlen is %d; inlen is %d\n", outlen, inlen);
132                 /* host: fill in the writeable buffers. */
133                 /* why we're getting these I don't know. */
134                 for (i = outlen; i < outlen + inlen; i++) {
135                         if (debug) fprintf(stderr, "CCC: send back empty writeable");
136                         iov[i].length = 0;
137                 }
138                 if (debug) printf("CCC: call add_used\n");
139                 /* host: now ack that we used them all. */
140                 add_used(v, head, outlen+inlen);
141                 if (debug) printf("CCC: DONE call add_used\n");
142         }
143         fprintf(stderr, "All done\n");
144         return NULL;
145 }
146
147 void *consin(void *arg)
148 {
149         struct virtio_threadarg *a = arg;
150         char *line, *outline;
151         static char consline[128];
152         static struct scatterlist iov[32];
153         static struct scatterlist out[] = { {NULL, sizeof(outline)}, };
154         static struct scatterlist in[] = { {NULL, sizeof(line)}, };
155
156         static unsigned int inlen, outlen, conslen;
157         struct virtqueue *v = a->arg->virtio;
158         fprintf(stderr, "consin thread ..\n");
159         uint16_t head, gaveit = 0, gotitback = 0;
160         uint32_t vv;
161         int i;
162         int num;
163         
164         if (debug) printf("Spin on console being read, print num queues, halt\n");
165
166         for(num = 0;! quit;num++) {
167                 int debug = 1;
168                 /* host: use any buffers we should have been sent. */
169                 head = wait_for_vq_desc(v, iov, &outlen, &inlen);
170                 if (debug)
171                         printf("vq desc head %d, gaveit %d gotitback %d\n", head, gaveit, gotitback);
172                 for(i = 0; debug && i < outlen + inlen; i++)
173                         printf("v[%d/%d] v %p len %d\n", i, outlen + inlen, iov[i].v, iov[i].length);
174                 if (debug)
175                         printf("outlen is %d; inlen is %d\n", outlen, inlen);
176                 /* host: fill in the writeable buffers. */
177                 for (i = outlen; i < outlen + inlen; i++) {
178                         /* host: read a line. */
179                         memset(consline, 0, 128);
180                         if (fgets(consline, 4096-256, stdin) == NULL) {
181                                 exit(0);
182                         } 
183                         if (debug) printf("GOT A LINE:%s:\n", consline);
184                         if (strlen(consline) < 3 && consline[0] == 'q' ) {
185                                 quit = 1;
186                                 break;
187                         }
188
189                         memmove(iov[i].v, consline, strlen(consline)+ 1);
190                         iov[i].length = strlen(consline) + 1;
191                 }
192                 if (debug) printf("call add_used\n");
193                 /* host: now ack that we used them all. */
194                 add_used(v, head, outlen+inlen);
195                 if (debug) printf("DONE call add_used\n");
196         }
197         fprintf(stderr, "All done\n");
198         return NULL;
199 }
200
201 static struct vqdev vqdev= {
202 name: "console",
203 dev: VIRTIO_ID_CONSOLE,
204 device_features: 0, /* Can't do it: linux console device does not support it. VIRTIO_F_VERSION_1*/
205 numvqs: 2,
206 vqs: {
207                 {name: "consin", maxqnum: 64, f: &consin, arg: (void *)0},
208                 {name: "consout", maxqnum: 64, f: consout, arg: (void *)0},
209         }
210 };
211
212 void lowmem() {
213         __asm__ __volatile__ (".section .lowmem, \"aw\"\n\tlow: \n\t.=0x1000\n\t.align 0x100000\n\t.previous\n");
214 }
215
216 int main(int argc, char **argv)
217 {
218         uint64_t virtiobase = 0x100000000ULL;
219         // lowmem is a bump allocated pointer to 2M at the "physbase" of memory 
220         void *lowmem = (void *) 0x1000000;
221         void *rsdp = (void *) 0x1000000;
222         struct vmctl vmctl;
223         int amt;
224         int vmmflags = 0; // Disabled probably forever. VMM_VMCALL_PRINTF;
225         uint64_t entry = 0x1200000, kerneladdress = 0x1200000;
226         int nr_gpcs = 1;
227         int fd = open("#c/vmctl", O_RDWR), ret;
228         void * x;
229         int kfd = -1;
230         static char cmd[512];
231         void *coreboot_tables = (void *) 0x1165000;
232 printf("%p %p %p %p\n", PGSIZE, PGSHIFT, PML1_SHIFT, PML1_PTE_REACH);
233
234         // mmap is not working for us at present.
235         if ((uint64_t)_kernel > GKERNBASE) {
236                 printf("kernel array @%p is above , GKERNBASE@%p sucks\n", _kernel, GKERNBASE);
237                 exit(1);
238         }
239         memset(_kernel, 0, sizeof(_kernel));
240         memset(lowmem, 0xff, 2*1048576);
241
242         if (fd < 0) {
243                 perror("#cons/sysctl");
244                 exit(1);
245         }
246         argc--,argv++;
247         // switches ...
248         // Sorry, I don't much like the gnu opt parsing code.
249         while (1) {
250                 if (*argv[0] != '-')
251                         break;
252                 switch(argv[0][1]) {
253                 case 'd':
254                         debug++;
255                         break;
256                 case 'v':
257                         vmmflags |= VMM_VMCALL_PRINTF;
258                         break;
259                 case 'm':
260                         argc--,argv++;
261                         maxresume = strtoull(argv[0], 0, 0);
262                         break;
263                 default:
264                         printf("BMAFR\n");
265                         break;
266                 }
267                 argc--,argv++;
268         }
269         if (argc < 1) {
270                 fprintf(stderr, "Usage: %s vmimage [-n (no vmcall printf)] [coreboot_tables [loadaddress [entrypoint]]]\n", argv[0]);
271                 exit(1);
272         }
273         if (argc > 1)
274                 coreboot_tables = (void *) strtoull(argv[1], 0, 0);
275         if (argc > 2)
276                 kerneladdress = strtoull(argv[2], 0, 0);
277         if (argc > 3)
278                 entry = strtoull(argv[3], 0, 0);
279         kfd = open(argv[0], O_RDONLY);
280         if (kfd < 0) {
281                 perror(argv[0]);
282                 exit(1);
283         }
284         // read in the kernel.
285         x = (void *)kerneladdress;
286         for(;;) {
287                 amt = read(kfd, x, 1048576);
288                 if (amt < 0) {
289                         perror("read");
290                         exit(1);
291                 }
292                 if (amt == 0) {
293                         break;
294                 }
295                 x += amt;
296         }
297         fprintf(stderr, "Read in %d bytes\n", x-kerneladdress);
298         close(kfd);
299         /* blob that is faulted in from the EPT first.  we need this to be in low
300          * memory (not above the normal mmap_break), so the EPT can look it up.
301          * Note that we won't get 4096.  The min is 1MB now, and ld is there. */
302         low1m = mmap((int*)4096, MiB-4096, PROT_READ | PROT_WRITE,
303                          MAP_ANONYMOUS, -1, 0);
304         if (low1m != (void *)4096) {
305                 perror("Unable to mmap low 1m");
306                 exit(1);
307         }
308         memset(low1m, 0xff, MiB-4096);
309
310         /* read in acpi. */
311         kfd = open("#P/realmodemem", 0);
312         if (kfd < 0) {
313                 perror("#P/realmodemem");
314         }
315         amt = read(kfd, low4k, sizeof(low4k));
316         if (amt < sizeof(low4k)) {
317                 fprintf(stderr, "read 4k: %d\n", amt);
318                 perror("read");
319                 exit(1);
320         }
321         memset(low4k, 0xff, 4096);
322         amt = read(kfd, low1m, MiB-4096);
323         if (amt < MiB-4096) {
324                 fprintf(stderr, "read mib-4k: %d\n", amt);
325                 perror("read");
326                 exit(1);
327         }
328         close(kfd);
329         printf("Read in %d bytes for RSDP\n", MiB-4096);
330         hexdump(stdout, low4k, 4096);
331
332         if (ros_syscall(SYS_setup_vmm, nr_gpcs, vmmflags, 0, 0, 0, 0) != nr_gpcs) {
333                 perror("Guest pcore setup failed");
334                 exit(1);
335         }
336
337         fprintf(stderr, "Run with %d cores and vmmflags 0x%x\n", nr_gpcs, vmmflags);
338         mcp = 1;
339         if (mcp) {
340                 my_retvals = malloc(sizeof(void*) * nr_threads);
341                 if (!my_retvals)
342                         perror("Init threads/malloc");
343
344                 pthread_can_vcore_request(FALSE);       /* 2LS won't manage vcores */
345                 pthread_need_tls(FALSE);
346                 pthread_mcp_init();                                     /* gives us one vcore */
347                 vcore_request(nr_threads - 1);          /* ghetto incremental interface */
348                 for (int i = 0; i < nr_threads; i++) {
349                         x = __procinfo.vcoremap;
350                         printf("%p\n", __procinfo.vcoremap);
351                         printf("Vcore %d mapped to pcore %d\n", i,
352                                 __procinfo.vcoremap[i].pcoreid);
353                 }
354         }
355
356         ret = syscall(33, 1);
357         if (ret < 0) {
358                 perror("vm setup");
359                 exit(1);
360         }
361         ret = posix_memalign((void **)&p512, 4096, 3*4096);
362         printf("memalign is %p\n", p512);
363         if (ret) {
364                 perror("ptp alloc");
365                 exit(1);
366         }
367         p1 = &p512[512];
368         p2m = &p512[1024];
369         uint64_t kernbase = 0; //0xffffffff80000000;
370         uint64_t highkernbase = 0xffffffff80000000;
371         p512[PML4(kernbase)] = (unsigned long long)p1 | 7;
372         p1[PML3(kernbase)] = /*0x87; */(unsigned long long)p2m | 7;
373         p512[PML4(highkernbase)] = (unsigned long long)p1 | 7;
374         p1[PML3(highkernbase)] = /*0x87; */(unsigned long long)p2m | 7;
375 #define _2MiB (0x200000)
376         int i;
377         for (i = 0; i < 512; i++) {
378                 p2m[PML2(kernbase + i * _2MiB)] = 0x87 | i * _2MiB;
379         }
380
381         kernbase >>= (0+12);
382         kernbase <<= (0 + 12);
383         uint8_t *kernel = (void *)GKERNBASE;
384         //write_coreboot_table(coreboot_tables, ((void *)VIRTIOBASE) /*kernel*/, KERNSIZE + 1048576);
385         hexdump(stdout, coreboot_tables, 512);
386         printf("kernbase for pml4 is 0x%llx and entry is %llx\n", kernbase, entry);
387         printf("p512 %p p512[0] is 0x%lx p1 %p p1[0] is 0x%x\n", p512, p512[0], p1, p1[0]);
388         vmctl.command = REG_RSP_RIP_CR3;
389         vmctl.cr3 = (uint64_t) p512;
390         vmctl.regs.tf_rip = entry;
391         vmctl.regs.tf_rsp = (uint64_t) &stack[1024];
392         if (mcp) {
393                 /* set up virtio bits, which depend on threads being enabled. */
394                 register_virtio_mmio(&vqdev, virtio_mmio_base);
395         }
396         printf("threads started\n");
397         printf("Writing command :%s:\n", cmd);
398
399         ret = write(fd, &vmctl, sizeof(vmctl));
400         if (ret != sizeof(vmctl)) {
401                 perror(cmd);
402         }
403         while (1) {
404                 void showstatus(FILE *f, struct vmctl *v);
405                 int c;
406                 uint8_t byte;
407                 vmctl.command = REG_RIP;
408                 if (maxresume-- == 0) {
409                         debug = 1;
410                         resumeprompt = 1;
411                 }
412                 if (debug) {
413                         printf("RIP %p, shutdown 0x%x\n", vmctl.regs.tf_rip, vmctl.shutdown);
414                         showstatus(stdout, &vmctl);
415                 }
416                 if (resumeprompt) {
417                         printf("RESUME?\n");
418                         c = getchar();
419                         if (c == 'q')
420                                 break;
421                 }
422                 if (vmctl.shutdown == SHUTDOWN_EPT_VIOLATION) {
423                         uint64_t gpa, *regp, val;
424                         uint8_t regx;
425                         int store, size;
426                         int advance;
427                         if (decode(&vmctl, &gpa, &regx, &regp, &store, &size, &advance)) {
428                                 printf("RIP %p, shutdown 0x%x\n", vmctl.regs.tf_rip, vmctl.shutdown);
429                                 showstatus(stdout, &vmctl);
430                                 quit = 1;
431                                 break;
432                         }
433                         if (debug) printf("%p %p %p %p %p %p\n", gpa, regx, regp, store, size, advance);
434                         if ((gpa & ~0xfffULL) == virtiobase) {
435                                 if (debug) printf("DO SOME VIRTIO\n");
436                                 // Lucky for us the various virtio ops are well-defined.
437                                 virtio_mmio(&vmctl, gpa, regx, regp, store);
438                         } else if (gpa < 4096) {
439                                 uint64_t val = 0;
440                                 memmove(&val, &low4k[gpa], size);
441                                 hexdump(stdout, &low4k[gpa], size);
442                                 printf("Low 1m, code %p read @ %p, size %d, val %p\n", vmctl.regs.tf_rip, gpa, size, val);
443                                 memmove(regp, &low4k[gpa], size);
444                                 hexdump(stdout, regp, size);
445                         } else {
446                                 printf("EPT violation: can't handle %p\n", gpa);
447                                 quit = 1;
448                                 break;
449                         }
450                         vmctl.regs.tf_rip += advance;
451                         if (debug) printf("Advance rip by %d bytes to %p\n", advance, vmctl.regs.tf_rip);
452                         vmctl.shutdown = 0;
453                         vmctl.gpa = 0;
454                         vmctl.command = REG_ALL;
455                 } else if (vmctl.shutdown == SHUTDOWN_UNHANDLED_EXIT_REASON) {
456                         switch(vmctl.ret_code){
457                         case  EXIT_REASON_VMCALL:
458                                 byte = vmctl.regs.tf_rdi;
459                                 printf("%c", byte);
460                                 if (byte == '\n') printf("%c", 'V');
461                                 vmctl.regs.tf_rip += 3;
462                                 break;
463                         case EXIT_REASON_EXTERNAL_INTERRUPT:
464                                 //debug = 1;
465                                 fprintf(stderr, "XINT 0x%x 0x%x\n", vmctl.intrinfo1, vmctl.intrinfo2);
466                                 vmctl.interrupt = 0x80000302; // b0d;
467                                 // That sent an NMI and we got it.
468
469                                 vmctl.interrupt = 0x80000320; // b0d;
470                                 // This fails on entry
471                                 
472                                 vmctl.interrupt = 0x80000306; // b0d;
473                                 // This succeedd in sending a UD.
474
475                                 vmctl.interrupt = 0x8000030f; // b0d;
476                                 
477                                 vmctl.command = RESUME;
478                                 break;
479                         case EXIT_REASON_IO_INSTRUCTION:
480                                 printf("IO @ %p\n", vmctl.regs.tf_rip);
481                                 io(&vmctl);
482                                 vmctl.shutdown = 0;
483                                 vmctl.gpa = 0;
484                                 vmctl.command = REG_ALL;
485                                 break;
486                         case EXIT_REASON_HLT:
487                                 printf("\n================== Guest halted. RIP. =======================\n");
488                                 quit = 1;
489                                 break;
490                         default:
491                                 fprintf(stderr, "Don't know how to handle exit %d\n", vmctl.ret_code);
492                                 quit = 1;
493                                 break;
494                         }
495                 }
496                 if (debug) printf("at bottom of switch, quit is %d\n", quit);
497                 if (quit)
498                         break;
499                 if (debug) printf("NOW DO A RESUME\n");
500                 ret = write(fd, &vmctl, sizeof(vmctl));
501                 if (ret != sizeof(vmctl)) {
502                         perror(cmd);
503                 }
504         }
505
506         /* later. 
507         for (int i = 0; i < nr_threads-1; i++) {
508                 int ret;
509                 if (pthread_join(my_threads[i], &my_retvals[i]))
510                         perror("pth_join failed");
511                 printf("%d %d\n", i, ret);
512         }
513  */
514
515         return 0;
516 }