Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Multiboot enhancements
[palacios.git] / palacios / src / palacios / vmm_multiboot.c
1 /* 
2  * This file is part of the Palacios Virtual Machine Monitor developed
3  * by the V3VEE Project with funding from the United States National 
4  * Science Foundation and the Department of Energy.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
10  * Copyright (c) 2015, The V3VEE Project <http://www.v3vee.org> 
11  * All rights reserved.
12  *
13  * Author:  Peter Dinda <pdinda@northwestern.edu>
14  *
15  * This is free software.  You are permitted to use,
16  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
17  */
18
19 #include <palacios/vmm_mem.h>
20 #include <palacios/vmm.h>
21 #include <palacios/vmm_util.h>
22 #include <palacios/vmm_emulator.h>
23 #include <palacios/vm_guest.h>
24 #include <palacios/vmm_debug.h>
25 #include <palacios/vmm_hypercall.h>
26
27 #include <palacios/vmm_xml.h>
28
29 #include <palacios/vm_guest_mem.h>
30
31 #include <palacios/vmm_debug.h>
32
33
34 /*
35
36   In a Pal file:
37
38   <files> 
39     <file id="multibootelf" filename="multibootelf.o" />
40   </files>
41
42   <multiboot enable="y" file_id="multibootelf" />
43
44
45 */
46
47 #ifndef V3_CONFIG_DEBUG_MULTIBOOT
48 #undef PrintDebug
49 #define PrintDebug(fmt, args...)
50 #endif
51
52
53 int v3_init_multiboot()
54 {
55     PrintDebug(VM_NONE,VCORE_NONE, "multiboot: init\n");
56     return 0;
57 }
58
59 int v3_deinit_multiboot()
60 {
61     PrintDebug(VM_NONE,VCORE_NONE, "multiboot: deinit\n");
62     return 0;
63 }
64
65
66
67 #define CEIL_DIV(x,y) (((x)/(y)) + !!((x)%(y)))
68
69 int v3_init_multiboot_vm(struct v3_vm_info *vm, struct v3_xml *config)
70 {
71     v3_cfg_tree_t *mb_config;
72     char *enable;
73     char *mb_file_id=0;
74
75     PrintDebug(vm, VCORE_NONE, "multiboot: vm init\n");
76
77     memset(&vm->mb_state,0,sizeof(struct v3_vm_multiboot));
78     vm->mb_state.is_multiboot=0;
79
80     if (!config || !(mb_config=v3_cfg_subtree(config,"multiboot"))) {
81         PrintDebug(vm,VCORE_NONE,"multiboot: no multiboot configuration found - normal boot will occur\n");
82         goto out_ok;
83     }
84     
85     if (!(enable=v3_cfg_val(mb_config,"enable")) || strcasecmp(enable,"y")) {
86         PrintDebug(vm,VCORE_NONE,"multiboot: multiboot configuration disabled\n");
87         goto out_ok;
88     }
89
90     if (!(mb_file_id=v3_cfg_val(mb_config,"file_id"))) { 
91         PrintError(vm,VCORE_NONE,"multiboot: multiboot block without file_id...\n");
92         return -1;
93     }
94
95     vm->mb_state.mb_file = v3_cfg_get_file(vm,mb_file_id);
96     
97     if (!vm->mb_state.mb_file) { 
98         PrintError(vm,VCORE_NONE,"multiboot: multiboot block contains bad file_id (%s)\n",mb_file_id);
99         return -1;
100     }
101
102     vm->mb_state.is_multiboot=1;
103
104  out_ok:
105     if (vm->mb_state.is_multiboot) {
106         V3_Print(vm,VCORE_NONE,"multiboot: file_id=%s (tag %s)]\n",
107                  mb_file_id,
108                  vm->mb_state.mb_file->tag);
109     } else {
110         V3_Print(vm,VCORE_NONE,"multiboot: This is not a multiboot VM\n");
111     }
112     return 0;
113     
114 }
115
116
117 int v3_deinit_multiboot_vm(struct v3_vm_info *vm)
118 {
119     PrintDebug(vm, VCORE_NONE, "multiboot: multiboot VM deinit\n");
120
121     return 0;
122 }
123
124 int v3_init_multiboot_core(struct guest_info *core)
125 {
126     PrintDebug(core->vm_info, VCORE_NONE, "multiboot: multiboot core init\n");
127
128     // Nothing to do at this point
129
130     return 0;
131 }
132
133 int v3_deinit_multiboot_core(struct guest_info *core)
134 {
135     PrintDebug(core->vm_info, VCORE_NONE, "multiboot: multiboot core deinit\n");
136
137     return 0;
138 }
139
140
141
142
143 #define ERROR(fmt, args...) PrintError(VM_NONE,VCORE_NONE,"multiboot: " fmt,##args)
144 #define INFO(fmt, args...) PrintDebug(VM_NONE,VCORE_NONE,"multiboot: " fmt,##args)
145
146
147  
148 /******************************************************************
149      Data contained in the ELF file we will attempt to boot  
150 ******************************************************************/
151
152 #define ELF_MAGIC    0x464c457f
153 #define MB2_MAGIC    0xe85250d6
154
155
156 /******************************************************************
157      Data we will pass to the kernel via rbx
158 ******************************************************************/
159
160 #define MB2_INFO_MAGIC    0x36d76289
161
162 typedef struct mb_info_header {
163     uint32_t  totalsize;
164     uint32_t  reserved;
165 } __attribute__((packed)) mb_info_header_t;
166
167 // A tag of type 0, size 8 indicates last value
168 //
169 typedef struct mb_info_tag {
170     uint32_t  type;
171     uint32_t  size;
172 } __attribute__((packed)) mb_info_tag_t;
173
174
175 #define MB_INFO_MEM_TAG  4
176 typedef struct mb_info_mem {
177     mb_info_tag_t tag;
178     uint32_t  mem_lower; // 0..640K in KB 
179     uint32_t  mem_upper; // in KB to first hole - 1 MB
180 } __attribute__((packed)) mb_info_mem_t;
181
182 #define MB_INFO_CMDLINE_TAG  1
183 // note alignment of 8 bytes required for each... 
184 typedef struct mb_info_cmdline {
185     mb_info_tag_t tag;
186     uint32_t  size;      // includes zero termination
187     uint8_t   string[];  // zero terminated
188 } __attribute__((packed)) mb_info_cmdline_t;
189
190
191 #define MEM_RAM   1
192 #define MEM_ACPI  3
193 #define MEM_RESV  4
194
195 typedef struct mb_info_memmap_entry {
196     uint64_t  base_addr;
197     uint64_t  length;
198     uint32_t  type;
199     uint32_t  reserved;
200 } __attribute__((packed)) mb_info_memmap_entry_t;
201
202 #define MB_INFO_MEMMAP_TAG  6
203 // note alignment of 8 bytes required for each... 
204 typedef struct mb_info_memmap {
205     mb_info_tag_t tag;
206     uint32_t  entry_size;     // multiple of 8
207     uint32_t  entry_version;  // 0
208     mb_info_memmap_entry_t  entries[];
209 } __attribute__((packed)) mb_info_memmap_t;
210
211 #define MB_INFO_HRT_TAG 0xf00df00d
212 typedef struct mb_info_hrt {
213     mb_info_tag_t  tag;
214     // apic ids are 0..num_apics-1
215     // apic and ioapic addresses are the well known places
216     uint32_t       total_num_apics;
217     uint32_t       first_hrt_apic_id;
218     uint32_t       have_hrt_ioapic;
219     uint32_t       first_hrt_ioapic_entry;
220     uint64_t       first_hrt_addr;
221 } __attribute__((packed)) mb_info_hrt_t;
222
223
224 // We are not doing:
225 //
226 // - BIOS Boot Devie
227 // - Modules
228 // - ELF symbols
229 // - Boot Loader name
230 // - APM table
231 // - VBE info
232 // - Framebuffer info
233 //
234
235 static int is_elf(uint8_t *data, uint64_t size)
236 {
237     if (*((uint32_t*)data)==ELF_MAGIC) {
238         return 1;
239     } else { 
240         return 0;
241     }
242 }
243
244 static mb_header_t *find_mb_header(uint8_t *data, uint64_t size)
245 {
246     uint64_t limit = size > 32768 ? 32768 : size;
247     uint64_t i;
248
249     // Scan for the .boot magic cookie
250     // must be in first 32K, assume 4 byte aligned
251     for (i=0;i<limit;i+=4) { 
252         if (*((uint32_t*)&data[i])==MB2_MAGIC) {
253             INFO("Found multiboot header at offset 0x%llx\n",i);
254             return (mb_header_t *) &data[i];
255         }
256     }
257     return 0;
258 }
259
260 static int checksum4_ok(uint32_t *data, uint64_t size)
261 {
262     int i;
263     uint32_t sum=0;
264
265     for (i=0;i<size;i++) {
266         sum+=data[i];
267     }
268
269     return sum==0;
270 }
271
272 static int parse_multiboot_kernel(uint8_t *data, uint64_t size, mb_data_t *mb)
273 {
274     uint64_t i;
275
276     mb_header_t *mb_header=0;
277     mb_tag_t *mb_tag=0;
278     mb_info_t *mb_inf=0;
279     mb_addr_t *mb_addr=0;
280     mb_entry_t *mb_entry=0;
281     mb_flags_t *mb_flags=0;
282     mb_framebuf_t *mb_framebuf=0;
283     mb_modalign_t *mb_modalign=0;
284     mb_mb64_hrt_t *mb_mb64_hrt=0;
285
286
287     if (!is_elf(data,size)) { 
288         ERROR("HRT is not an ELF\n");
289         return -1;
290     }
291
292     mb_header = find_mb_header(data,size);
293
294     if (!mb_header) { 
295         ERROR("No multiboot header found\n");
296         return -1;
297     }
298
299     // Checksum applies only to the header itself, not to 
300     // the subsequent tags... 
301     if (!checksum4_ok((uint32_t*)mb_header,4)) { 
302         ERROR("Multiboot header has bad checksum\n");
303         return -1;
304     }
305
306     INFO("Multiboot header: arch=0x%x, headerlen=0x%x\n", mb_header->arch, mb_header->headerlen);
307
308     mb_tag = (mb_tag_t*)((void*)mb_header+16);
309
310     while (!(mb_tag->type==0 && mb_tag->size==8)) {
311         INFO("tag: type 0x%x flags=0x%x size=0x%x\n",mb_tag->type, mb_tag->flags,mb_tag->size);
312         switch (mb_tag->type) {
313             case MB_TAG_INFO: {
314                 if (mb_inf) { 
315                     ERROR("Multiple info tags found!\n");
316                     return -1;
317                 }
318                 mb_inf = (mb_info_t*)mb_tag;
319                 INFO(" info request - types follow\n");
320                 for (i=0;(mb_tag->size-8)/4;i++) {
321                     INFO("  %llu: type 0x%x\n", i, mb_inf->types[i]);
322                 }
323             }
324                 break;
325
326             case MB_TAG_ADDRESS: {
327                 if (mb_addr) { 
328                     ERROR("Multiple address tags found!\n");
329                     return -1;
330                 }
331                 mb_addr = (mb_addr_t*)mb_tag;
332                 INFO(" address\n");
333                 INFO("  header_addr     =  0x%x\n", mb_addr->header_addr);
334                 INFO("  load_addr       =  0x%x\n", mb_addr->load_addr);
335                 INFO("  load_end_addr   =  0x%x\n", mb_addr->load_end_addr);
336                 INFO("  bss_end_addr    =  0x%x\n", mb_addr->bss_end_addr);
337             }
338                 break;
339
340             case MB_TAG_ENTRY: {
341                 if (mb_entry) { 
342                     ERROR("Multiple entry tags found!\n");
343                     return -1;
344                 }
345                 mb_entry=(mb_entry_t*)mb_tag;
346                 INFO(" entry\n");
347                 INFO("  entry_addr      =  0x%x\n", mb_entry->entry_addr);
348             }
349                 break;
350                 
351             case MB_TAG_FLAGS: {
352                 if (mb_flags) { 
353                     ERROR("Multiple flags tags found!\n");
354                     return -1;
355                 }
356                 mb_flags = (mb_flags_t*)mb_tag;
357                 INFO(" flags\n");
358                 INFO("  console_flags   =  0x%x\n", mb_flags->console_flags);
359             }
360                 break;
361                 
362             case MB_TAG_FRAMEBUF: {
363                 if (mb_framebuf) { 
364                     ERROR("Multiple framebuf tags found!\n");
365                     return -1;
366                 }
367                 mb_framebuf = (mb_framebuf_t*)mb_tag;
368                 INFO(" framebuf\n");
369                 INFO("  width           =  0x%x\n", mb_framebuf->width);
370                 INFO("  height          =  0x%x\n", mb_framebuf->height);
371                 INFO("  depth           =  0x%x\n", mb_framebuf->depth);
372             }
373                 break;
374
375             case MB_TAG_MODALIGN: {
376                 if (mb_modalign) { 
377                     ERROR("Multiple modalign tags found!\n");
378                     return -1;
379                 }
380                 mb_modalign = (mb_modalign_t*)mb_tag;
381                 INFO(" modalign\n");
382                 INFO("  size            =  0x%x\n", mb_modalign->size);
383             }
384                 break;
385 #if 0
386             case MB_TAG_MB64_HRT: {
387                 if (mb_mb64_hrt) { 
388                     ERROR("Multiple mb64_hrt tags found!\n");
389                     return -1;
390                 }
391                 mb_mb64_hrt = (mb_mb64_hrt_t*)mb_tag;
392                 INFO(" mb64_hrt\n");
393             }
394                 break;
395 #endif
396                 
397             default: 
398                 INFO("Unknown tag... Skipping...\n");
399                 break;
400         }
401         mb_tag = (mb_tag_t *)(((void*)mb_tag) + mb_tag->size);
402     }
403
404     // copy out to caller
405     mb->header=mb_header;
406     mb->info=mb_inf;
407     mb->addr=mb_addr;
408     mb->entry=mb_entry;
409     mb->flags=mb_flags;
410     mb->framebuf=mb_framebuf;
411     mb->modalign=mb_modalign;
412     mb->mb64_hrt=mb_mb64_hrt;
413
414     return 0;
415 }
416
417
418 int v3_parse_multiboot_header(struct v3_cfg_file *file, mb_data_t *result)
419 {
420     return parse_multiboot_kernel(file->data,file->size,result);
421 }
422
423
424 #define APIC_BASE     0xfee00000
425 #define IOAPIC_BASE   0xfec00000
426
427 /*
428   MB_INFO_HEADER
429   MB_HRT  (if this is an HVM
430   MB_BASIC_MEMORY
431   MB_MEMORY_MAP
432     0..640K  RAM
433     640K..1024 reserved
434     1024..ioapic_base RAM
435     ioapic_base to ioapic_base+page reserved
436     ioapic_base+page to apic_base ram
437     apic_base oto apic_base+page reserved
438     apic_base+page to total RAM
439
440    
441  The multiboot structure that is written reflects the 
442  perspective of the core given the kind of VM it is part of.
443
444  Regular VM
445     - core does not matter 
446     - all memory visible
447
448  HVM
449    ROS core
450     - only ROS memory visible
451     - regular multiboot or bios boot assumed
452    HRT core
453     - all memory visible
454     - HRT64 multiboot assumed
455
456 */
457
458 uint64_t v3_build_multiboot_table(struct guest_info *core, uint8_t *dest, uint64_t size)
459 {
460     struct v3_vm_info *vm = core->vm_info;
461     mb_info_header_t *header=0;
462 #ifdef V3_CONFIG_HVM
463     mb_info_hrt_t *hrt=0;
464 #endif
465     mb_info_mem_t *mem=0;
466     mb_info_memmap_t *memmap=0;
467     mb_info_tag_t *tag=0;
468     uint64_t num_mem=0, cur_mem=0;
469     
470     uint64_t total_mem = vm->mem_size;
471
472 #ifdef V3_CONFIG_HVM
473     if (vm->hvm_state.is_hvm) { 
474         if (v3_is_hvm_ros_core(core)) {
475             PrintDebug(core->vm_info,core,"multiboot: hvm: building mb table from ROS core perspective\n");
476             total_mem = v3_get_hvm_ros_memsize(vm);
477         } else {
478             PrintDebug(core->vm_info,core,"multiboot: hvm: building mb table from HRT core perspective\n");
479             total_mem = v3_get_hvm_hrt_memsize(vm);     
480         }
481     }
482 #endif
483
484     // assume we have > 1 MB + apic+ioapic
485     num_mem = 5;
486     if (total_mem>IOAPIC_BASE+PAGE_SIZE) {
487         num_mem++;
488     }
489     if (total_mem>APIC_BASE+PAGE_SIZE) {
490         num_mem++;
491     }
492
493
494     uint64_t needed = 
495         sizeof(mb_info_header_t) +
496 #ifdef V3_CONFIG_HVM
497         core->vm_info->hvm_state.is_hvm && core->hvm_state.is_hrt ? sizeof(mb_info_hrt_t) : 0 
498 #endif
499         + 
500         sizeof(mb_info_mem_t) + 
501         sizeof(mb_info_memmap_t) + 
502         sizeof(mb_info_memmap_entry_t) * num_mem  +
503         sizeof(mb_info_tag_t);
504
505     if (needed>size) { 
506         return 0;
507     }
508
509     uint8_t *next;
510
511     if (needed>size) {
512         ERROR("Cannot fit MB info in needed space\n");
513         return -1;
514     }
515
516     next = dest;
517
518     header = (mb_info_header_t*)next;
519     next += sizeof(mb_info_header_t);
520
521 #if V3_CONFIG_HVM
522     if (core->vm_info->hvm_state.is_hvm && v3_is_hvm_hrt_core(core)) { 
523         hrt = (mb_info_hrt_t*)next;
524         next += sizeof(mb_info_hrt_t);
525     }
526 #endif
527
528     mem = (mb_info_mem_t*)next;
529     next += sizeof(mb_info_mem_t);
530
531     memmap = (mb_info_memmap_t*)next;
532     next += sizeof(mb_info_memmap_t) + num_mem * sizeof(mb_info_memmap_entry_t);
533
534     tag = (mb_info_tag_t*)next;
535     next += sizeof(mb_info_tag_t);
536
537     header->totalsize = (uint32_t)(next - dest);
538     header->reserved = 0;
539
540 #ifdef V3_CONFIG_HVM
541     if (core->vm_info->hvm_state.is_hvm && v3_is_hvm_hrt_core(core)) { 
542         hrt->tag.type = MB_INFO_HRT_TAG;
543         hrt->tag.size = sizeof(mb_info_hrt_t);
544         hrt->total_num_apics = vm->num_cores;
545         hrt->first_hrt_apic_id = vm->hvm_state.first_hrt_core;
546         hrt->have_hrt_ioapic=0;
547         hrt->first_hrt_ioapic_entry=0;
548         hrt->first_hrt_addr = vm->hvm_state.first_hrt_gpa;
549     }
550 #endif
551
552     mem->tag.type = MB_INFO_MEM_TAG;
553     mem->tag.size = sizeof(mb_info_mem_t);
554     mem->mem_lower = 640; // thank you, bill gates
555     mem->mem_upper = (total_mem  - 1024 * 1024) / 1024;
556
557     memmap->tag.type = MB_INFO_MEMMAP_TAG;
558     memmap->tag.size = sizeof(mb_info_memmap_t) + num_mem * sizeof(mb_info_memmap_entry_t);
559     memmap->entry_size = 24;
560     memmap->entry_version = 0;
561
562     cur_mem=0;
563
564     // first 640K
565     memmap->entries[cur_mem].base_addr = 0;
566     memmap->entries[cur_mem].length = 640*1024;
567     memmap->entries[cur_mem].type = MEM_RAM;
568     memmap->entries[cur_mem].reserved = 0;
569     cur_mem++;
570
571     // legacy io (640K->1 MB)
572     memmap->entries[cur_mem].base_addr = 640*1024;
573     memmap->entries[cur_mem].length = 384*1024;
574     memmap->entries[cur_mem].type = MEM_RESV;
575     memmap->entries[cur_mem].reserved = 1;
576     cur_mem++;
577
578     // first meg to ioapic
579     memmap->entries[cur_mem].base_addr = 1024*1024;
580     memmap->entries[cur_mem].length = (total_mem < IOAPIC_BASE ? total_mem : IOAPIC_BASE) - 1024*1024;
581     memmap->entries[cur_mem].type = MEM_RAM;
582     memmap->entries[cur_mem].reserved = 0;
583     cur_mem++;
584
585     // ioapic reservation
586     memmap->entries[cur_mem].base_addr = IOAPIC_BASE;
587     memmap->entries[cur_mem].length = PAGE_SIZE;
588     memmap->entries[cur_mem].type = MEM_RESV;
589     memmap->entries[cur_mem].reserved = 1;
590     cur_mem++;
591
592     if (total_mem > (IOAPIC_BASE + PAGE_SIZE)) {
593         // memory between ioapic and apic
594         memmap->entries[cur_mem].base_addr = IOAPIC_BASE+PAGE_SIZE;
595         memmap->entries[cur_mem].length = (total_mem < APIC_BASE ? total_mem : APIC_BASE) - (IOAPIC_BASE+PAGE_SIZE);;
596         memmap->entries[cur_mem].type = MEM_RAM;
597         memmap->entries[cur_mem].reserved = 0;
598         cur_mem++;
599     } 
600
601     // apic
602     memmap->entries[cur_mem].base_addr = APIC_BASE;
603     memmap->entries[cur_mem].length = PAGE_SIZE;
604     memmap->entries[cur_mem].type = MEM_RESV;
605     memmap->entries[cur_mem].reserved = 1;
606     cur_mem++;
607
608     if (total_mem > (APIC_BASE + PAGE_SIZE)) {
609         // memory after apic
610         memmap->entries[cur_mem].base_addr = APIC_BASE+PAGE_SIZE;
611         memmap->entries[cur_mem].length = total_mem - (APIC_BASE+PAGE_SIZE);
612         memmap->entries[cur_mem].type = MEM_RAM;
613         memmap->entries[cur_mem].reserved = 0;
614         cur_mem++;
615     } 
616
617     for (cur_mem=0;cur_mem<num_mem;cur_mem++) { 
618         PrintDebug(vm, VCORE_NONE,
619                    "multiboot: entry %llu: %p (%llx bytes) - type %x %s\n",
620                    cur_mem, 
621                    (void*) memmap->entries[cur_mem].base_addr,
622                    memmap->entries[cur_mem].length,
623                    memmap->entries[cur_mem].type,
624                    memmap->entries[cur_mem].reserved ? "reserved" : "");
625     }
626
627
628
629     // This demarcates end of list
630     tag->type = 0;
631     tag->size = 8;
632
633     return header->totalsize;
634
635 }
636
637
638 int v3_write_multiboot_kernel(struct v3_vm_info *vm, mb_data_t *mb, struct v3_cfg_file *file,
639                               void *base, uint64_t limit)
640 {
641     uint32_t offset=0;
642     uint32_t header_offset = (uint32_t) ((uint64_t)(mb->header) - (uint64_t)(file->data));
643     uint32_t size;
644
645     if (!mb->addr || !mb->entry) { 
646         PrintError(vm,VCORE_NONE, "multiboot: kernel is missing address or entry point\n");
647         return -1;
648     }
649
650     if (((void*)(uint64_t)(mb->addr->header_addr) < base ) ||
651         ((void*)(uint64_t)(mb->addr->load_end_addr) > base+limit) ||
652         ((void*)(uint64_t)(mb->addr->bss_end_addr) > base+limit)) { 
653         PrintError(vm,VCORE_NONE, "multiboot: kernel is not within the allowed portion of VM\n");
654         return -1;
655     }
656
657     offset = header_offset - (mb->addr->header_addr - mb->addr->load_addr);
658     size = mb->addr->load_end_addr - mb->addr->load_addr;
659     
660     if (size != file->size-offset) { 
661         V3_Print(vm,VCORE_NONE,"multiboot: strange: size computed as %u, but file->size-offset = %llu\n",size,file->size-offset);
662     }
663
664     // We are trying to do as little ELF loading here as humanly possible
665     v3_write_gpa_memory(&vm->cores[0],
666                         (addr_t)(mb->addr->load_addr),
667                         size,
668                         file->data+offset);
669
670     PrintDebug(vm,VCORE_NONE,
671                "multiboot: wrote 0x%llx bytes starting at offset 0x%llx to %p\n",
672                (uint64_t) size,
673                (uint64_t) offset,
674                (void*)(addr_t)(mb->addr->load_addr));
675
676     size = mb->addr->bss_end_addr - mb->addr->load_end_addr + 1;
677
678     // Now we need to zero the BSS
679     v3_set_gpa_memory(&vm->cores[0],
680                       (addr_t)(mb->addr->load_end_addr),
681                       size,
682                       0);
683                       
684     PrintDebug(vm,VCORE_NONE,
685                "multiboot: zeroed 0x%llx bytes starting at %p\n",
686                (uint64_t) size,
687                (void*)(addr_t)(mb->addr->load_end_addr));
688                       
689
690     return 0;
691
692 }
693
694
695 static int setup_multiboot_kernel(struct v3_vm_info *vm)
696 {
697     void *base = 0;
698     uint64_t limit = vm->mem_size;
699
700
701     if (vm->mb_state.mb_file->size > limit) { 
702         PrintError(vm,VCORE_NONE,"multiboot: Cannot map kernel because it is too big (%llu bytes, but only have %llu space\n", vm->mb_state.mb_file->size, (uint64_t)limit);
703         return -1;
704     }
705
706     if (!is_elf(vm->mb_state.mb_file->data,vm->mb_state.mb_file->size)) { 
707         PrintError(vm,VCORE_NONE,"multiboot: supplied kernel is not an ELF\n");
708         return -1;
709     } else {
710         if (find_mb_header(vm->mb_state.mb_file->data,vm->mb_state.mb_file->size)) { 
711             PrintDebug(vm,VCORE_NONE,"multiboot: appears to be a multiboot kernel\n");
712             if (v3_parse_multiboot_header(vm->mb_state.mb_file,&vm->mb_state.mb_data)) { 
713                 PrintError(vm,VCORE_NONE,"multiboot: cannot parse multiboot kernel header\n");
714                 return -1;
715             }
716             if (v3_write_multiboot_kernel(vm, &(vm->mb_state.mb_data),vm->mb_state.mb_file,base,limit)) { 
717                 PrintError(vm,VCORE_NONE,"multiboot: multiboot kernel setup failed\n");
718                 return -1;
719             } 
720         } else {
721             PrintError(vm,VCORE_NONE,"multiboot: multiboot kernel has no header\n");
722             return -1;
723         }
724     }
725     
726     return 0;
727     
728 }
729
730 // 32 bit GDT entries
731 //
732 //         base24-31    flags2  limit16-19 access8  base16-23   base0-15   limit0-15
733 // null       0           0          0       0         0           0           0
734 // code       0           1100       f     10011010    0           0         ffff
735 // data       0           1100       f     10010010    0           0         ffff
736 //
737 // null =   00 00 00 00 00 00 00 00
738 // code =   00 cf 9a 00 00 00 ff ff 
739 // data =   00 cf 92 00 00 00 ff ff
740 //
741 static uint64_t gdt32[3] = {
742     0x0000000000000000, /* null */
743     0x00cf9a000000ffff, /* code (note lme=0) */
744     0x00cf92000000ffff, /* data */
745 };
746
747 static void write_gdt(struct v3_vm_info *vm, void *base, uint64_t limit)
748 {
749     v3_write_gpa_memory(&vm->cores[0],(addr_t)base,limit,(uint8_t*) gdt32);
750
751     PrintDebug(vm,VCORE_NONE,"multiboot: wrote GDT at %p\n",base);
752 }
753
754         
755 static void write_tss(struct v3_vm_info *vm, void *base, uint64_t limit)
756 {
757     v3_set_gpa_memory(&vm->cores[0],(addr_t)base,limit,0);
758
759     PrintDebug(vm,VCORE_NONE,"multiboot: wrote TSS at %p\n",base);
760 }
761
762 static void write_table(struct v3_vm_info *vm, void *base, uint64_t limit)
763 {
764     uint64_t size;
765     uint8_t buf[256];
766
767     limit = limit < 256 ? limit : 256;
768
769     size = v3_build_multiboot_table(&vm->cores[0], buf, limit);
770
771     if (size>256 || size==0) { 
772         PrintError(vm,VCORE_NONE,"multiboot: cannot build multiboot table\n");
773         return;
774     }
775     
776     v3_write_gpa_memory(&vm->cores[0],(addr_t)base,size,buf);
777
778 }
779
780
781
782 /*
783   GPA layout:
784
785   GDT
786   TSS
787   MBinfo   
788   Kernel at its desired load address (or error)
789
790 */
791
792
793 int v3_setup_multiboot_vm_for_boot(struct v3_vm_info *vm)
794 {
795     void *kernel_start_gpa;
796     void *kernel_end_gpa;
797     void *mb_gpa;
798     void *tss_gpa;
799     void *gdt_gpa;
800
801     if (!vm->mb_state.is_multiboot) { 
802         PrintDebug(vm,VCORE_NONE,"multiboot: skipping multiboot setup for boot as this is not a multiboot VM\n");
803         return 0;
804     }
805
806     
807     if (setup_multiboot_kernel(vm)) {
808         PrintError(vm,VCORE_NONE,"multiboot: failed to setup kernel\n");
809         return -1;
810     } 
811
812     kernel_start_gpa = (void*) (uint64_t) (vm->mb_state.mb_data.addr->load_addr);
813     kernel_end_gpa = (void*) (uint64_t) (vm->mb_state.mb_data.addr->bss_end_addr);
814
815     // Is there room below the kernel? 
816     if ((uint64_t)kernel_start_gpa > 19*4096 ) {
817         // at least 3 pages between 64K and start of kernel 
818         // place at 64K
819         mb_gpa=(void*)(16*4096);
820     } else {
821         // is there room above the kernel?
822         if ((uint64_t)kernel_end_gpa < vm->mem_size-4*4096) { 
823             if (((uint64_t)kernel_end_gpa + 4 * 4096) <= 0xffffffff) { 
824                 mb_gpa=(void*) (4096*((uint64_t)kernel_end_gpa/4096 + 1));
825             } else {
826                 PrintError(vm,VCORE_NONE,"multiboot: no room for mb data below 4 GB\n");
827                 return -1;
828             } 
829         } else {
830             PrintError(vm,VCORE_NONE,"multiboot: no room for mb data above kernel\n");
831             return -1;
832         }
833     }
834
835     PrintDebug(vm,VCORE_NONE,"multiboot: mb data will start at %p\n",mb_gpa);
836
837     vm->mb_state.mb_data_gpa=mb_gpa;
838
839     tss_gpa = mb_gpa + 1 * 4096;
840     gdt_gpa = mb_gpa + 2 * 4096;
841
842     write_table(vm,mb_gpa,4096);
843     
844     write_tss(vm,tss_gpa,4096);
845
846     write_gdt(vm,gdt_gpa,4096);
847
848     PrintDebug(vm,VCORE_NONE,"multiboot: setup of memory done\n");
849
850     return 0;
851 }
852
853 /*
854   On entry:
855
856    IDTR not set
857    GDTR points to stub GDT
858    TR   points to stub TSS
859    CR0  has PE and not PG
860    EIP  is entry point to kernel
861    EBX  points to multiboot info
862    EAX  multiboot magic cookie
863
864 */
865 int v3_setup_multiboot_core_for_boot(struct guest_info *core)
866 {
867     void *base;
868     uint64_t limit;
869
870     if (!core->vm_info->mb_state.is_multiboot) {
871         PrintDebug(core->vm_info,core,"multiboot: skipping mb core setup as this is not an mb VM\n");
872         return 0;
873     }
874         
875     if (core->vcpu_id != 0) {
876         PrintDebug(core->vm_info,core,"multiboot: skipping mb core setup as this is not the BSP core\n");
877         return 0;
878     }
879
880
881     PrintDebug(core->vm_info, core, "multiboot: setting up MB BSP core for boot\n");
882
883     
884     memset(&core->vm_regs,0,sizeof(core->vm_regs));
885     memset(&core->ctrl_regs,0,sizeof(core->ctrl_regs));
886     memset(&core->dbg_regs,0,sizeof(core->dbg_regs));
887     memset(&core->segments,0,sizeof(core->segments));    
888     memset(&core->msrs,0,sizeof(core->msrs));    
889     memset(&core->fp_state,0,sizeof(core->fp_state));    
890
891     // We need to be in protected mode at ring zero
892     core->cpl = 0; // we are going right into the kernel
893     core->cpu_mode = PROTECTED;
894     core->mem_mode = PHYSICAL_MEM; 
895     // default run-state is fine, we are core zero
896     // core->core_run_state = CORE_RUNNING ;
897
898     // right into the kernel
899     core->rip = (uint64_t) core->vm_info->mb_state.mb_data.entry->entry_addr;
900
901     // Setup CRs for protected mode
902     // CR0:  PE (but no PG)
903     core->ctrl_regs.cr0 = 0x1;
904     core->shdw_pg_state.guest_cr0 = core->ctrl_regs.cr0;
905
906     // CR2: don't care (output from #PF)
907     // CR3: don't care (no paging)
908     core->ctrl_regs.cr3 = 0;
909     core->shdw_pg_state.guest_cr3 = core->ctrl_regs.cr3;
910
911     // CR4: no features 
912     core->ctrl_regs.cr4 = 0x0;
913     core->shdw_pg_state.guest_cr4 = core->ctrl_regs.cr4;
914     // CR8 as usual
915     // RFLAGS zeroed is fine: come in with interrupts off
916     // EFER needs SVME and LME but not LMA (last 16 bits: 0 0 0 1 0 1 0 0   0 0 0 0 0 0 0 0
917     core->ctrl_regs.efer = 0x1400;
918     core->shdw_pg_state.guest_efer.value = core->ctrl_regs.efer;
919
920
921     /* 
922        Notes on selectors:
923
924        selector is 13 bits of index, 1 bit table indicator 
925        (0=>GDT), 2 bit RPL
926        
927        index is scaled by 8, even in long mode, where some entries 
928        are 16 bytes long.... 
929           -> code, data descriptors have 8 byte format
930              because base, limit, etc, are ignored (no segmentation)
931           -> interrupt/trap gates have 16 byte format 
932              because offset needs to be 64 bits
933     */
934     
935     // There is no IDTR set and interrupts are disabled
936
937     // Install our stub GDT
938     core->segments.gdtr.selector = 0;
939     core->segments.gdtr.base = (addr_t) core->vm_info->mb_state.mb_data_gpa+2*4096;
940     core->segments.gdtr.limit = 4096-1;
941     core->segments.gdtr.type = 0x6;
942     core->segments.gdtr.system = 1; 
943     core->segments.gdtr.dpl = 0;
944     core->segments.gdtr.present = 1;
945     core->segments.gdtr.long_mode = 0;
946     
947     // And our TSS
948     core->segments.tr.selector = 0;
949     core->segments.tr.base = (addr_t) core->vm_info->mb_state.mb_data_gpa+1*4096;
950     core->segments.tr.limit = 4096-1;
951     core->segments.tr.type = 0x6;
952     core->segments.tr.system = 1; 
953     core->segments.tr.dpl = 0;
954     core->segments.tr.present = 1;
955     core->segments.tr.long_mode = 0;
956     
957     base = 0x0;
958     limit = -1;
959
960     // And CS
961     core->segments.cs.selector = 0x8 ; // entry 1 of GDT (RPL=0)
962     core->segments.cs.base = (addr_t) base;
963     core->segments.cs.limit = limit;
964     core->segments.cs.type = 0xa;
965     core->segments.cs.system = 1; 
966     core->segments.cs.dpl = 0;
967     core->segments.cs.present = 1;
968     core->segments.cs.long_mode = 0;
969     core->segments.cs.db = 1; // 32 bit operand and address size
970     core->segments.cs.granularity = 1; // pages
971
972     // DS, SS, etc are identical
973     core->segments.ds.selector = 0x10; // entry 2 of GDT (RPL=0)
974     core->segments.ds.base = (addr_t) base;
975     core->segments.ds.limit = limit;
976     core->segments.ds.type = 0x2;
977     core->segments.ds.system = 1; 
978     core->segments.ds.dpl = 0;
979     core->segments.ds.present = 1;
980     core->segments.ds.long_mode = 0;
981     core->segments.ds.db = 1; // 32 bit operand and address size
982     core->segments.ds.granularity = 1; // pages
983
984     memcpy(&core->segments.ss,&core->segments.ds,sizeof(core->segments.ds));
985     memcpy(&core->segments.es,&core->segments.ds,sizeof(core->segments.ds));
986     memcpy(&core->segments.fs,&core->segments.ds,sizeof(core->segments.ds));
987     memcpy(&core->segments.gs,&core->segments.ds,sizeof(core->segments.ds));
988     
989
990
991     // Now for our magic - this signals
992     // the kernel that a multiboot loader loaded it
993     // and that rbx points to its offered data
994     core->vm_regs.rax = MB2_INFO_MAGIC;
995
996     core->vm_regs.rbx = (uint64_t) (core->vm_info->mb_state.mb_data_gpa);
997
998     // reset paging here for shadow... 
999
1000     if (core->shdw_pg_mode != NESTED_PAGING) { 
1001         PrintError(core->vm_info, core, "multiboot: shadow paging guest... this will end badly\n");
1002         return -1;
1003     }
1004
1005
1006     return 0;
1007 }
1008
1009
1010 int v3_handle_multiboot_reset(struct guest_info *core)
1011 {
1012     int rc;
1013
1014     if (core->core_run_state!=CORE_RESETTING) { 
1015         return 0;
1016     }
1017
1018     if (!core->vm_info->mb_state.is_multiboot) { 
1019         return 0;
1020     }
1021
1022     // wait for everyone
1023     v3_counting_barrier(&core->vm_info->reset_barrier);
1024
1025     if (core->vcpu_id==0) {
1026         // I am leader (this is true if I am a ROS core or this is a non-HVM)
1027         core->vm_info->run_state = VM_RESETTING;
1028     }
1029
1030     rc=0;
1031         
1032     if (core->vcpu_id==0) {
1033         // we will recopy the image
1034         rc |= v3_setup_multiboot_vm_for_boot(core->vm_info);
1035     }
1036
1037     rc |= v3_setup_multiboot_core_for_boot(core);
1038
1039     if (core->vcpu_id==0) { 
1040         core->core_run_state = CORE_RUNNING;
1041         core->vm_info->run_state = VM_RUNNING;
1042     } else {
1043         // for APs, we need to bring them back to the init state
1044         core->cpu_mode = REAL;
1045         core->mem_mode = PHYSICAL_MEM;
1046         core->core_run_state = CORE_STOPPED;
1047     }
1048
1049     // sync on the way out
1050     v3_counting_barrier(&core->vm_info->reset_barrier);
1051
1052     if (rc<0) {
1053         return rc;
1054     } else {
1055         return 1; // reboot
1056     }
1057 }
1058