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.


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