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.


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