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.


14a0d2f2e8b5cd488ab09780f33b6730fa3b5fc5
[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
29 #ifdef V3_CONFIG_SVM
30 #include <palacios/svm.h>
31 #endif
32 #ifdef V3_CONFIG_VMX
33 #include <palacios/vmx.h>
34 #endif
35
36
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;
40
41
42
43
44 static void init_cpu(void * arg) {
45     uint32_t cpu_id = (uint32_t)(addr_t)arg;
46
47 #ifdef V3_CONFIG_SVM
48     if (v3_is_svm_capable()) {
49         PrintDebug("Machine is SVM Capable\n");
50         v3_init_svm_cpu(cpu_id);
51         
52     } else 
53 #endif
54 #ifdef V3_CONFIG_VMX
55     if (v3_is_vmx_capable()) {
56         PrintDebug("Machine is VMX Capable\n");
57         v3_init_vmx_cpu(cpu_id);
58
59     } else 
60 #endif
61     {
62        PrintError("CPU has no virtualization Extensions\n");
63     }
64 }
65
66
67 static void deinit_cpu(void * arg) {
68     uint32_t cpu_id = (uint32_t)(addr_t)arg;
69
70
71     switch (v3_cpu_types[cpu_id]) {
72 #ifdef V3_CONFIG_SVM
73         case V3_SVM_CPU:
74         case V3_SVM_REV3_CPU:
75             PrintDebug("Deinitializing SVM CPU %d\n", cpu_id);
76             v3_deinit_svm_cpu(cpu_id);
77             break;
78 #endif
79 #ifdef V3_CONFIG_VMX
80         case V3_VMX_CPU:
81         case V3_VMX_EPT_CPU:
82         case V3_VMX_EPT_UG_CPU:
83             PrintDebug("Deinitializing VMX CPU %d\n", cpu_id);
84             v3_deinit_vmx_cpu(cpu_id);
85             break;
86 #endif
87         case V3_INVALID_CPU:
88         default:
89             PrintError("CPU has no virtualization Extensions\n");
90             break;
91     }
92 }
93
94
95
96 void Init_V3(struct v3_os_hooks * hooks, int num_cpus) {
97     int i;
98
99     V3_Print("V3 Print statement to fix a Kitten page fault bug\n");
100
101     // Set global variables. 
102     os_hooks = hooks;
103
104     for (i = 0; i < V3_CONFIG_MAX_CPUS; i++) {
105         v3_cpu_types[i] = V3_INVALID_CPU;
106     }
107
108     // Register all the possible device types
109     V3_init_devices();
110
111     // Register all shadow paging handlers
112     V3_init_shdw_paging();
113
114     // Register all extensions
115     V3_init_extensions();
116
117
118 #ifdef V3_CONFIG_SYMMOD
119     V3_init_symmod();
120 #endif
121
122
123
124 #ifdef V3_CONFIG_MULTITHREAD_OS
125     if ((hooks) && (hooks->call_on_cpu)) {
126
127         for (i = 0; i < num_cpus; i++) {
128
129             V3_Print("Initializing VMM extensions on cpu %d\n", i);
130             hooks->call_on_cpu(i, &init_cpu, (void *)(addr_t)i);
131         }
132     }
133 #else 
134     init_cpu(0);
135 #endif
136
137 }
138
139
140 void Shutdown_V3() {
141     int i;
142
143     V3_deinit_devices();
144     V3_deinit_shdw_paging();
145
146     V3_deinit_extensions();
147
148 #ifdef V3_CONFIG_SYMMOD
149     V3_deinit_symmod();
150 #endif
151
152
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);
159             }
160         }
161     }
162 #else 
163     deinit_cpu(0);
164 #endif
165
166 }
167
168
169 v3_cpu_arch_t v3_get_cpu_type(int cpu_id) {
170     return v3_cpu_types[cpu_id];
171 }
172
173
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);
176
177     if (vm == NULL) {
178         PrintError("Could not configure guest\n");
179         return NULL;
180     }
181
182     V3_Print("CORE 0 RIP=%p\n", (void *)(addr_t)(vm->cores[0].rip));
183
184     if (name == NULL) {
185         name = "[V3_VM]";
186     } else if (strlen(name) >= 128) {
187         PrintError("VM name is too long. Will be truncated to 128 chars.\n");
188     }
189
190     memset(vm->name, 0, 128);
191     strncpy(vm->name, name, 127);
192
193     return vm;
194 }
195
196
197
198
199 static int start_core(void * p)
200 {
201     struct guest_info * core = (struct guest_info *)p;
202
203
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);
206
207     switch (v3_cpu_types[0]) {
208 #ifdef V3_CONFIG_SVM
209         case V3_SVM_CPU:
210         case V3_SVM_REV3_CPU:
211             return v3_start_svm_guest(core);
212             break;
213 #endif
214 #if V3_CONFIG_VMX
215         case V3_VMX_CPU:
216         case V3_VMX_EPT_CPU:
217         case V3_VMX_EPT_UG_CPU:
218             return v3_start_vmx_guest(core);
219             break;
220 #endif
221         default:
222             PrintError("Attempting to enter a guest on an invalid CPU\n");
223             return -1;
224     }
225     // should not happen
226     return 0;
227 }
228
229
230 // For the moment very ugly. Eventually we will shift the cpu_mask to an arbitrary sized type...
231 #ifdef V3_CONFIG_MULTITHREAD_OS
232 #define MAX_CORES 32
233 #else
234 #define MAX_CORES 1
235 #endif
236
237
238 int v3_start_vm(struct v3_vm_info * vm, unsigned int cpu_mask) {
239     uint32_t i;
240     uint8_t * core_mask = (uint8_t *)&cpu_mask; // This is to make future expansion easier
241     uint32_t avail_cores = 0;
242     int vcore_id = 0;
243
244     /// CHECK IF WE ARE MULTICORE ENABLED....
245
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));
248
249
250     // Check that enough cores are present in the mask to handle vcores
251     for (i = 0; i < MAX_CORES; i++) {
252         int major = i / 8;
253         int minor = i % 8;
254         
255         if (core_mask[major] & (0x1 << minor)) {
256             avail_cores++;
257         }
258     }
259
260
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);
264         return -1;
265     }
266
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++) {
270         int major = 0;
271         int minor = 0;
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;
275
276         if (specified_cpu != NULL) {
277             core_idx = atoi(specified_cpu);
278             
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);
281             }
282
283             i--; // We reset the logical core idx. Not strictly necessary I guess... 
284         } else {
285
286             if (i == V3_Get_CPU()) {
287                 // We skip the local CPU because it is reserved for vcore 0
288                 continue;
289             }
290             
291             core_idx = i;
292         }
293
294         major = core_idx / 8;
295         minor = core_idx % 8;
296
297
298         if ((core_mask[major] & (0x1 << minor)) == 0) {
299             PrintError("Logical CPU %d not available for virtual core %d; not started\n",
300                        core_idx, vcore_id);
301
302             if (specified_cpu != NULL) {
303                 PrintError("CPU was specified explicitly (%d). HARD ERROR\n", core_idx);
304                 v3_stop_vm(vm);
305                 return -1;
306             }
307
308             continue;
309         }
310
311         PrintDebug("Starting virtual core %u on logical core %u\n", 
312                    vcore_id, core_idx);
313         
314         sprintf(core->exec_name, "%s-%u", vm->name, vcore_id);
315
316         PrintDebug("run: core=%u, func=0x%p, arg=0x%p, name=%s\n",
317                    core_idx, start_core, core, core->exec_name);
318
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);
322
323         if (core->core_thread == NULL) {
324             PrintError("Thread launch failed\n");
325             v3_stop_vm(vm);
326             return -1;
327         }
328
329         vcore_id++;
330     }
331 #endif
332
333     sprintf(vm->cores[0].exec_name, "%s", vm->name);
334
335     vm->cores[0].pcpu_id = V3_Get_CPU();
336
337     if (start_core(&(vm->cores[0])) != 0) {
338         PrintError("Error starting VM core 0\n");
339         v3_stop_vm(vm);
340         return -1;
341     }
342
343
344     return 0;
345
346 }
347
348
349 int v3_reset_vm_core(struct guest_info * core, addr_t rip) {
350     
351     switch (v3_cpu_types[core->pcpu_id]) {
352 #ifdef V3_CONFIG_SVM
353         case V3_SVM_CPU:
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);
357 #endif
358 #ifdef V3_CONFIG_VMX
359         case V3_VMX_CPU:
360         case V3_VMX_EPT_CPU:
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);
364 #endif
365         case V3_INVALID_CPU:
366         default:
367             PrintError("CPU has no virtualization Extensions\n");
368             break;
369     }
370
371     return -1;
372 }
373
374
375
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;
379
380     if ((vcore_id < 0) || (vcore_id >= vm->num_cores)) {
381         PrintError("Attempted to migrate invalid virtual core (%d)\n", vcore_id);
382         return -1;
383     }
384
385     core = &(vm->cores[vcore_id]);
386
387     if (target_cpu == core->pcpu_id) {
388         PrintError("Attempted to migrate to local core (%d)\n", target_cpu);
389         // well that was pointless
390         return 0;
391     }
392
393     if (core->core_thread == NULL) {
394         PrintError("Attempted to migrate a core without a valid thread context\n");
395         return -1;
396     }
397
398     while (v3_raise_barrier(vm, NULL) == -1);
399
400     V3_Print("Performing Migration from %d to %d\n", core->pcpu_id, target_cpu);
401
402     // Double check that we weren't preemptively migrated
403     if (target_cpu != core->pcpu_id) {    
404
405         V3_Print("Moving Core\n");
406
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);
411             return -1;
412         } 
413         
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
417         */
418         core->pcpu_id = target_cpu;
419
420         V3_Print("core now at %d\n", core->pcpu_id);
421         
422     }
423
424
425
426
427     v3_lower_barrier(vm);
428
429     return 0;
430 }
431
432
433
434 int v3_stop_vm(struct v3_vm_info * vm) {
435
436     vm->run_state = VM_STOPPED;
437
438     // force exit all cores via a cross call/IPI
439
440     while (1) {
441         int i = 0;
442         int still_running = 0;
443
444         for (i = 0; i < vm->num_cores; i++) {
445             if (vm->cores[i].core_run_state != CORE_STOPPED) {
446                 still_running = 1;
447             }
448         }
449
450         if (still_running == 0) {
451             break;
452         }
453
454         v3_yield(NULL);
455     }
456     
457     V3_Print("VM stopped. Returning\n");
458
459     return 0;
460 }
461
462
463 int v3_pause_vm(struct v3_vm_info * vm) {
464
465     if (vm->run_state != VM_RUNNING) {
466         PrintError("Tried to pause a VM that was not running\n");
467         return -1;
468     }
469
470     while (v3_raise_barrier(vm, NULL) == -1);
471
472     vm->run_state = VM_PAUSED;
473
474     return 0;
475 }
476
477
478 int v3_continue_vm(struct v3_vm_info * vm) {
479
480     if (vm->run_state != VM_PAUSED) {
481         PrintError("Tried to continue a VM that was not paused\n");
482         return -1;
483     }
484
485     v3_lower_barrier(vm);
486
487     vm->run_state = VM_RUNNING;
488
489     return 0;
490 }
491
492 #ifdef V3_CONFIG_CHECKPOINT
493 #include <palacios/vmm_checkpoint.h>
494
495 int v3_save_vm(struct v3_vm_info * vm, char * store, char * url) {
496     return v3_chkpt_save_vm(vm, store, url);
497 }
498
499
500 int v3_load_vm(struct v3_vm_info * vm, char * store, char * url) {
501     return v3_chkpt_load_vm(vm, store, url);
502 }
503 #endif
504
505
506 int v3_free_vm(struct v3_vm_info * vm) {
507     int i = 0;
508     // deinitialize guest (free memory, etc...)
509
510     v3_free_vm_devices(vm);
511
512     // free cores
513     for (i = 0; i < vm->num_cores; i++) {
514         v3_free_core(&(vm->cores[i]));
515     }
516
517     // free vm
518     v3_free_vm_internal(vm);
519
520     v3_free_config(vm);
521
522     V3_Free(vm);
523
524     return 0;
525 }
526
527
528 #ifdef __V3_32BIT__
529
530 v3_cpu_mode_t v3_get_host_cpu_mode() {
531     uint32_t cr4_val;
532     struct cr4_32 * cr4;
533
534     __asm__ (
535              "movl %%cr4, %0; "
536              : "=r"(cr4_val) 
537              );
538
539     
540     cr4 = (struct cr4_32 *)&(cr4_val);
541
542     if (cr4->pae == 1) {
543         return PROTECTED_PAE;
544     } else {
545         return PROTECTED;
546     }
547 }
548
549 #elif __V3_64BIT__
550
551 v3_cpu_mode_t v3_get_host_cpu_mode() {
552     return LONG;
553 }
554
555 #endif 
556
557
558 #define V3_Yield(addr)                                  \
559     do {                                                \
560         extern struct v3_os_hooks * os_hooks;           \
561         if ((os_hooks) && (os_hooks)->yield_cpu) {      \
562             (os_hooks)->yield_cpu();                    \
563         }                                               \
564     } while (0)                                         \
565
566
567
568 void v3_yield_cond(struct guest_info * info) {
569     uint64_t cur_cycle;
570     cur_cycle = v3_get_host_time(&info->time_state);
571
572     if (cur_cycle > (info->yield_start_cycle + info->vm_info->yield_cycle_period)) {
573
574         /*
575           PrintDebug("Conditional Yield (cur_cyle=%p, start_cycle=%p, period=%p)\n", 
576           (void *)cur_cycle, (void *)info->yield_start_cycle, (void *)info->yield_cycle_period);
577         */
578         V3_Yield();
579         info->yield_start_cycle = v3_get_host_time(&info->time_state);
580     }
581 }
582
583
584 /* 
585  * unconditional cpu yield 
586  * if the yielding thread is a guest context, the guest quantum is reset on resumption 
587  * Non guest context threads should call this function with a NULL argument
588  */
589 void v3_yield(struct guest_info * info) {
590     V3_Yield();
591
592     if (info) {
593         info->yield_start_cycle = v3_get_host_time(&info->time_state);
594     }
595 }
596
597
598
599
600 void v3_print_cond(const char * fmt, ...) {
601     if (v3_dbg_enable == 1) {
602         char buf[2048];
603         va_list ap;
604
605         va_start(ap, fmt);
606         vsnprintf(buf, 2048, fmt, ap);
607         va_end(ap);
608
609         V3_Print("%s", buf);
610     }    
611 }
612
613
614 #ifdef V3_CONFIG_MULTITHREAD_OS
615
616 void v3_interrupt_cpu(struct v3_vm_info * vm, int logical_cpu, int vector) {
617     extern struct v3_os_hooks * os_hooks;
618
619     if ((os_hooks) && (os_hooks)->interrupt_cpu) {
620         (os_hooks)->interrupt_cpu(vm, logical_cpu, vector);
621     }
622 }
623 #endif
624
625
626
627 int v3_vm_enter(struct guest_info * info) {
628     switch (v3_cpu_types[0]) {
629 #ifdef V3_CONFIG_SVM
630         case V3_SVM_CPU:
631         case V3_SVM_REV3_CPU:
632             return v3_svm_enter(info);
633             break;
634 #endif
635 #if V3_CONFIG_VMX
636         case V3_VMX_CPU:
637         case V3_VMX_EPT_CPU:
638         case V3_VMX_EPT_UG_CPU:
639             return v3_vmx_enter(info);
640             break;
641 #endif
642         default:
643             PrintError("Attemping to enter a guest on an invalid CPU\n");
644             return -1;
645     }
646 }