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