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.


Separate VNET from Palacios, add its own host_hooks for interfaces from host OS for...
[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: in start_core (RIP=%p)\n", 
201                core->cpu_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             /* This assumes that the core 0 thread has been mapped to physical core 0 */
284             if (i == V3_Get_CPU()) {
285                 // We skip the local CPU because it is reserved for vcore 0
286                 continue;
287             }
288             
289             core_idx = i;
290         }
291
292         major = core_idx / 8;
293         minor = core_idx % 8;
294
295
296         if ((core_mask[major] & (0x1 << minor)) == 0) {
297             PrintError("Logical CPU %d not available for virtual core %d; not started\n",
298                        core_idx, vcore_id);
299
300             if (specified_cpu != NULL) {
301                 PrintError("CPU was specified explicitly (%d). HARD ERROR\n", core_idx);
302                 v3_stop_vm(vm);
303                 return -1;
304             }
305
306             continue;
307         }
308
309         PrintDebug("Starting virtual core %u on logical core %u\n", 
310                    vcore_id, core_idx);
311         
312         sprintf(core->exec_name, "%s-%u", vm->name, vcore_id);
313
314         PrintDebug("run: core=%u, func=0x%p, arg=0x%p, name=%s\n",
315                    core_idx, start_core, core, core->exec_name);
316
317         // TODO: actually manage these threads instead of just launching them
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     if (start_core(&(vm->cores[0])) != 0) {
333         PrintError("Error starting VM core 0\n");
334         v3_stop_vm(vm);
335         return -1;
336     }
337
338
339     return 0;
340
341 }
342
343
344
345
346 int v3_stop_vm(struct v3_vm_info * vm) {
347
348     vm->run_state = VM_STOPPED;
349
350     // force exit all cores via a cross call/IPI
351
352     while (1) {
353         int i = 0;
354         int still_running = 0;
355
356         for (i = 0; i < vm->num_cores; i++) {
357             if (vm->cores[i].core_run_state != CORE_STOPPED) {
358                 still_running = 1;
359             }
360         }
361
362         if (still_running == 0) {
363             break;
364         }
365
366         v3_yield(NULL);
367     }
368     
369     V3_Print("VM stopped. Returning\n");
370
371     return 0;
372 }
373
374
375 int v3_free_vm(struct v3_vm_info * vm) {
376     int i = 0;
377     // deinitialize guest (free memory, etc...)
378
379     v3_free_vm_devices(vm);
380
381     // free cores
382     for (i = 0; i < vm->num_cores; i++) {
383         v3_free_core(&(vm->cores[i]));
384     }
385
386     // free vm
387     v3_free_vm_internal(vm);
388
389     v3_free_config(vm);
390
391     V3_Free(vm);
392
393     return 0;
394 }
395
396
397 #ifdef __V3_32BIT__
398
399 v3_cpu_mode_t v3_get_host_cpu_mode() {
400     uint32_t cr4_val;
401     struct cr4_32 * cr4;
402
403     __asm__ (
404              "movl %%cr4, %0; "
405              : "=r"(cr4_val) 
406              );
407
408     
409     cr4 = (struct cr4_32 *)&(cr4_val);
410
411     if (cr4->pae == 1) {
412         return PROTECTED_PAE;
413     } else {
414         return PROTECTED;
415     }
416 }
417
418 #elif __V3_64BIT__
419
420 v3_cpu_mode_t v3_get_host_cpu_mode() {
421     return LONG;
422 }
423
424 #endif 
425
426
427 #define V3_Yield(addr)                                  \
428     do {                                                \
429         extern struct v3_os_hooks * os_hooks;           \
430         if ((os_hooks) && (os_hooks)->yield_cpu) {      \
431             (os_hooks)->yield_cpu();                    \
432         }                                               \
433     } while (0)                                         \
434
435
436
437 void v3_yield_cond(struct guest_info * info) {
438     uint64_t cur_cycle;
439     cur_cycle = v3_get_host_time(&info->time_state);
440
441     if (cur_cycle > (info->yield_start_cycle + info->vm_info->yield_cycle_period)) {
442
443         /*
444           PrintDebug("Conditional Yield (cur_cyle=%p, start_cycle=%p, period=%p)\n", 
445           (void *)cur_cycle, (void *)info->yield_start_cycle, (void *)info->yield_cycle_period);
446         */
447         V3_Yield();
448         info->yield_start_cycle = v3_get_host_time(&info->time_state);
449     }
450 }
451
452
453 /* 
454  * unconditional cpu yield 
455  * if the yielding thread is a guest context, the guest quantum is reset on resumption 
456  * Non guest context threads should call this function with a NULL argument
457  */
458 void v3_yield(struct guest_info * info) {
459     V3_Yield();
460
461     if (info) {
462         info->yield_start_cycle = v3_get_host_time(&info->time_state);
463     }
464 }
465
466
467
468
469 void v3_print_cond(const char * fmt, ...) {
470     if (v3_dbg_enable == 1) {
471         char buf[2048];
472         va_list ap;
473
474         va_start(ap, fmt);
475         vsnprintf(buf, 2048, fmt, ap);
476         va_end(ap);
477
478         V3_Print("%s", buf);
479     }    
480 }
481
482
483 #ifdef V3_CONFIG_MULTITHREAD_OS
484
485 void v3_interrupt_cpu(struct v3_vm_info * vm, int logical_cpu, int vector) {
486     extern struct v3_os_hooks * os_hooks;
487
488     if ((os_hooks) && (os_hooks)->interrupt_cpu) {
489         (os_hooks)->interrupt_cpu(vm, logical_cpu, vector);
490     }
491 }
492 #endif
493
494
495
496 int v3_vm_enter(struct guest_info * info) {
497     switch (v3_cpu_types[0]) {
498 #ifdef V3_CONFIG_SVM
499         case V3_SVM_CPU:
500         case V3_SVM_REV3_CPU:
501             return v3_svm_enter(info);
502             break;
503 #endif
504 #if V3_CONFIG_VMX
505         case V3_VMX_CPU:
506         case V3_VMX_EPT_CPU:
507         case V3_VMX_EPT_UG_CPU:
508             return v3_vmx_enter(info);
509             break;
510 #endif
511         default:
512             PrintError("Attemping to enter a guest on an invalid CPU\n");
513             return -1;
514     }
515 }