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.


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