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.
6 * The V3VEE Project is a joint project between Northwestern University
7 * and the University of New Mexico. You can find out more at
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.
14 * Author: Jack Lange <jarusl@cs.northwestern.edu>
16 * This is free software. You are permitted to use,
17 * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
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>
32 #include <palacios/svm.h>
35 #include <palacios/vmx.h>
38 #ifdef V3_CONFIG_CHECKPOINT
39 #include <palacios/vmm_checkpoint.h>
43 v3_cpu_arch_t v3_cpu_types[V3_CONFIG_MAX_CPUS];
44 v3_cpu_arch_t v3_mach_type = V3_INVALID_CPU;
46 struct v3_os_hooks * os_hooks = NULL;
47 int v3_dbg_enable = 0;
52 static void init_cpu(void * arg) {
53 uint32_t cpu_id = (uint32_t)(addr_t)arg;
56 if (v3_is_svm_capable()) {
57 PrintDebug("Machine is SVM Capable\n");
58 v3_init_svm_cpu(cpu_id);
63 if (v3_is_vmx_capable()) {
64 PrintDebug("Machine is VMX Capable\n");
65 v3_init_vmx_cpu(cpu_id);
70 PrintError("CPU has no virtualization Extensions\n");
75 static void deinit_cpu(void * arg) {
76 uint32_t cpu_id = (uint32_t)(addr_t)arg;
79 switch (v3_cpu_types[cpu_id]) {
83 PrintDebug("Deinitializing SVM CPU %d\n", cpu_id);
84 v3_deinit_svm_cpu(cpu_id);
90 case V3_VMX_EPT_UG_CPU:
91 PrintDebug("Deinitializing VMX CPU %d\n", cpu_id);
92 v3_deinit_vmx_cpu(cpu_id);
97 PrintError("CPU has no virtualization Extensions\n");
104 void Init_V3(struct v3_os_hooks * hooks, int num_cpus) {
107 V3_Print("V3 Print statement to fix a Kitten page fault bug\n");
109 // Set global variables.
112 // Determine the global machine type
113 v3_mach_type = V3_INVALID_CPU;
115 for (i = 0; i < V3_CONFIG_MAX_CPUS; i++) {
116 v3_cpu_types[i] = V3_INVALID_CPU;
119 // Register all the possible device types
122 // Register all shadow paging handlers
123 V3_init_shdw_paging();
125 // Register all extensions
126 V3_init_extensions();
129 #ifdef V3_CONFIG_SYMMOD
133 #ifdef V3_CONFIG_CHECKPOINT
134 V3_init_checkpoint();
140 if ((hooks) && (hooks->call_on_cpu)) {
141 for (i = 0; i < num_cpus; i++) {
143 V3_Print("Initializing VMM extensions on cpu %d\n", i);
144 hooks->call_on_cpu(i, &init_cpu, (void *)(addr_t)i);
146 if (v3_mach_type == V3_INVALID_CPU) {
147 v3_mach_type = v3_cpu_types[i];
161 V3_deinit_shdw_paging();
163 V3_deinit_extensions();
165 #ifdef V3_CONFIG_SYMMOD
169 #ifdef V3_CONFIG_CHECKPOINT
170 V3_deinit_checkpoint();
174 if ((os_hooks) && (os_hooks->call_on_cpu)) {
175 for (i = 0; i < V3_CONFIG_MAX_CPUS; i++) {
176 if (v3_cpu_types[i] != V3_INVALID_CPU) {
177 V3_Call_On_CPU(i, deinit_cpu, (void *)(addr_t)i);
178 //deinit_cpu((void *)(addr_t)i);
186 v3_cpu_arch_t v3_get_cpu_type(int cpu_id) {
187 return v3_cpu_types[cpu_id];
191 struct v3_vm_info * v3_create_vm(void * cfg, void * priv_data, char * name) {
192 struct v3_vm_info * vm = v3_config_guest(cfg, priv_data);
195 PrintError("Could not configure guest\n");
199 V3_Print("CORE 0 RIP=%p\n", (void *)(addr_t)(vm->cores[0].rip));
203 } else if (strlen(name) >= 128) {
204 PrintError("VM name is too long. Will be truncated to 128 chars.\n");
207 memset(vm->name, 0, 128);
208 strncpy(vm->name, name, 127);
216 static int start_core(void * p)
218 struct guest_info * core = (struct guest_info *)p;
221 PrintDebug("virtual core %u (on logical core %u): in start_core (RIP=%p)\n",
222 core->vcpu_id, core->pcpu_id, (void *)(addr_t)core->rip);
224 switch (v3_mach_type) {
227 case V3_SVM_REV3_CPU:
228 return v3_start_svm_guest(core);
234 case V3_VMX_EPT_UG_CPU:
235 return v3_start_vmx_guest(core);
239 PrintError("Attempting to enter a guest on an invalid CPU\n");
247 // For the moment very ugly. Eventually we will shift the cpu_mask to an arbitrary sized type...
251 int v3_start_vm(struct v3_vm_info * vm, unsigned int cpu_mask) {
253 uint8_t * core_mask = (uint8_t *)&cpu_mask; // This is to make future expansion easier
254 uint32_t avail_cores = 0;
257 /// CHECK IF WE ARE MULTICORE ENABLED....
259 V3_Print("V3 -- Starting VM (%u cores)\n", vm->num_cores);
260 V3_Print("CORE 0 RIP=%p\n", (void *)(addr_t)(vm->cores[0].rip));
263 // Check that enough cores are present in the mask to handle vcores
264 for (i = 0; i < MAX_CORES; i++) {
268 if (core_mask[major] & (0x1 << minor)) {
269 if (v3_cpu_types[i] == V3_INVALID_CPU) {
270 core_mask[major] &= ~(0x1 << minor);
278 if (vm->num_cores > avail_cores) {
279 PrintError("Attempted to start a VM with too many cores (vm->num_cores = %d, avail_cores = %d, MAX=%d)\n",
280 vm->num_cores, avail_cores, MAX_CORES);
284 vm->run_state = VM_RUNNING;
286 // Spawn off threads for each core.
287 // We work backwards, so that core 0 is always started last.
288 for (i = 0, vcore_id = vm->num_cores - 1; (i < MAX_CORES) && (vcore_id >= 0); i++) {
291 struct guest_info * core = &(vm->cores[vcore_id]);
292 char * specified_cpu = v3_cfg_val(core->core_cfg_data, "target_cpu");
293 uint32_t core_idx = 0;
295 if (specified_cpu != NULL) {
296 core_idx = atoi(specified_cpu);
298 if ((core_idx < 0) || (core_idx >= MAX_CORES)) {
299 PrintError("Target CPU out of bounds (%d) (MAX_CORES=%d)\n", core_idx, MAX_CORES);
302 i--; // We reset the logical core idx. Not strictly necessary I guess...
307 major = core_idx / 8;
308 minor = core_idx % 8;
310 if ((core_mask[major] & (0x1 << minor)) == 0) {
311 PrintError("Logical CPU %d not available for virtual core %d; not started\n",
314 if (specified_cpu != NULL) {
315 PrintError("CPU was specified explicitly (%d). HARD ERROR\n", core_idx);
323 PrintDebug("Starting virtual core %u on logical core %u\n",
326 sprintf(core->exec_name, "%s-%u", vm->name, vcore_id);
328 PrintDebug("run: core=%u, func=0x%p, arg=0x%p, name=%s\n",
329 core_idx, start_core, core, core->exec_name);
331 core->core_run_state = CORE_STOPPED; // core zero will turn itself on
332 core->pcpu_id = core_idx;
333 core->core_thread = V3_CREATE_THREAD_ON_CPU(core_idx, start_core, core, core->exec_name);
335 if (core->core_thread == NULL) {
336 PrintError("Thread launch failed\n");
345 PrintError("Error starting VM: Not enough available CPU cores\n");
356 int v3_reset_vm_core(struct guest_info * core, addr_t rip) {
358 switch (v3_cpu_types[core->pcpu_id]) {
361 case V3_SVM_REV3_CPU:
362 PrintDebug("Resetting SVM Guest CPU %d\n", core->vcpu_id);
363 return v3_reset_svm_vm_core(core, rip);
368 case V3_VMX_EPT_UG_CPU:
369 PrintDebug("Resetting VMX Guest CPU %d\n", core->vcpu_id);
370 return v3_reset_vmx_vm_core(core, rip);
374 PrintError("CPU has no virtualization Extensions\n");
383 /* move a virtual core to different physical core */
384 int v3_move_vm_core(struct v3_vm_info * vm, int vcore_id, int target_cpu) {
385 struct guest_info * core = NULL;
387 if ((vcore_id < 0) || (vcore_id >= vm->num_cores)) {
388 PrintError("Attempted to migrate invalid virtual core (%d)\n", vcore_id);
392 core = &(vm->cores[vcore_id]);
394 if (target_cpu == core->pcpu_id) {
395 PrintError("Attempted to migrate to local core (%d)\n", target_cpu);
396 // well that was pointless
400 if (core->core_thread == NULL) {
401 PrintError("Attempted to migrate a core without a valid thread context\n");
405 while (v3_raise_barrier(vm, NULL) == -1);
407 V3_Print("Performing Migration from %d to %d\n", core->pcpu_id, target_cpu);
409 // Double check that we weren't preemptively migrated
410 if (target_cpu != core->pcpu_id) {
412 V3_Print("Moving Core\n");
416 switch (v3_cpu_types[core->pcpu_id]) {
419 case V3_VMX_EPT_UG_CPU:
420 PrintDebug("Flushing VMX Guest CPU %d\n", core->vcpu_id);
421 V3_Call_On_CPU(core->pcpu_id, (void (*)(void *))v3_flush_vmx_vm_core, (void *)core);
428 if (V3_MOVE_THREAD_TO_CPU(target_cpu, core->core_thread) != 0) {
429 PrintError("Failed to move Vcore %d to CPU %d\n",
430 core->vcpu_id, target_cpu);
431 v3_lower_barrier(vm);
435 /* There will be a benign race window here:
436 core->pcpu_id will be set to the target core before its fully "migrated"
437 However the core will NEVER run on the old core again, its just in flight to the new core
439 core->pcpu_id = target_cpu;
441 V3_Print("core now at %d\n", core->pcpu_id);
444 v3_lower_barrier(vm);
451 int v3_stop_vm(struct v3_vm_info * vm) {
453 vm->run_state = VM_STOPPED;
455 // Sanity check to catch any weird execution states
456 if (v3_wait_for_barrier(vm, NULL) == 0) {
457 v3_lower_barrier(vm);
460 // XXX force exit all cores via a cross call/IPI XXX
464 int still_running = 0;
466 for (i = 0; i < vm->num_cores; i++) {
467 if (vm->cores[i].core_run_state != CORE_STOPPED) {
472 if (still_running == 0) {
479 V3_Print("VM stopped. Returning\n");
485 int v3_pause_vm(struct v3_vm_info * vm) {
487 if (vm->run_state != VM_RUNNING) {
488 PrintError("Tried to pause a VM that was not running\n");
492 while (v3_raise_barrier(vm, NULL) == -1);
494 vm->run_state = VM_PAUSED;
500 int v3_continue_vm(struct v3_vm_info * vm) {
502 if (vm->run_state != VM_PAUSED) {
503 PrintError("Tried to continue a VM that was not paused\n");
507 vm->run_state = VM_RUNNING;
509 v3_lower_barrier(vm);
516 static int sim_callback(struct guest_info * core, void * private_data) {
517 struct v3_bitmap * timeout_map = private_data;
519 v3_bitmap_set(timeout_map, core->vcpu_id);
521 V3_Print("Simulation callback activated (guest_rip=%p)\n", (void *)core->rip);
523 while (v3_bitmap_check(timeout_map, core->vcpu_id) == 1) {
533 int v3_simulate_vm(struct v3_vm_info * vm, unsigned int msecs) {
534 struct v3_bitmap timeout_map;
538 uint64_t cpu_khz = V3_CPU_KHZ();
540 if (vm->run_state != VM_PAUSED) {
541 PrintError("VM must be paused before simulation begins\n");
545 /* AT this point VM is paused */
548 v3_bitmap_init(&timeout_map, vm->num_cores);
553 // calculate cycles from msecs...
554 // IMPORTANT: Floating point not allowed.
555 cycles = (msecs * cpu_khz);
559 V3_Print("Simulating %u msecs (%llu cycles) [CPU_KHZ=%llu]\n", msecs, cycles, cpu_khz);
563 for (i = 0; i < vm->num_cores; i++) {
564 if (v3_add_core_timeout(&(vm->cores[i]), cycles, sim_callback, &timeout_map) == -1) {
565 PrintError("Could not register simulation timeout for core %d\n", i);
570 V3_Print("timeouts set on all cores\n ");
573 // Run the simulation
574 // vm->run_state = VM_SIMULATING;
575 vm->run_state = VM_RUNNING;
576 v3_lower_barrier(vm);
579 V3_Print("Barrier lowered: We are now Simulating!!\n");
581 // block until simulation is complete
582 while (all_blocked == 0) {
585 for (i = 0; i < vm->num_cores; i++) {
586 if (v3_bitmap_check(&timeout_map, i) == 0) {
591 if (all_blocked == 1) {
599 V3_Print("Simulation is complete\n");
601 // Simulation is complete
602 // Reset back to PAUSED state
604 v3_raise_barrier_nowait(vm, NULL);
605 vm->run_state = VM_PAUSED;
607 v3_bitmap_reset(&timeout_map);
609 v3_wait_for_barrier(vm, NULL);
615 #ifdef V3_CONFIG_CHECKPOINT
616 #include <palacios/vmm_checkpoint.h>
618 int v3_save_vm(struct v3_vm_info * vm, char * store, char * url) {
619 return v3_chkpt_save_vm(vm, store, url);
623 int v3_load_vm(struct v3_vm_info * vm, char * store, char * url) {
624 return v3_chkpt_load_vm(vm, store, url);
629 int v3_free_vm(struct v3_vm_info * vm) {
631 // deinitialize guest (free memory, etc...)
633 v3_free_vm_devices(vm);
636 for (i = 0; i < vm->num_cores; i++) {
637 v3_free_core(&(vm->cores[i]));
641 v3_free_vm_internal(vm);
653 v3_cpu_mode_t v3_get_host_cpu_mode() {
663 cr4 = (struct cr4_32 *)&(cr4_val);
666 return PROTECTED_PAE;
674 v3_cpu_mode_t v3_get_host_cpu_mode() {
681 #define V3_Yield(addr) \
683 extern struct v3_os_hooks * os_hooks; \
684 if ((os_hooks) && (os_hooks)->yield_cpu) { \
685 (os_hooks)->yield_cpu(); \
691 void v3_yield_cond(struct guest_info * info) {
693 cur_cycle = v3_get_host_time(&info->time_state);
695 if (cur_cycle > (info->yield_start_cycle + info->vm_info->yield_cycle_period)) {
696 //PrintDebug("Conditional Yield (cur_cyle=%p, start_cycle=%p, period=%p)\n",
697 // (void *)cur_cycle, (void *)info->yield_start_cycle,
698 // (void *)info->yield_cycle_period);
701 info->yield_start_cycle = v3_get_host_time(&info->time_state);
707 * unconditional cpu yield
708 * if the yielding thread is a guest context, the guest quantum is reset on resumption
709 * Non guest context threads should call this function with a NULL argument
711 void v3_yield(struct guest_info * info) {
715 info->yield_start_cycle = v3_get_host_time(&info->time_state);
722 void v3_print_cond(const char * fmt, ...) {
723 if (v3_dbg_enable == 1) {
728 vsnprintf(buf, 2048, fmt, ap);
737 void v3_interrupt_cpu(struct v3_vm_info * vm, int logical_cpu, int vector) {
738 extern struct v3_os_hooks * os_hooks;
740 if ((os_hooks) && (os_hooks)->interrupt_cpu) {
741 (os_hooks)->interrupt_cpu(vm, logical_cpu, vector);
747 int v3_vm_enter(struct guest_info * info) {
748 switch (v3_mach_type) {
751 case V3_SVM_REV3_CPU:
752 return v3_svm_enter(info);
758 case V3_VMX_EPT_UG_CPU:
759 return v3_vmx_enter(info);
763 PrintError("Attemping to enter a guest on an invalid CPU\n");