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.


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