ffbd4c16337262889fab8adcb816cd707e998b2e
[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 <ros/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 /* this test will run the "kernel" in the negative address space. We hope. */
26 int *mmap_blob;
27 unsigned long long stack[1024];
28 volatile int shared = 0;
29 volatile int quit = 0;
30 int mcp = 1;
31
32 #define MiB 0x100000u
33 #define GiB (1u<<30)
34 #define GKERNBASE (16*MiB)
35 #define KERNSIZE (128*MiB+GKERNBASE)
36 uint8_t _kernel[KERNSIZE];
37
38 unsigned long long *p512, *p1, *p2m;
39
40 void **my_retvals;
41 int nr_threads = 3;
42 int debug = 0;
43 /* unlike Linux, this shared struct is for both host and guest. */
44 //      struct virtqueue *constoguest = 
45 //              vring_new_virtqueue(0, 512, 8192, 0, inpages, NULL, NULL, "test");
46 uint64_t virtio_mmio_base = 0x100000000;
47
48 void *consout(void *arg)
49 {
50         char *line, *consline, *outline;
51         static struct scatterlist out[] = { {NULL, sizeof(outline)}, };
52         static struct scatterlist in[] = { {NULL, sizeof(line)}, };
53         static struct scatterlist iov[32];
54         struct virtio_threadarg *a = arg;
55         static unsigned int inlen, outlen, conslen;
56         struct virtqueue *v = a->arg->virtio;
57         fprintf(stderr, "talk thread ..\n");
58         uint16_t head, gaveit = 0, gotitback = 0;
59         uint32_t vv;
60         int i;
61         int num;
62         if (debug) {
63                 printf("----------------------- TT a %p\n", a);
64                 printf("talk thread ttargs %x v %x\n", a, v);
65         }
66         
67         for(num = 0;;num++) {
68                 /* host: use any buffers we should have been sent. */
69                 head = wait_for_vq_desc(v, iov, &outlen, &inlen);
70                 if (debug)
71                         printf("CCC: vq desc head %d, gaveit %d gotitback %d\n", head, gaveit, gotitback);
72                 for(i = 0; debug && i < outlen + inlen; i++)
73                         printf("CCC: v[%d/%d] v %p len %d\n", i, outlen + inlen, iov[i].v, iov[i].length);
74                 /* host: if we got an output buffer, just output it. */
75                 for(i = 0; i < outlen; i++) {
76                         num++;
77                         int j;
78                         for (j = 0; j < iov[i].length; j++)
79                                 printf("%c", ((char *)iov[i].v)[j]);
80                 }
81                 
82                 if (debug)
83                         printf("CCC: outlen is %d; inlen is %d\n", outlen, inlen);
84                 /* host: fill in the writeable buffers. */
85                 /* why we're getting these I don't know. */
86                 for (i = outlen; i < outlen + inlen; i++) {
87                         if (debug) fprintf(stderr, "CCC: send back empty writeable");
88                         iov[i].length = 0;
89                 }
90                 if (debug) printf("CCC: call add_used\n");
91                 /* host: now ack that we used them all. */
92                 add_used(v, head, outlen+inlen);
93                 if (debug) printf("CCC: DONE call add_used\n");
94         }
95         fprintf(stderr, "All done\n");
96         return NULL;
97 }
98
99 void *consin(void *arg)
100 {
101         struct virtio_threadarg *a = arg;
102         char *line, *outline;
103         static char consline[128];
104         static struct scatterlist iov[32];
105         static struct scatterlist out[] = { {NULL, sizeof(outline)}, };
106         static struct scatterlist in[] = { {NULL, sizeof(line)}, };
107
108         static unsigned int inlen, outlen, conslen;
109         struct virtqueue *v = a->arg->virtio;
110         fprintf(stderr, "consin thread ..\n");
111         uint16_t head, gaveit = 0, gotitback = 0;
112         uint32_t vv;
113         int i;
114         int num;
115         
116         if (debug) printf("Spin on console being read, print num queues, halt\n");
117
118         for(num = 0;;num++) {
119                 int debug = 1;
120                 /* host: use any buffers we should have been sent. */
121                 head = wait_for_vq_desc(v, iov, &outlen, &inlen);
122                 if (debug)
123                         printf("vq desc head %d, gaveit %d gotitback %d\n", head, gaveit, gotitback);
124                 for(i = 0; debug && i < outlen + inlen; i++)
125                         printf("v[%d/%d] v %p len %d\n", i, outlen + inlen, iov[i].v, iov[i].length);
126                 if (debug)
127                         printf("outlen is %d; inlen is %d\n", outlen, inlen);
128                 /* host: fill in the writeable buffers. */
129                 for (i = outlen; i < outlen + inlen; i++) {
130                         /* host: read a line. */
131                         memset(consline, 0, 128);
132                         if (fgets(consline, 4096-256, stdin) == NULL) {
133                                 exit(0);
134                         } 
135                         if (debug) printf("GOT A LINE:%s:\n", consline);
136                         memmove(iov[i].v, consline, strlen(consline)+ 1);
137                         iov[i].length = strlen(consline) + 1;
138                 }
139                 if (debug) printf("call add_used\n");
140                 /* host: now ack that we used them all. */
141                 add_used(v, head, outlen+inlen);
142                 if (debug) printf("DONE call add_used\n");
143         }
144         fprintf(stderr, "All done\n");
145         return NULL;
146 }
147
148 static struct vqdev vqdev= {
149 name: "console",
150 dev: VIRTIO_ID_CONSOLE,
151 device_features: 0, /* Can't do it: linux console device does not support it. VIRTIO_F_VERSION_1*/
152 numvqs: 2,
153 vqs: {
154                 {name: "consin", maxqnum: 64, f: &consin, arg: (void *)0},
155                 {name: "consout", maxqnum: 64, f: consout, arg: (void *)0},
156         }
157 };
158
159 int main(int argc, char **argv)
160 {
161         uint64_t virtiobase = 0x100000000ULL;
162         struct vmctl vmctl;
163         int amt;
164         int vmmflags = 0; // Disabled probably forever. VMM_VMCALL_PRINTF;
165         uint64_t entry = 0x1000000, kerneladdress = 0x1000000;
166         int nr_gpcs = 1;
167         int fd = open("#c/vmctl", O_RDWR), ret;
168         void * x;
169         int kfd = -1;
170         static char cmd[512];
171         void *coreboot_tables = (void *) 0x1165000;
172
173         // mmap is not working for us at present.
174         if ((uint64_t)_kernel > GKERNBASE) {
175                 printf("kernel array @%p is above , GKERNBASE@%p sucks\n", _kernel, GKERNBASE);
176                 exit(1);
177         }
178         memset(_kernel, 0, sizeof(_kernel));
179
180         if (fd < 0) {
181                 perror("#cons/sysctl");
182                 exit(1);
183         }
184         argc--,argv++;
185         // switches ...
186         // Sorry, I don't much like the gnu opt parsing code.
187         while (1) {
188                 if (*argv[0] != '-')
189                         break;
190                 switch(argv[0][1]) {
191                 case 'v':
192                         vmmflags |= VMM_VMCALL_PRINTF;
193                         break;
194                 default:
195                         printf("BMAFR\n");
196                         break;
197                 }
198                 argc--,argv++;
199         }
200         if (argc < 1) {
201                 fprintf(stderr, "Usage: %s vmimage [-n (no vmcall printf)] [coreboot_tables [loadaddress [entrypoint]]]\n", argv[0]);
202                 exit(1);
203         }
204         if (argc > 1)
205                 coreboot_tables = (void *) strtoull(argv[1], 0, 0);
206         if (argc > 2)
207                 kerneladdress = strtoull(argv[2], 0, 0);
208         if (argc > 3)
209                 entry = strtoull(argv[3], 0, 0);
210         kfd = open(argv[0], O_RDONLY);
211         if (kfd < 0) {
212                 perror(argv[0]);
213                 exit(1);
214         }
215         // read in the kernel.
216         x = (void *)kerneladdress;
217         for(;;) {
218                 amt = read(kfd, x, 1048576);
219                 if (amt < 0) {
220                         perror("read");
221                         exit(1);
222                 }
223                 if (amt == 0) {
224                         break;
225                 }
226                 x += amt;
227         }
228         fprintf(stderr, "Read in %d bytes\n", x-kerneladdress);
229
230         fprintf(stderr, "Run with %d cores and vmmflags 0x%x\n", nr_gpcs, vmmflags);
231         if (ros_syscall(SYS_setup_vmm, nr_gpcs, vmmflags, 0, 0, 0, 0) != nr_gpcs) {
232                 perror("Guest pcore setup failed");
233                 exit(1);
234         }
235         /* blob that is faulted in from the EPT first.  we need this to be in low
236          * memory (not above the normal mmap_break), so the EPT can look it up.
237          * Note that we won't get 4096.  The min is 1MB now, and ld is there. */
238         mmap_blob = mmap((int*)4096, PGSIZE, PROT_READ | PROT_WRITE,
239                          MAP_ANONYMOUS, -1, 0);
240         if (mmap_blob == MAP_FAILED) {
241                 perror("Unable to mmap");
242                 exit(1);
243         }
244
245         mcp = 1;
246         if (mcp) {
247                 my_retvals = malloc(sizeof(void*) * nr_threads);
248                 if (!my_retvals)
249                         perror("Init threads/malloc");
250
251                 pthread_can_vcore_request(FALSE);       /* 2LS won't manage vcores */
252                 pthread_need_tls(FALSE);
253                 pthread_mcp_init();                                     /* gives us one vcore */
254                 vcore_request(nr_threads - 1);          /* ghetto incremental interface */
255                 for (int i = 0; i < nr_threads; i++) {
256                         x = __procinfo.vcoremap;
257                         printf("%p\n", __procinfo.vcoremap);
258                         printf("Vcore %d mapped to pcore %d\n", i,
259                                 __procinfo.vcoremap[i].pcoreid);
260                 }
261         }
262
263         ret = syscall(33, 1);
264         if (ret < 0) {
265                 perror("vm setup");
266                 exit(1);
267         }
268         ret = posix_memalign((void **)&p512, 4096, 3*4096);
269         printf("memalign is %p\n", p512);
270         if (ret) {
271                 perror("ptp alloc");
272                 exit(1);
273         }
274         p1 = &p512[512];
275         p2m = &p512[1024];
276         uint64_t kernbase = 0; //0xffffffff80000000;
277         uint64_t highkernbase = 0xffffffff80000000;
278         p512[PML4(kernbase)] = (unsigned long long)p1 | 7;
279         p1[PML3(kernbase)] = /*0x87; */(unsigned long long)p2m | 7;
280         p512[PML4(highkernbase)] = (unsigned long long)p1 | 7;
281         p1[PML3(highkernbase)] = /*0x87; */(unsigned long long)p2m | 7;
282 #define _2MiB (0x200000)
283         int i;
284         for (i = 0; i < 512; i++) {
285                 p2m[PML2(kernbase + i * _2MiB)] = 0x87 | i * _2MiB;
286         }
287
288         kernbase >>= (0+12);
289         kernbase <<= (0 + 12);
290         uint8_t *kernel = (void *)GKERNBASE;
291         //write_coreboot_table(coreboot_tables, ((void *)VIRTIOBASE) /*kernel*/, KERNSIZE + 1048576);
292         hexdump(stdout, coreboot_tables, 512);
293         printf("kernbase for pml4 is 0x%llx and entry is %llx\n", kernbase, entry);
294         printf("p512 %p p512[0] is 0x%lx p1 %p p1[0] is 0x%x\n", p512, p512[0], p1, p1[0]);
295         vmctl.command = REG_RSP_RIP_CR3;
296         vmctl.cr3 = (uint64_t) p512;
297         vmctl.regs.tf_rip = entry;
298         vmctl.regs.tf_rsp = (uint64_t) &stack[1024];
299         if (mcp) {
300                 /* set up virtio bits, which depend on threads being enabled. */
301                 register_virtio_mmio(&vqdev, virtio_mmio_base);
302         }
303         printf("threads started\n");
304         printf("Writing command :%s:\n", cmd);
305
306         ret = write(fd, &vmctl, sizeof(vmctl));
307         if (ret != sizeof(vmctl)) {
308                 perror(cmd);
309         }
310         while (1) {
311                 void showstatus(FILE *f, struct vmctl *v);
312                 int c;
313                 uint8_t byte;
314                 vmctl.command = REG_RIP;
315                 if (debug) printf("RESUME?\n");
316                 //c = getchar();
317                 //if (c == 'q')
318                         //break;
319                 if (debug) printf("RIP %p, shutdown 0x%x\n", vmctl.regs.tf_rip, vmctl.shutdown);
320                 //showstatus(stdout, &vmctl);
321                 // this will be in a function, someday.
322                 // A rough check: is the GPA 
323                 if ((vmctl.shutdown == SHUTDOWN_EPT_VIOLATION) && ((vmctl.gpa & ~0xfffULL) == virtiobase)) {
324                         if (debug) printf("DO SOME VIRTIO\n");
325                         virtio_mmio(&vmctl);
326                         vmctl.shutdown = 0;
327                         vmctl.gpa = 0;
328                         vmctl.command = REG_ALL;
329                 }
330                 if (vmctl.shutdown == SHUTDOWN_UNHANDLED_EXIT_REASON) {
331                         switch(vmctl.ret_code){
332                         case  EXIT_REASON_VMCALL:
333                                 byte = vmctl.regs.tf_rdi;
334                                 printf("%c", byte);
335                                 if (byte == '\n') printf("%c", 'V');
336                                 vmctl.regs.tf_rip += 3;
337                                 break;
338                         default:
339                                 fprintf(stderr, "Don't know how to handle exit %d\n", vmctl.ret_code);
340                                 quit = 1;
341                                 break;
342                         }
343                 }
344                 if (quit)
345                         break;
346                 if (debug) printf("NOW DO A RESUME\n");
347                 ret = write(fd, &vmctl, sizeof(vmctl));
348                 if (ret != sizeof(vmctl)) {
349                         perror(cmd);
350                 }
351         }
352
353         printf("shared is %d, blob is %d\n", shared, *mmap_blob);
354
355         quit = 1;
356         /* later. 
357         for (int i = 0; i < nr_threads-1; i++) {
358                 int ret;
359                 if (pthread_join(my_threads[i], &my_retvals[i]))
360                         perror("pth_join failed");
361                 printf("%d %d\n", i, ret);
362         }
363  */
364
365         return 0;
366 }