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>
30 #include <palacios/svm.h>
33 #include <palacios/vmx.h>
37 v3_cpu_arch_t v3_cpu_types[V3_CONFIG_MAX_CPUS];
38 struct v3_os_hooks * os_hooks = NULL;
39 int v3_dbg_enable = 0;
44 static void init_cpu(void * arg) {
45 uint32_t cpu_id = (uint32_t)(addr_t)arg;
48 if (v3_is_svm_capable()) {
49 PrintDebug("Machine is SVM Capable\n");
50 v3_init_svm_cpu(cpu_id);
55 if (v3_is_vmx_capable()) {
56 PrintDebug("Machine is VMX Capable\n");
57 v3_init_vmx_cpu(cpu_id);
62 PrintError("CPU has no virtualization Extensions\n");
67 static void deinit_cpu(void * arg) {
68 uint32_t cpu_id = (uint32_t)(addr_t)arg;
71 switch (v3_cpu_types[cpu_id]) {
75 PrintDebug("Deinitializing SVM CPU %d\n", cpu_id);
76 v3_deinit_svm_cpu(cpu_id);
82 case V3_VMX_EPT_UG_CPU:
83 PrintDebug("Deinitializing VMX CPU %d\n", cpu_id);
84 v3_deinit_vmx_cpu(cpu_id);
89 PrintError("CPU has no virtualization Extensions\n");
96 void Init_V3(struct v3_os_hooks * hooks, int num_cpus) {
99 V3_Print("V3 Print statement to fix a Kitten page fault bug\n");
101 // Set global variables.
104 for (i = 0; i < V3_CONFIG_MAX_CPUS; i++) {
105 v3_cpu_types[i] = V3_INVALID_CPU;
108 // Register all the possible device types
111 // Register all shadow paging handlers
112 V3_init_shdw_paging();
114 // Register all extensions
115 V3_init_extensions();
118 #ifdef V3_CONFIG_SYMMOD
124 #ifdef V3_CONFIG_MULTITHREAD_OS
125 if ((hooks) && (hooks->call_on_cpu)) {
127 for (i = 0; i < num_cpus; i++) {
129 V3_Print("Initializing VMM extensions on cpu %d\n", i);
130 hooks->call_on_cpu(i, &init_cpu, (void *)(addr_t)i);
144 V3_deinit_shdw_paging();
146 V3_deinit_extensions();
148 #ifdef V3_CONFIG_SYMMOD
153 #ifdef V3_CONFIG_MULTITHREAD_OS
154 if ((os_hooks) && (os_hooks->call_on_cpu)) {
155 for (i = 0; i < V3_CONFIG_MAX_CPUS; i++) {
156 if (v3_cpu_types[i] != V3_INVALID_CPU) {
157 V3_Call_On_CPU(i, deinit_cpu, (void *)(addr_t)i);
158 //deinit_cpu((void *)(addr_t)i);
169 v3_cpu_arch_t v3_get_cpu_type(int cpu_id) {
170 return v3_cpu_types[cpu_id];
174 struct v3_vm_info * v3_create_vm(void * cfg, void * priv_data, char * name) {
175 struct v3_vm_info * vm = v3_config_guest(cfg, priv_data);
178 PrintError("Could not configure guest\n");
182 V3_Print("CORE 0 RIP=%p\n", (void *)(addr_t)(vm->cores[0].rip));
186 } else if (strlen(name) >= 128) {
187 PrintError("VM name is too long. Will be truncated to 128 chars.\n");
190 memset(vm->name, 0, 128);
191 strncpy(vm->name, name, 127);
199 static int start_core(void * p)
201 struct guest_info * core = (struct guest_info *)p;
204 PrintDebug("virtual core %u (on logical core %u): in start_core (RIP=%p)\n",
205 core->vcpu_id, core->pcpu_id, (void *)(addr_t)core->rip);
207 switch (v3_cpu_types[0]) {
210 case V3_SVM_REV3_CPU:
211 return v3_start_svm_guest(core);
217 case V3_VMX_EPT_UG_CPU:
218 return v3_start_vmx_guest(core);
222 PrintError("Attempting to enter a guest on an invalid CPU\n");
230 // For the moment very ugly. Eventually we will shift the cpu_mask to an arbitrary sized type...
231 #ifdef V3_CONFIG_MULTITHREAD_OS
238 int v3_start_vm(struct v3_vm_info * vm, unsigned int cpu_mask) {
240 uint8_t * core_mask = (uint8_t *)&cpu_mask; // This is to make future expansion easier
241 uint32_t avail_cores = 0;
244 /// CHECK IF WE ARE MULTICORE ENABLED....
246 V3_Print("V3 -- Starting VM (%u cores)\n", vm->num_cores);
247 V3_Print("CORE 0 RIP=%p\n", (void *)(addr_t)(vm->cores[0].rip));
250 // Check that enough cores are present in the mask to handle vcores
251 for (i = 0; i < MAX_CORES; i++) {
255 if (core_mask[major] & (0x1 << minor)) {
261 if (vm->num_cores > avail_cores) {
262 PrintError("Attempted to start a VM with too many cores (vm->num_cores = %d, avail_cores = %d, MAX=%d)\n",
263 vm->num_cores, avail_cores, MAX_CORES);
267 #ifdef V3_CONFIG_MULTITHREAD_OS
268 // spawn off new threads, for other cores
269 for (i = 0, vcore_id = 1; (i < MAX_CORES) && (vcore_id < vm->num_cores); i++) {
272 struct guest_info * core = &(vm->cores[vcore_id]);
273 char * specified_cpu = v3_cfg_val(core->core_cfg_data, "target_cpu");
274 uint32_t core_idx = 0;
276 if (specified_cpu != NULL) {
277 core_idx = atoi(specified_cpu);
279 if ((core_idx < 0) || (core_idx >= MAX_CORES)) {
280 PrintError("Target CPU out of bounds (%d) (MAX_CORES=%d)\n", core_idx, MAX_CORES);
283 i--; // We reset the logical core idx. Not strictly necessary I guess...
286 if (i == V3_Get_CPU()) {
287 // We skip the local CPU because it is reserved for vcore 0
294 major = core_idx / 8;
295 minor = core_idx % 8;
298 if ((core_mask[major] & (0x1 << minor)) == 0) {
299 PrintError("Logical CPU %d not available for virtual core %d; not started\n",
302 if (specified_cpu != NULL) {
303 PrintError("CPU was specified explicitly (%d). HARD ERROR\n", core_idx);
311 PrintDebug("Starting virtual core %u on logical core %u\n",
314 sprintf(core->exec_name, "%s-%u", vm->name, vcore_id);
316 PrintDebug("run: core=%u, func=0x%p, arg=0x%p, name=%s\n",
317 core_idx, start_core, core, core->exec_name);
319 // TODO: actually manage these threads instead of just launching them
320 core->pcpu_id = core_idx;
321 core->core_thread = V3_CREATE_THREAD_ON_CPU(core_idx, start_core, core, core->exec_name);
323 if (core->core_thread == NULL) {
324 PrintError("Thread launch failed\n");
333 sprintf(vm->cores[0].exec_name, "%s", vm->name);
335 vm->cores[0].pcpu_id = V3_Get_CPU();
337 if (start_core(&(vm->cores[0])) != 0) {
338 PrintError("Error starting VM core 0\n");
349 int v3_reset_vm_core(struct guest_info * core, addr_t rip) {
351 switch (v3_cpu_types[core->pcpu_id]) {
354 case V3_SVM_REV3_CPU:
355 PrintDebug("Resetting SVM Guest CPU %d\n", core->vcpu_id);
356 return v3_reset_svm_vm_core(core, rip);
361 case V3_VMX_EPT_UG_CPU:
362 PrintDebug("Resetting VMX Guest CPU %d\n", core->vcpu_id);
363 return v3_reset_vmx_vm_core(core, rip);
367 PrintError("CPU has no virtualization Extensions\n");
376 /* move a virtual core to different physical core */
377 int v3_move_vm_core(struct v3_vm_info * vm, int vcore_id, int target_cpu) {
378 struct guest_info * core = NULL;
380 if ((vcore_id < 0) || (vcore_id >= vm->num_cores)) {
381 PrintError("Attempted to migrate invalid virtual core (%d)\n", vcore_id);
385 core = &(vm->cores[vcore_id]);
387 if (target_cpu == core->pcpu_id) {
388 PrintError("Attempted to migrate to local core (%d)\n", target_cpu);
389 // well that was pointless
393 if (core->core_thread == NULL) {
394 PrintError("Attempted to migrate a core without a valid thread context\n");
398 while (v3_raise_barrier(vm, NULL) == -1);
400 V3_Print("Performing Migration from %d to %d\n", core->pcpu_id, target_cpu);
402 // Double check that we weren't preemptively migrated
403 if (target_cpu != core->pcpu_id) {
405 V3_Print("Moving Core\n");
407 if (V3_MOVE_THREAD_TO_CPU(target_cpu, core->core_thread) != 0) {
408 PrintError("Failed to move Vcore %d to CPU %d\n",
409 core->vcpu_id, target_cpu);
410 v3_lower_barrier(vm);
414 /* There will be a benign race window here:
415 core->pcpu_id will be set to the target core before its fully "migrated"
416 However the core will NEVER run on the old core again, its just in flight to the new core
418 core->pcpu_id = target_cpu;
420 V3_Print("core now at %d\n", core->pcpu_id);
427 v3_lower_barrier(vm);
434 int v3_stop_vm(struct v3_vm_info * vm) {
436 vm->run_state = VM_STOPPED;
438 // force exit all cores via a cross call/IPI
442 int still_running = 0;
444 for (i = 0; i < vm->num_cores; i++) {
445 if (vm->cores[i].core_run_state != CORE_STOPPED) {
450 if (still_running == 0) {
457 V3_Print("VM stopped. Returning\n");
463 int v3_pause_vm(struct v3_vm_info * vm) {
465 if (vm->run_state != VM_RUNNING) {
466 PrintError("Tried to pause a VM that was not running\n");
470 while (v3_raise_barrier(vm, NULL) == -1);
472 vm->run_state = VM_PAUSED;
478 int v3_continue_vm(struct v3_vm_info * vm) {
480 if (vm->run_state != VM_PAUSED) {
481 PrintError("Tried to continue a VM that was not paused\n");
485 v3_lower_barrier(vm);
487 vm->run_state = VM_RUNNING;
493 int v3_free_vm(struct v3_vm_info * vm) {
495 // deinitialize guest (free memory, etc...)
497 v3_free_vm_devices(vm);
500 for (i = 0; i < vm->num_cores; i++) {
501 v3_free_core(&(vm->cores[i]));
505 v3_free_vm_internal(vm);
517 v3_cpu_mode_t v3_get_host_cpu_mode() {
527 cr4 = (struct cr4_32 *)&(cr4_val);
530 return PROTECTED_PAE;
538 v3_cpu_mode_t v3_get_host_cpu_mode() {
545 #define V3_Yield(addr) \
547 extern struct v3_os_hooks * os_hooks; \
548 if ((os_hooks) && (os_hooks)->yield_cpu) { \
549 (os_hooks)->yield_cpu(); \
555 void v3_yield_cond(struct guest_info * info) {
557 cur_cycle = v3_get_host_time(&info->time_state);
559 if (cur_cycle > (info->yield_start_cycle + info->vm_info->yield_cycle_period)) {
562 PrintDebug("Conditional Yield (cur_cyle=%p, start_cycle=%p, period=%p)\n",
563 (void *)cur_cycle, (void *)info->yield_start_cycle, (void *)info->yield_cycle_period);
566 info->yield_start_cycle = v3_get_host_time(&info->time_state);
572 * unconditional cpu yield
573 * if the yielding thread is a guest context, the guest quantum is reset on resumption
574 * Non guest context threads should call this function with a NULL argument
576 void v3_yield(struct guest_info * info) {
580 info->yield_start_cycle = v3_get_host_time(&info->time_state);
587 void v3_print_cond(const char * fmt, ...) {
588 if (v3_dbg_enable == 1) {
593 vsnprintf(buf, 2048, fmt, ap);
601 #ifdef V3_CONFIG_MULTITHREAD_OS
603 void v3_interrupt_cpu(struct v3_vm_info * vm, int logical_cpu, int vector) {
604 extern struct v3_os_hooks * os_hooks;
606 if ((os_hooks) && (os_hooks)->interrupt_cpu) {
607 (os_hooks)->interrupt_cpu(vm, logical_cpu, vector);
614 int v3_vm_enter(struct guest_info * info) {
615 switch (v3_cpu_types[0]) {
618 case V3_SVM_REV3_CPU:
619 return v3_svm_enter(info);
625 case V3_VMX_EPT_UG_CPU:
626 return v3_vmx_enter(info);
630 PrintError("Attemping to enter a guest on an invalid CPU\n");