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.


vmx cleanup
[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     if (vm == NULL) {
187         PrintError("Could not configure guest\n");
188         return NULL;
189     }
190
191     V3_Print("CORE 0 RIP=%p\n", (void *)(addr_t)(vm->cores[0].rip));
192
193     if (name == NULL) {
194         name = "[V3_VM]";
195     } else if (strlen(name) >= 128) {
196         PrintError("VM name is too long. Will be truncated to 128 chars.\n");
197     }
198
199     memset(vm->name, 0, 128);
200     strncpy(vm->name, name, 127);
201
202     return vm;
203 }
204
205
206 static int start_core(void * p)
207 {
208     struct guest_info * core = (struct guest_info *)p;
209
210
211     PrintDebug("virtual core %u: in start_core (RIP=%p)\n", 
212                core->cpu_id, (void *)(addr_t)core->rip);
213
214     switch (v3_cpu_types[0]) {
215 #ifdef CONFIG_SVM
216         case V3_SVM_CPU:
217         case V3_SVM_REV3_CPU:
218             return v3_start_svm_guest(core);
219             break;
220 #endif
221 #if CONFIG_VMX
222         case V3_VMX_CPU:
223         case V3_VMX_EPT_CPU:
224             return v3_start_vmx_guest(core);
225             break;
226 #endif
227         default:
228             PrintError("Attempting to enter a guest on an invalid CPU\n");
229             return -1;
230     }
231     // should not happen
232     return 0;
233 }
234
235
236 // For the moment very ugly. Eventually we will shift the cpu_mask to an arbitrary sized type...
237 #ifdef CONFIG_MULTITHREAD_OS
238 #define MAX_CORES 32
239 #else
240 #define MAX_CORES 1
241 #endif
242
243
244 int v3_start_vm(struct v3_vm_info * vm, unsigned int cpu_mask) {
245     uint32_t i;
246     uint8_t * core_mask = (uint8_t *)&cpu_mask; // This is to make future expansion easier
247     uint32_t avail_cores = 0;
248     int vcore_id = 0;
249
250     /// CHECK IF WE ARE MULTICORE ENABLED....
251
252     V3_Print("V3 --  Starting VM (%u cores)\n", vm->num_cores);
253     V3_Print("CORE 0 RIP=%p\n", (void *)(addr_t)(vm->cores[0].rip));
254
255
256     // Check that enough cores are present in the mask to handle vcores
257     for (i = 0; i < MAX_CORES; i++) {
258         int major = i / 8;
259         int minor = i % 8;
260         
261         if (core_mask[major] & (0x1 << minor)) {
262             avail_cores++;
263         }
264     }
265
266
267     if (vm->num_cores > avail_cores) {
268         PrintError("Attempted to start a VM with too many cores (vm->num_cores = %d, avail_cores = %d, MAX=%d)\n", 
269                    vm->num_cores, avail_cores, MAX_CORES);
270         return -1;
271     }
272
273 #ifdef CONFIG_MULTITHREAD_OS
274     // spawn off new threads, for other cores
275     for (i = 0, vcore_id = 1; (i < MAX_CORES) && (vcore_id < vm->num_cores); i++) {
276         int major = 0;
277         int minor = 0;
278         void * core_thread = NULL;
279         struct guest_info * core = &(vm->cores[vcore_id]);
280         char * specified_cpu = v3_cfg_val(core->core_cfg_data, "target_cpu");
281         uint32_t core_idx = 0;
282
283         if (specified_cpu != NULL) {
284             core_idx = atoi(specified_cpu);
285             
286             if ((core_idx < 0) || (core_idx >= MAX_CORES)) {
287                 PrintError("Target CPU out of bounds (%d) (MAX_CORES=%d)\n", core_idx, MAX_CORES);
288             }
289
290             i--; // We reset the logical core idx. Not strictly necessary I guess... 
291         } else {
292
293             /* This assumes that the core 0 thread has been mapped to physical core 0 */
294             if (i == V3_Get_CPU()) {
295                 // We skip the local CPU because it is reserved for vcore 0
296                 continue;
297             }
298             
299             core_idx = i;
300         }
301
302         major = core_idx / 8;
303         minor = core_idx % 8;
304
305
306         if ((core_mask[major] & (0x1 << minor)) == 0) {
307             PrintError("Logical CPU %d not available for virtual core %d; not started\n",
308                        core_idx, vcore_id);
309
310             if (specified_cpu != NULL) {
311                 PrintError("CPU was specified explicitly (%d). HARD ERROR\n", core_idx);
312                 v3_stop_vm(vm);
313                 return -1;
314             }
315
316             continue;
317         }
318
319         PrintDebug("Starting virtual core %u on logical core %u\n", 
320                    vcore_id, core_idx);
321         
322         sprintf(core->exec_name, "%s-%u", vm->name, vcore_id);
323
324         PrintDebug("run: core=%u, func=0x%p, arg=0x%p, name=%s\n",
325                    core_idx, start_core, core, core->exec_name);
326
327         // TODO: actually manage these threads instead of just launching them
328         core_thread = V3_CREATE_THREAD_ON_CPU(core_idx, start_core, core, core->exec_name);
329
330         if (core_thread == NULL) {
331             PrintError("Thread launch failed\n");
332             v3_stop_vm(vm);
333             return -1;
334         }
335
336         vcore_id++;
337     }
338 #endif
339
340     sprintf(vm->cores[0].exec_name, "%s", vm->name);
341
342     if (start_core(&(vm->cores[0])) != 0) {
343         PrintError("Error starting VM core 0\n");
344         v3_stop_vm(vm);
345         return -1;
346     }
347
348
349     return 0;
350
351 }
352
353
354
355
356 int v3_stop_vm(struct v3_vm_info * vm) {
357
358     vm->run_state = VM_STOPPED;
359
360     // force exit all cores via a cross call/IPI
361
362     while (1) {
363         int i = 0;
364         int still_running = 0;
365
366         for (i = 0; i < vm->num_cores; i++) {
367             if (vm->cores[i].core_run_state != CORE_STOPPED) {
368                 still_running = 1;
369             }
370         }
371
372         if (still_running == 0) {
373             break;
374         }
375
376         v3_yield(NULL);
377     }
378     
379     V3_Print("VM stopped. Returning\n");
380
381     return 0;
382 }
383
384
385 int v3_free_vm(struct v3_vm_info * vm) {
386     int i = 0;
387     // deinitialize guest (free memory, etc...)
388
389     v3_free_vm_devices(vm);
390
391     // free cores
392     for (i = 0; i < vm->num_cores; i++) {
393         v3_free_core(&(vm->cores[i]));
394     }
395
396     // free vm
397     v3_free_vm_internal(vm);
398
399     v3_free_config(vm);
400
401     V3_Free(vm);
402
403     return 0;
404 }
405
406
407 #ifdef __V3_32BIT__
408
409 v3_cpu_mode_t v3_get_host_cpu_mode() {
410     uint32_t cr4_val;
411     struct cr4_32 * cr4;
412
413     __asm__ (
414              "movl %%cr4, %0; "
415              : "=r"(cr4_val) 
416              );
417
418     
419     cr4 = (struct cr4_32 *)&(cr4_val);
420
421     if (cr4->pae == 1) {
422         return PROTECTED_PAE;
423     } else {
424         return PROTECTED;
425     }
426 }
427
428 #elif __V3_64BIT__
429
430 v3_cpu_mode_t v3_get_host_cpu_mode() {
431     return LONG;
432 }
433
434 #endif 
435
436
437 #define V3_Yield(addr)                                  \
438     do {                                                \
439         extern struct v3_os_hooks * os_hooks;           \
440         if ((os_hooks) && (os_hooks)->yield_cpu) {      \
441             (os_hooks)->yield_cpu();                    \
442         }                                               \
443     } while (0)                                         \
444
445
446
447 void v3_yield_cond(struct guest_info * info) {
448     uint64_t cur_cycle;
449     cur_cycle = v3_get_host_time(&info->time_state);
450
451     if (cur_cycle > (info->yield_start_cycle + info->vm_info->yield_cycle_period)) {
452
453         /*
454           PrintDebug("Conditional Yield (cur_cyle=%p, start_cycle=%p, period=%p)\n", 
455           (void *)cur_cycle, (void *)info->yield_start_cycle, (void *)info->yield_cycle_period);
456         */
457         V3_Yield();
458         info->yield_start_cycle = v3_get_host_time(&info->time_state);
459     }
460 }
461
462
463 /* 
464  * unconditional cpu yield 
465  * if the yielding thread is a guest context, the guest quantum is reset on resumption 
466  * Non guest context threads should call this function with a NULL argument
467  */
468 void v3_yield(struct guest_info * info) {
469     V3_Yield();
470
471     if (info) {
472         info->yield_start_cycle = v3_get_host_time(&info->time_state);
473     }
474 }
475
476
477
478
479 void v3_print_cond(const char * fmt, ...) {
480     if (v3_dbg_enable == 1) {
481         char buf[2048];
482         va_list ap;
483
484         va_start(ap, fmt);
485         vsnprintf(buf, 2048, fmt, ap);
486         va_end(ap);
487
488         V3_Print("%s", buf);
489     }    
490 }
491
492
493 #ifdef CONFIG_MULTITHREAD_OS
494
495 void v3_interrupt_cpu(struct v3_vm_info * vm, int logical_cpu, int vector) {
496     extern struct v3_os_hooks * os_hooks;
497
498     if ((os_hooks) && (os_hooks)->interrupt_cpu) {
499         (os_hooks)->interrupt_cpu(vm, logical_cpu, vector);
500     }
501 }
502 #endif
503
504
505
506 int v3_vm_enter(struct guest_info * info) {
507     switch (v3_cpu_types[0]) {
508 #ifdef CONFIG_SVM
509         case V3_SVM_CPU:
510         case V3_SVM_REV3_CPU:
511             return v3_svm_enter(info);
512             break;
513 #endif
514 #if CONFIG_VMX
515         case V3_VMX_CPU:
516         case V3_VMX_EPT_CPU:
517             return v3_vmx_enter(info);
518             break;
519 #endif
520         default:
521             PrintError("Attemping to enter a guest on an invalid CPU\n");
522             return -1;
523     }
524 }