vmm: Moves gpci into guest_thread
[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 <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 <ros/arch/mmu.h>
20 #include <ros/arch/membar.h>
21 #include <ros/vmm.h>
22 #include <parlib/uthread.h>
23 #include <vmm/linux_bootparam.h>
24 #include <getopt.h>
25 #include <parlib/alarm.h>
26
27 #include <vmm/virtio.h>
28 #include <vmm/virtio_blk.h>
29 #include <vmm/virtio_mmio.h>
30 #include <vmm/virtio_ids.h>
31 #include <vmm/virtio_config.h>
32 #include <vmm/virtio_console.h>
33 #include <vmm/virtio_net.h>
34 #include <vmm/virtio_lguest_console.h>
35
36 #include <vmm/sched.h>
37 #include <vmm/net.h>
38 #include <sys/eventfd.h>
39 #include <sys/uio.h>
40 #include <parlib/opts.h>
41
42 struct virtual_machine local_vm = {.root_mtx = UTH_MUTEX_INIT},
43                             *vm = &local_vm;
44
45 struct vmm_gpcore_init *gpcis;
46
47 void vapic_status_dump(FILE *f, void *vapic);
48
49 #if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 1)
50 #error "Get a gcc newer than 4.4.0"
51 #else
52 #define BITOP_ADDR(x) "+m" (*(volatile long *) (x))
53 #endif
54
55 static void virtio_poke_guest(uint8_t vec, uint32_t dest)
56 {
57         if (dest < vm->nr_gpcs) {
58                 vmm_interrupt_guest(vm, dest, vec);
59                 return;
60         }
61         if (dest != 0xffffffff)
62                 panic("INVALID DESTINATION: 0x%02x\n", dest);
63
64         for (int i = 0; i < vm->nr_gpcs; i++)
65                 vmm_interrupt_guest(vm, i, vec);
66 }
67
68 static struct virtio_mmio_dev cons_mmio_dev = {
69         .poke_guest = virtio_poke_guest,
70 };
71
72 static struct virtio_console_config cons_cfg;
73 static struct virtio_console_config cons_cfg_d;
74
75 static struct virtio_vq_dev cons_vqdev = {
76         .name = "console",
77         .dev_id = VIRTIO_ID_CONSOLE,
78         .dev_feat =
79         (1ULL << VIRTIO_F_VERSION_1) | (1 << VIRTIO_RING_F_INDIRECT_DESC),
80         .num_vqs = 2,
81         .cfg = &cons_cfg,
82         .cfg_d = &cons_cfg_d,
83         .cfg_sz = sizeof(struct virtio_console_config),
84         .transport_dev = &cons_mmio_dev,
85         .vqs = {
86                 {
87                         .name = "cons_receiveq",
88                         .qnum_max = 64,
89                         .srv_fn = cons_receiveq_fn,
90                         .vqdev = &cons_vqdev
91                 },
92                 {
93                         .name = "cons_transmitq",
94                         .qnum_max = 64,
95                         .srv_fn = cons_transmitq_fn,
96                         .vqdev = &cons_vqdev
97                 },
98         }
99 };
100
101 static struct virtio_mmio_dev net_mmio_dev = {
102         .poke_guest = virtio_poke_guest,
103 };
104
105 static struct virtio_net_config net_cfg = {
106         .max_virtqueue_pairs = 1
107 };
108 static struct virtio_net_config net_cfg_d = {
109         .max_virtqueue_pairs = 1
110 };
111
112 static struct virtio_vq_dev net_vqdev = {
113         .name = "network",
114         .dev_id = VIRTIO_ID_NET,
115         .dev_feat = (1ULL << VIRTIO_F_VERSION_1 | 1 << VIRTIO_NET_F_MAC),
116
117         .num_vqs = 2,
118         .cfg = &net_cfg,
119         .cfg_d = &net_cfg_d,
120         .cfg_sz = sizeof(struct virtio_net_config),
121         .transport_dev = &net_mmio_dev,
122         .vqs = {
123                 {
124                         .name = "net_receiveq",
125                         .qnum_max = 64,
126                         .srv_fn = net_receiveq_fn,
127                         .vqdev = &net_vqdev
128                 },
129                 {
130                         .name = "net_transmitq",
131                         .qnum_max = 64,
132                         .srv_fn = net_transmitq_fn,
133                         .vqdev = &net_vqdev
134                 },
135         }
136 };
137
138 static struct virtio_mmio_dev blk_mmio_dev = {
139         .poke_guest = virtio_poke_guest,
140 };
141
142 static struct virtio_blk_config blk_cfg = {
143 };
144
145 static struct virtio_blk_config blk_cfg_d = {
146 };
147
148 static struct virtio_vq_dev blk_vqdev = {
149         .name = "block",
150         .dev_id = VIRTIO_ID_BLOCK,
151         .dev_feat = (1ULL << VIRTIO_F_VERSION_1),
152
153         .num_vqs = 1,
154         .cfg = &blk_cfg,
155         .cfg_d = &blk_cfg_d,
156         .cfg_sz = sizeof(struct virtio_blk_config),
157         .transport_dev = &blk_mmio_dev,
158         .vqs = {
159                 {
160                         .name = "blk_request",
161                         .qnum_max = 64,
162                         .srv_fn = blk_request,
163                         .vqdev = &blk_vqdev
164                 },
165         }
166 };
167
168 /* Parse func: given a line of text, it sets any vnet options */
169 static void __parse_vnet_opts(char *_line)
170 {
171         char *eq, *spc;
172
173         /* Check all bools first */
174         if (!strcmp(_line, "snoop")) {
175                 vnet_snoop = TRUE;
176                 return;
177         }
178         if (!strcmp(_line, "map_diagnostics")) {
179                 vnet_map_diagnostics = TRUE;
180                 return;
181         }
182         if (!strcmp(_line, "real_address")) {
183                 vnet_real_ip_addrs = TRUE;
184                 return;
185         }
186         /* Numeric fields, must have an = */
187         eq = strchr(_line, '=');
188         if (!eq)
189                 return;
190         *eq++ = 0;
191         /* Drop spaces before =.  atoi trims any spaces after =. */
192         while ((spc = strrchr(_line, ' ')))
193                 *spc = 0;
194         if (!strcmp(_line, "nat_timeout")) {
195                 vnet_nat_timeout = atoi(eq);
196                 return;
197         }
198 }
199
200 static void set_vnet_opts(char *net_opts)
201 {
202         if (parse_opts_file(net_opts, __parse_vnet_opts))
203                 perror("parse opts file");
204 }
205
206 /* Parse func: given a line of text, it builds any vnet port forwardings. */
207 static void __parse_vnet_port_fwds(char *_line)
208 {
209         char *tok, *tok_save = 0;
210         char *proto, *host_port, *guest_port;
211
212         tok = strtok_r(_line, ":", &tok_save);
213         if (!tok)
214                 return;
215         if (strcmp(tok, "port"))
216                 return;
217         tok = strtok_r(NULL, ":", &tok_save);
218         if (!tok) {
219                 fprintf(stderr, "%s, port with no proto!", __func__);
220                 return;
221         }
222         proto = tok;
223         tok = strtok_r(NULL, ":", &tok_save);
224         if (!tok) {
225                 fprintf(stderr, "%s, port with no host port!", __func__);
226                 return;
227         }
228         host_port = tok;
229         tok = strtok_r(NULL, ":", &tok_save);
230         if (!tok) {
231                 fprintf(stderr, "%s, port with no guest port!", __func__);
232                 return;
233         }
234         guest_port = tok;
235         vnet_port_forward(proto, host_port, guest_port);
236 }
237
238 static void set_vnet_port_fwds(char *net_opts)
239 {
240         if (parse_opts_file(net_opts, __parse_vnet_port_fwds))
241                 perror("parse opts file");
242 }
243
244 /* We map the APIC-access page, the per core Virtual APIC page and the
245  * per core Posted Interrupt Descriptors.
246  * Note: check if the PID/PIR needs to be a 4k page. */
247 void alloc_intr_pages(void)
248 {
249         void *a_page;
250         void *pages, *pir;
251
252         a_page = mmap((void *)APIC_GPA, PGSIZE, PROT_READ | PROT_WRITE,
253                       MAP_POPULATE | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
254         fprintf(stderr, "a_page mmap pointer %p\n", a_page);
255
256         if (a_page != (void *)APIC_GPA) {
257                 perror("Could not mmap APIC");
258                 exit(1);
259         }
260         /* The VM should never actually read from this page. */
261         for (int i = 0; i < PGSIZE/4; i++)
262                 ((uint32_t *)a_page)[i] = 0xDEADBEEF;
263
264         /* Allocate VAPIC and PIR pages. */
265         pages = mmap((void*)0, vm->nr_gpcs * 2 * PGSIZE, PROT_READ | PROT_WRITE,
266                      MAP_POPULATE | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
267         if (pages == MAP_FAILED) {
268                 perror("Unable to map VAPIC and PIR pages.");
269                 exit(1);
270         }
271
272         /* We use the first vm->nr_gpcs pages for the VAPIC, and the second set
273          * for the PIRs. Each VAPIC and PIR gets its own 4k page. */
274         pir = pages + (vm->nr_gpcs * PGSIZE);
275
276         /* Set the addresses in the gpcis.  These gpcis get copied into the
277          * guest_threads during their construction. */
278         for (int i = 0; i < vm->nr_gpcs; i++) {
279                 gpcis[i].posted_irq_desc = pir + (PGSIZE * i);
280                 gpcis[i].vapic_addr = pages + (PGSIZE * i);
281                 gpcis[i].apic_addr = a_page;
282                 gpcis[i].fsbase = 0;
283                 gpcis[i].gsbase = 0;
284
285                 /* Set APIC ID. */
286                 ((uint32_t *)gpcis[i].vapic_addr)[0x20/4] = i;
287                 /* Set APIC VERSION. */
288                 ((uint32_t *)gpcis[i].vapic_addr)[0x30/4] = 0x01060015;
289                 /* Set LOGICAL APIC ID. */
290                 ((uint32_t *)gpcis[i].vapic_addr)[0xD0/4] = 1 << i;
291         }
292 }
293
294 /* This rams all cores with a valid timer vector and initial count
295  * with a timer interrupt. Used only for debugging/as a temporary workaround */
296 void *inject_timer_spurious(void *args)
297 {
298         struct vmm_gpcore_init *curgpci;
299         uint32_t initial_count;
300         uint8_t vector;
301
302         for (int i = 0; i < vm->nr_gpcs; i++) {
303                 curgpci = gth_to_gpci(gpcid_to_gth(vm, i));
304                 vector = ((uint32_t *)curgpci->vapic_addr)[0x32] & 0xff;
305                 initial_count = ((uint32_t *)curgpci->vapic_addr)[0x38];
306                 if (initial_count && vector)
307                         vmm_interrupt_guest(vm, i, vector);
308         }
309         return 0;
310 }
311
312 /* This injects the timer interrupt to the guest. */
313 void *inject_timer(void *args)
314 {
315         struct guest_thread *gth = (struct guest_thread*)args;
316         struct vmm_gpcore_init *gpci = gth_to_gpci(gth);
317         uint8_t vector = ((uint32_t *)gpci->vapic_addr)[0x32] & 0xff;
318
319         vmm_interrupt_guest(vm, gth->gpc_id, vector);
320         return 0;
321 }
322
323 /* This handler must never call __set_alarm after interrupting the guest,
324  * otherwise the guest could try to write to the timer msrs and cause a
325  * race condition. */
326 void timer_alarm_handler(struct alarm_waiter *waiter)
327 {
328         uint8_t vector;
329         uint32_t initial_count;
330         uint32_t divide_config_reg;
331         uint32_t multiplier;
332         uint32_t timer_mode;
333         struct guest_thread *gth = (struct guest_thread*)waiter->data;
334         struct vmm_gpcore_init *gpci = gth_to_gpci(gth);
335
336         vector = ((uint32_t *)gpci->vapic_addr)[0x32] & 0xff;
337         timer_mode = (((uint32_t *)gpci->vapic_addr)[0x32] >> 17) & 0x03;
338         initial_count = ((uint32_t *)gpci->vapic_addr)[0x38];
339         divide_config_reg = ((uint32_t *)gpci->vapic_addr)[0x3E];
340
341         /* Don't blame me for this. Look at the intel manual
342          * Vol 3 10.5.4 APIC Timer */
343         multiplier = (((divide_config_reg & 0x08) >> 1) |
344                       (divide_config_reg & 0x03)) + 1;
345         multiplier &= 0x07;
346
347         if (vector && initial_count && timer_mode == 0x01) {
348                 /* This is periodic, we reset the alarm */
349                 set_awaiter_rel(waiter, initial_count << multiplier);
350                 __set_alarm(waiter);
351         }
352
353         /* We spin up a task to inject the timer because vmm_interrupt_guest
354          * may block and we can't do that from vcore context. */
355         vmm_run_task(vm, inject_timer, gth);
356 }
357
358 /* This sets up the structs for each of the guest pcore's timers, but
359  * doesn't actually start the alarms until the core writes all the reasonable
360  * values to the x2apic msrs. */
361 void init_timer_alarms(void)
362 {
363         for (uint64_t i = 0; i < vm->nr_gpcs; i++) {
364                 struct alarm_waiter *timer_alarm = malloc(sizeof(struct alarm_waiter));
365                 struct guest_thread *gth = gpcid_to_gth(vm, i);
366
367                 /* TODO: consider a struct to bundle a bunch of things, not just
368                  * timer_alarm. */
369                 gth->user_data = (void *)timer_alarm;
370                 timer_alarm->data = gth;
371                 init_awaiter(timer_alarm, timer_alarm_handler);
372         }
373 }
374
375 int main(int argc, char **argv)
376 {
377         int debug = 0;
378         unsigned long long memsize = GiB;
379         uintptr_t memstart = MinMemory;
380         uintptr_t memend;
381         struct boot_params *bp;
382         char cmdline_default[512] = {0};
383         char *cmdline_extra = "\0";
384         char *cmdline;
385         void *a = (void *)0xe0000;
386         int vmmflags = 0;
387         uint64_t entry = 0;
388         int ret;
389         struct vm_trapframe *vm_tf;
390         uint64_t tsc_freq_khz;
391         char *cmdlinep;
392         int cmdlinesz, len, cmdline_fd;
393         char *disk_image_file = NULL;
394         int c;
395         struct stat stat_result;
396         int num_read;
397         int option_index;
398         char *smbiostable = NULL;
399         char *net_opts = NULL;
400         uint64_t num_pcs = 1;
401         bool is_greedy = FALSE;
402         bool is_scp = FALSE;
403         char *initrd = NULL;
404         uint64_t initrd_start = 0, initrd_size = 0;
405         uint64_t kernel_max_address;
406
407         static struct option long_options[] = {
408                 {"debug",         no_argument,       0, 'd'},
409                 {"vmm_vmcall",    no_argument,       0, 'v'},
410                 {"maxresume",     required_argument, 0, 'R'},
411                 {"memsize",       required_argument, 0, 'm'},
412                 {"memstart",      required_argument, 0, 'M'},
413                 {"cmdline_extra", required_argument, 0, 'c'},
414                 {"greedy",        no_argument,       0, 'g'},
415                 {"initrd",        required_argument, 0, 'i'},
416                 {"scp",           no_argument,       0, 's'},
417                 {"image_file",    required_argument, 0, 'f'},
418                 {"cmdline",       required_argument, 0, 'k'},
419                 {"net",           required_argument, 0, 'n'},
420                 {"num_cores",     required_argument, 0, 'N'},
421                 {"smbiostable",   required_argument, 0, 't'},
422                 {"help",          no_argument,       0, 'h'},
423                 {0, 0, 0, 0}
424         };
425
426         fprintf(stderr, "%p %p %p %p\n", PGSIZE, PGSHIFT, PML1_SHIFT,
427                 PML1_PTE_REACH);
428
429         if ((uintptr_t)__procinfo.program_end >= MinMemory) {
430                 fprintf(stderr,
431                         "Panic: vmrunkernel binary extends into guest memory\n");
432                 exit(1);
433         }
434
435         vm->low4k = malloc(PGSIZE);
436         memset(vm->low4k, 0xff, PGSIZE);
437         vm->low4k[0x40e] = 0;
438         vm->low4k[0x40f] = 0;
439         // Why is this here? Because the static initializer is getting
440         // set to 1.  Yes, 1. This might be part of the weirdness
441         // Barrett is reporting with linker sets. So let's leave it
442         // here until we trust our toolchain.
443         if (memsize != GiB)
444                 fprintf(stderr, "static initializers are broken\n");
445         memsize = GiB;
446
447         while ((c = getopt_long(argc, argv, "dvi:m:M:c:gsf:k:N:n:t:hR:",
448                                 long_options, &option_index)) != -1) {
449                 switch (c) {
450                 case 'd':
451                         debug++;
452                         break;
453                 case 'v':
454                         vmmflags |= VMM_CTL_FL_KERN_PRINTC;
455                         break;
456                 case 'm':
457                         memsize = strtoull(optarg, 0, 0);
458                         break;
459                 case 'M':
460                         memstart = strtoull(optarg, 0, 0);
461                         break;
462                 case 'c':
463                         cmdline_extra = optarg;
464                 case 'g':       /* greedy */
465                         parlib_never_yield = TRUE;
466                         if (is_scp) {
467                                 fprintf(stderr, "Can't be both greedy and an SCP\n");
468                                 exit(1);
469                         }
470                         is_greedy = TRUE;
471                         break;
472                 case 's':       /* scp */
473                         parlib_wants_to_be_mcp = FALSE;
474                         if (is_greedy) {
475                                 fprintf(stderr, "Can't be both greedy and an SCP\n");
476                                 exit(1);
477                         }
478                         is_scp = TRUE;
479                         break;
480                 case 'f':       /* file to pass to blk_init */
481                         disk_image_file = optarg;
482                         break;
483                 case 'i':
484                         initrd = optarg;
485                         break;
486                 case 'k':       /* specify file to get cmdline args from */
487                         cmdline_fd = open(optarg, O_RDONLY);
488                         if (cmdline_fd < 0) {
489                                 fprintf(stderr, "failed to open file: %s\n", optarg);
490                                 exit(1);
491                         }
492                         if (stat(optarg, &stat_result) == -1) {
493                                 fprintf(stderr, "stat of %s failed\n", optarg);
494                                 exit(1);
495                         }
496                         len = stat_result.st_size;
497                         if (len > 512) {
498                                 fprintf(stderr, "command line options exceed 512 bytes!");
499                                 exit(1);
500                         }
501                         num_read = read(cmdline_fd, cmdline_default, len);
502                         if (num_read != len) {
503                                 fprintf(stderr, "read failed len was : %d, num_read was: %d\n",
504                                         len, num_read);
505                                 exit(1);
506                         }
507                         close(cmdline_fd);
508                         break;
509                 case 't':
510                         smbiostable = optarg;
511                         break;
512                 case 'n':
513                         net_opts = optarg;
514                         break;
515                 case 'N':
516                         num_pcs = strtoull(optarg, 0, 0);
517                         break;
518                 case 'h':
519                 default:
520                         // Sadly, the getopt_long struct does
521                         // not have a pointer to help text.
522                         for (int i = 0;
523                              i < sizeof(long_options)/sizeof(long_options[0]) - 1;
524                              i++) {
525                                 struct option *l = &long_options[i];
526
527                                 fprintf(stderr, "%s or %c%s\n", l->name, l->val,
528                                         l->has_arg ? " <arg>" : "");
529                         }
530                         exit(0);
531                 }
532         }
533
534         if (strlen(cmdline_default) == 0) {
535                 fprintf(stderr, "WARNING: No command line parameter file specified.\n");
536         }
537         argc -= optind;
538         argv += optind;
539         if (argc < 1) {
540                 fprintf(stderr, "Usage: %s vmimage [-n (no vmcall printf)]\n", argv[0]);
541                 exit(1);
542         }
543
544         // Set vm->nr_gpcs before it's referenced in the struct setups below.
545         vm->nr_gpcs = num_pcs;
546         fprintf(stderr, "NUM PCS: %d\n", num_pcs);
547         /* These are only used to be passed to vmm_init, which makes copies
548          * internally */
549         gpcis = (struct vmm_gpcore_init *)
550                         malloc(num_pcs * sizeof(struct vmm_gpcore_init));
551         alloc_intr_pages();
552
553         memend = memstart + memsize - 1;
554         if (memend >= BRK_START) {
555                 fprintf(stderr,
556                         "memstart 0x%llx memsize 0x%llx -> 0x%llx is too large; overlaps BRK_START at %p\n",
557                         memstart, memsize, memstart + memsize, BRK_START);
558                 exit(1);
559         }
560
561         mmap_memory(vm, memstart, memsize);
562
563         entry = load_elf(argv[0], 0, &kernel_max_address, NULL);
564         if (entry == 0) {
565                 fprintf(stderr, "Unable to load kernel %s\n", argv[0]);
566                 exit(1);
567         }
568
569         a = setup_biostables(vm, a, smbiostable);
570
571         bp = a;
572         a = init_e820map(vm, bp);
573
574         if (initrd) {
575                 initrd_start = ROUNDUP(kernel_max_address, PGSIZE);
576                 fprintf(stderr, "kernel_max_address is %#p; Load initrd @ %#p\n",
577                         kernel_max_address, initrd_start);
578                 initrd_size = setup_initrd(initrd, (void *)initrd_start,
579                                            memend - initrd_start + 1);
580                 if (initrd_size <= 0) {
581                         fprintf(stderr, "Unable to load initrd %s\n", initrd);
582                         exit(1);
583                 }
584
585                 bp->hdr.ramdisk_image = initrd_start;
586                 bp->hdr.ramdisk_size = initrd_size;
587                 bp->hdr.root_dev = 0x100;
588                 bp->hdr.type_of_loader = 0xff;
589                 fprintf(stderr, "Set bp initrd to %p / %p\n",
590                         initrd_start, initrd_size);
591         }
592
593         /* The MMIO address of the console device is really the address of an
594          * unbacked EPT page: accesses to this page will cause a page fault that
595          * traps to the host, which will examine the fault, see it was for the
596          * known MMIO address, and fulfill the MMIO read or write on the guest's
597          * behalf accordingly. We place the virtio space at 512 GB higher than the
598          * guest physical memory to avoid a full page table walk. */
599         uintptr_t virtio_mmio_base_addr_hint;
600         uintptr_t virtio_mmio_base_addr;
601
602         virtio_mmio_base_addr_hint =
603             ROUNDUP((bp->e820_map[bp->e820_entries - 1].addr +
604                      bp->e820_map[bp->e820_entries - 1].size),
605                      PML4_PTE_REACH);
606
607         /* mmap with prot_none so we don't accidentally mmap something else here.
608          * We give space for 512 devices right now.
609          * TODO(ganshun): Make it dynamic based on number of virtio devices. */
610         virtio_mmio_base_addr =
611             (uintptr_t) mmap((void *) virtio_mmio_base_addr_hint, 512 * PGSIZE,
612                              PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
613
614         if (!virtio_mmio_base_addr || virtio_mmio_base_addr >= BRK_START) {
615                 /* Either we were unable to mmap at all or we mapped it too high. */
616                 panic("Unable to mmap protect space for virtio devices, got 0x%016x",
617                       virtio_mmio_base_addr);
618         }
619
620         cons_mmio_dev.addr =
621                 virtio_mmio_base_addr + PGSIZE * VIRTIO_MMIO_CONSOLE_DEV;
622         cons_mmio_dev.vqdev = &cons_vqdev;
623         vm->virtio_mmio_devices[VIRTIO_MMIO_CONSOLE_DEV] = &cons_mmio_dev;
624
625         net_mmio_dev.addr =
626                 virtio_mmio_base_addr + PGSIZE * VIRTIO_MMIO_NETWORK_DEV;
627         net_mmio_dev.vqdev = &net_vqdev;
628         vm->virtio_mmio_devices[VIRTIO_MMIO_NETWORK_DEV] = &net_mmio_dev;
629
630         if (disk_image_file != NULL) {
631                 blk_mmio_dev.addr =
632                         virtio_mmio_base_addr + PGSIZE * VIRTIO_MMIO_BLOCK_DEV;
633                 blk_mmio_dev.vqdev = &blk_vqdev;
634                 vm->virtio_mmio_devices[VIRTIO_MMIO_BLOCK_DEV] = &blk_mmio_dev;
635                 blk_init_fn(&blk_vqdev, disk_image_file);
636         }
637
638         set_vnet_opts(net_opts);
639         vnet_init(vm, &net_vqdev);
640         set_vnet_port_fwds(net_opts);
641
642         /* Set the kernel command line parameters */
643         a += 4096;
644         cmdline = a;
645         a += 4096;
646
647         bp->hdr.cmd_line_ptr = (uintptr_t) cmdline;
648
649         tsc_freq_khz = get_tsc_freq()/1000;
650         len = snprintf(cmdline, 4096, "%s tscfreq=%lld %s", cmdline_default,
651                        tsc_freq_khz, cmdline_extra);
652
653         cmdlinesz = 4096 - len;
654         cmdlinep = cmdline + len;
655
656         for (int i = 0; i < VIRTIO_MMIO_MAX_NUM_DEV; i++) {
657                 if (vm->virtio_mmio_devices[i] == NULL)
658                         continue;
659
660                 /* Append all the virtio mmio base addresses. */
661
662                 /* Since the lower number irqs are no longer being used, the irqs
663                  * can now be assigned starting from 0.
664                  */
665                 vm->virtio_mmio_devices[i]->irq = i;
666                 len = snprintf(cmdlinep, cmdlinesz,
667                                "\n virtio_mmio.device=1K@0x%llx:%lld",
668                                vm->virtio_mmio_devices[i]->addr,
669                                vm->virtio_mmio_devices[i]->irq);
670                 if (len >= cmdlinesz) {
671                         fprintf(stderr, "Too many arguments to the linux command line.");
672                         exit(1);
673                 }
674                 cmdlinesz -= len;
675                 cmdlinep += len;
676         }
677
678         /* Set maxcpus to the number of cores we're giving the guest. */
679         len = snprintf(cmdlinep, cmdlinesz,
680                        "\n maxcpus=%lld\n possible_cpus=%lld", vm->nr_gpcs,
681                        vm->nr_gpcs);
682         if (len >= cmdlinesz) {
683                 fprintf(stderr, "Too many arguments to the linux command line.");
684                 exit(1);
685         }
686         cmdlinesz -= len;
687         cmdlinep += len;
688
689         ret = vmm_init(vm, gpcis, vmmflags);
690         assert(!ret);
691         free(gpcis);
692
693         init_timer_alarms();
694
695         setup_paging(vm);
696
697         vm_tf = gth_to_vmtf(vm->gths[0]);
698         vm_tf->tf_cr3 = (uint64_t) vm->root;
699         vm_tf->tf_rip = entry;
700         vm_tf->tf_rsp = 0xe0000;
701         vm_tf->tf_rsi = (uint64_t) bp;
702         vm->up_gpcs = 1;
703         fprintf(stderr, "Start guest: cr3 %p rip %p\n", vm_tf->tf_cr3, entry);
704         start_guest_thread(vm->gths[0]);
705
706         uthread_sleep_forever();
707         return 0;
708 }