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.


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