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.


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