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.


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