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.


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