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