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 VM run states and added pause 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
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     vm->run_state = VM_PAUSED;
417
418     return 0;
419 }
420
421
422 int v3_continue_vm(struct v3_vm_info * vm) {
423
424     if (vm->run_state != VM_PAUSED) {
425         PrintError("Tried to continue a VM that was not paused\n");
426         return -1;
427     }
428
429     v3_lower_barrier(vm);
430
431     vm->run_state = VM_RUNNING;
432
433     return 0;
434 }
435
436
437 int v3_free_vm(struct v3_vm_info * vm) {
438     int i = 0;
439     // deinitialize guest (free memory, etc...)
440
441     v3_free_vm_devices(vm);
442
443     // free cores
444     for (i = 0; i < vm->num_cores; i++) {
445         v3_free_core(&(vm->cores[i]));
446     }
447
448     // free vm
449     v3_free_vm_internal(vm);
450
451     v3_free_config(vm);
452
453     V3_Free(vm);
454
455     return 0;
456 }
457
458
459 #ifdef __V3_32BIT__
460
461 v3_cpu_mode_t v3_get_host_cpu_mode() {
462     uint32_t cr4_val;
463     struct cr4_32 * cr4;
464
465     __asm__ (
466              "movl %%cr4, %0; "
467              : "=r"(cr4_val) 
468              );
469
470     
471     cr4 = (struct cr4_32 *)&(cr4_val);
472
473     if (cr4->pae == 1) {
474         return PROTECTED_PAE;
475     } else {
476         return PROTECTED;
477     }
478 }
479
480 #elif __V3_64BIT__
481
482 v3_cpu_mode_t v3_get_host_cpu_mode() {
483     return LONG;
484 }
485
486 #endif 
487
488
489 #define V3_Yield(addr)                                  \
490     do {                                                \
491         extern struct v3_os_hooks * os_hooks;           \
492         if ((os_hooks) && (os_hooks)->yield_cpu) {      \
493             (os_hooks)->yield_cpu();                    \
494         }                                               \
495     } while (0)                                         \
496
497
498
499 void v3_yield_cond(struct guest_info * info) {
500     uint64_t cur_cycle;
501     cur_cycle = v3_get_host_time(&info->time_state);
502
503     if (cur_cycle > (info->yield_start_cycle + info->vm_info->yield_cycle_period)) {
504
505         /*
506           PrintDebug("Conditional Yield (cur_cyle=%p, start_cycle=%p, period=%p)\n", 
507           (void *)cur_cycle, (void *)info->yield_start_cycle, (void *)info->yield_cycle_period);
508         */
509         V3_Yield();
510         info->yield_start_cycle = v3_get_host_time(&info->time_state);
511     }
512 }
513
514
515 /* 
516  * unconditional cpu yield 
517  * if the yielding thread is a guest context, the guest quantum is reset on resumption 
518  * Non guest context threads should call this function with a NULL argument
519  */
520 void v3_yield(struct guest_info * info) {
521     V3_Yield();
522
523     if (info) {
524         info->yield_start_cycle = v3_get_host_time(&info->time_state);
525     }
526 }
527
528
529
530
531 void v3_print_cond(const char * fmt, ...) {
532     if (v3_dbg_enable == 1) {
533         char buf[2048];
534         va_list ap;
535
536         va_start(ap, fmt);
537         vsnprintf(buf, 2048, fmt, ap);
538         va_end(ap);
539
540         V3_Print("%s", buf);
541     }    
542 }
543
544
545 #ifdef V3_CONFIG_MULTITHREAD_OS
546
547 void v3_interrupt_cpu(struct v3_vm_info * vm, int logical_cpu, int vector) {
548     extern struct v3_os_hooks * os_hooks;
549
550     if ((os_hooks) && (os_hooks)->interrupt_cpu) {
551         (os_hooks)->interrupt_cpu(vm, logical_cpu, vector);
552     }
553 }
554 #endif
555
556
557
558 int v3_vm_enter(struct guest_info * info) {
559     switch (v3_cpu_types[0]) {
560 #ifdef V3_CONFIG_SVM
561         case V3_SVM_CPU:
562         case V3_SVM_REV3_CPU:
563             return v3_svm_enter(info);
564             break;
565 #endif
566 #if V3_CONFIG_VMX
567         case V3_VMX_CPU:
568         case V3_VMX_EPT_CPU:
569         case V3_VMX_EPT_UG_CPU:
570             return v3_vmx_enter(info);
571             break;
572 #endif
573         default:
574             PrintError("Attemping to enter a guest on an invalid CPU\n");
575             return -1;
576     }
577 }