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.


APIC and CR8 changes for vector priorization vs TPR
[palacios.git] / palacios / src / palacios / vmm.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, Jack Lange <jarusl@cs.northwestern.edu> 
11  * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org> 
12  * All rights reserved.
13  *
14  * Author: Jack Lange <jarusl@cs.northwestern.edu>
15  *
16  * This is free software.  You are permitted to use,
17  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
18  */
19
20 #include <palacios/vmm.h>
21 #include <palacios/vmm_intr.h>
22 #include <palacios/vmm_config.h>
23 #include <palacios/vm_guest.h>
24 #include <palacios/vmm_ctrl_regs.h>
25 #include <palacios/vmm_lowlevel.h>
26 #include <palacios/vmm_sprintf.h>
27 #include <palacios/vmm_extensions.h>
28 #include <palacios/vmm_timeout.h>
29
30
31 #ifdef V3_CONFIG_SVM
32 #include <palacios/svm.h>
33 #endif
34 #ifdef V3_CONFIG_VMX
35 #include <palacios/vmx.h>
36 #endif
37
38 #ifdef V3_CONFIG_CHECKPOINT
39 #include <palacios/vmm_checkpoint.h>
40 #endif
41
42
43 v3_cpu_arch_t v3_cpu_types[V3_CONFIG_MAX_CPUS];
44 v3_cpu_arch_t v3_mach_type = V3_INVALID_CPU;
45
46 struct v3_os_hooks * os_hooks = NULL;
47 int v3_dbg_enable = 0;
48
49
50
51
52 static void init_cpu(void * arg) {
53     uint32_t cpu_id = (uint32_t)(addr_t)arg;
54
55 #ifdef V3_CONFIG_SVM
56     if (v3_is_svm_capable()) {
57         PrintDebug("Machine is SVM Capable\n");
58         v3_init_svm_cpu(cpu_id);
59         
60     } else 
61 #endif
62 #ifdef V3_CONFIG_VMX
63     if (v3_is_vmx_capable()) {
64         PrintDebug("Machine is VMX Capable\n");
65         v3_init_vmx_cpu(cpu_id);
66
67     } else 
68 #endif
69     {
70        PrintError("CPU has no virtualization Extensions\n");
71     }
72 }
73
74
75 static void deinit_cpu(void * arg) {
76     uint32_t cpu_id = (uint32_t)(addr_t)arg;
77
78
79     switch (v3_cpu_types[cpu_id]) {
80 #ifdef V3_CONFIG_SVM
81         case V3_SVM_CPU:
82         case V3_SVM_REV3_CPU:
83             PrintDebug("Deinitializing SVM CPU %d\n", cpu_id);
84             v3_deinit_svm_cpu(cpu_id);
85             break;
86 #endif
87 #ifdef V3_CONFIG_VMX
88         case V3_VMX_CPU:
89         case V3_VMX_EPT_CPU:
90         case V3_VMX_EPT_UG_CPU:
91             PrintDebug("Deinitializing VMX CPU %d\n", cpu_id);
92             v3_deinit_vmx_cpu(cpu_id);
93             break;
94 #endif
95         case V3_INVALID_CPU:
96         default:
97             PrintError("CPU has no virtualization Extensions\n");
98             break;
99     }
100 }
101
102
103 void Init_V3(struct v3_os_hooks * hooks, char * cpu_mask, int num_cpus) {
104     int i = 0;
105     int minor = 0;
106     int major = 0;
107
108     V3_Print("V3 Print statement to fix a Kitten page fault bug\n");
109
110     // Set global variables. 
111     os_hooks = hooks;
112
113     // Determine the global machine type
114     v3_mach_type = V3_INVALID_CPU;
115
116     for (i = 0; i < V3_CONFIG_MAX_CPUS; i++) {
117         v3_cpu_types[i] = V3_INVALID_CPU;
118     }
119
120     // Register all the possible device types
121     V3_init_devices();
122
123     // Register all shadow paging handlers
124     V3_init_shdw_paging();
125
126     // Register all extensions
127     V3_init_extensions();
128
129
130 #ifdef V3_CONFIG_SYMMOD
131     V3_init_symmod();
132 #endif
133
134 #ifdef V3_CONFIG_CHECKPOINT
135     V3_init_checkpoint();
136 #endif
137
138     if ((hooks) && (hooks->call_on_cpu)) {
139
140         for (i = 0; i < num_cpus; i++) {
141             major = i / 8;
142             minor = i % 8;
143
144             if ((cpu_mask == NULL) || (*(cpu_mask + major) & (0x1 << minor))) {
145                 V3_Print("Initializing VMM extensions on cpu %d\n", i);
146                 hooks->call_on_cpu(i, &init_cpu, (void *)(addr_t)i);
147
148                 if (v3_mach_type == V3_INVALID_CPU) {
149                     v3_mach_type = v3_cpu_types[i];
150                 }   
151             }
152         }
153     }
154 }
155
156
157
158 void Shutdown_V3() {
159     int i;
160
161     V3_deinit_devices();
162     V3_deinit_shdw_paging();
163
164     V3_deinit_extensions();
165
166 #ifdef V3_CONFIG_SYMMOD
167     V3_deinit_symmod();
168 #endif
169
170 #ifdef V3_CONFIG_CHECKPOINT
171     V3_deinit_checkpoint();
172 #endif
173
174
175     if ((os_hooks) && (os_hooks->call_on_cpu)) {
176         for (i = 0; i < V3_CONFIG_MAX_CPUS; i++) {
177             if (v3_cpu_types[i] != V3_INVALID_CPU) {
178                 V3_Call_On_CPU(i, deinit_cpu, (void *)(addr_t)i);
179                 //deinit_cpu((void *)(addr_t)i);
180             }
181         }
182     }
183
184 }
185
186
187 v3_cpu_arch_t v3_get_cpu_type(int cpu_id) {
188     return v3_cpu_types[cpu_id];
189 }
190
191
192 struct v3_vm_info * v3_create_vm(void * cfg, void * priv_data, char * name) {
193     struct v3_vm_info * vm = v3_config_guest(cfg, priv_data);
194
195     if (vm == NULL) {
196         PrintError("Could not configure guest\n");
197         return NULL;
198     }
199
200     V3_Print("CORE 0 RIP=%p\n", (void *)(addr_t)(vm->cores[0].rip));
201
202     if (name == NULL) {
203         name = "[V3_VM]";
204     } else if (strlen(name) >= 128) {
205         PrintError("VM name is too long. Will be truncated to 128 chars.\n");
206     }
207
208     memset(vm->name, 0, 128);
209     strncpy(vm->name, name, 127);
210
211     return vm;
212 }
213
214
215
216
217 static int start_core(void * p)
218 {
219     struct guest_info * core = (struct guest_info *)p;
220
221
222     PrintDebug("virtual core %u (on logical core %u): in start_core (RIP=%p)\n", 
223                core->vcpu_id, core->pcpu_id, (void *)(addr_t)core->rip);
224
225     switch (v3_mach_type) {
226 #ifdef V3_CONFIG_SVM
227         case V3_SVM_CPU:
228         case V3_SVM_REV3_CPU:
229             return v3_start_svm_guest(core);
230             break;
231 #endif
232 #if V3_CONFIG_VMX
233         case V3_VMX_CPU:
234         case V3_VMX_EPT_CPU:
235         case V3_VMX_EPT_UG_CPU:
236             return v3_start_vmx_guest(core);
237             break;
238 #endif
239         default:
240             PrintError("Attempting to enter a guest on an invalid CPU\n");
241             return -1;
242     }
243     // should not happen
244     return 0;
245 }
246
247
248 // For the moment very ugly. Eventually we will shift the cpu_mask to an arbitrary sized type...
249 #define MAX_CORES 32
250
251
252 int v3_start_vm(struct v3_vm_info * vm, unsigned int cpu_mask) {
253     uint32_t i;
254     uint8_t * core_mask = (uint8_t *)&cpu_mask; // This is to make future expansion easier
255     uint32_t avail_cores = 0;
256     int vcore_id = 0;
257
258
259     if (vm->run_state != VM_STOPPED) {
260         PrintError("VM has already been launched (state=%d)\n", (int)vm->run_state);
261         return -1;
262     }
263
264     /// CHECK IF WE ARE MULTICORE ENABLED....
265
266     V3_Print("V3 --  Starting VM (%u cores)\n", vm->num_cores);
267     V3_Print("CORE 0 RIP=%p\n", (void *)(addr_t)(vm->cores[0].rip));
268
269
270     // Check that enough cores are present in the mask to handle vcores
271     for (i = 0; i < MAX_CORES; i++) {
272         int major = i / 8;
273         int minor = i % 8;
274         
275         if (core_mask[major] & (0x1 << minor)) {
276             if (v3_cpu_types[i] == V3_INVALID_CPU) {
277                 core_mask[major] &= ~(0x1 << minor);
278             } else {
279                 avail_cores++;
280             }
281         }
282     }
283
284
285     if (vm->num_cores > avail_cores) {
286         PrintError("Attempted to start a VM with too many cores (vm->num_cores = %d, avail_cores = %d, MAX=%d)\n", 
287                    vm->num_cores, avail_cores, MAX_CORES);
288         return -1;
289     }
290
291     vm->run_state = VM_RUNNING;
292
293     // Spawn off threads for each core. 
294     // We work backwards, so that core 0 is always started last.
295     for (i = 0, vcore_id = vm->num_cores - 1; (i < MAX_CORES) && (vcore_id >= 0); i++) {
296         int major = 0;
297         int minor = 0;
298         struct guest_info * core = &(vm->cores[vcore_id]);
299         char * specified_cpu = v3_cfg_val(core->core_cfg_data, "target_cpu");
300         uint32_t core_idx = 0;
301
302         if (specified_cpu != NULL) {
303             core_idx = atoi(specified_cpu);
304             
305             if ((core_idx < 0) || (core_idx >= MAX_CORES)) {
306                 PrintError("Target CPU out of bounds (%d) (MAX_CORES=%d)\n", core_idx, MAX_CORES);
307             }
308
309             i--; // We reset the logical core idx. Not strictly necessary I guess... 
310         } else {
311             core_idx = i;
312         }
313
314         major = core_idx / 8;
315         minor = core_idx % 8;
316
317         if ((core_mask[major] & (0x1 << minor)) == 0) {
318             PrintError("Logical CPU %d not available for virtual core %d; not started\n",
319                        core_idx, vcore_id);
320
321             if (specified_cpu != NULL) {
322                 PrintError("CPU was specified explicitly (%d). HARD ERROR\n", core_idx);
323                 v3_stop_vm(vm);
324                 return -1;
325             }
326
327             continue;
328         }
329
330         PrintDebug("Starting virtual core %u on logical core %u\n", 
331                    vcore_id, core_idx);
332         
333         sprintf(core->exec_name, "%s-%u", vm->name, vcore_id);
334
335         PrintDebug("run: core=%u, func=0x%p, arg=0x%p, name=%s\n",
336                    core_idx, start_core, core, core->exec_name);
337
338         core->core_run_state = CORE_STOPPED;  // core zero will turn itself on
339         core->pcpu_id = core_idx;
340         core->core_thread = V3_CREATE_THREAD_ON_CPU(core_idx, start_core, core, core->exec_name);
341
342         if (core->core_thread == NULL) {
343             PrintError("Thread launch failed\n");
344             v3_stop_vm(vm);
345             return -1;
346         }
347
348         vcore_id--;
349     }
350
351     if (vcore_id >= 0) {
352         PrintError("Error starting VM: Not enough available CPU cores\n");
353         v3_stop_vm(vm);
354         return -1;
355     }
356
357
358     return 0;
359
360 }
361
362
363 int v3_reset_vm_core(struct guest_info * core, addr_t rip) {
364     
365     switch (v3_cpu_types[core->pcpu_id]) {
366 #ifdef V3_CONFIG_SVM
367         case V3_SVM_CPU:
368         case V3_SVM_REV3_CPU:
369             PrintDebug("Resetting SVM Guest CPU %d\n", core->vcpu_id);
370             return v3_reset_svm_vm_core(core, rip);
371 #endif
372 #ifdef V3_CONFIG_VMX
373         case V3_VMX_CPU:
374         case V3_VMX_EPT_CPU:
375         case V3_VMX_EPT_UG_CPU:
376             PrintDebug("Resetting VMX Guest CPU %d\n", core->vcpu_id);
377             return v3_reset_vmx_vm_core(core, rip);
378 #endif
379         case V3_INVALID_CPU:
380         default:
381             PrintError("CPU has no virtualization Extensions\n");
382             break;
383     }
384
385     return -1;
386 }
387
388
389
390 /* move a virtual core to different physical core */
391 int v3_move_vm_core(struct v3_vm_info * vm, int vcore_id, int target_cpu) {
392     struct guest_info * core = NULL;
393
394     if ((vcore_id < 0) || (vcore_id >= vm->num_cores)) {
395         PrintError("Attempted to migrate invalid virtual core (%d)\n", vcore_id);
396         return -1;
397     }
398
399     core = &(vm->cores[vcore_id]);
400
401     if (target_cpu == core->pcpu_id) {
402         PrintError("Attempted to migrate to local core (%d)\n", target_cpu);
403         // well that was pointless
404         return 0;
405     }
406
407     if (core->core_thread == NULL) {
408         PrintError("Attempted to migrate a core without a valid thread context\n");
409         return -1;
410     }
411
412     while (v3_raise_barrier(vm, NULL) == -1);
413
414     V3_Print("Performing Migration from %d to %d\n", core->pcpu_id, target_cpu);
415
416     // Double check that we weren't preemptively migrated
417     if (target_cpu != core->pcpu_id) {    
418
419         V3_Print("Moving Core\n");
420
421
422 #ifdef V3_CONFIG_VMX
423         switch (v3_cpu_types[core->pcpu_id]) {
424             case V3_VMX_CPU:
425             case V3_VMX_EPT_CPU:
426             case V3_VMX_EPT_UG_CPU:
427                 PrintDebug("Flushing VMX Guest CPU %d\n", core->vcpu_id);
428                 V3_Call_On_CPU(core->pcpu_id, (void (*)(void *))v3_flush_vmx_vm_core, (void *)core);
429                 break;
430             default:
431                 break;
432         }
433 #endif
434
435         if (V3_MOVE_THREAD_TO_CPU(target_cpu, core->core_thread) != 0) {
436             PrintError("Failed to move Vcore %d to CPU %d\n", 
437                        core->vcpu_id, target_cpu);
438             v3_lower_barrier(vm);
439             return -1;
440         } 
441         
442         /* There will be a benign race window here:
443            core->pcpu_id will be set to the target core before its fully "migrated"
444            However the core will NEVER run on the old core again, its just in flight to the new core
445         */
446         core->pcpu_id = target_cpu;
447
448         V3_Print("core now at %d\n", core->pcpu_id);    
449     }
450
451     v3_lower_barrier(vm);
452
453     return 0;
454 }
455
456
457
458 int v3_stop_vm(struct v3_vm_info * vm) {
459
460     if ((vm->run_state != VM_RUNNING) && 
461         (vm->run_state != VM_SIMULATING)) {
462         PrintError("Tried to stop VM in invalid runstate (%d)\n", vm->run_state);
463         return -1;
464     }
465
466     vm->run_state = VM_STOPPED;
467
468     // Sanity check to catch any weird execution states
469     if (v3_wait_for_barrier(vm, NULL) == 0) {
470         v3_lower_barrier(vm);
471     }
472     
473     // XXX force exit all cores via a cross call/IPI XXX
474
475     while (1) {
476         int i = 0;
477         int still_running = 0;
478
479         for (i = 0; i < vm->num_cores; i++) {
480             if (vm->cores[i].core_run_state != CORE_STOPPED) {
481                 still_running = 1;
482             }
483         }
484
485         if (still_running == 0) {
486             break;
487         }
488
489         v3_yield(NULL,-1);
490     }
491     
492     V3_Print("VM stopped. Returning\n");
493
494     return 0;
495 }
496
497
498 int v3_pause_vm(struct v3_vm_info * vm) {
499
500     if (vm->run_state != VM_RUNNING) {
501         PrintError("Tried to pause a VM that was not running\n");
502         return -1;
503     }
504
505     while (v3_raise_barrier(vm, NULL) == -1);
506
507     vm->run_state = VM_PAUSED;
508
509     return 0;
510 }
511
512
513 int v3_continue_vm(struct v3_vm_info * vm) {
514
515     if (vm->run_state != VM_PAUSED) {
516         PrintError("Tried to continue a VM that was not paused\n");
517         return -1;
518     }
519
520     vm->run_state = VM_RUNNING;
521
522     v3_lower_barrier(vm);
523
524     return 0;
525 }
526
527
528
529 static int sim_callback(struct guest_info * core, void * private_data) {
530     struct v3_bitmap * timeout_map = private_data;
531
532     v3_bitmap_set(timeout_map, core->vcpu_id);
533     
534     V3_Print("Simulation callback activated (guest_rip=%p)\n", (void *)core->rip);
535
536     while (v3_bitmap_check(timeout_map, core->vcpu_id) == 1) {
537         v3_yield(NULL,-1);
538     }
539
540     return 0;
541 }
542
543
544
545
546 int v3_simulate_vm(struct v3_vm_info * vm, unsigned int msecs) {
547     struct v3_bitmap timeout_map;
548     int i = 0;
549     int all_blocked = 0;
550     uint64_t cycles = 0;
551     uint64_t cpu_khz = V3_CPU_KHZ();
552
553     if (vm->run_state != VM_PAUSED) {
554         PrintError("VM must be paused before simulation begins\n");
555         return -1;
556     }
557
558     /* AT this point VM is paused */
559     
560     // initialize bitmap
561     v3_bitmap_init(&timeout_map, vm->num_cores);
562
563
564
565
566     // calculate cycles from msecs...
567     // IMPORTANT: Floating point not allowed.
568     cycles = (msecs * cpu_khz);
569     
570
571
572     V3_Print("Simulating %u msecs (%llu cycles) [CPU_KHZ=%llu]\n", msecs, cycles, cpu_khz);
573
574     // set timeout
575     
576     for (i = 0; i < vm->num_cores; i++) {
577         if (v3_add_core_timeout(&(vm->cores[i]), cycles, sim_callback, &timeout_map) == -1) {
578             PrintError("Could not register simulation timeout for core %d\n", i);
579             return -1;
580         }
581     }
582
583     V3_Print("timeouts set on all cores\n ");
584
585     
586     // Run the simulation
587 //    vm->run_state = VM_SIMULATING;
588     vm->run_state = VM_RUNNING;
589     v3_lower_barrier(vm);
590
591
592     V3_Print("Barrier lowered: We are now Simulating!!\n");
593
594     // block until simulation is complete    
595     while (all_blocked == 0) {
596         all_blocked = 1;
597
598         for (i = 0; i < vm->num_cores; i++) {
599             if (v3_bitmap_check(&timeout_map, i)  == 0) {
600                 all_blocked = 0;
601             }
602         }
603
604         if (all_blocked == 1) {
605             break;
606         }
607
608         v3_yield(NULL,-1);
609     }
610
611
612     V3_Print("Simulation is complete\n");
613
614     // Simulation is complete
615     // Reset back to PAUSED state
616
617     v3_raise_barrier_nowait(vm, NULL);
618     vm->run_state = VM_PAUSED;
619     
620     v3_bitmap_reset(&timeout_map);
621
622     v3_wait_for_barrier(vm, NULL);
623
624     return 0;
625
626 }
627
628 #ifdef V3_CONFIG_CHECKPOINT
629 #include <palacios/vmm_checkpoint.h>
630
631 int v3_save_vm(struct v3_vm_info * vm, char * store, char * url) {
632     return v3_chkpt_save_vm(vm, store, url);
633 }
634
635
636 int v3_load_vm(struct v3_vm_info * vm, char * store, char * url) {
637     return v3_chkpt_load_vm(vm, store, url);
638 }
639
640 #ifdef V3_CONFIG_LIVE_MIGRATION
641 int v3_send_vm(struct v3_vm_info * vm, char * store, char * url) {
642     return v3_chkpt_send_vm(vm, store, url);
643 }
644
645
646 int v3_receive_vm(struct v3_vm_info * vm, char * store, char * url) {
647     return v3_chkpt_receive_vm(vm, store, url);
648 }
649 #endif
650
651 #endif
652
653
654 int v3_free_vm(struct v3_vm_info * vm) {
655     int i = 0;
656     // deinitialize guest (free memory, etc...)
657
658     if ((vm->run_state != VM_STOPPED) &&
659         (vm->run_state != VM_ERROR)) {
660         PrintError("Tried to Free VM in invalid runstate (%d)\n", vm->run_state);
661         return -1;
662     }
663
664     v3_free_vm_devices(vm);
665
666     // free cores
667     for (i = 0; i < vm->num_cores; i++) {
668         v3_free_core(&(vm->cores[i]));
669     }
670
671     // free vm
672     v3_free_vm_internal(vm);
673
674     v3_free_config(vm);
675
676     V3_Free(vm);
677
678     return 0;
679 }
680
681
682 #ifdef __V3_32BIT__
683
684 v3_cpu_mode_t v3_get_host_cpu_mode() {
685     uint32_t cr4_val;
686     struct cr4_32 * cr4;
687
688     __asm__ (
689              "movl %%cr4, %0; "
690              : "=r"(cr4_val) 
691              );
692
693     
694     cr4 = (struct cr4_32 *)&(cr4_val);
695
696     if (cr4->pae == 1) {
697         return PROTECTED_PAE;
698     } else {
699         return PROTECTED;
700     }
701 }
702
703 #elif __V3_64BIT__
704
705 v3_cpu_mode_t v3_get_host_cpu_mode() {
706     return LONG;
707 }
708
709 #endif 
710
711
712
713
714
715 void v3_yield_cond(struct guest_info * info, int usec) {
716     uint64_t cur_cycle;
717     cur_cycle = v3_get_host_time(&info->time_state);
718
719     if (cur_cycle > (info->yield_start_cycle + info->vm_info->yield_cycle_period)) {
720         //PrintDebug("Conditional Yield (cur_cyle=%p, start_cycle=%p, period=%p)\n", 
721         //           (void *)cur_cycle, (void *)info->yield_start_cycle, 
722         //         (void *)info->yield_cycle_period);
723         
724         if (usec < 0) { 
725             V3_Yield();
726         } else {
727             V3_Yield_Timed(usec);
728         }
729
730         info->yield_start_cycle +=  info->vm_info->yield_cycle_period;
731     }
732 }
733  
734
735 /* 
736  * unconditional cpu yield 
737  * if the yielding thread is a guest context, the guest quantum is reset on resumption 
738  * Non guest context threads should call this function with a NULL argument
739  *
740  * usec <0  => the non-timed yield is used
741  * usec >=0 => the timed yield is used, which also usually implies interruptible
742  */ 
743 void v3_yield(struct guest_info * info, int usec) {
744     if (usec < 0) { 
745         V3_Yield();
746     } else {
747         V3_Yield_Timed(usec);
748     }
749
750     if (info) {
751         info->yield_start_cycle +=  info->vm_info->yield_cycle_period;
752     }
753 }
754
755
756
757
758 void v3_print_cond(const char * fmt, ...) {
759     if (v3_dbg_enable == 1) {
760         char buf[2048];
761         va_list ap;
762
763         va_start(ap, fmt);
764         vsnprintf(buf, 2048, fmt, ap);
765         va_end(ap);
766
767         V3_Print("%s", buf);
768     }    
769 }
770
771
772
773 void v3_interrupt_cpu(struct v3_vm_info * vm, int logical_cpu, int vector) {
774     extern struct v3_os_hooks * os_hooks;
775
776     if ((os_hooks) && (os_hooks)->interrupt_cpu) {
777         (os_hooks)->interrupt_cpu(vm, logical_cpu, vector);
778     }
779 }
780
781
782
783 int v3_vm_enter(struct guest_info * info) {
784     switch (v3_mach_type) {
785 #ifdef V3_CONFIG_SVM
786         case V3_SVM_CPU:
787         case V3_SVM_REV3_CPU:
788             return v3_svm_enter(info);
789             break;
790 #endif
791 #if V3_CONFIG_VMX
792         case V3_VMX_CPU:
793         case V3_VMX_EPT_CPU:
794         case V3_VMX_EPT_UG_CPU:
795             return v3_vmx_enter(info);
796             break;
797 #endif
798         default:
799             PrintError("Attemping to enter a guest on an invalid CPU\n");
800             return -1;
801     }
802 }