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