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.


added error checking to guest launch to handle core mapping bugs
[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     // Spawn off threads for each core. 
274     // We work backwards, so that core 0 is always started last.
275     for (i = 0, vcore_id = vm->num_cores - 1; (i < MAX_CORES) && (vcore_id >= 0); i++) {
276         int major = 0;
277         int minor = 0;
278         struct guest_info * core = &(vm->cores[vcore_id]);
279         char * specified_cpu = v3_cfg_val(core->core_cfg_data, "target_cpu");
280         uint32_t core_idx = 0;
281
282         if (specified_cpu != NULL) {
283             core_idx = atoi(specified_cpu);
284             
285             if ((core_idx < 0) || (core_idx >= MAX_CORES)) {
286                 PrintError("Target CPU out of bounds (%d) (MAX_CORES=%d)\n", core_idx, MAX_CORES);
287             }
288
289             i--; // We reset the logical core idx. Not strictly necessary I guess... 
290         } else {
291             core_idx = i;
292         }
293
294         major = core_idx / 8;
295         minor = core_idx % 8;
296
297         if ((core_mask[major] & (0x1 << minor)) == 0) {
298             PrintError("Logical CPU %d not available for virtual core %d; not started\n",
299                        core_idx, vcore_id);
300
301             if (specified_cpu != NULL) {
302                 PrintError("CPU was specified explicitly (%d). HARD ERROR\n", core_idx);
303                 v3_stop_vm(vm);
304                 return -1;
305             }
306
307             continue;
308         }
309
310         PrintDebug("Starting virtual core %u on logical core %u\n", 
311                    vcore_id, core_idx);
312         
313         sprintf(core->exec_name, "%s-%u", vm->name, vcore_id);
314
315         PrintDebug("run: core=%u, func=0x%p, arg=0x%p, name=%s\n",
316                    core_idx, start_core, core, core->exec_name);
317
318         core->pcpu_id = core_idx;
319         core->core_thread = V3_CREATE_THREAD_ON_CPU(core_idx, start_core, core, core->exec_name);
320
321         if (core->core_thread == NULL) {
322             PrintError("Thread launch failed\n");
323             v3_stop_vm(vm);
324             return -1;
325         }
326
327         vcore_id--;
328     }
329
330     if (vcore_id >= 0) {
331         PrintError("Error starting VM: Not enough available CPU cores\n");
332         v3_stop_vm(vm);
333         return -1;
334     }
335
336
337     return 0;
338
339 }
340
341
342 int v3_reset_vm_core(struct guest_info * core, addr_t rip) {
343     
344     switch (v3_cpu_types[core->pcpu_id]) {
345 #ifdef V3_CONFIG_SVM
346         case V3_SVM_CPU:
347         case V3_SVM_REV3_CPU:
348             PrintDebug("Resetting SVM Guest CPU %d\n", core->vcpu_id);
349             return v3_reset_svm_vm_core(core, rip);
350 #endif
351 #ifdef V3_CONFIG_VMX
352         case V3_VMX_CPU:
353         case V3_VMX_EPT_CPU:
354         case V3_VMX_EPT_UG_CPU:
355             PrintDebug("Resetting VMX Guest CPU %d\n", core->vcpu_id);
356             return v3_reset_vmx_vm_core(core, rip);
357 #endif
358         case V3_INVALID_CPU:
359         default:
360             PrintError("CPU has no virtualization Extensions\n");
361             break;
362     }
363
364     return -1;
365 }
366
367
368
369 /* move a virtual core to different physical core */
370 int v3_move_vm_core(struct v3_vm_info * vm, int vcore_id, int target_cpu) {
371     struct guest_info * core = NULL;
372
373     if ((vcore_id < 0) || (vcore_id >= vm->num_cores)) {
374         PrintError("Attempted to migrate invalid virtual core (%d)\n", vcore_id);
375         return -1;
376     }
377
378     core = &(vm->cores[vcore_id]);
379
380     if (target_cpu == core->pcpu_id) {
381         PrintError("Attempted to migrate to local core (%d)\n", target_cpu);
382         // well that was pointless
383         return 0;
384     }
385
386     if (core->core_thread == NULL) {
387         PrintError("Attempted to migrate a core without a valid thread context\n");
388         return -1;
389     }
390
391     while (v3_raise_barrier(vm, NULL) == -1);
392
393     V3_Print("Performing Migration from %d to %d\n", core->pcpu_id, target_cpu);
394
395     // Double check that we weren't preemptively migrated
396     if (target_cpu != core->pcpu_id) {    
397
398         V3_Print("Moving Core\n");
399
400
401 #ifdef V3_CONFIG_VMX
402         switch (v3_cpu_types[core->pcpu_id]) {
403             case V3_VMX_CPU:
404             case V3_VMX_EPT_CPU:
405             case V3_VMX_EPT_UG_CPU:
406                 PrintDebug("Flushing VMX Guest CPU %d\n", core->vcpu_id);
407                 V3_Call_On_CPU(core->pcpu_id, (void (*)(void *))v3_flush_vmx_vm_core, (void *)core);
408                 break;
409             default:
410                 break;
411         }
412 #endif
413
414         if (V3_MOVE_THREAD_TO_CPU(target_cpu, core->core_thread) != 0) {
415             PrintError("Failed to move Vcore %d to CPU %d\n", 
416                        core->vcpu_id, target_cpu);
417             v3_lower_barrier(vm);
418             return -1;
419         } 
420         
421         /* There will be a benign race window here:
422            core->pcpu_id will be set to the target core before its fully "migrated"
423            However the core will NEVER run on the old core again, its just in flight to the new core
424         */
425         core->pcpu_id = target_cpu;
426
427         V3_Print("core now at %d\n", core->pcpu_id);    
428     }
429
430     v3_lower_barrier(vm);
431
432     return 0;
433 }
434
435
436
437 int v3_stop_vm(struct v3_vm_info * vm) {
438
439     vm->run_state = VM_STOPPED;
440
441     // force exit all cores via a cross call/IPI
442
443     while (1) {
444         int i = 0;
445         int still_running = 0;
446
447         for (i = 0; i < vm->num_cores; i++) {
448             if (vm->cores[i].core_run_state != CORE_STOPPED) {
449                 still_running = 1;
450             }
451         }
452
453         if (still_running == 0) {
454             break;
455         }
456
457         v3_yield(NULL);
458     }
459     
460     V3_Print("VM stopped. Returning\n");
461
462     return 0;
463 }
464
465
466 int v3_pause_vm(struct v3_vm_info * vm) {
467
468     if (vm->run_state != VM_RUNNING) {
469         PrintError("Tried to pause a VM that was not running\n");
470         return -1;
471     }
472
473     while (v3_raise_barrier(vm, NULL) == -1);
474
475     vm->run_state = VM_PAUSED;
476
477     return 0;
478 }
479
480
481 int v3_continue_vm(struct v3_vm_info * vm) {
482
483     if (vm->run_state != VM_PAUSED) {
484         PrintError("Tried to continue a VM that was not paused\n");
485         return -1;
486     }
487
488     v3_lower_barrier(vm);
489
490     vm->run_state = VM_RUNNING;
491
492     return 0;
493 }
494
495 #ifdef V3_CONFIG_CHECKPOINT
496 #include <palacios/vmm_checkpoint.h>
497
498 int v3_save_vm(struct v3_vm_info * vm, char * store, char * url) {
499     return v3_chkpt_save_vm(vm, store, url);
500 }
501
502
503 int v3_load_vm(struct v3_vm_info * vm, char * store, char * url) {
504     return v3_chkpt_load_vm(vm, store, url);
505 }
506 #endif
507
508
509 int v3_free_vm(struct v3_vm_info * vm) {
510     int i = 0;
511     // deinitialize guest (free memory, etc...)
512
513     v3_free_vm_devices(vm);
514
515     // free cores
516     for (i = 0; i < vm->num_cores; i++) {
517         v3_free_core(&(vm->cores[i]));
518     }
519
520     // free vm
521     v3_free_vm_internal(vm);
522
523     v3_free_config(vm);
524
525     V3_Free(vm);
526
527     return 0;
528 }
529
530
531 #ifdef __V3_32BIT__
532
533 v3_cpu_mode_t v3_get_host_cpu_mode() {
534     uint32_t cr4_val;
535     struct cr4_32 * cr4;
536
537     __asm__ (
538              "movl %%cr4, %0; "
539              : "=r"(cr4_val) 
540              );
541
542     
543     cr4 = (struct cr4_32 *)&(cr4_val);
544
545     if (cr4->pae == 1) {
546         return PROTECTED_PAE;
547     } else {
548         return PROTECTED;
549     }
550 }
551
552 #elif __V3_64BIT__
553
554 v3_cpu_mode_t v3_get_host_cpu_mode() {
555     return LONG;
556 }
557
558 #endif 
559
560
561 #define V3_Yield(addr)                                  \
562     do {                                                \
563         extern struct v3_os_hooks * os_hooks;           \
564         if ((os_hooks) && (os_hooks)->yield_cpu) {      \
565             (os_hooks)->yield_cpu();                    \
566         }                                               \
567     } while (0)                                         \
568
569
570
571 void v3_yield_cond(struct guest_info * info) {
572     uint64_t cur_cycle;
573     cur_cycle = v3_get_host_time(&info->time_state);
574
575     if (cur_cycle > (info->yield_start_cycle + info->vm_info->yield_cycle_period)) {
576         //PrintDebug("Conditional Yield (cur_cyle=%p, start_cycle=%p, period=%p)\n", 
577         //           (void *)cur_cycle, (void *)info->yield_start_cycle, 
578         //         (void *)info->yield_cycle_period);
579         
580         V3_Yield();
581         info->yield_start_cycle = v3_get_host_time(&info->time_state);
582     }
583 }
584
585
586 /* 
587  * unconditional cpu yield 
588  * if the yielding thread is a guest context, the guest quantum is reset on resumption 
589  * Non guest context threads should call this function with a NULL argument
590  */
591 void v3_yield(struct guest_info * info) {
592     V3_Yield();
593
594     if (info) {
595         info->yield_start_cycle = v3_get_host_time(&info->time_state);
596     }
597 }
598
599
600
601
602 void v3_print_cond(const char * fmt, ...) {
603     if (v3_dbg_enable == 1) {
604         char buf[2048];
605         va_list ap;
606
607         va_start(ap, fmt);
608         vsnprintf(buf, 2048, fmt, ap);
609         va_end(ap);
610
611         V3_Print("%s", buf);
612     }    
613 }
614
615
616
617 void v3_interrupt_cpu(struct v3_vm_info * vm, int logical_cpu, int vector) {
618     extern struct v3_os_hooks * os_hooks;
619
620     if ((os_hooks) && (os_hooks)->interrupt_cpu) {
621         (os_hooks)->interrupt_cpu(vm, logical_cpu, vector);
622     }
623 }
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 }