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.


Addition of basic multiboot functionality plus refactor of HVM
[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 } __attribute__((packed)) mb_info_hrt_t;
221
222
223 // We are not doing:
224 //
225 // - BIOS Boot Devie
226 // - Modules
227 // - ELF symbols
228 // - Boot Loader name
229 // - APM table
230 // - VBE info
231 // - Framebuffer info
232 //
233
234 static int is_elf(uint8_t *data, uint64_t size)
235 {
236     if (*((uint32_t*)data)==ELF_MAGIC) {
237         return 1;
238     } else { 
239         return 0;
240     }
241 }
242
243 static mb_header_t *find_mb_header(uint8_t *data, uint64_t size)
244 {
245     uint64_t limit = size > 32768 ? 32768 : size;
246     uint64_t i;
247
248     // Scan for the .boot magic cookie
249     // must be in first 32K, assume 4 byte aligned
250     for (i=0;i<limit;i+=4) { 
251         if (*((uint32_t*)&data[i])==MB2_MAGIC) {
252             INFO("Found multiboot header at offset 0x%llx\n",i);
253             return (mb_header_t *) &data[i];
254         }
255     }
256     return 0;
257 }
258
259 static int checksum4_ok(uint32_t *data, uint64_t size)
260 {
261     int i;
262     uint32_t sum=0;
263
264     for (i=0;i<size;i++) {
265         sum+=data[i];
266     }
267
268     return sum==0;
269 }
270
271 static int parse_multiboot_kernel(uint8_t *data, uint64_t size, mb_data_t *mb)
272 {
273     uint64_t i;
274
275     mb_header_t *mb_header=0;
276     mb_tag_t *mb_tag=0;
277     mb_info_t *mb_inf=0;
278     mb_addr_t *mb_addr=0;
279     mb_entry_t *mb_entry=0;
280     mb_flags_t *mb_flags=0;
281     mb_framebuf_t *mb_framebuf=0;
282     mb_modalign_t *mb_modalign=0;
283     mb_mb64_hrt_t *mb_mb64_hrt=0;
284
285
286     if (!is_elf(data,size)) { 
287         ERROR("HRT is not an ELF\n");
288         return -1;
289     }
290
291     mb_header = find_mb_header(data,size);
292
293     if (!mb_header) { 
294         ERROR("No multiboot header found\n");
295         return -1;
296     }
297
298     // Checksum applies only to the header itself, not to 
299     // the subsequent tags... 
300     if (!checksum4_ok((uint32_t*)mb_header,4)) { 
301         ERROR("Multiboot header has bad checksum\n");
302         return -1;
303     }
304
305     INFO("Multiboot header: arch=0x%x, headerlen=0x%x\n", mb_header->arch, mb_header->headerlen);
306
307     mb_tag = (mb_tag_t*)((void*)mb_header+16);
308
309     while (!(mb_tag->type==0 && mb_tag->size==8)) {
310         INFO("tag: type 0x%x flags=0x%x size=0x%x\n",mb_tag->type, mb_tag->flags,mb_tag->size);
311         switch (mb_tag->type) {
312             case MB_TAG_INFO: {
313                 if (mb_inf) { 
314                     ERROR("Multiple info tags found!\n");
315                     return -1;
316                 }
317                 mb_inf = (mb_info_t*)mb_tag;
318                 INFO(" info request - types follow\n");
319                 for (i=0;(mb_tag->size-8)/4;i++) {
320                     INFO("  %llu: type 0x%x\n", i, mb_inf->types[i]);
321                 }
322             }
323                 break;
324
325             case MB_TAG_ADDRESS: {
326                 if (mb_addr) { 
327                     ERROR("Multiple address tags found!\n");
328                     return -1;
329                 }
330                 mb_addr = (mb_addr_t*)mb_tag;
331                 INFO(" address\n");
332                 INFO("  header_addr     =  0x%x\n", mb_addr->header_addr);
333                 INFO("  load_addr       =  0x%x\n", mb_addr->load_addr);
334                 INFO("  load_end_addr   =  0x%x\n", mb_addr->load_end_addr);
335                 INFO("  bss_end_addr    =  0x%x\n", mb_addr->bss_end_addr);
336             }
337                 break;
338
339             case MB_TAG_ENTRY: {
340                 if (mb_entry) { 
341                     ERROR("Multiple entry tags found!\n");
342                     return -1;
343                 }
344                 mb_entry=(mb_entry_t*)mb_tag;
345                 INFO(" entry\n");
346                 INFO("  entry_addr      =  0x%x\n", mb_entry->entry_addr);
347             }
348                 break;
349                 
350             case MB_TAG_FLAGS: {
351                 if (mb_flags) { 
352                     ERROR("Multiple flags tags found!\n");
353                     return -1;
354                 }
355                 mb_flags = (mb_flags_t*)mb_tag;
356                 INFO(" flags\n");
357                 INFO("  console_flags   =  0x%x\n", mb_flags->console_flags);
358             }
359                 break;
360                 
361             case MB_TAG_FRAMEBUF: {
362                 if (mb_framebuf) { 
363                     ERROR("Multiple framebuf tags found!\n");
364                     return -1;
365                 }
366                 mb_framebuf = (mb_framebuf_t*)mb_tag;
367                 INFO(" framebuf\n");
368                 INFO("  width           =  0x%x\n", mb_framebuf->width);
369                 INFO("  height          =  0x%x\n", mb_framebuf->height);
370                 INFO("  depth           =  0x%x\n", mb_framebuf->depth);
371             }
372                 break;
373
374             case MB_TAG_MODALIGN: {
375                 if (mb_modalign) { 
376                     ERROR("Multiple modalign tags found!\n");
377                     return -1;
378                 }
379                 mb_modalign = (mb_modalign_t*)mb_tag;
380                 INFO(" modalign\n");
381                 INFO("  size            =  0x%x\n", mb_modalign->size);
382             }
383                 break;
384 #if 0
385             case MB_TAG_MB64_HRT: {
386                 if (mb_mb64_hrt) { 
387                     ERROR("Multiple mb64_hrt tags found!\n");
388                     return -1;
389                 }
390                 mb_mb64_hrt = (mb_mb64_hrt_t*)mb_tag;
391                 INFO(" mb64_hrt\n");
392             }
393                 break;
394 #endif
395                 
396             default: 
397                 INFO("Unknown tag... Skipping...\n");
398                 break;
399         }
400         mb_tag = (mb_tag_t *)(((void*)mb_tag) + mb_tag->size);
401     }
402
403     // copy out to caller
404     mb->header=mb_header;
405     mb->info=mb_inf;
406     mb->addr=mb_addr;
407     mb->entry=mb_entry;
408     mb->flags=mb_flags;
409     mb->framebuf=mb_framebuf;
410     mb->modalign=mb_modalign;
411     mb->mb64_hrt=mb_mb64_hrt;
412
413     return 0;
414 }
415
416
417 int v3_parse_multiboot_header(struct v3_cfg_file *file, mb_data_t *result)
418 {
419     return parse_multiboot_kernel(file->data,file->size,result);
420 }
421
422
423 #define APIC_BASE     0xfee00000
424 #define IOAPIC_BASE   0xfec00000
425
426 /*
427   MB_INFO_HEADER
428   MB_HRT  (if this is an HVM
429   MB_BASIC_MEMORY
430   MB_MEMORY_MAP
431     0..640K  RAM
432     640K..1024 reserved
433     1024..ioapic_base RAM
434     ioapic_base to ioapic_base+page reserved
435     ioapic_base+page to apic_base ram
436     apic_base oto apic_base+page reserved
437     apic_base+page to total RAM
438
439    
440  The multiboot structure that is written reflects the 
441  perspective of the core given the kind of VM it is part of.
442
443  Regular VM
444     - core does not matter 
445     - all memory visible
446
447  HVM
448    ROS core
449     - only ROS memory visible
450     - regular multiboot or bios boot assumed
451    HRT core
452     - full HRT memory visible
453     - HRT64 multiboot assumed
454
455 */
456
457 uint64_t v3_build_multiboot_table(struct guest_info *core, uint8_t *dest, uint64_t size)
458 {
459     struct v3_vm_info *vm = core->vm_info;
460     mb_info_header_t *header;
461 #ifdef V3_CONFIG_HVM
462     mb_info_hrt_t *hrt;
463 #endif
464     mb_info_mem_t *mem;
465     mb_info_memmap_t *memmap;
466     mb_info_tag_t *tag;
467     uint64_t num_mem, cur_mem;
468     
469     uint64_t total_mem = vm->mem_size;
470
471 #ifdef V3_CONFIG_HVM
472     if (vm->hvm_state.is_hvm) { 
473         if (v3_is_hvm_ros_core(core)) {
474             PrintDebug(core->vm_info,core,"multiboot: hvm: building mb table from ROS core perspective\n");
475             total_mem = v3_get_hvm_ros_memsize(vm);
476         } else {
477             PrintDebug(core->vm_info,core,"multiboot: hvm: building mb table from HRT core perspective\n");
478             total_mem = v3_get_hvm_hrt_memsize(vm);     
479         }
480     }
481 #endif
482
483     // assume we have > 1 MB + apic+ioapic
484     num_mem = 5;
485     if (total_mem>IOAPIC_BASE+PAGE_SIZE) {
486         num_mem++;
487     }
488     if (total_mem>APIC_BASE+PAGE_SIZE) {
489         num_mem++;
490     }
491
492
493     uint64_t needed = 
494         sizeof(mb_info_header_t) +
495 #ifdef V3_CONFIG_HVM
496         core->vm_info->hvm_state.is_hvm && core->hvm_state.is_hrt ? sizeof(mb_info_hrt_t) : 0 
497 #endif
498         + 
499         sizeof(mb_info_mem_t) + 
500         sizeof(mb_info_memmap_t) + 
501         sizeof(mb_info_memmap_entry_t) * num_mem  +
502         sizeof(mb_info_tag_t);
503
504     if (needed>size) { 
505         return 0;
506     }
507
508     uint8_t *next;
509
510     if (needed>size) {
511         ERROR("Cannot fit MB info in needed space\n");
512         return -1;
513     }
514
515     next = dest;
516
517     header = (mb_info_header_t*)next;
518     next += sizeof(mb_info_header_t);
519
520 #if V3_CONFIG_HVM
521     if (core->vm_info->hvm_state.is_hvm && v3_is_hvm_hrt_core(core)) { 
522         hrt = (mb_info_hrt_t*)next;
523         next += sizeof(mb_info_hrt_t);
524     }
525 #endif
526
527     mem = (mb_info_mem_t*)next;
528     next += sizeof(mb_info_mem_t);
529
530     memmap = (mb_info_memmap_t*)next;
531     next += sizeof(mb_info_memmap_t) + num_mem * sizeof(mb_info_memmap_entry_t);
532
533     tag = (mb_info_tag_t*)next;
534     next += sizeof(mb_info_tag_t);
535
536     header->totalsize = (uint32_t)(next - dest);
537     header->reserved = 0;
538
539 #ifdef V3_CONFIG_HVM
540     if (core->vm_info->hvm_state.is_hvm && v3_is_hvm_hrt_core(core)) { 
541         hrt->tag.type = MB_INFO_HRT_TAG;
542         hrt->tag.size = sizeof(mb_info_hrt_t);
543         hrt->total_num_apics = vm->num_cores;
544         hrt->first_hrt_apic_id = vm->hvm_state.first_hrt_core;
545         hrt->have_hrt_ioapic=0;
546         hrt->first_hrt_ioapic_entry=0;
547     }
548 #endif
549
550     mem->tag.type = MB_INFO_MEM_TAG;
551     mem->tag.size = sizeof(mb_info_mem_t);
552     mem->mem_lower = 640; // thank you, bill gates
553     mem->mem_upper = (total_mem  - 1024 * 1024) / 1024;
554
555     memmap->tag.type = MB_INFO_MEMMAP_TAG;
556     memmap->tag.size = sizeof(mb_info_memmap_t) + num_mem * sizeof(mb_info_memmap_entry_t);
557     memmap->entry_size = 24;
558     memmap->entry_version = 0;
559
560     cur_mem=0;
561
562     // first 640K
563     memmap->entries[cur_mem].base_addr = 0;
564     memmap->entries[cur_mem].length = 640*1024;
565     memmap->entries[cur_mem].type = MEM_RAM;
566     memmap->entries[cur_mem].reserved = 0;
567     cur_mem++;
568
569     // legacy io (640K->1 MB)
570     memmap->entries[cur_mem].base_addr = 640*1024;
571     memmap->entries[cur_mem].length = 384*1024;
572     memmap->entries[cur_mem].type = MEM_RESV;
573     memmap->entries[cur_mem].reserved = 1;
574     cur_mem++;
575
576     // first meg to ioapic
577     memmap->entries[cur_mem].base_addr = 1024*1024;
578     memmap->entries[cur_mem].length = (total_mem < IOAPIC_BASE ? total_mem : IOAPIC_BASE) - 1024*1024;
579     memmap->entries[cur_mem].type = MEM_RAM;
580     memmap->entries[cur_mem].reserved = 0;
581     cur_mem++;
582
583     // ioapic reservation
584     memmap->entries[cur_mem].base_addr = IOAPIC_BASE;
585     memmap->entries[cur_mem].length = PAGE_SIZE;
586     memmap->entries[cur_mem].type = MEM_RESV;
587     memmap->entries[cur_mem].reserved = 1;
588     cur_mem++;
589
590     if (total_mem > (IOAPIC_BASE + PAGE_SIZE)) {
591         // memory between ioapic and apic
592         memmap->entries[cur_mem].base_addr = IOAPIC_BASE+PAGE_SIZE;
593         memmap->entries[cur_mem].length = (total_mem < APIC_BASE ? total_mem : APIC_BASE) - (IOAPIC_BASE+PAGE_SIZE);;
594         memmap->entries[cur_mem].type = MEM_RAM;
595         memmap->entries[cur_mem].reserved = 0;
596         cur_mem++;
597     } 
598
599     // apic
600     memmap->entries[cur_mem].base_addr = APIC_BASE;
601     memmap->entries[cur_mem].length = PAGE_SIZE;
602     memmap->entries[cur_mem].type = MEM_RESV;
603     memmap->entries[cur_mem].reserved = 1;
604     cur_mem++;
605
606     if (total_mem > (APIC_BASE + PAGE_SIZE)) {
607         // memory after apic
608         memmap->entries[cur_mem].base_addr = APIC_BASE+PAGE_SIZE;
609         memmap->entries[cur_mem].length = total_mem - (APIC_BASE+PAGE_SIZE);
610         memmap->entries[cur_mem].type = MEM_RAM;
611         memmap->entries[cur_mem].reserved = 0;
612         cur_mem++;
613     } 
614
615     for (cur_mem=0;cur_mem<num_mem;cur_mem++) { 
616         PrintDebug(vm, VCORE_NONE,
617                    "multiboot: entry %llu: %p (%llx bytes) - type %x %s\n",
618                    cur_mem, 
619                    (void*) memmap->entries[cur_mem].base_addr,
620                    memmap->entries[cur_mem].length,
621                    memmap->entries[cur_mem].type,
622                    memmap->entries[cur_mem].reserved ? "reserved" : "");
623     }
624
625
626
627     // This demarcates end of list
628     tag->type = 0;
629     tag->size = 8;
630
631     return header->totalsize;
632
633 }
634
635
636 int v3_write_multiboot_kernel(struct v3_vm_info *vm, mb_data_t *mb, struct v3_cfg_file *file,
637                               void *base, uint64_t limit)
638 {
639     uint32_t offset;
640
641     if (!mb->addr || !mb->entry) { 
642         PrintError(vm,VCORE_NONE, "multiboot: kernel is missing address or entry point\n");
643         return -1;
644     }
645
646     if (((void*)(uint64_t)(mb->addr->header_addr) < base ) ||
647         ((void*)(uint64_t)(mb->addr->load_end_addr) > base+limit) ||
648         ((void*)(uint64_t)(mb->addr->bss_end_addr) > base+limit)) { 
649         PrintError(vm,VCORE_NONE, "multiboot: kernel is not within the allowed portion of VM\n");
650         return -1;
651     }
652
653     offset = mb->addr->load_addr - mb->addr->header_addr;
654
655     // Skip the ELF header - assume 1 page... weird.... 
656     // We are trying to do as little ELF loading here as humanly possible
657     v3_write_gpa_memory(&vm->cores[0],
658                         (addr_t)(mb->addr->load_addr),
659                         file->size-PAGE_SIZE-offset,
660                         file->data+PAGE_SIZE+offset);
661
662     PrintDebug(vm,VCORE_NONE,
663                "multiboot: wrote 0x%llx bytes starting at offset 0x%llx to %p\n",
664                (uint64_t) file->size-PAGE_SIZE-offset,
665                (uint64_t) PAGE_SIZE+offset,
666                (void*)(addr_t)(mb->addr->load_addr));
667
668     return 0;
669
670 }
671
672
673 static int setup_multiboot_kernel(struct v3_vm_info *vm)
674 {
675     void *base = 0;
676     uint64_t limit = vm->mem_size;
677
678
679     if (vm->mb_state.mb_file->size > limit) { 
680         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);
681         return -1;
682     }
683
684     if (!is_elf(vm->mb_state.mb_file->data,vm->mb_state.mb_file->size)) { 
685         PrintError(vm,VCORE_NONE,"multiboot: supplied kernel is not an ELF\n");
686         return -1;
687     } else {
688         if (find_mb_header(vm->mb_state.mb_file->data,vm->mb_state.mb_file->size)) { 
689             PrintDebug(vm,VCORE_NONE,"multiboot: appears to be a multiboot kernel\n");
690             if (v3_parse_multiboot_header(vm->mb_state.mb_file,&vm->mb_state.mb_data)) { 
691                 PrintError(vm,VCORE_NONE,"multiboot: cannot parse multiboot kernel header\n");
692                 return -1;
693             }
694             if (v3_write_multiboot_kernel(vm, &(vm->mb_state.mb_data),vm->mb_state.mb_file,base,limit)) { 
695                 PrintError(vm,VCORE_NONE,"multiboot: multiboot kernel setup failed\n");
696                 return -1;
697             } 
698         } else {
699             PrintError(vm,VCORE_NONE,"multiboot: multiboot kernel has no header\n");
700             return -1;
701         }
702     }
703     
704     return 0;
705     
706 }
707
708 // 32 bit GDT entries
709 //
710 //         base24-31    flags2  limit16-19 access8  base16-23   base0-15   limit0-15
711 // null       0           0          0       0         0           0           0
712 // code       0           1100       f     10011010    0           0         ffff
713 // data       0           1100       f     10010010    0           0         ffff
714 //
715 // null =   00 00 00 00 00 00 00 00
716 // code =   00 cf 9a 00 00 00 ff ff 
717 // data =   00 cf 92 00 00 00 ff ff
718 //
719 static uint64_t gdt32[3] = {
720     0x0000000000000000, /* null */
721     0x00cf9a000000ffff, /* code (note lme=0) */
722     0x00cf92000000ffff, /* data */
723 };
724
725 static void write_gdt(struct v3_vm_info *vm, void *base, uint64_t limit)
726 {
727     v3_write_gpa_memory(&vm->cores[0],(addr_t)base,limit,(uint8_t*) gdt32);
728
729     PrintDebug(vm,VCORE_NONE,"multiboot: wrote GDT at %p\n",base);
730 }
731
732         
733 static void write_tss(struct v3_vm_info *vm, void *base, uint64_t limit)
734 {
735     int i;
736     uint64_t tss_data=0x0;
737
738     for (i=0;i<limit/8;i++) {
739         v3_write_gpa_memory(&vm->cores[0],(addr_t)(base+8*i),8,(uint8_t*) &tss_data);
740     }
741
742     PrintDebug(vm,VCORE_NONE,"multiboot: wrote TSS at %p\n",base);
743 }
744
745 static void write_table(struct v3_vm_info *vm, void *base, uint64_t limit)
746 {
747     uint64_t size;
748     uint8_t buf[256];
749
750     limit = limit < 256 ? limit : 256;
751
752     size = v3_build_multiboot_table(&vm->cores[0], buf, limit);
753
754     if (size>256 || size==0) { 
755         PrintError(vm,VCORE_NONE,"multiboot: cannot build multiboot table\n");
756         return;
757     }
758     
759     v3_write_gpa_memory(&vm->cores[0],(addr_t)base,size,buf);
760
761 }
762
763
764
765 /*
766   GPA layout:
767
768   GDT
769   TSS
770   MBinfo   
771   Kernel at its desired load address (or error)
772
773 */
774
775
776 int v3_setup_multiboot_vm_for_boot(struct v3_vm_info *vm)
777 {
778     void *kernel_start_gpa;
779     void *kernel_end_gpa;
780     void *mb_gpa;
781     void *tss_gpa;
782     void *gdt_gpa;
783
784     if (!vm->mb_state.is_multiboot) { 
785         PrintDebug(vm,VCORE_NONE,"multiboot: skipping multiboot setup for boot as this is not a multiboot VM\n");
786         return 0;
787     }
788
789     
790     if (setup_multiboot_kernel(vm)) {
791         PrintError(vm,VCORE_NONE,"multiboot: failed to setup kernel\n");
792         return -1;
793     } 
794
795     kernel_start_gpa = (void*) (uint64_t) (vm->mb_state.mb_data.addr->load_addr);
796     kernel_end_gpa = (void*) (uint64_t) (vm->mb_state.mb_data.addr->bss_end_addr);
797
798     // Is there room below the kernel? 
799     if ((uint64_t)kernel_start_gpa > 19*4096 ) {
800         // at least 3 pages between 64K and start of kernel 
801         // place at 64K
802         mb_gpa=(void*)(16*4096);
803     } else {
804         // is there room above the kernel?
805         if ((uint64_t)kernel_end_gpa < vm->mem_size-4*4096) { 
806             if (((uint64_t)kernel_end_gpa + 4 * 4096) <= 0xffffffff) { 
807                 mb_gpa=(void*) (4096*((uint64_t)kernel_end_gpa/4096 + 1));
808             } else {
809                 PrintError(vm,VCORE_NONE,"multiboot: no room for mb data below 4 GB\n");
810                 return -1;
811             } 
812         } else {
813             PrintError(vm,VCORE_NONE,"multiboot: no room for mb data above kernel\n");
814             return -1;
815         }
816     }
817
818     PrintDebug(vm,VCORE_NONE,"multiboot: mb data will start at %p\n",mb_gpa);
819
820     vm->mb_state.mb_data_gpa=mb_gpa;
821
822     tss_gpa = mb_gpa + 1 * 4096;
823     gdt_gpa = mb_gpa + 2 * 4096;
824
825     write_table(vm,mb_gpa,4096);
826     
827     write_tss(vm,tss_gpa,4096);
828
829     write_gdt(vm,gdt_gpa,4096);
830
831     PrintDebug(vm,VCORE_NONE,"multiboot: setup of memory done\n");
832
833     return 0;
834 }
835
836 /*
837   On entry:
838
839    IDTR not set
840    GDTR points to stub GDT
841    TR   points to stub TSS
842    CR0  has PE and not PG
843    EIP  is entry point to kernel
844    EBX  points to multiboot info
845    EAX  multiboot magic cookie
846
847 */
848 int v3_setup_multiboot_core_for_boot(struct guest_info *core)
849 {
850     void *base;
851     uint64_t limit;
852
853     if (!core->vm_info->mb_state.is_multiboot) {
854         PrintDebug(core->vm_info,core,"multiboot: skipping mb core setup as this is not an mb VM\n");
855         return 0;
856     }
857         
858     if (core->vcpu_id != 0) {
859         PrintDebug(core->vm_info,core,"multiboot: skipping mb core setup as this is not the BSP core\n");
860         return 0;
861     }
862
863
864     PrintDebug(core->vm_info, core, "multiboot: setting up MB BSP core for boot\n");
865
866     
867     memset(&core->vm_regs,0,sizeof(core->vm_regs));
868     memset(&core->ctrl_regs,0,sizeof(core->ctrl_regs));
869     memset(&core->dbg_regs,0,sizeof(core->dbg_regs));
870     memset(&core->segments,0,sizeof(core->segments));    
871     memset(&core->msrs,0,sizeof(core->msrs));    
872     memset(&core->fp_state,0,sizeof(core->fp_state));    
873
874     // We need to be in protected mode at ring zero
875     core->cpl = 0; // we are going right into the kernel
876     core->cpu_mode = PROTECTED;
877     core->mem_mode = PHYSICAL_MEM; 
878     // default run-state is fine, we are core zero
879     // core->core_run_state = CORE_RUNNING ;
880
881     // right into the kernel
882     core->rip = (uint64_t) core->vm_info->mb_state.mb_data.entry->entry_addr;
883
884     // Setup CRs for protected mode
885     // CR0:  PE (but no PG)
886     core->ctrl_regs.cr0 = 0x1;
887     core->shdw_pg_state.guest_cr0 = core->ctrl_regs.cr0;
888
889     // CR2: don't care (output from #PF)
890     // CR3: don't care (no paging)
891     core->ctrl_regs.cr3 = 0;
892     core->shdw_pg_state.guest_cr3 = core->ctrl_regs.cr3;
893
894     // CR4: no features 
895     core->ctrl_regs.cr4 = 0x0;
896     core->shdw_pg_state.guest_cr4 = core->ctrl_regs.cr4;
897     // CR8 as usual
898     // RFLAGS zeroed is fine: come in with interrupts off
899     // 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
900     core->ctrl_regs.efer = 0x1400;
901     core->shdw_pg_state.guest_efer.value = core->ctrl_regs.efer;
902
903
904     /* 
905        Notes on selectors:
906
907        selector is 13 bits of index, 1 bit table indicator 
908        (0=>GDT), 2 bit RPL
909        
910        index is scaled by 8, even in long mode, where some entries 
911        are 16 bytes long.... 
912           -> code, data descriptors have 8 byte format
913              because base, limit, etc, are ignored (no segmentation)
914           -> interrupt/trap gates have 16 byte format 
915              because offset needs to be 64 bits
916     */
917     
918     // There is no IDTR set and interrupts are disabled
919
920     // Install our stub GDT
921     core->segments.gdtr.selector = 0;
922     core->segments.gdtr.base = (addr_t) core->vm_info->mb_state.mb_data_gpa+2*4096;
923     core->segments.gdtr.limit = 4096-1;
924     core->segments.gdtr.type = 0x6;
925     core->segments.gdtr.system = 1; 
926     core->segments.gdtr.dpl = 0;
927     core->segments.gdtr.present = 1;
928     core->segments.gdtr.long_mode = 0;
929     
930     // And our TSS
931     core->segments.tr.selector = 0;
932     core->segments.tr.base = (addr_t) core->vm_info->mb_state.mb_data_gpa+1*4096;
933     core->segments.tr.limit = 4096-1;
934     core->segments.tr.type = 0x6;
935     core->segments.tr.system = 1; 
936     core->segments.tr.dpl = 0;
937     core->segments.tr.present = 1;
938     core->segments.tr.long_mode = 0;
939     
940     base = 0x0;
941     limit = -1;
942
943     // And CS
944     core->segments.cs.selector = 0x8 ; // entry 1 of GDT (RPL=0)
945     core->segments.cs.base = (addr_t) base;
946     core->segments.cs.limit = limit;
947     core->segments.cs.type = 0xe;
948     core->segments.cs.system = 0; 
949     core->segments.cs.dpl = 0;
950     core->segments.cs.present = 1;
951     core->segments.cs.long_mode = 0;
952
953     // DS, SS, etc are identical
954     core->segments.ds.selector = 0x10; // entry 2 of GDT (RPL=0)
955     core->segments.ds.base = (addr_t) base;
956     core->segments.ds.limit = limit;
957     core->segments.ds.type = 0x6;
958     core->segments.ds.system = 0; 
959     core->segments.ds.dpl = 0;
960     core->segments.ds.present = 1;
961     core->segments.ds.long_mode = 0;
962     
963     memcpy(&core->segments.ss,&core->segments.ds,sizeof(core->segments.ds));
964     memcpy(&core->segments.es,&core->segments.ds,sizeof(core->segments.ds));
965     memcpy(&core->segments.fs,&core->segments.ds,sizeof(core->segments.ds));
966     memcpy(&core->segments.gs,&core->segments.ds,sizeof(core->segments.ds));
967     
968
969
970     // Now for our magic - this signals
971     // the kernel that a multiboot loader loaded it
972     // and that rbx points to its offered data
973     core->vm_regs.rax = MB2_INFO_MAGIC;
974
975     core->vm_regs.rbx = (uint64_t) (core->vm_info->mb_state.mb_data_gpa);
976
977     // reset paging here for shadow... 
978
979     if (core->shdw_pg_mode != NESTED_PAGING) { 
980         PrintError(core->vm_info, core, "multiboot: shadow paging guest... this will end badly\n");
981         return -1;
982     }
983
984
985     return 0;
986 }