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.


refactoring to the point of compilation
[palacios.git] / palacios / src / palacios / vmx.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) 2008, Peter Dinda <pdinda@northwestern.edu> 
11  * Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu>
12  * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org> 
13  * All rights reserved.
14  *
15  * Author: Peter Dinda <pdinda@northwestern.edu>
16  *         Jack Lange <jarusl@cs.northwestern.edu>
17  *
18  * This is free software.  You are permitted to use,
19  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
20  */
21
22
23 #include <palacios/vmx.h>
24 #include <palacios/vmm.h>
25 #include <palacios/vmx_handler.h>
26 #include <palacios/vmcs.h>
27 #include <palacios/vmx_lowlevel.h>
28 #include <palacios/vmm_lowlevel.h>
29 #include <palacios/vmm_ctrl_regs.h>
30 #include <palacios/vmm_config.h>
31 #include <palacios/vm_guest_mem.h>
32 #include <palacios/vmm_direct_paging.h>
33 #include <palacios/vmx_io.h>
34 #include <palacios/vmx_msr.h>
35
36 static addr_t host_vmcs_ptrs[CONFIG_MAX_CPUS] = { [0 ... CONFIG_MAX_CPUS - 1] = 0};
37
38
39
40 extern int v3_vmx_launch(struct v3_gprs * vm_regs, struct guest_info * info, struct v3_ctrl_regs * ctrl_regs);
41 extern int v3_vmx_resume(struct v3_gprs * vm_regs, struct guest_info * info, struct v3_ctrl_regs * ctrl_regs);
42
43 static int inline check_vmcs_write(vmcs_field_t field, addr_t val) {
44     int ret = 0;
45
46     ret = vmcs_write(field,val);
47
48     if (ret != VMX_SUCCESS) {
49         PrintError("VMWRITE error on %s!: %d\n", v3_vmcs_field_to_str(field), ret);
50         return 1;
51     }
52
53     return 0;
54 }
55
56 static int inline check_vmcs_read(vmcs_field_t field, void * val) {
57     int ret = 0;
58
59     ret = vmcs_read(field, val);
60
61     if (ret != VMX_SUCCESS) {
62         PrintError("VMREAD error on %s!: %d\n", v3_vmcs_field_to_str(field), ret);
63     }
64
65     return ret;
66 }
67
68 #if 0
69 // For the 32 bit reserved bit fields 
70 // MB1s are in the low 32 bits, MBZs are in the high 32 bits of the MSR
71 static uint32_t sanitize_bits1(uint32_t msr_num, uint32_t val) {
72     v3_msr_t mask_msr;
73
74     PrintDebug("sanitize_bits1 (MSR:%x)\n", msr_num);
75
76     v3_get_msr(msr_num, &mask_msr.hi, &mask_msr.lo);
77
78     PrintDebug("MSR %x = %x : %x \n", msr_num, mask_msr.hi, mask_msr.lo);
79
80     val |= mask_msr.lo;
81     val |= mask_msr.hi;
82   
83     return val;
84 }
85
86
87
88 static addr_t sanitize_bits2(uint32_t msr_num0, uint32_t msr_num1, addr_t val) {
89     v3_msr_t msr0, msr1;
90     addr_t msr0_val, msr1_val;
91
92     PrintDebug("sanitize_bits2 (MSR0=%x, MSR1=%x)\n", msr_num0, msr_num1);
93
94     v3_get_msr(msr_num0, &msr0.hi, &msr0.lo);
95     v3_get_msr(msr_num1, &msr1.hi, &msr1.lo);
96   
97     // This generates a mask that is the natural bit width of the CPU
98     msr0_val = msr0.value;
99     msr1_val = msr1.value;
100
101     PrintDebug("MSR %x = %p, %x = %p \n", msr_num0, (void*)msr0_val, msr_num1, (void*)msr1_val);
102
103     val |= msr0_val;
104     val |= msr1_val;
105
106     return val;
107 }
108
109
110
111 #endif
112
113
114 static addr_t allocate_vmcs() {
115     reg_ex_t msr;
116     struct vmcs_data * vmcs_page = NULL;
117
118     PrintDebug("Allocating page\n");
119
120     vmcs_page = (struct vmcs_data *)V3_VAddr(V3_AllocPages(1));
121     memset(vmcs_page, 0, 4096);
122
123     v3_get_msr(VMX_BASIC_MSR, &(msr.e_reg.high), &(msr.e_reg.low));
124     
125     vmcs_page->revision = ((struct vmx_basic_msr*)&msr)->revision;
126     PrintDebug("VMX Revision: 0x%x\n",vmcs_page->revision);
127
128     return (addr_t)V3_PAddr((void *)vmcs_page);
129 }
130
131
132
133
134 static int init_vmcs_bios(struct guest_info * info, struct vmx_data * vmx_state) {
135     int vmx_ret = 0;
136
137     PrintDebug("Loading VMCS\n");
138     vmx_ret = vmcs_load(vmx_state->vmcs_ptr_phys);
139
140     if (vmx_ret != VMX_SUCCESS) {
141         PrintError("VMPTRLD failed\n");
142         return -1;
143     }
144
145
146
147     /******* Setup Host State **********/
148
149     /* Cache GDTR, IDTR, and TR in host struct */
150     addr_t gdtr_base;
151     struct {
152         uint16_t selector;
153         addr_t   base;
154     } __attribute__((packed)) tmp_seg;
155     
156
157     __asm__ __volatile__(
158                          "sgdt (%0);"
159                          :
160                          : "q"(&tmp_seg)
161                          : "memory"
162                          );
163     gdtr_base = tmp_seg.base;
164     vmx_state->host_state.gdtr.base = gdtr_base;
165
166     __asm__ __volatile__(
167                          "sidt (%0);"
168                          :
169                          : "q"(&tmp_seg)
170                          : "memory"
171                          );
172     vmx_state->host_state.idtr.base = tmp_seg.base;
173
174     __asm__ __volatile__(
175                          "str (%0);"
176                          :
177                          : "q"(&tmp_seg)
178                          : "memory"
179                          );
180     vmx_state->host_state.tr.selector = tmp_seg.selector;
181
182     /* The GDTR *index* is bits 3-15 of the selector. */
183     struct tss_descriptor * desc = NULL;
184     desc = (struct tss_descriptor *)(gdtr_base + (8 * (tmp_seg.selector >> 3)));
185
186     tmp_seg.base = ((desc->base1) |
187                     (desc->base2 << 16) |
188                     (desc->base3 << 24) |
189 #ifdef __V3_64BIT__
190                     ((uint64_t)desc->base4 << 32)
191 #else 
192                     (0)
193 #endif
194                     );
195
196     vmx_state->host_state.tr.base = tmp_seg.base;
197
198   
199
200     /********** Setup and VMX Control Fields from MSR ***********/
201     /* Setup IO map */
202     /***** THEES NEED TO BE MOVED TO A GLOBAL LOCATION ***/
203     v3_init_vmx_io_map(info->vm_info);
204     v3_init_vmx_msr_map(info->vm_info);
205     /**** ****/
206
207     struct v3_msr tmp_msr;
208
209     v3_get_msr(VMX_PINBASED_CTLS_MSR, &(tmp_msr.hi), &(tmp_msr.lo));
210
211     /* Add external interrupts, NMI exiting, and virtual NMI */
212     vmx_state->pin_ctrls.value =  tmp_msr.lo;
213     vmx_state->pin_ctrls.nmi_exit = 1;
214     vmx_state->pin_ctrls.ext_int_exit = 1;
215
216     v3_get_msr(VMX_PROCBASED_CTLS_MSR, &(tmp_msr.hi), &(tmp_msr.lo));
217
218     vmx_state->pri_proc_ctrls.value = tmp_msr.lo;
219     vmx_state->pri_proc_ctrls.use_io_bitmap = 1;
220     vmx_state->pri_proc_ctrls.hlt_exit = 1;
221     vmx_state->pri_proc_ctrls.invlpg_exit = 1;
222     vmx_state->pri_proc_ctrls.use_msr_bitmap = 1;
223     vmx_state->pri_proc_ctrls.pause_exit = 1;
224
225     vmx_ret |= check_vmcs_write(VMCS_IO_BITMAP_A_ADDR, (addr_t)V3_PAddr(info->vm_info->io_map.arch_data));
226     vmx_ret |= check_vmcs_write(VMCS_IO_BITMAP_B_ADDR, 
227             (addr_t)V3_PAddr(info->vm_info->io_map.arch_data) + PAGE_SIZE_4KB);
228
229
230     vmx_ret |= check_vmcs_write(VMCS_MSR_BITMAP, (addr_t)V3_PAddr(info->vm_info->msr_map.arch_data));
231
232     v3_get_msr(VMX_EXIT_CTLS_MSR, &(tmp_msr.hi), &(tmp_msr.lo));
233     vmx_state->exit_ctrls.value = tmp_msr.lo;
234     vmx_state->exit_ctrls.host_64_on = 1;
235
236     if ((vmx_state->exit_ctrls.save_efer == 1) || (vmx_state->exit_ctrls.ld_efer == 1)) {
237         vmx_state->ia32e_avail = 1;
238     }
239
240     v3_get_msr(VMX_ENTRY_CTLS_MSR, &(tmp_msr.hi), &(tmp_msr.lo));
241     vmx_state->entry_ctrls.value = tmp_msr.lo;
242
243     {
244         struct vmx_exception_bitmap excp_bmap;
245         excp_bmap.value = 0;
246         
247         excp_bmap.pf = 1;
248     
249         vmx_ret |= check_vmcs_write(VMCS_EXCP_BITMAP, excp_bmap.value);
250     }
251     /******* Setup VMXAssist guest state ***********/
252
253     info->rip = 0xd0000;
254     info->vm_regs.rsp = 0x80000;
255
256     struct rflags * flags = (struct rflags *)&(info->ctrl_regs.rflags);
257     flags->rsvd1 = 1;
258
259     /* Print Control MSRs */
260     v3_get_msr(VMX_CR0_FIXED0_MSR, &(tmp_msr.hi), &(tmp_msr.lo));
261     PrintDebug("CR0 MSR: %p\n", (void *)(addr_t)tmp_msr.value);
262
263     v3_get_msr(VMX_CR4_FIXED0_MSR, &(tmp_msr.hi), &(tmp_msr.lo));
264     PrintDebug("CR4 MSR: %p\n", (void *)(addr_t)tmp_msr.value);
265
266
267 #define GUEST_CR0 0x80000031
268 #define GUEST_CR4 0x00002000
269     info->ctrl_regs.cr0 = GUEST_CR0;
270     info->ctrl_regs.cr4 = GUEST_CR4;
271
272     ((struct cr0_32 *)&(info->shdw_pg_state.guest_cr0))->pe = 1;
273    
274     /* Setup paging */
275     if (info->shdw_pg_mode == SHADOW_PAGING) {
276         PrintDebug("Creating initial shadow page table\n");
277
278         if (v3_init_passthrough_pts(info) == -1) {
279             PrintError("Could not initialize passthrough page tables\n");
280             return -1;
281         }
282         
283 #define CR0_PE 0x00000001
284 #define CR0_PG 0x80000000
285
286
287         vmx_ret |= check_vmcs_write(VMCS_CR0_MASK, (CR0_PE | CR0_PG) );
288         vmx_ret |= check_vmcs_write(VMCS_CR4_MASK, CR4_VMXE);
289
290         info->ctrl_regs.cr3 = info->direct_map_pt;
291
292         // vmx_state->pinbased_ctrls |= NMI_EXIT;
293
294         /* Add CR exits */
295         vmx_state->pri_proc_ctrls.cr3_ld_exit = 1;
296         vmx_state->pri_proc_ctrls.cr3_str_exit = 1;
297     }
298
299     // Setup segment registers
300     {
301         struct v3_segment * seg_reg = (struct v3_segment *)&(info->segments);
302
303         int i;
304
305         for (i = 0; i < 10; i++) {
306             seg_reg[i].selector = 3 << 3;
307             seg_reg[i].limit = 0xffff;
308             seg_reg[i].base = 0x0;
309         }
310
311         info->segments.cs.selector = 2<<3;
312
313         /* Set only the segment registers */
314         for (i = 0; i < 6; i++) {
315             seg_reg[i].limit = 0xfffff;
316             seg_reg[i].granularity = 1;
317             seg_reg[i].type = 3;
318             seg_reg[i].system = 1;
319             seg_reg[i].dpl = 0;
320             seg_reg[i].present = 1;
321             seg_reg[i].db = 1;
322         }
323
324         info->segments.cs.type = 0xb;
325
326         info->segments.ldtr.selector = 0x20;
327         info->segments.ldtr.type = 2;
328         info->segments.ldtr.system = 0;
329         info->segments.ldtr.present = 1;
330         info->segments.ldtr.granularity = 0;
331
332     
333         /************* Map in GDT and vmxassist *************/
334
335         uint64_t  gdt[] __attribute__ ((aligned(32))) = {
336             0x0000000000000000ULL,              /* 0x00: reserved */
337             0x0000830000000000ULL,              /* 0x08: 32-bit TSS */
338             //0x0000890000000000ULL,            /* 0x08: 32-bit TSS */
339             0x00CF9b000000FFFFULL,              /* 0x10: CS 32-bit */
340             0x00CF93000000FFFFULL,              /* 0x18: DS 32-bit */
341             0x000082000000FFFFULL,              /* 0x20: LDTR 32-bit */
342         };
343
344 #define VMXASSIST_GDT   0x10000
345         addr_t vmxassist_gdt = 0;
346
347         if (guest_pa_to_host_va(info, VMXASSIST_GDT, &vmxassist_gdt) == -1) {
348             PrintError("Could not find VMXASSIST GDT destination\n");
349             return -1;
350         }
351
352         memcpy((void *)vmxassist_gdt, gdt, sizeof(uint64_t) * 5);
353         
354         info->segments.gdtr.base = VMXASSIST_GDT;
355
356 #define VMXASSIST_TSS   0x40000
357         uint64_t vmxassist_tss = VMXASSIST_TSS;
358         gdt[0x08 / sizeof(gdt[0])] |=
359             ((vmxassist_tss & 0xFF000000) << (56 - 24)) |
360             ((vmxassist_tss & 0x00FF0000) << (32 - 16)) |
361             ((vmxassist_tss & 0x0000FFFF) << (16)) |
362             (8392 - 1);
363
364         info->segments.tr.selector = 0x08;
365         info->segments.tr.base = vmxassist_tss;
366
367         //info->segments.tr.type = 0x9; 
368         info->segments.tr.type = 0x3;
369         info->segments.tr.system = 0;
370         info->segments.tr.present = 1;
371         info->segments.tr.granularity = 0;
372     }
373  
374     // setup VMXASSIST
375     { 
376 #define VMXASSIST_START 0x000d0000
377         extern uint8_t v3_vmxassist_start[];
378         extern uint8_t v3_vmxassist_end[];
379         addr_t vmxassist_dst = 0;
380
381         if (guest_pa_to_host_va(info, VMXASSIST_START, &vmxassist_dst) == -1) {
382             PrintError("Could not find VMXASSIST destination\n");
383             return -1;
384         }
385
386         memcpy((void *)vmxassist_dst, v3_vmxassist_start, v3_vmxassist_end - v3_vmxassist_start);
387     }    
388
389     /*** Write all the info to the VMCS ***/
390
391 #define DEBUGCTL_MSR 0x1d9
392     v3_get_msr(DEBUGCTL_MSR, &(tmp_msr.hi), &(tmp_msr.lo));
393     vmx_ret |= check_vmcs_write(VMCS_GUEST_DBG_CTL, tmp_msr.value);
394
395     info->dbg_regs.dr7 = 0x400;
396
397     vmx_ret |= check_vmcs_write(VMCS_LINK_PTR, (addr_t)0xffffffffffffffffULL);
398     
399
400     if (v3_update_vmcs_ctrl_fields(info)) {
401         PrintError("Could not write control fields!\n");
402         return -1;
403     }
404     
405     if (v3_update_vmcs_host_state(info)) {
406         PrintError("Could not write host state\n");
407         return -1;
408     }
409
410
411     vmx_state->state = VMXASSIST_DISABLED;
412
413     return 0;
414 }
415
416 int v3_init_vmx_vmcs(struct guest_info * info, v3_vm_class_t vm_class) {
417     struct vmx_data * vmx_state = NULL;
418     int vmx_ret = 0;
419     
420     vmx_state = (struct vmx_data *)V3_Malloc(sizeof(struct vmx_data));
421
422     PrintDebug("vmx_data pointer: %p\n", (void *)vmx_state);
423
424     PrintDebug("Allocating VMCS\n");
425     vmx_state->vmcs_ptr_phys = allocate_vmcs();
426
427     PrintDebug("VMCS pointer: %p\n", (void *)(vmx_state->vmcs_ptr_phys));
428
429     info->vmm_data = vmx_state;
430
431     PrintDebug("Initializing VMCS (addr=%p)\n", info->vmm_data);
432     
433     // TODO: Fix vmcs fields so they're 32-bit
434
435     PrintDebug("Clearing VMCS: %p\n", (void *)vmx_state->vmcs_ptr_phys);
436     vmx_ret = vmcs_clear(vmx_state->vmcs_ptr_phys);
437
438     if (vmx_ret != VMX_SUCCESS) {
439         PrintError("VMCLEAR failed\n");
440         return -1; 
441     }
442
443     if (vm_class == V3_PC_VM) {
444         PrintDebug("Initializing VMCS\n");
445         init_vmcs_bios(info, vmx_state);
446     } else {
447         PrintError("Invalid VM Class\n");
448         return -1;
449     }
450
451     return 0;
452 }
453
454 static int update_irq_exit_state(struct guest_info * info) {
455     struct vmx_exit_idt_vec_info idt_vec_info;
456
457     check_vmcs_read(VMCS_IDT_VECTOR_INFO, &(idt_vec_info.value));
458
459     if ((info->intr_core_state.irq_started == 1) && (idt_vec_info.valid == 0)) {
460 #ifdef CONFIG_DEBUG_INTERRUPTS
461         PrintDebug("Calling v3_injecting_intr\n");
462 #endif
463         info->intr_core_state.irq_started = 0;
464         v3_injecting_intr(info, info->intr_core_state.irq_vector, V3_EXTERNAL_IRQ);
465     }
466
467     return 0;
468 }
469
470 static int update_irq_entry_state(struct guest_info * info) {
471     struct vmx_exit_idt_vec_info idt_vec_info;
472     struct vmcs_interrupt_state intr_core_state;
473     struct vmx_data * vmx_info = (struct vmx_data *)(info->vmm_data);
474
475     check_vmcs_read(VMCS_IDT_VECTOR_INFO, &(idt_vec_info.value));
476     check_vmcs_read(VMCS_GUEST_INT_STATE, &(intr_core_state));
477
478     /* Check for pending exceptions to inject */
479     if (v3_excp_pending(info)) {
480         struct vmx_entry_int_info int_info;
481         int_info.value = 0;
482
483         // In VMX, almost every exception is hardware
484         // Software exceptions are pretty much only for breakpoint or overflow
485         int_info.type = 3;
486         int_info.vector = v3_get_excp_number(info);
487
488         if (info->excp_state.excp_error_code_valid) {
489             check_vmcs_write(VMCS_ENTRY_EXCP_ERR, info->excp_state.excp_error_code);
490             int_info.error_code = 1;
491
492 #ifdef CONFIG_DEBUG_INTERRUPTS
493             PrintDebug("Injecting exception %d with error code %x\n", 
494                     int_info.vector, info->excp_state.excp_error_code);
495 #endif
496         }
497
498         int_info.valid = 1;
499 #ifdef CONFIG_DEBUG_INTERRUPTS
500         PrintDebug("Injecting exception %d (EIP=%p)\n", int_info.vector, (void *)info->rip);
501 #endif
502         check_vmcs_write(VMCS_ENTRY_INT_INFO, int_info.value);
503
504         v3_injecting_excp(info, int_info.vector);
505
506     } else if ((((struct rflags *)&(info->ctrl_regs.rflags))->intr == 1) && 
507                (intr_core_state.val == 0)) {
508        
509         if ((info->intr_core_state.irq_started == 1) && (idt_vec_info.valid == 1)) {
510
511 #ifdef CONFIG_DEBUG_INTERRUPTS
512             PrintDebug("IRQ pending from previous injection\n");
513 #endif
514
515             // Copy the IDT vectoring info over to reinject the old interrupt
516             if (idt_vec_info.error_code == 1) {
517                 uint32_t err_code = 0;
518
519                 check_vmcs_read(VMCS_IDT_VECTOR_ERR, &err_code);
520                 check_vmcs_write(VMCS_ENTRY_EXCP_ERR, err_code);
521             }
522
523             idt_vec_info.undef = 0;
524             check_vmcs_write(VMCS_ENTRY_INT_INFO, idt_vec_info.value);
525
526         } else {
527             struct vmx_entry_int_info ent_int;
528             ent_int.value = 0;
529
530             switch (v3_intr_pending(info)) {
531                 case V3_EXTERNAL_IRQ: {
532                     info->intr_core_state.irq_vector = v3_get_intr(info); 
533                     ent_int.vector = info->intr_core_state.irq_vector;
534                     ent_int.type = 0;
535                     ent_int.error_code = 0;
536                     ent_int.valid = 1;
537
538 #ifdef CONFIG_DEBUG_INTERRUPTS
539                     PrintDebug("Injecting Interrupt %d at exit %u(EIP=%p)\n", 
540                                info->intr_core_state.irq_vector, 
541                                (uint32_t)info->num_exits, 
542                                (void *)info->rip);
543 #endif
544
545                     check_vmcs_write(VMCS_ENTRY_INT_INFO, ent_int.value);
546                     info->intr_core_state.irq_started = 1;
547
548                     break;
549                 }
550                 case V3_NMI:
551                     PrintDebug("Injecting NMI\n");
552
553                     ent_int.type = 2;
554                     ent_int.vector = 2;
555                     ent_int.valid = 1;
556                     check_vmcs_write(VMCS_ENTRY_INT_INFO, ent_int.value);
557
558                     break;
559                 case V3_SOFTWARE_INTR:
560                     PrintDebug("Injecting software interrupt\n");
561                     ent_int.type = 4;
562
563                     ent_int.valid = 1;
564                     check_vmcs_write(VMCS_ENTRY_INT_INFO, ent_int.value);
565
566                     break;
567                 case V3_VIRTUAL_IRQ:
568                     // Not sure what to do here, Intel doesn't have virtual IRQs
569                     // May be the same as external interrupts/IRQs
570
571                     break;
572                 case V3_INVALID_INTR:
573                 default:
574                     break;
575             }
576         }
577     } else if ((v3_intr_pending(info)) && (vmx_info->pri_proc_ctrls.int_wndw_exit == 0)) {
578         // Enable INTR window exiting so we know when IF=1
579         uint32_t instr_len;
580
581         check_vmcs_read(VMCS_EXIT_INSTR_LEN, &instr_len);
582
583 #ifdef CONFIG_DEBUG_INTERRUPTS
584         PrintDebug("Enabling Interrupt-Window exiting: %d\n", instr_len);
585 #endif
586
587         vmx_info->pri_proc_ctrls.int_wndw_exit = 1;
588         check_vmcs_write(VMCS_PROC_CTRLS, vmx_info->pri_proc_ctrls.value);
589     }
590
591
592     return 0;
593 }
594
595
596
597 static struct vmx_exit_info exit_log[10];
598
599 static void print_exit_log(struct guest_info * info) {
600     int cnt = info->num_exits % 10;
601     int i = 0;
602     
603
604     V3_Print("\nExit Log (%d total exits):\n", (uint32_t)info->num_exits);
605
606     for (i = 0; i < 10; i++) {
607         struct vmx_exit_info * tmp = &exit_log[cnt];
608
609         V3_Print("%d:\texit_reason = %p\n", i, (void *)(addr_t)tmp->exit_reason);
610         V3_Print("\texit_qual = %p\n", (void *)tmp->exit_qual);
611         V3_Print("\tint_info = %p\n", (void *)(addr_t)tmp->int_info);
612         V3_Print("\tint_err = %p\n", (void *)(addr_t)tmp->int_err);
613         V3_Print("\tinstr_info = %p\n", (void *)(addr_t)tmp->instr_info);
614
615         cnt--;
616
617         if (cnt == -1) {
618             cnt = 9;
619         }
620
621     }
622
623 }
624
625 /* 
626  * CAUTION and DANGER!!! 
627  * 
628  * The VMCS CANNOT(!!) be accessed outside of the cli/sti calls inside this function
629  * When exectuing a symbiotic call, the VMCS WILL be overwritten, so any dependencies 
630  * on its contents will cause things to break. The contents at the time of the exit WILL 
631  * change before the exit handler is executed.
632  */
633 int v3_vmx_enter(struct guest_info * info) {
634     int ret = 0;
635     uint64_t tmp_tsc = 0;
636     struct vmx_exit_info exit_info;
637
638     // Conditionally yield the CPU if the timeslice has expired
639     v3_yield_cond(info);
640
641
642     // v3_print_guest_state(info);
643
644     // disable global interrupts for vm state transition
645     v3_disable_ints();
646
647     v3_vmx_restore_vmcs(info);
648
649
650 #ifdef CONFIG_SYMBIOTIC
651     if (info->vm_info->sym_state.symcalls[info->cpu_id].sym_call_active == 0) {
652         update_irq_entry_state(info);
653     }
654 #else 
655     update_irq_entry_state(info);
656 #endif
657
658     {
659         addr_t guest_cr3;
660         vmcs_read(VMCS_GUEST_CR3, &guest_cr3);
661         vmcs_write(VMCS_GUEST_CR3, guest_cr3);
662     }
663
664     rdtscll(info->time_state.cached_host_tsc);
665
666     if (info->vm_info->run_state == VM_STOPPED) {
667         info->vm_info->run_state = VM_RUNNING;
668         ret = v3_vmx_launch(&(info->vm_regs), info, &(info->ctrl_regs));
669     } else {
670         ret = v3_vmx_resume(&(info->vm_regs), info, &(info->ctrl_regs));
671     }
672
673     //  PrintDebug("VMX Exit: ret=%d\n", ret);
674
675     if (ret != VMX_SUCCESS) {
676         uint32_t error = 0;
677
678         vmcs_read(VMCS_INSTR_ERR, &error);
679         PrintError("VMENTRY Error: %d\n", error);
680
681         return -1;
682     }
683
684     rdtscll(tmp_tsc);
685
686     info->num_exits++;
687
688     v3_update_time(info, tmp_tsc - info->time_state.cached_host_tsc);
689
690     /* Update guest state */
691     v3_vmx_save_vmcs(info);
692
693     // info->cpl = info->segments.cs.selector & 0x3;
694
695     info->mem_mode = v3_get_vm_mem_mode(info);
696     info->cpu_mode = v3_get_vm_cpu_mode(info);
697
698
699     check_vmcs_read(VMCS_EXIT_INSTR_LEN, &(exit_info.instr_len));
700     check_vmcs_read(VMCS_EXIT_INSTR_INFO, &(exit_info.instr_info));
701     check_vmcs_read(VMCS_EXIT_REASON, &(exit_info.exit_reason));
702     check_vmcs_read(VMCS_EXIT_QUAL, &(exit_info.exit_qual));
703     check_vmcs_read(VMCS_EXIT_INT_INFO, &(exit_info.int_info));
704     check_vmcs_read(VMCS_EXIT_INT_ERR, &(exit_info.int_err));
705     check_vmcs_read(VMCS_GUEST_LINEAR_ADDR, &(exit_info.guest_linear_addr));
706
707     //PrintDebug("VMX Exit taken, id-qual: %u-%lu\n", exit_info.exit_reason, exit_info.exit_qual);
708
709     exit_log[info->num_exits % 10] = exit_info;
710
711
712 #ifdef CONFIG_SYMBIOTIC
713     if (info->vm_info->sym_state.symcalls[info->cpu_id].sym_call_active == 0) {
714         update_irq_exit_state(info);
715     }
716 #else
717     update_irq_exit_state(info);
718 #endif
719
720     // reenable global interrupts after vm exit
721     v3_enable_ints();
722
723     // Conditionally yield the CPU if the timeslice has expired
724     v3_yield_cond(info);
725
726     if (v3_handle_vmx_exit(info, &exit_info) == -1) {
727         PrintError("Error in VMX exit handler\n");
728         return -1;
729     }
730
731     return 0;
732 }
733
734
735 int v3_start_vmx_guest(struct guest_info* info) {
736
737
738     PrintDebug("Launching VMX guest\n");
739
740     rdtscll(info->time_state.cached_host_tsc);
741
742
743     while (1) {
744         if (v3_vmx_enter(info) == -1) {
745             v3_print_vmcs();
746             print_exit_log(info);
747             return -1;
748         }
749
750 /*
751         if ((info->num_exits % 5000) == 0) {
752             V3_Print("VMX Exit number %d\n", (uint32_t)info->num_exits);
753         }
754 */
755
756     }
757
758     return 0;
759 }
760
761
762 int v3_is_vmx_capable() {
763     v3_msr_t feature_msr;
764     uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
765
766     v3_cpuid(0x1, &eax, &ebx, &ecx, &edx);
767
768     PrintDebug("ECX: 0x%x\n", ecx);
769
770     if (ecx & CPUID_1_ECX_VTXFLAG) {
771         v3_get_msr(VMX_FEATURE_CONTROL_MSR, &(feature_msr.hi), &(feature_msr.lo));
772         
773         PrintDebug("MSRREGlow: 0x%.8x\n", feature_msr.lo);
774
775         if ((feature_msr.lo & FEATURE_CONTROL_VALID) != FEATURE_CONTROL_VALID) {
776             PrintDebug("VMX is locked -- enable in the BIOS\n");
777             return 0;
778         }
779
780     } else {
781         PrintDebug("VMX not supported on this cpu\n");
782         return 0;
783     }
784
785     return 1;
786 }
787
788 static int has_vmx_nested_paging() {
789     return 0;
790 }
791
792
793
794 void v3_init_vmx_cpu(int cpu_id) {
795     extern v3_cpu_arch_t v3_cpu_types[];
796     struct v3_msr tmp_msr;
797     uint64_t ret = 0;
798
799     v3_get_msr(VMX_CR4_FIXED0_MSR,&(tmp_msr.hi),&(tmp_msr.lo));
800     
801     __asm__ __volatile__ (
802                           "movq %%cr4, %%rbx;"
803                           "orq  $0x00002000, %%rbx;"
804                           "movq %%rbx, %0;"
805                           : "=m"(ret) 
806                           :
807                           : "%rbx"
808                           );
809
810     if ((~ret & tmp_msr.value) == 0) {
811         __asm__ __volatile__ (
812                               "movq %0, %%cr4;"
813                               :
814                               : "q"(ret)
815                               );
816     } else {
817         PrintError("Invalid CR4 Settings!\n");
818         return;
819     }
820
821     __asm__ __volatile__ (
822                           "movq %%cr0, %%rbx; "
823                           "orq  $0x00000020,%%rbx; "
824                           "movq %%rbx, %%cr0;"
825                           :
826                           :
827                           : "%rbx"
828                           );
829     //
830     // Should check and return Error here.... 
831
832
833     // Setup VMXON Region
834     host_vmcs_ptrs[cpu_id] = allocate_vmcs();
835
836     PrintDebug("VMXON pointer: 0x%p\n", (void *)host_vmcs_ptrs[cpu_id]);
837
838     if (v3_enable_vmx(host_vmcs_ptrs[cpu_id]) == VMX_SUCCESS) {
839         PrintDebug("VMX Enabled\n");
840     } else {
841         PrintError("VMX initialization failure\n");
842         return;
843     }
844     
845
846     if (has_vmx_nested_paging() == 1) {
847         v3_cpu_types[cpu_id] = V3_VMX_EPT_CPU;
848     } else {
849         v3_cpu_types[cpu_id] = V3_VMX_CPU;
850     }
851
852 }
853