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.


Initial support for moving virtual cores among different physical CPUs
[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
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         struct guest_info * core = &(vm->cores[vcore_id]);
272         char * specified_cpu = v3_cfg_val(core->core_cfg_data, "target_cpu");
273         uint32_t core_idx = 0;
274
275         if (specified_cpu != NULL) {
276             core_idx = atoi(specified_cpu);
277             
278             if ((core_idx < 0) || (core_idx >= MAX_CORES)) {
279                 PrintError("Target CPU out of bounds (%d) (MAX_CORES=%d)\n", core_idx, MAX_CORES);
280             }
281
282             i--; // We reset the logical core idx. Not strictly necessary I guess... 
283         } else {
284
285             if (i == V3_Get_CPU()) {
286                 // We skip the local CPU because it is reserved for vcore 0
287                 continue;
288             }
289             
290             core_idx = i;
291         }
292
293         major = core_idx / 8;
294         minor = core_idx % 8;
295
296
297         if ((core_mask[major] & (0x1 << minor)) == 0) {
298             PrintError("Logical CPU %d not available for virtual core %d; not started\n",
299                        core_idx, vcore_id);
300
301             if (specified_cpu != NULL) {
302                 PrintError("CPU was specified explicitly (%d). HARD ERROR\n", core_idx);
303                 v3_stop_vm(vm);
304                 return -1;
305             }
306
307             continue;
308         }
309
310         PrintDebug("Starting virtual core %u on logical core %u\n", 
311                    vcore_id, core_idx);
312         
313         sprintf(core->exec_name, "%s-%u", vm->name, vcore_id);
314
315         PrintDebug("run: core=%u, func=0x%p, arg=0x%p, name=%s\n",
316                    core_idx, start_core, core, core->exec_name);
317
318         // TODO: actually manage these threads instead of just launching them
319         core->pcpu_id = core_idx;
320         core->core_thread = V3_CREATE_THREAD_ON_CPU(core_idx, start_core, core, core->exec_name);
321
322         if (core->core_thread == NULL) {
323             PrintError("Thread launch failed\n");
324             v3_stop_vm(vm);
325             return -1;
326         }
327
328         vcore_id++;
329     }
330 #endif
331
332     sprintf(vm->cores[0].exec_name, "%s", vm->name);
333
334     vm->cores[0].pcpu_id = V3_Get_CPU();
335
336     if (start_core(&(vm->cores[0])) != 0) {
337         PrintError("Error starting VM core 0\n");
338         v3_stop_vm(vm);
339         return -1;
340     }
341
342
343     return 0;
344
345 }
346
347
348 int v3_reset_vm_core(struct guest_info * core, addr_t rip) {
349     
350     switch (v3_cpu_types[core->pcpu_id]) {
351 #ifdef V3_CONFIG_SVM
352         case V3_SVM_CPU:
353         case V3_SVM_REV3_CPU:
354             PrintDebug("Resetting SVM Guest CPU %d\n", core->vcpu_id);
355             return v3_reset_svm_vm_core(core, rip);
356 #endif
357 #ifdef V3_CONFIG_VMX
358         case V3_VMX_CPU:
359         case V3_VMX_EPT_CPU:
360         case V3_VMX_EPT_UG_CPU:
361             PrintDebug("Resetting VMX Guest CPU %d\n", core->vcpu_id);
362             return v3_reset_vmx_vm_core(core, rip);
363 #endif
364         case V3_INVALID_CPU:
365         default:
366             PrintError("CPU has no virtualization Extensions\n");
367             break;
368     }
369
370     return -1;
371 }
372
373
374
375 /* move a virtual core to different physical core */
376 int v3_move_vm_core(struct v3_vm_info * vm, int vcore_id, int target_cpu) {
377     struct guest_info * core = NULL;
378
379     if(vcore_id < 0 || vcore_id > vm->num_cores) {
380         return -1;
381     }
382
383     core = &(vm->cores[vcore_id]);
384
385     if(target_cpu != core->pcpu_id &&
386         core->core_move_state != CORE_MOVE_PENDING){
387         core->core_move_state = CORE_MOVE_PENDING;
388         core->target_pcpu_id = target_cpu;
389         v3_interrupt_cpu(vm, core->pcpu_id, 0);
390
391         while(core->core_move_state != CORE_MOVE_DONE){
392             v3_yield(NULL);
393         }
394     }
395
396         
397     return 0;
398 }
399
400
401
402 int v3_stop_vm(struct v3_vm_info * vm) {
403
404     vm->run_state = VM_STOPPED;
405
406     // force exit all cores via a cross call/IPI
407
408     while (1) {
409         int i = 0;
410         int still_running = 0;
411
412         for (i = 0; i < vm->num_cores; i++) {
413             if (vm->cores[i].core_run_state != CORE_STOPPED) {
414                 still_running = 1;
415             }
416         }
417
418         if (still_running == 0) {
419             break;
420         }
421
422         v3_yield(NULL);
423     }
424     
425     V3_Print("VM stopped. Returning\n");
426
427     return 0;
428 }
429
430
431 int v3_pause_vm(struct v3_vm_info * vm) {
432
433     if (vm->run_state != VM_RUNNING) {
434         PrintError("Tried to pause a VM that was not running\n");
435         return -1;
436     }
437
438     while (v3_raise_barrier(vm, NULL) == -1);
439
440     vm->run_state = VM_PAUSED;
441
442     return 0;
443 }
444
445
446 int v3_continue_vm(struct v3_vm_info * vm) {
447
448     if (vm->run_state != VM_PAUSED) {
449         PrintError("Tried to continue a VM that was not paused\n");
450         return -1;
451     }
452
453     v3_lower_barrier(vm);
454
455     vm->run_state = VM_RUNNING;
456
457     return 0;
458 }
459
460
461 int v3_free_vm(struct v3_vm_info * vm) {
462     int i = 0;
463     // deinitialize guest (free memory, etc...)
464
465     v3_free_vm_devices(vm);
466
467     // free cores
468     for (i = 0; i < vm->num_cores; i++) {
469         v3_free_core(&(vm->cores[i]));
470     }
471
472     // free vm
473     v3_free_vm_internal(vm);
474
475     v3_free_config(vm);
476
477     V3_Free(vm);
478
479     return 0;
480 }
481
482
483 #ifdef __V3_32BIT__
484
485 v3_cpu_mode_t v3_get_host_cpu_mode() {
486     uint32_t cr4_val;
487     struct cr4_32 * cr4;
488
489     __asm__ (
490              "movl %%cr4, %0; "
491              : "=r"(cr4_val) 
492              );
493
494     
495     cr4 = (struct cr4_32 *)&(cr4_val);
496
497     if (cr4->pae == 1) {
498         return PROTECTED_PAE;
499     } else {
500         return PROTECTED;
501     }
502 }
503
504 #elif __V3_64BIT__
505
506 v3_cpu_mode_t v3_get_host_cpu_mode() {
507     return LONG;
508 }
509
510 #endif 
511
512
513 #define V3_Yield(addr)                                  \
514     do {                                                \
515         extern struct v3_os_hooks * os_hooks;           \
516         if ((os_hooks) && (os_hooks)->yield_cpu) {      \
517             (os_hooks)->yield_cpu();                    \
518         }                                               \
519     } while (0)                                         \
520
521
522
523 void v3_yield_cond(struct guest_info * info) {
524     uint64_t cur_cycle;
525     cur_cycle = v3_get_host_time(&info->time_state);
526
527     if (cur_cycle > (info->yield_start_cycle + info->vm_info->yield_cycle_period)) {
528
529         /*
530           PrintDebug("Conditional Yield (cur_cyle=%p, start_cycle=%p, period=%p)\n", 
531           (void *)cur_cycle, (void *)info->yield_start_cycle, (void *)info->yield_cycle_period);
532         */
533         V3_Yield();
534         info->yield_start_cycle = v3_get_host_time(&info->time_state);
535     }
536 }
537
538
539 /* 
540  * unconditional cpu yield 
541  * if the yielding thread is a guest context, the guest quantum is reset on resumption 
542  * Non guest context threads should call this function with a NULL argument
543  */
544 void v3_yield(struct guest_info * info) {
545     V3_Yield();
546
547     if (info) {
548         info->yield_start_cycle = v3_get_host_time(&info->time_state);
549     }
550 }
551
552
553
554
555 void v3_print_cond(const char * fmt, ...) {
556     if (v3_dbg_enable == 1) {
557         char buf[2048];
558         va_list ap;
559
560         va_start(ap, fmt);
561         vsnprintf(buf, 2048, fmt, ap);
562         va_end(ap);
563
564         V3_Print("%s", buf);
565     }    
566 }
567
568
569 #ifdef V3_CONFIG_MULTITHREAD_OS
570
571 void v3_interrupt_cpu(struct v3_vm_info * vm, int logical_cpu, int vector) {
572     extern struct v3_os_hooks * os_hooks;
573
574     if ((os_hooks) && (os_hooks)->interrupt_cpu) {
575         (os_hooks)->interrupt_cpu(vm, logical_cpu, vector);
576     }
577 }
578 #endif
579
580
581
582 int v3_vm_enter(struct guest_info * info) {
583     switch (v3_cpu_types[0]) {
584 #ifdef V3_CONFIG_SVM
585         case V3_SVM_CPU:
586         case V3_SVM_REV3_CPU:
587             return v3_svm_enter(info);
588             break;
589 #endif
590 #if V3_CONFIG_VMX
591         case V3_VMX_CPU:
592         case V3_VMX_EPT_CPU:
593         case V3_VMX_EPT_UG_CPU:
594             return v3_vmx_enter(info);
595             break;
596 #endif
597         default:
598             PrintError("Attemping to enter a guest on an invalid CPU\n");
599             return -1;
600     }
601 }