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.


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