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.


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