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.


Refactoring and additions to direct paging (nested and passthrough)
[palacios.git] / palacios / src / palacios / vmm_direct_paging.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, Steven Jaconette <stevenjaconette2007@u.northwestern.edu> 
11  * Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu> 
12  * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org> 
13  * All rights reserved.
14  *
15  * Author: Steven Jaconette <stevenjaconette2007@u.northwestern.edu>
16  *         Peter Dinda <pdinda@northwestern.edu> (refactor + events)
17  *
18  * This is free software.  You are permitted to use,
19  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
20  */
21
22 #include <palacios/vmm_direct_paging.h>
23 #include <palacios/vmm_paging.h>
24 #include <palacios/vmm.h>
25 #include <palacios/vm_guest_mem.h>
26 #include <palacios/vm_guest.h>
27 #include <palacios/vmm_ctrl_regs.h>
28
29
30 #if !defined(V3_CONFIG_DEBUG_NESTED_PAGING) && !defined(V3_CONFIG_DEBUG_SHADOW_PAGING)
31 #undef PrintDebug
32 #define PrintDebug(fmt, args...)
33 #endif
34
35
36
37 /*
38
39   "Direct Paging" combines these three functionalities:
40
41    1. Passthrough paging for SVM and VMX
42
43       Passthrough paging is used for shadow paging when
44       the guest does not have paging turn on, for example 
45       when it is running in real mode or protected mode 
46       early in a typical boot process.    Passthrough page
47       tables are shadow page tables that are built assuming
48       the guest virtual to guest physical mapping is the identity.
49       Thus, what they implement are the GPA->HPA mapping. 
50
51       Passthrough page tables are built using 32PAE paging.
52       
53
54    2. Nested paging on SVM
55   
56       The SVM nested page tables have the same format as
57       regular page tables.   For this reason, we can reuse 
58       much of the passthrough implementation.   A nested page
59       table mapping is a GPA->HPA mapping, creating a very 
60       simlar model as with passthrough paging, just that it's 
61       always active, whether the guest has paging on or not.
62
63
64    3. Nested paging on VMX
65
66       The VMX nested page tables have a different format
67       than regular page tables.  For this reason, we have
68       implemented them in the vmx_npt.h file.  The code
69       here then is a wrapper, allowing us to make nested
70       paging functionality appear uniform across VMX and SVM
71       elsewhere in the codebase.
72
73 */
74
75
76
77 static inline int is_vmx_nested()
78 {
79     extern v3_cpu_arch_t v3_mach_type;
80
81     return (v3_mach_type==V3_VMX_EPT_CPU || v3_mach_type==V3_VMX_EPT_UG_CPU);
82 }
83
84 static inline int is_svm_nested()
85 {
86     extern v3_cpu_arch_t v3_mach_type;
87
88     return (v3_mach_type==V3_SVM_REV3_CPU);
89 }
90
91
92 struct passthrough_event_callback {
93     int (*callback)(struct guest_info *core, struct v3_passthrough_pg_event *event, void *priv_data);
94     void *priv_data;
95
96     struct list_head node;
97 };
98
99
100 static int have_passthrough_callbacks(struct guest_info *core)
101 {
102     return !list_empty(&(core->vm_info->passthrough_impl.event_callback_list));
103 }
104
105 static void dispatch_passthrough_event(struct guest_info *core, struct v3_passthrough_pg_event *event)
106 {
107     struct passthrough_event_callback *cb,*temp;
108     
109     list_for_each_entry_safe(cb,
110                              temp,
111                              &(core->vm_info->passthrough_impl.event_callback_list),
112                              node) {
113         cb->callback(core,event,cb->priv_data);
114     }
115 }
116
117 struct nested_event_callback {
118     int (*callback)(struct guest_info *core, struct v3_nested_pg_event *event, void *priv_data);
119     void *priv_data;
120
121     struct list_head node;
122 };
123
124
125 static int have_nested_callbacks(struct guest_info *core)
126 {
127     return !list_empty(&(core->vm_info->nested_impl.event_callback_list));
128 }
129
130 static void dispatch_nested_event(struct guest_info *core, struct v3_nested_pg_event *event)
131 {
132     struct nested_event_callback *cb,*temp;
133     
134     list_for_each_entry_safe(cb,
135                              temp,
136                              &(core->vm_info->nested_impl.event_callback_list),
137                              node) {
138         cb->callback(core,event,cb->priv_data);
139     }
140 }
141
142
143
144
145 static addr_t create_generic_pt_page(struct guest_info *core) {
146     void * page = 0;
147     void *temp;
148
149     temp = V3_AllocPagesExtended(1, PAGE_SIZE_4KB, -1, 0); // no constraints
150
151     if (!temp) {  
152         PrintError(VM_NONE, VCORE_NONE,"Cannot allocate page\n");
153         return 0;
154     }
155
156     page = V3_VAddr(temp);
157     memset(page, 0, PAGE_SIZE);
158
159     return (addr_t)page;
160 }
161
162 // Inline handler functions for each cpu mode
163 #include "vmm_direct_paging_32.h"
164 #include "vmm_direct_paging_32pae.h"
165 #include "vmm_direct_paging_64.h"
166
167
168
169 int v3_init_passthrough_pts(struct guest_info * info) {
170     info->direct_map_pt = (addr_t)V3_PAddr((void *)create_generic_pt_page(info));
171     return 0;
172 }
173
174
175 int v3_free_passthrough_pts(struct guest_info * core) {
176     v3_cpu_mode_t mode = v3_get_vm_cpu_mode(core);
177
178     // Delete the old direct map page tables
179     switch(mode) {
180         case REAL:
181         case PROTECTED:
182           // Intentional fallthrough here
183           // There are *only* PAE tables
184         case PROTECTED_PAE:
185         case LONG:
186         case LONG_32_COMPAT:
187             // Long mode will only use 32PAE page tables...
188             delete_page_tables_32pae((pdpe32pae_t *)V3_VAddr((void *)(core->direct_map_pt)));
189             break;
190         default:
191             PrintError(core->vm_info, core, "Unknown CPU Mode\n");
192             return -1;
193             break;
194     }
195
196     return 0;
197 }
198
199
200 int v3_reset_passthrough_pts(struct guest_info * core) {
201
202     v3_free_passthrough_pts(core);
203
204     // create new direct map page table
205     v3_init_passthrough_pts(core);
206     
207     return 0;
208 }
209
210
211
212 int v3_activate_passthrough_pt(struct guest_info * info) {
213     // For now... But we need to change this....
214     // As soon as shadow paging becomes active the passthrough tables are hosed
215     // So this will cause chaos if it is called at that time
216
217     if (have_passthrough_callbacks(info)) { 
218         struct v3_passthrough_pg_event event={PASSTHROUGH_ACTIVATE,PASSTHROUGH_PREIMPL,0,{0,0,0,0,0,0},0,0};
219         dispatch_passthrough_event(info,&event);
220     }
221         
222     struct cr3_32_PAE * shadow_cr3 = (struct cr3_32_PAE *) &(info->ctrl_regs.cr3);
223     struct cr4_32 * shadow_cr4 = (struct cr4_32 *) &(info->ctrl_regs.cr4);
224     addr_t shadow_pt_addr = *(addr_t*)&(info->direct_map_pt);
225     // Passthrough PTs will only be PAE page tables.
226     shadow_cr3->pdpt_base_addr = shadow_pt_addr >> 5;
227     shadow_cr4->pae = 1;
228     PrintDebug(info->vm_info, info, "Activated Passthrough Page tables\n");
229
230     if (have_passthrough_callbacks(info)) { 
231         struct v3_passthrough_pg_event event={PASSTHROUGH_ACTIVATE,PASSTHROUGH_POSTIMPL,0,{0,0,0,0,0,0},0,0};
232         dispatch_passthrough_event(info,&event);
233     }
234
235     return 0;
236 }
237
238
239
240 int v3_handle_passthrough_pagefault(struct guest_info * info, addr_t fault_addr, pf_error_t error_code,
241                                     addr_t *actual_start, addr_t *actual_end) {
242     v3_cpu_mode_t mode = v3_get_vm_cpu_mode(info);
243     addr_t start, end;
244     int rc;
245
246     if (have_passthrough_callbacks(info)) {                                    
247         struct v3_passthrough_pg_event event={PASSTHROUGH_PAGEFAULT,PASSTHROUGH_PREIMPL,fault_addr,error_code,fault_addr,fault_addr};
248         dispatch_passthrough_event(info,&event);        
249     }
250
251     if (!actual_start) { actual_start=&start; }
252     if (!actual_end) { actual_end=&end; }
253
254
255     rc=-1;
256
257     switch(mode) {
258         case REAL:
259         case PROTECTED:
260           // Note intentional fallthrough here
261           // There are only PAE page tables now
262         case PROTECTED_PAE:
263         case LONG:
264         case LONG_32_COMPAT:
265             // Long mode will only use 32PAE page tables...
266             rc=handle_passthrough_pagefault_32pae(info, fault_addr, error_code, actual_start, actual_end);
267
268         default:
269             PrintError(info->vm_info, info, "Unknown CPU Mode\n");
270             break;
271     }
272
273     if (have_passthrough_callbacks(info)) {                                    
274         struct v3_passthrough_pg_event event={PASSTHROUGH_PAGEFAULT,PASSTHROUGH_POSTIMPL,fault_addr,error_code,*actual_start,*actual_end};
275         dispatch_passthrough_event(info,&event);        
276     }
277
278     return rc;
279 }
280
281
282
283 int v3_invalidate_passthrough_addr(struct guest_info * info, addr_t inv_addr, 
284                                    addr_t *actual_start, addr_t *actual_end) {
285
286     v3_cpu_mode_t mode = v3_get_vm_cpu_mode(info);
287     addr_t start, end;
288     int rc;
289
290     if (have_passthrough_callbacks(info)) {                                    
291         struct v3_passthrough_pg_event event={PASSTHROUGH_INVALIDATE_RANGE,PASSTHROUGH_PREIMPL,0,{0,0,0,0,0,0},PAGE_ADDR(inv_addr),PAGE_ADDR(inv_addr)+PAGE_SIZE-1};
292         dispatch_passthrough_event(info,&event);        
293     }
294
295     if (!actual_start) { actual_start=&start;}
296     if (!actual_end) { actual_end=&end;}
297
298
299
300     rc=-1;
301
302     switch(mode) {
303         case REAL:
304         case PROTECTED:
305           // Intentional fallthrough - there
306           // are only PAE page tables now
307         case PROTECTED_PAE:
308         case LONG:
309         case LONG_32_COMPAT:
310             // Long mode will only use 32PAE page tables...
311           rc=invalidate_addr_32pae(info, inv_addr, actual_start, actual_end);
312
313         default:
314             PrintError(info->vm_info, info, "Unknown CPU Mode\n");
315             break;
316     }
317
318     if (have_passthrough_callbacks(info)) {                                    
319         struct v3_passthrough_pg_event event={PASSTHROUGH_INVALIDATE_RANGE,PASSTHROUGH_POSTIMPL,0,{0,0,0,0,0,0},*actual_start,*actual_end};
320         dispatch_passthrough_event(info,&event);        
321     }
322
323
324     return rc;
325 }
326
327
328 int v3_invalidate_passthrough_addr_range(struct guest_info * info, 
329                                          addr_t inv_addr_start, addr_t inv_addr_end,
330                                          addr_t *actual_start, addr_t *actual_end) {
331     v3_cpu_mode_t mode = v3_get_vm_cpu_mode(info);
332     addr_t start, end;
333     int rc;
334
335     if (!actual_start) { actual_start=&start;}
336     if (!actual_end) { actual_end=&end;}
337
338     if (have_passthrough_callbacks(info)) {                                    
339         struct v3_passthrough_pg_event event={PASSTHROUGH_INVALIDATE_RANGE,PASSTHROUGH_PREIMPL,0,{0,0,0,0,0,0},PAGE_ADDR(inv_addr_start),PAGE_ADDR(inv_addr_end-1)+PAGE_SIZE-1};
340         dispatch_passthrough_event(info,&event);        
341     }
342     
343     rc=-1;
344
345     switch(mode) {
346         case REAL:
347         case PROTECTED:
348           // Intentional fallthrough
349           // There are only PAE PTs now
350         case PROTECTED_PAE:
351         case LONG:
352         case LONG_32_COMPAT:
353             // Long mode will only use 32PAE page tables...
354           rc=invalidate_addr_32pae_range(info, inv_addr_start, inv_addr_end, actual_start, actual_end);
355
356         default:
357             PrintError(info->vm_info, info, "Unknown CPU Mode\n");
358             break;
359     }
360
361     if (have_passthrough_callbacks(info)) {                                    
362         struct v3_passthrough_pg_event event={PASSTHROUGH_INVALIDATE_RANGE,PASSTHROUGH_POSTIMPL,0,{0,0,0,0,0,0},*actual_start,*actual_end};
363         dispatch_passthrough_event(info,&event);        
364     }
365
366     return rc;
367 }
368
369
370 int v3_init_passthrough_paging(struct v3_vm_info *vm)
371 {
372   INIT_LIST_HEAD(&(vm->passthrough_impl.event_callback_list));
373   return 0;
374 }
375
376 int v3_deinit_passthrough_paging(struct v3_vm_info *vm)
377 {
378   struct passthrough_event_callback *cb,*temp;
379   
380   list_for_each_entry_safe(cb,
381                            temp,
382                            &(vm->passthrough_impl.event_callback_list),
383                            node) {
384     list_del(&(cb->node));
385     V3_Free(cb);
386   }
387   
388   return 0;
389 }
390
391 int v3_init_passthrough_paging_core(struct guest_info *core)
392 {
393   // currently nothing to init
394   return 0;
395 }
396
397 int v3_deinit_passthrough_paging_core(struct guest_info *core)
398 {
399   // currently nothing to deinit
400   return 0;
401 }
402
403
404 // inline nested paging support for Intel and AMD
405 #include "svm_npt.h"
406 #include "vmx_npt.h"
407
408
409 inline void convert_to_pf_error(void *pfinfo, pf_error_t *out)
410 {
411   if (is_vmx_nested()) {
412 #ifdef V3_CONFIG_VMX
413     ept_exit_qual_to_pf_error((struct ept_exit_qual *)pfinfo, out);
414 #endif
415   } else {
416     *out = *(pf_error_t *)pfinfo;
417   }
418 }
419
420 int v3_handle_nested_pagefault(struct guest_info * info, addr_t fault_addr, void *pfinfo, addr_t *actual_start, addr_t *actual_end)
421 {
422   int rc;
423   pf_error_t err;
424   addr_t start, end;
425
426   if (!actual_start) { actual_start=&start; }
427   if (!actual_end) { actual_end=&end; }
428
429   convert_to_pf_error(pfinfo,&err);
430
431   if (have_nested_callbacks(info)) {                                   
432       struct v3_nested_pg_event event={NESTED_PAGEFAULT,NESTED_PREIMPL,fault_addr,err,fault_addr,fault_addr};
433       dispatch_nested_event(info,&event);       
434   }
435
436   
437   if (is_vmx_nested()) { 
438     rc = handle_vmx_nested_pagefault(info,fault_addr,pfinfo,actual_start,actual_end);
439   } else {
440     rc = handle_svm_nested_pagefault(info,fault_addr,pfinfo,actual_start,actual_end);
441   }
442   
443   if (have_nested_callbacks(info)) {
444     struct v3_nested_pg_event event={NESTED_PAGEFAULT,NESTED_POSTIMPL,fault_addr,err,*actual_start,*actual_end};
445     dispatch_nested_event(info,&event);
446   }
447   
448   return rc;
449 }
450   
451
452
453 int v3_invalidate_nested_addr(struct guest_info * info, addr_t inv_addr,
454                               addr_t *actual_start, addr_t *actual_end) 
455 {
456   int rc;
457   
458   addr_t start, end;
459
460   if (!actual_start) { actual_start=&start; }
461   if (!actual_end) { actual_end=&end; }
462   
463
464   if (have_nested_callbacks(info)) { 
465     struct v3_nested_pg_event event={NESTED_INVALIDATE_RANGE,NESTED_PREIMPL,0,{0,0,0,0,0,0},PAGE_ADDR(inv_addr),PAGE_ADDR(inv_addr)+PAGE_SIZE-1};
466     dispatch_nested_event(info,&event);
467   }
468
469   if (is_vmx_nested()) {
470     rc = handle_vmx_invalidate_nested_addr(info, inv_addr, actual_start, actual_end);
471   } else {
472     rc = handle_svm_invalidate_nested_addr(info, inv_addr, actual_start, actual_end);
473   }
474   
475   if (have_nested_callbacks(info)) { 
476     struct v3_nested_pg_event event={NESTED_INVALIDATE_RANGE,NESTED_POSTIMPL,0,{0,0,0,0,0,0},*actual_start, *actual_end};
477     dispatch_nested_event(info,&event);
478   }
479   return rc;
480 }
481
482
483 int v3_invalidate_nested_addr_range(struct guest_info * info, 
484                                     addr_t inv_addr_start, addr_t inv_addr_end,
485                                     addr_t *actual_start, addr_t *actual_end) 
486 {
487   int rc;
488
489   addr_t start, end;
490
491   if (!actual_start) { actual_start=&start; }
492   if (!actual_end) { actual_end=&end; }
493
494   if (have_nested_callbacks(info)) { 
495     struct v3_nested_pg_event event={NESTED_INVALIDATE_RANGE,NESTED_PREIMPL,0,{0,0,0,0,0,0},PAGE_ADDR(inv_addr_start),PAGE_ADDR(inv_addr_end-1)+PAGE_SIZE-1};
496     dispatch_nested_event(info,&event);
497   }
498   
499   if (is_vmx_nested()) {
500     rc = handle_vmx_invalidate_nested_addr_range(info, inv_addr_start, inv_addr_end, actual_start, actual_end);
501   } else {
502     rc = handle_svm_invalidate_nested_addr_range(info, inv_addr_start, inv_addr_end, actual_start, actual_end);
503   }
504   
505
506   if (have_nested_callbacks(info)) { 
507     struct v3_nested_pg_event event={NESTED_INVALIDATE_RANGE,NESTED_PREIMPL,0,{0,0,0,0,0,0},*actual_start, *actual_end};
508     dispatch_nested_event(info,&event);
509   }
510   
511   return rc;
512   
513 }
514
515
516 int v3_init_nested_paging(struct v3_vm_info *vm)
517 {
518   INIT_LIST_HEAD(&(vm->nested_impl.event_callback_list));
519   return 0;
520 }
521
522 int v3_init_nested_paging_core(struct guest_info *core, void *hwinfo)
523 {
524   if (is_vmx_nested()) { 
525     return init_ept(core, (struct vmx_hw_info *) hwinfo);
526   } else {
527     // no initialization for SVM
528     return 0;
529   }
530 }
531     
532 int v3_deinit_nested_paging(struct v3_vm_info *vm)
533 {
534   struct nested_event_callback *cb,*temp;
535   
536   list_for_each_entry_safe(cb,
537                            temp,
538                            &(vm->nested_impl.event_callback_list),
539                            node) {
540     list_del(&(cb->node));
541     V3_Free(cb);
542   }
543   
544   return 0;
545 }
546
547 int v3_deinit_nested_paging_core(struct guest_info *core)
548 {
549   // nothing to do..  probably dealloc?  FIXME PAD
550
551   return 0;
552 }