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.


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