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.


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