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.


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