Start supporting virtio stuff in vmrunkernel.c
[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 int mcp = 1;
26
27 #define MiB 0x100000u
28 #define GiB (1u<<30)
29 #define VIRTIOBASE (15*MiB)
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 static void setupconsole(void *v)
61 {
62         // try to make linux happy.
63         // this is not really endian safe but ... well ... WE'RE ON THE SAME MACHINE
64         write32(v+VIRTIO_MMIO_MAGIC_VALUE, ('v' | 'i' << 8 | 'r' << 16 | 't' << 24));
65         // no constant for this is defined anywhere. It's just 1.
66         write32(v+VIRTIO_MMIO_VERSION, 1);
67         write32(v+VIRTIO_MMIO_DEVICE_ID, VIRTIO_ID_CONSOLE);
68         write32(v+VIRTIO_MMIO_QUEUE_NUM_MAX, 1);
69         write32(v+VIRTIO_MMIO_QUEUE_PFN, 0);
70 }
71
72 int debug = 1;
73
74 struct ttargs {
75         void *virtio;
76 };
77
78 void *talk_thread(void *arg)
79 {
80         struct ttargs *a = arg;
81         void *v = a->virtio;
82         fprintf(stderr, "talk thread ..\n");
83         uint16_t head;
84         uint32_t vv;
85         int i;
86         int num;
87         printf("Sleep 15 seconds\n");
88         uthread_sleep(15);
89         printf("----------------------- TT a %p\n", a);
90         printf("talk thread ttargs %x v %x\n", a, v);
91         
92         if (debug) printf("Spin on console being read, print num queues, halt\n");
93         while ((vv = read32(v+VIRTIO_MMIO_DRIVER_FEATURES)) == 0) {
94                 printf("no ready ... \n");
95                 if (debug) {
96                         hexdump(stdout, v, 128);
97                 }
98                 printf("sleep 1 second\n");
99                 uthread_sleep(1);
100         }
101         if (debug)printf("vv %x, set selector %x\n", vv, read32(v + VIRTIO_MMIO_DRIVER_FEATURES_SEL));
102         if (debug) printf("loop forever");
103         while (1)
104                 ;
105         for(num = 0;;num++) {
106                 /* host: use any buffers we should have been sent. */
107                 head = wait_for_vq_desc(guesttocons, iov, &outlen, &inlen);
108                 if (debug)
109                         printf("vq desc head %d, gaveit %d gotitback %d\n", head, gaveit, gotitback);
110                 for(i = 0; debug && i < outlen + inlen; i++)
111                         printf("v[%d/%d] v %p len %d\n", i, outlen + inlen, iov[i].v, iov[i].length);
112                 /* host: if we got an output buffer, just output it. */
113                 for(i = 0; i < outlen; i++) {
114                         num++;
115                         printf("Host:%s:\n", (char *)iov[i].v);
116                 }
117                 
118                 if (debug)
119                         printf("outlen is %d; inlen is %d\n", outlen, inlen);
120                 /* host: fill in the writeable buffers. */
121                 for (i = outlen; i < outlen + inlen; i++) {
122                         /* host: read a line. */
123                         memset(consline, 0, 128);
124                         if (1) {
125                                 if (fgets(consline, 4096-256, stdin) == NULL) {
126                                         exit(0);
127                                 } 
128                                 if (debug) printf("GOT A LINE:%s:\n", consline);
129                         } else {
130                                 sprintf(consline, "hi there. %d\n", i);
131                         }
132                         memmove(iov[i].v, consline, strlen(consline)+ 1);
133                         iov[i].length = strlen(consline) + 1;
134                 }
135                 if (debug) printf("call add_used\n");
136                 /* host: now ack that we used them all. */
137                 add_used(guesttocons, head, outlen+inlen);
138                 if (debug) printf("DONE call add_used\n");
139         }
140         fprintf(stderr, "All done\n");
141         return NULL;
142 }
143
144 struct ttargs t;
145         
146
147 int main(int argc, char **argv)
148 {
149         int amt;
150         int vmmflags = VMM_VMCALL_PRINTF;
151         uint64_t entry = 0x1000000, kerneladdress = 0x1000000;
152         int nr_gpcs = 1;
153         int fd = open("#cons/sysctl", O_RDWR), ret;
154         void * x;
155         int kfd = -1;
156         static char cmd[512];
157         void *coreboot_tables = (void *) 0x1165000;
158         /* kernel has to be in the range VIRTIOBASE to KERNSIZE+GKERNBASE for now. */
159         // mmap is not working for us at present.
160         if ((uint64_t)_kernel > VIRTIOBASE) {
161                 printf("kernel array @%p is above , VIRTIOBASE@%p sucks\n", _kernel, VIRTIOBASE);
162                 exit(1);
163         }
164         memset(_kernel, 0, sizeof(_kernel));
165
166         if (fd < 0) {
167                 perror("#cons/sysctl");
168                 exit(1);
169         }
170         argc--,argv++;
171         // switches ...
172         // Sorry, I don't much like the gnu opt parsing code.
173         while (1) {
174                 if (*argv[0] != '-')
175                         break;
176                 switch(argv[0][1]) {
177                 case 'n':
178                         vmmflags &= ~VMM_VMCALL_PRINTF;
179                         break;
180                 default:
181                         printf("BMAFR\n");
182                         break;
183                 }
184                 argc--,argv++;
185         }
186         if (argc < 1) {
187                 fprintf(stderr, "Usage: %s vmimage [-n (no vmcall printf)] [coreboot_tables [loadaddress [entrypoint]]]\n", argv[0]);
188                 exit(1);
189         }
190         if (argc > 1)
191                 coreboot_tables = (void *) strtoull(argv[1], 0, 0);
192         if (argc > 2)
193                 kerneladdress = strtoull(argv[2], 0, 0);
194         if (argc > 3)
195                 entry = strtoull(argv[3], 0, 0);
196         kfd = open(argv[0], O_RDONLY);
197         if (kfd < 0) {
198                 perror(argv[0]);
199                 exit(1);
200         }
201         // read in the kernel.
202         x = (void *)kerneladdress;
203         for(;;) {
204                 amt = read(kfd, x, 1048576);
205                 if (amt < 0) {
206                         perror("read");
207                         exit(1);
208                 }
209                 if (amt == 0) {
210                         break;
211                 }
212                 x += amt;
213         }
214         fprintf(stderr, "Read in %d bytes\n", x-kerneladdress);
215
216         fprintf(stderr, "Run with %d cores and vmmflags 0x%x\n", nr_gpcs, vmmflags);
217         if (ros_syscall(SYS_setup_vmm, nr_gpcs, vmmflags, 0, 0, 0, 0) != nr_gpcs) {
218                 perror("Guest pcore setup failed");
219                 exit(1);
220         }
221         /* blob that is faulted in from the EPT first.  we need this to be in low
222          * memory (not above the normal mmap_break), so the EPT can look it up.
223          * Note that we won't get 4096.  The min is 1MB now, and ld is there. */
224         mmap_blob = mmap((int*)4096, PGSIZE, PROT_READ | PROT_WRITE,
225                          MAP_ANONYMOUS, -1, 0);
226         if (mmap_blob == MAP_FAILED) {
227                 perror("Unable to mmap");
228                 exit(1);
229         }
230
231         mcp = 1;
232         if (mcp) {
233                 my_threads = malloc(sizeof(pthread_t) * nr_threads);
234                 my_retvals = malloc(sizeof(void*) * nr_threads);
235                 if (!(my_retvals && my_threads))
236                         perror("Init threads/malloc");
237
238                 pthread_can_vcore_request(FALSE);       /* 2LS won't manage vcores */
239                 pthread_need_tls(FALSE);
240                 pthread_mcp_init();                                     /* gives us one vcore */
241                 vcore_request(nr_threads - 1);          /* ghetto incremental interface */
242                 for (int i = 0; i < nr_threads; i++) {
243                         x = __procinfo.vcoremap;
244                         printf("%p\n", __procinfo.vcoremap);
245                         printf("Vcore %d mapped to pcore %d\n", i,
246                                 __procinfo.vcoremap[i].pcoreid);
247                 }
248         }
249
250         t.virtio = (void *)VIRTIOBASE;
251
252         ret = syscall(33, 1);
253         if (ret < 0) {
254                 perror("vm setup");
255                 exit(1);
256         }
257         ret = posix_memalign((void **)&p512, 4096, 3*4096);
258         printf("memalign is %p\n", p512);
259         if (ret) {
260                 perror("ptp alloc");
261                 exit(1);
262         }
263         p1 = &p512[512];
264         p2m = &p512[1024];
265         uint64_t kernbase = 0; //0xffffffff80000000;
266         uint64_t highkernbase = 0xffffffff80000000;
267         p512[PML4(kernbase)] = (unsigned long long)p1 | 7;
268         p1[PML3(kernbase)] = /*0x87; */(unsigned long long)p2m | 7;
269         p512[PML4(highkernbase)] = (unsigned long long)p1 | 7;
270         p1[PML3(highkernbase)] = /*0x87; */(unsigned long long)p2m | 7;
271 #define _2MiB (0x200000)
272         int i;
273         for (i = 0; i < 512; i++) {
274                 p2m[PML2(kernbase + i * _2MiB)] = 0x87 | i * _2MiB;
275         }
276
277         kernbase >>= (0+12);
278         kernbase <<= (0 + 12);
279         uint8_t *kernel = (void *)GKERNBASE;
280         write_coreboot_table(coreboot_tables, ((void *)VIRTIOBASE) /*kernel*/, KERNSIZE + 1048576);
281         hexdump(stdout, coreboot_tables, 512);
282         setupconsole((void *)VIRTIOBASE);
283         hexdump(stdout, (void *)VIRTIOBASE, 128);
284         printf("kernbase for pml4 is 0x%llx and entry is %llx\n", kernbase, entry);
285         printf("p512 %p p512[0] is 0x%lx p1 %p p1[0] is 0x%x\n", p512, p512[0], p1, p1[0]);
286         sprintf(cmd, "V 0x%llx 0x%llx 0x%llx", entry, (unsigned long long) &stack[1024], (unsigned long long) p512);
287         if (mcp) {
288                 if (pthread_create(&my_threads[0], NULL, &talk_thread, &t))
289                         perror("pth_create failed");
290         }
291         printf("threads started\n");
292         printf("Writing command :%s:\n", cmd);
293         // sys_getpcoreid
294         ret = write(fd, cmd, strlen(cmd));
295         if (ret != strlen(cmd)) {
296                 perror(cmd);
297         }
298         sprintf(cmd, "V 0 0 0");
299         while (1) {
300                 int c;
301                 printf("RESUME?\n");
302                 c = getchar();
303                 if (c == 'q')
304                         break;
305                 ret = write(fd, cmd, strlen(cmd));
306                 if (ret != strlen(cmd)) {
307                         perror(cmd);
308                 }
309         }
310         hexdump(stdout, (void *)VIRTIOBASE, 512);
311         printf("shared is %d, blob is %d\n", shared, *mmap_blob);
312
313         for (int i = 0; i < nr_threads-1; i++) {
314                 int ret;
315                 if (pthread_join(my_threads[i], &my_retvals[i]))
316                         perror("pth_join failed");
317                 printf("%d %d\n", i, ret);
318         }
319
320         return 0;
321 }