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.


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