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.


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