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.


separated virtual cores from physical cores
[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 #ifdef V3_CONFIG_VNET
37 #include <vnet/vnet.h>
38 #endif
39
40
41 v3_cpu_arch_t v3_cpu_types[V3_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 V3_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 V3_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 V3_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 V3_CONFIG_VMX
83         case V3_VMX_CPU:
84         case V3_VMX_EPT_CPU:
85         case V3_VMX_EPT_UG_CPU:
86             PrintDebug("Deinitializing VMX CPU %d\n", cpu_id);
87             v3_deinit_vmx_cpu(cpu_id);
88             break;
89 #endif
90         case V3_INVALID_CPU:
91         default:
92             PrintError("CPU has no virtualization Extensions\n");
93             break;
94     }
95 }
96
97
98
99 void Init_V3(struct v3_os_hooks * hooks, int num_cpus) {
100     int i;
101
102     V3_Print("V3 Print statement to fix a Kitten page fault bug\n");
103
104     // Set global variables. 
105     os_hooks = hooks;
106
107     for (i = 0; i < V3_CONFIG_MAX_CPUS; i++) {
108         v3_cpu_types[i] = V3_INVALID_CPU;
109     }
110
111     // Register all the possible device types
112     V3_init_devices();
113
114     // Register all shadow paging handlers
115     V3_init_shdw_paging();
116
117     // Register all extensions
118     V3_init_extensions();
119
120
121 #ifdef V3_CONFIG_SYMMOD
122     V3_init_symmod();
123 #endif
124
125
126 #ifdef V3_CONFIG_VNET
127     v3_init_vnet();
128 #endif
129
130
131 #ifdef V3_CONFIG_MULTITHREAD_OS
132     if ((hooks) && (hooks->call_on_cpu)) {
133
134         for (i = 0; i < num_cpus; i++) {
135
136             V3_Print("Initializing VMM extensions on cpu %d\n", i);
137             hooks->call_on_cpu(i, &init_cpu, (void *)(addr_t)i);
138         }
139     }
140 #else 
141     init_cpu(0);
142 #endif
143
144 }
145
146
147 void Shutdown_V3() {
148     int i;
149
150     V3_deinit_devices();
151     V3_deinit_shdw_paging();
152
153     V3_deinit_extensions();
154
155 #ifdef V3_CONFIG_SYMMOD
156     V3_deinit_symmod();
157 #endif
158
159
160 #ifdef V3_CONFIG_VNET
161     v3_deinit_vnet();
162 #endif
163
164 #ifdef V3_CONFIG_MULTITHREAD_OS
165     if ((os_hooks) && (os_hooks->call_on_cpu)) {
166         for (i = 0; i < V3_CONFIG_MAX_CPUS; i++) {
167             if (v3_cpu_types[i] != V3_INVALID_CPU) {
168                 deinit_cpu((void *)(addr_t)i);
169             }
170         }
171     }
172 #else 
173     deinit_cpu(0);
174 #endif
175
176 }
177
178
179 v3_cpu_arch_t v3_get_cpu_type(int cpu_id) {
180     return v3_cpu_types[cpu_id];
181 }
182
183
184 struct v3_vm_info * v3_create_vm(void * cfg, void * priv_data, char * name) {
185     struct v3_vm_info * vm = v3_config_guest(cfg, priv_data);
186
187     if (vm == NULL) {
188         PrintError("Could not configure guest\n");
189         return NULL;
190     }
191
192     V3_Print("CORE 0 RIP=%p\n", (void *)(addr_t)(vm->cores[0].rip));
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 (on logical core %u): in start_core (RIP=%p)\n", 
213                core->vcpu_id, core->pcpu_id, (void *)(addr_t)core->rip);
214
215     switch (v3_cpu_types[0]) {
216 #ifdef V3_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 V3_CONFIG_VMX
223         case V3_VMX_CPU:
224         case V3_VMX_EPT_CPU:
225         case V3_VMX_EPT_UG_CPU:
226             return v3_start_vmx_guest(core);
227             break;
228 #endif
229         default:
230             PrintError("Attempting to enter a guest on an invalid CPU\n");
231             return -1;
232     }
233     // should not happen
234     return 0;
235 }
236
237
238 // For the moment very ugly. Eventually we will shift the cpu_mask to an arbitrary sized type...
239 #ifdef V3_CONFIG_MULTITHREAD_OS
240 #define MAX_CORES 32
241 #else
242 #define MAX_CORES 1
243 #endif
244
245
246 int v3_start_vm(struct v3_vm_info * vm, unsigned int cpu_mask) {
247     uint32_t i;
248     uint8_t * core_mask = (uint8_t *)&cpu_mask; // This is to make future expansion easier
249     uint32_t avail_cores = 0;
250     int vcore_id = 0;
251
252     /// CHECK IF WE ARE MULTICORE ENABLED....
253
254     V3_Print("V3 --  Starting VM (%u cores)\n", vm->num_cores);
255     V3_Print("CORE 0 RIP=%p\n", (void *)(addr_t)(vm->cores[0].rip));
256
257
258     // Check that enough cores are present in the mask to handle vcores
259     for (i = 0; i < MAX_CORES; i++) {
260         int major = i / 8;
261         int minor = i % 8;
262         
263         if (core_mask[major] & (0x1 << minor)) {
264             avail_cores++;
265         }
266     }
267
268
269     if (vm->num_cores > avail_cores) {
270         PrintError("Attempted to start a VM with too many cores (vm->num_cores = %d, avail_cores = %d, MAX=%d)\n", 
271                    vm->num_cores, avail_cores, MAX_CORES);
272         return -1;
273     }
274
275 #ifdef V3_CONFIG_MULTITHREAD_OS
276     // spawn off new threads, for other cores
277     for (i = 0, vcore_id = 1; (i < MAX_CORES) && (vcore_id < vm->num_cores); i++) {
278         int major = 0;
279         int minor = 0;
280         void * core_thread = NULL;
281         struct guest_info * core = &(vm->cores[vcore_id]);
282         char * specified_cpu = v3_cfg_val(core->core_cfg_data, "target_cpu");
283         uint32_t core_idx = 0;
284
285         if (specified_cpu != NULL) {
286             core_idx = atoi(specified_cpu);
287             
288             if ((core_idx < 0) || (core_idx >= MAX_CORES)) {
289                 PrintError("Target CPU out of bounds (%d) (MAX_CORES=%d)\n", core_idx, MAX_CORES);
290             }
291
292             i--; // We reset the logical core idx. Not strictly necessary I guess... 
293         } else {
294
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->pcpu_id = core_idx;
330         core_thread = V3_CREATE_THREAD_ON_CPU(core_idx, start_core, core, core->exec_name);
331
332         if (core_thread == NULL) {
333             PrintError("Thread launch failed\n");
334             v3_stop_vm(vm);
335             return -1;
336         }
337
338         vcore_id++;
339     }
340 #endif
341
342     sprintf(vm->cores[0].exec_name, "%s", vm->name);
343
344     vm->cores[0].pcpu_id = V3_Get_CPU();
345
346     if (start_core(&(vm->cores[0])) != 0) {
347         PrintError("Error starting VM core 0\n");
348         v3_stop_vm(vm);
349         return -1;
350     }
351
352
353     return 0;
354
355 }
356
357
358
359
360 int v3_stop_vm(struct v3_vm_info * vm) {
361
362     vm->run_state = VM_STOPPED;
363
364     // force exit all cores via a cross call/IPI
365
366     while (1) {
367         int i = 0;
368         int still_running = 0;
369
370         for (i = 0; i < vm->num_cores; i++) {
371             if (vm->cores[i].core_run_state != CORE_STOPPED) {
372                 still_running = 1;
373             }
374         }
375
376         if (still_running == 0) {
377             break;
378         }
379
380         v3_yield(NULL);
381     }
382     
383     V3_Print("VM stopped. Returning\n");
384
385     return 0;
386 }
387
388
389 int v3_free_vm(struct v3_vm_info * vm) {
390     int i = 0;
391     // deinitialize guest (free memory, etc...)
392
393     v3_free_vm_devices(vm);
394
395     // free cores
396     for (i = 0; i < vm->num_cores; i++) {
397         v3_free_core(&(vm->cores[i]));
398     }
399
400     // free vm
401     v3_free_vm_internal(vm);
402
403     v3_free_config(vm);
404
405     V3_Free(vm);
406
407     return 0;
408 }
409
410
411 #ifdef __V3_32BIT__
412
413 v3_cpu_mode_t v3_get_host_cpu_mode() {
414     uint32_t cr4_val;
415     struct cr4_32 * cr4;
416
417     __asm__ (
418              "movl %%cr4, %0; "
419              : "=r"(cr4_val) 
420              );
421
422     
423     cr4 = (struct cr4_32 *)&(cr4_val);
424
425     if (cr4->pae == 1) {
426         return PROTECTED_PAE;
427     } else {
428         return PROTECTED;
429     }
430 }
431
432 #elif __V3_64BIT__
433
434 v3_cpu_mode_t v3_get_host_cpu_mode() {
435     return LONG;
436 }
437
438 #endif 
439
440
441 #define V3_Yield(addr)                                  \
442     do {                                                \
443         extern struct v3_os_hooks * os_hooks;           \
444         if ((os_hooks) && (os_hooks)->yield_cpu) {      \
445             (os_hooks)->yield_cpu();                    \
446         }                                               \
447     } while (0)                                         \
448
449
450
451 void v3_yield_cond(struct guest_info * info) {
452     uint64_t cur_cycle;
453     cur_cycle = v3_get_host_time(&info->time_state);
454
455     if (cur_cycle > (info->yield_start_cycle + info->vm_info->yield_cycle_period)) {
456
457         /*
458           PrintDebug("Conditional Yield (cur_cyle=%p, start_cycle=%p, period=%p)\n", 
459           (void *)cur_cycle, (void *)info->yield_start_cycle, (void *)info->yield_cycle_period);
460         */
461         V3_Yield();
462         info->yield_start_cycle = v3_get_host_time(&info->time_state);
463     }
464 }
465
466
467 /* 
468  * unconditional cpu yield 
469  * if the yielding thread is a guest context, the guest quantum is reset on resumption 
470  * Non guest context threads should call this function with a NULL argument
471  */
472 void v3_yield(struct guest_info * info) {
473     V3_Yield();
474
475     if (info) {
476         info->yield_start_cycle = v3_get_host_time(&info->time_state);
477     }
478 }
479
480
481
482
483 void v3_print_cond(const char * fmt, ...) {
484     if (v3_dbg_enable == 1) {
485         char buf[2048];
486         va_list ap;
487
488         va_start(ap, fmt);
489         vsnprintf(buf, 2048, fmt, ap);
490         va_end(ap);
491
492         V3_Print("%s", buf);
493     }    
494 }
495
496
497 #ifdef V3_CONFIG_MULTITHREAD_OS
498
499 void v3_interrupt_cpu(struct v3_vm_info * vm, int logical_cpu, int vector) {
500     extern struct v3_os_hooks * os_hooks;
501
502     if ((os_hooks) && (os_hooks)->interrupt_cpu) {
503         (os_hooks)->interrupt_cpu(vm, logical_cpu, vector);
504     }
505 }
506 #endif
507
508
509
510 int v3_vm_enter(struct guest_info * info) {
511     switch (v3_cpu_types[0]) {
512 #ifdef V3_CONFIG_SVM
513         case V3_SVM_CPU:
514         case V3_SVM_REV3_CPU:
515             return v3_svm_enter(info);
516             break;
517 #endif
518 #if V3_CONFIG_VMX
519         case V3_VMX_CPU:
520         case V3_VMX_EPT_CPU:
521         case V3_VMX_EPT_UG_CPU:
522             return v3_vmx_enter(info);
523             break;
524 #endif
525         default:
526             PrintError("Attemping to enter a guest on an invalid CPU\n");
527             return -1;
528     }
529 }