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