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.


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