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.


deinitialization fixes
[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                 V3_Call_On_CPU(i, deinit_cpu, (void *)(addr_t)i);
158                 //deinit_cpu((void *)(addr_t)i);
159             }
160         }
161     }
162 #else 
163     deinit_cpu(0);
164 #endif
165
166 }
167
168
169 v3_cpu_arch_t v3_get_cpu_type(int cpu_id) {
170     return v3_cpu_types[cpu_id];
171 }
172
173
174 struct v3_vm_info * v3_create_vm(void * cfg, void * priv_data, char * name) {
175     struct v3_vm_info * vm = v3_config_guest(cfg, priv_data);
176
177     if (vm == NULL) {
178         PrintError("Could not configure guest\n");
179         return NULL;
180     }
181
182     V3_Print("CORE 0 RIP=%p\n", (void *)(addr_t)(vm->cores[0].rip));
183
184     if (name == NULL) {
185         name = "[V3_VM]";
186     } else if (strlen(name) >= 128) {
187         PrintError("VM name is too long. Will be truncated to 128 chars.\n");
188     }
189
190     memset(vm->name, 0, 128);
191     strncpy(vm->name, name, 127);
192
193     return vm;
194 }
195
196
197
198
199 static int start_core(void * p)
200 {
201     struct guest_info * core = (struct guest_info *)p;
202
203
204     PrintDebug("virtual core %u (on logical core %u): in start_core (RIP=%p)\n", 
205                core->vcpu_id, core->pcpu_id, (void *)(addr_t)core->rip);
206
207     switch (v3_cpu_types[0]) {
208 #ifdef V3_CONFIG_SVM
209         case V3_SVM_CPU:
210         case V3_SVM_REV3_CPU:
211             return v3_start_svm_guest(core);
212             break;
213 #endif
214 #if V3_CONFIG_VMX
215         case V3_VMX_CPU:
216         case V3_VMX_EPT_CPU:
217         case V3_VMX_EPT_UG_CPU:
218             return v3_start_vmx_guest(core);
219             break;
220 #endif
221         default:
222             PrintError("Attempting to enter a guest on an invalid CPU\n");
223             return -1;
224     }
225     // should not happen
226     return 0;
227 }
228
229
230 // For the moment very ugly. Eventually we will shift the cpu_mask to an arbitrary sized type...
231 #ifdef V3_CONFIG_MULTITHREAD_OS
232 #define MAX_CORES 32
233 #else
234 #define MAX_CORES 1
235 #endif
236
237
238 int v3_start_vm(struct v3_vm_info * vm, unsigned int cpu_mask) {
239     uint32_t i;
240     uint8_t * core_mask = (uint8_t *)&cpu_mask; // This is to make future expansion easier
241     uint32_t avail_cores = 0;
242     int vcore_id = 0;
243
244     /// CHECK IF WE ARE MULTICORE ENABLED....
245
246     V3_Print("V3 --  Starting VM (%u cores)\n", vm->num_cores);
247     V3_Print("CORE 0 RIP=%p\n", (void *)(addr_t)(vm->cores[0].rip));
248
249
250     // Check that enough cores are present in the mask to handle vcores
251     for (i = 0; i < MAX_CORES; i++) {
252         int major = i / 8;
253         int minor = i % 8;
254         
255         if (core_mask[major] & (0x1 << minor)) {
256             avail_cores++;
257         }
258     }
259
260
261     if (vm->num_cores > avail_cores) {
262         PrintError("Attempted to start a VM with too many cores (vm->num_cores = %d, avail_cores = %d, MAX=%d)\n", 
263                    vm->num_cores, avail_cores, MAX_CORES);
264         return -1;
265     }
266
267 #ifdef V3_CONFIG_MULTITHREAD_OS
268     // spawn off new threads, for other cores
269     for (i = 0, vcore_id = 1; (i < MAX_CORES) && (vcore_id < vm->num_cores); i++) {
270         int major = 0;
271         int minor = 0;
272         void * core_thread = NULL;
273         struct guest_info * core = &(vm->cores[vcore_id]);
274         char * specified_cpu = v3_cfg_val(core->core_cfg_data, "target_cpu");
275         uint32_t core_idx = 0;
276
277         if (specified_cpu != NULL) {
278             core_idx = atoi(specified_cpu);
279             
280             if ((core_idx < 0) || (core_idx >= MAX_CORES)) {
281                 PrintError("Target CPU out of bounds (%d) (MAX_CORES=%d)\n", core_idx, MAX_CORES);
282             }
283
284             i--; // We reset the logical core idx. Not strictly necessary I guess... 
285         } else {
286
287             if (i == V3_Get_CPU()) {
288                 // We skip the local CPU because it is reserved for vcore 0
289                 continue;
290             }
291             
292             core_idx = i;
293         }
294
295         major = core_idx / 8;
296         minor = core_idx % 8;
297
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         // TODO: actually manage these threads instead of just launching them
321         core->pcpu_id = core_idx;
322         core_thread = V3_CREATE_THREAD_ON_CPU(core_idx, start_core, core, core->exec_name);
323
324         if (core_thread == NULL) {
325             PrintError("Thread launch failed\n");
326             v3_stop_vm(vm);
327             return -1;
328         }
329
330         vcore_id++;
331     }
332 #endif
333
334     sprintf(vm->cores[0].exec_name, "%s", vm->name);
335
336     vm->cores[0].pcpu_id = V3_Get_CPU();
337
338     if (start_core(&(vm->cores[0])) != 0) {
339         PrintError("Error starting VM core 0\n");
340         v3_stop_vm(vm);
341         return -1;
342     }
343
344
345     return 0;
346
347 }
348
349
350 int v3_reset_vm_core(struct guest_info * core, addr_t rip) {
351     
352     switch (v3_cpu_types[core->pcpu_id]) {
353 #ifdef V3_CONFIG_SVM
354         case V3_SVM_CPU:
355         case V3_SVM_REV3_CPU:
356             PrintDebug("Resetting SVM Guest CPU %d\n", core->vcpu_id);
357             return v3_reset_svm_vm_core(core, rip);
358 #endif
359 #ifdef V3_CONFIG_VMX
360         case V3_VMX_CPU:
361         case V3_VMX_EPT_CPU:
362         case V3_VMX_EPT_UG_CPU:
363             PrintDebug("Resetting VMX Guest CPU %d\n", core->vcpu_id);
364             return v3_reset_vmx_vm_core(core, rip);
365 #endif
366         case V3_INVALID_CPU:
367         default:
368             PrintError("CPU has no virtualization Extensions\n");
369             break;
370     }
371
372     return -1;
373 }
374
375
376
377
378
379 int v3_stop_vm(struct v3_vm_info * vm) {
380
381     vm->run_state = VM_STOPPED;
382
383     // force exit all cores via a cross call/IPI
384
385     while (1) {
386         int i = 0;
387         int still_running = 0;
388
389         for (i = 0; i < vm->num_cores; i++) {
390             if (vm->cores[i].core_run_state != CORE_STOPPED) {
391                 still_running = 1;
392             }
393         }
394
395         if (still_running == 0) {
396             break;
397         }
398
399         v3_yield(NULL);
400     }
401     
402     V3_Print("VM stopped. Returning\n");
403
404     return 0;
405 }
406
407
408 int v3_pause_vm(struct v3_vm_info * vm) {
409
410     if (vm->run_state != VM_RUNNING) {
411         PrintError("Tried to pause a VM that was not running\n");
412         return -1;
413     }
414
415     while (v3_raise_barrier(vm, NULL) == -1);
416
417     vm->run_state = VM_PAUSED;
418
419     return 0;
420 }
421
422
423 int v3_continue_vm(struct v3_vm_info * vm) {
424
425     if (vm->run_state != VM_PAUSED) {
426         PrintError("Tried to continue a VM that was not paused\n");
427         return -1;
428     }
429
430     v3_lower_barrier(vm);
431
432     vm->run_state = VM_RUNNING;
433
434     return 0;
435 }
436
437
438 int v3_free_vm(struct v3_vm_info * vm) {
439     int i = 0;
440     // deinitialize guest (free memory, etc...)
441
442     v3_free_vm_devices(vm);
443
444     // free cores
445     for (i = 0; i < vm->num_cores; i++) {
446         v3_free_core(&(vm->cores[i]));
447     }
448
449     // free vm
450     v3_free_vm_internal(vm);
451
452     v3_free_config(vm);
453
454     V3_Free(vm);
455
456     return 0;
457 }
458
459
460 #ifdef __V3_32BIT__
461
462 v3_cpu_mode_t v3_get_host_cpu_mode() {
463     uint32_t cr4_val;
464     struct cr4_32 * cr4;
465
466     __asm__ (
467              "movl %%cr4, %0; "
468              : "=r"(cr4_val) 
469              );
470
471     
472     cr4 = (struct cr4_32 *)&(cr4_val);
473
474     if (cr4->pae == 1) {
475         return PROTECTED_PAE;
476     } else {
477         return PROTECTED;
478     }
479 }
480
481 #elif __V3_64BIT__
482
483 v3_cpu_mode_t v3_get_host_cpu_mode() {
484     return LONG;
485 }
486
487 #endif 
488
489
490 #define V3_Yield(addr)                                  \
491     do {                                                \
492         extern struct v3_os_hooks * os_hooks;           \
493         if ((os_hooks) && (os_hooks)->yield_cpu) {      \
494             (os_hooks)->yield_cpu();                    \
495         }                                               \
496     } while (0)                                         \
497
498
499
500 void v3_yield_cond(struct guest_info * info) {
501     uint64_t cur_cycle;
502     cur_cycle = v3_get_host_time(&info->time_state);
503
504     if (cur_cycle > (info->yield_start_cycle + info->vm_info->yield_cycle_period)) {
505
506         /*
507           PrintDebug("Conditional Yield (cur_cyle=%p, start_cycle=%p, period=%p)\n", 
508           (void *)cur_cycle, (void *)info->yield_start_cycle, (void *)info->yield_cycle_period);
509         */
510         V3_Yield();
511         info->yield_start_cycle = v3_get_host_time(&info->time_state);
512     }
513 }
514
515
516 /* 
517  * unconditional cpu yield 
518  * if the yielding thread is a guest context, the guest quantum is reset on resumption 
519  * Non guest context threads should call this function with a NULL argument
520  */
521 void v3_yield(struct guest_info * info) {
522     V3_Yield();
523
524     if (info) {
525         info->yield_start_cycle = v3_get_host_time(&info->time_state);
526     }
527 }
528
529
530
531
532 void v3_print_cond(const char * fmt, ...) {
533     if (v3_dbg_enable == 1) {
534         char buf[2048];
535         va_list ap;
536
537         va_start(ap, fmt);
538         vsnprintf(buf, 2048, fmt, ap);
539         va_end(ap);
540
541         V3_Print("%s", buf);
542     }    
543 }
544
545
546 #ifdef V3_CONFIG_MULTITHREAD_OS
547
548 void v3_interrupt_cpu(struct v3_vm_info * vm, int logical_cpu, int vector) {
549     extern struct v3_os_hooks * os_hooks;
550
551     if ((os_hooks) && (os_hooks)->interrupt_cpu) {
552         (os_hooks)->interrupt_cpu(vm, logical_cpu, vector);
553     }
554 }
555 #endif
556
557
558
559 int v3_vm_enter(struct guest_info * info) {
560     switch (v3_cpu_types[0]) {
561 #ifdef V3_CONFIG_SVM
562         case V3_SVM_CPU:
563         case V3_SVM_REV3_CPU:
564             return v3_svm_enter(info);
565             break;
566 #endif
567 #if V3_CONFIG_VMX
568         case V3_VMX_CPU:
569         case V3_VMX_EPT_CPU:
570         case V3_VMX_EPT_UG_CPU:
571             return v3_vmx_enter(info);
572             break;
573 #endif
574         default:
575             PrintError("Attemping to enter a guest on an invalid CPU\n");
576             return -1;
577     }
578 }