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.


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