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.


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