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.
6 * The V3VEE Project is a joint project between Northwestern University
7 * and the University of New Mexico. You can find out more at
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.
15 * Author: Steven Jaconette <stevenjaconette2007@u.northwestern.edu>
16 * Peter Dinda <pdinda@northwestern.edu> (refactor + events)
18 * This is free software. You are permitted to use,
19 * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
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>
30 #if !defined(V3_CONFIG_DEBUG_NESTED_PAGING) && !defined(V3_CONFIG_DEBUG_SHADOW_PAGING)
32 #define PrintDebug(fmt, args...)
39 "Direct Paging" combines these three functionalities:
41 1. Passthrough paging for SVM and VMX
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.
51 Passthrough page tables are built using 32PAE paging.
54 2. Nested paging on SVM
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.
64 3. Nested paging on VMX
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.
77 static inline int is_vmx_nested()
79 extern v3_cpu_arch_t v3_mach_type;
81 return (v3_mach_type==V3_VMX_EPT_CPU || v3_mach_type==V3_VMX_EPT_UG_CPU);
84 static inline int is_svm_nested()
86 extern v3_cpu_arch_t v3_mach_type;
88 return (v3_mach_type==V3_SVM_REV3_CPU);
92 struct passthrough_event_callback {
93 int (*callback)(struct guest_info *core, struct v3_passthrough_pg_event *event, void *priv_data);
96 struct list_head node;
100 static int have_passthrough_callbacks(struct guest_info *core)
102 return !list_empty(&(core->vm_info->passthrough_impl.event_callback_list));
105 static void dispatch_passthrough_event(struct guest_info *core, struct v3_passthrough_pg_event *event)
107 struct passthrough_event_callback *cb,*temp;
109 list_for_each_entry_safe(cb,
111 &(core->vm_info->passthrough_impl.event_callback_list),
113 cb->callback(core,event,cb->priv_data);
117 struct nested_event_callback {
118 int (*callback)(struct guest_info *core, struct v3_nested_pg_event *event, void *priv_data);
121 struct list_head node;
125 static int have_nested_callbacks(struct guest_info *core)
127 return !list_empty(&(core->vm_info->nested_impl.event_callback_list));
130 static void dispatch_nested_event(struct guest_info *core, struct v3_nested_pg_event *event)
132 struct nested_event_callback *cb,*temp;
134 list_for_each_entry_safe(cb,
136 &(core->vm_info->nested_impl.event_callback_list),
138 cb->callback(core,event,cb->priv_data);
145 static addr_t create_generic_pt_page(struct guest_info *core) {
149 temp = V3_AllocPagesExtended(1, PAGE_SIZE_4KB, -1, 0); // no constraints
152 PrintError(VM_NONE, VCORE_NONE,"Cannot allocate page\n");
156 page = V3_VAddr(temp);
157 memset(page, 0, PAGE_SIZE);
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"
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));
175 int v3_free_passthrough_pts(struct guest_info * core) {
176 v3_cpu_mode_t mode = v3_get_vm_cpu_mode(core);
178 // Delete the old direct map page tables
182 // Intentional fallthrough here
183 // There are *only* PAE tables
187 // Long mode will only use 32PAE page tables...
188 delete_page_tables_32pae((pdpe32pae_t *)V3_VAddr((void *)(core->direct_map_pt)));
191 PrintError(core->vm_info, core, "Unknown CPU Mode\n");
200 int v3_reset_passthrough_pts(struct guest_info * core) {
202 v3_free_passthrough_pts(core);
204 // create new direct map page table
205 v3_init_passthrough_pts(core);
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
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);
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;
228 PrintDebug(info->vm_info, info, "Activated Passthrough Page tables\n");
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);
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);
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);
251 if (!actual_start) { actual_start=&start; }
252 if (!actual_end) { actual_end=&end; }
260 // Note intentional fallthrough here
261 // There are only PAE page tables now
265 // Long mode will only use 32PAE page tables...
266 rc=handle_passthrough_pagefault_32pae(info, fault_addr, error_code, actual_start, actual_end);
269 PrintError(info->vm_info, info, "Unknown CPU Mode\n");
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);
283 int v3_invalidate_passthrough_addr(struct guest_info * info, addr_t inv_addr,
284 addr_t *actual_start, addr_t *actual_end) {
286 v3_cpu_mode_t mode = v3_get_vm_cpu_mode(info);
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);
295 if (!actual_start) { actual_start=&start;}
296 if (!actual_end) { actual_end=&end;}
305 // Intentional fallthrough - there
306 // are only PAE page tables now
310 // Long mode will only use 32PAE page tables...
311 rc=invalidate_addr_32pae(info, inv_addr, actual_start, actual_end);
314 PrintError(info->vm_info, info, "Unknown CPU Mode\n");
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);
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);
335 if (!actual_start) { actual_start=&start;}
336 if (!actual_end) { actual_end=&end;}
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);
348 // Intentional fallthrough
349 // There are only PAE PTs now
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);
357 PrintError(info->vm_info, info, "Unknown CPU Mode\n");
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);
370 int v3_init_passthrough_paging(struct v3_vm_info *vm)
372 INIT_LIST_HEAD(&(vm->passthrough_impl.event_callback_list));
376 int v3_deinit_passthrough_paging(struct v3_vm_info *vm)
378 struct passthrough_event_callback *cb,*temp;
380 list_for_each_entry_safe(cb,
382 &(vm->passthrough_impl.event_callback_list),
384 list_del(&(cb->node));
391 int v3_init_passthrough_paging_core(struct guest_info *core)
393 // currently nothing to init
397 int v3_deinit_passthrough_paging_core(struct guest_info *core)
399 // currently nothing to deinit
404 int v3_register_passthrough_paging_event_callback(struct v3_vm_info *vm,
405 int (*callback)(struct guest_info *core,
406 struct v3_passthrough_pg_event *,
410 struct passthrough_event_callback *ec = V3_Malloc(sizeof(struct passthrough_event_callback));
413 PrintError(vm, VCORE_NONE, "Unable to allocate for a nested paging event callback\n");
417 ec->callback = callback;
418 ec->priv_data = priv_data;
420 list_add(&(ec->node),&(vm->passthrough_impl.event_callback_list));
428 int v3_unregister_passthrough_paging_event_callback(struct v3_vm_info *vm,
429 int (*callback)(struct guest_info *core,
430 struct v3_passthrough_pg_event *,
434 struct passthrough_event_callback *cb,*temp;
436 list_for_each_entry_safe(cb,
438 &(vm->passthrough_impl.event_callback_list),
440 if ((callback == cb->callback) && (priv_data == cb->priv_data)) {
441 list_del(&(cb->node));
447 PrintError(vm, VCORE_NONE, "No callback found!\n");
453 // inline nested paging support for Intel and AMD
458 inline void convert_to_pf_error(void *pfinfo, pf_error_t *out)
460 if (is_vmx_nested()) {
462 ept_exit_qual_to_pf_error((struct ept_exit_qual *)pfinfo, out);
465 *out = *(pf_error_t *)pfinfo;
469 int v3_handle_nested_pagefault(struct guest_info * info, addr_t fault_addr, void *pfinfo, addr_t *actual_start, addr_t *actual_end)
475 if (!actual_start) { actual_start=&start; }
476 if (!actual_end) { actual_end=&end; }
478 convert_to_pf_error(pfinfo,&err);
480 if (have_nested_callbacks(info)) {
481 struct v3_nested_pg_event event={NESTED_PAGEFAULT,NESTED_PREIMPL,fault_addr,err,fault_addr,fault_addr};
482 dispatch_nested_event(info,&event);
486 if (is_vmx_nested()) {
487 rc = handle_vmx_nested_pagefault(info,fault_addr,pfinfo,actual_start,actual_end);
489 rc = handle_svm_nested_pagefault(info,fault_addr,pfinfo,actual_start,actual_end);
492 if (have_nested_callbacks(info)) {
493 struct v3_nested_pg_event event={NESTED_PAGEFAULT,NESTED_POSTIMPL,fault_addr,err,*actual_start,*actual_end};
494 dispatch_nested_event(info,&event);
502 int v3_invalidate_nested_addr(struct guest_info * info, addr_t inv_addr,
503 addr_t *actual_start, addr_t *actual_end)
509 if (!actual_start) { actual_start=&start; }
510 if (!actual_end) { actual_end=&end; }
513 if (have_nested_callbacks(info)) {
514 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};
515 dispatch_nested_event(info,&event);
518 if (is_vmx_nested()) {
519 rc = handle_vmx_invalidate_nested_addr(info, inv_addr, actual_start, actual_end);
521 rc = handle_svm_invalidate_nested_addr(info, inv_addr, actual_start, actual_end);
524 if (have_nested_callbacks(info)) {
525 struct v3_nested_pg_event event={NESTED_INVALIDATE_RANGE,NESTED_POSTIMPL,0,{0,0,0,0,0,0},*actual_start, *actual_end};
526 dispatch_nested_event(info,&event);
532 int v3_invalidate_nested_addr_range(struct guest_info * info,
533 addr_t inv_addr_start, addr_t inv_addr_end,
534 addr_t *actual_start, addr_t *actual_end)
540 if (!actual_start) { actual_start=&start; }
541 if (!actual_end) { actual_end=&end; }
543 if (have_nested_callbacks(info)) {
544 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};
545 dispatch_nested_event(info,&event);
548 if (is_vmx_nested()) {
549 rc = handle_vmx_invalidate_nested_addr_range(info, inv_addr_start, inv_addr_end, actual_start, actual_end);
551 rc = handle_svm_invalidate_nested_addr_range(info, inv_addr_start, inv_addr_end, actual_start, actual_end);
555 if (have_nested_callbacks(info)) {
556 struct v3_nested_pg_event event={NESTED_INVALIDATE_RANGE,NESTED_PREIMPL,0,{0,0,0,0,0,0},*actual_start, *actual_end};
557 dispatch_nested_event(info,&event);
565 int v3_init_nested_paging(struct v3_vm_info *vm)
567 INIT_LIST_HEAD(&(vm->nested_impl.event_callback_list));
571 int v3_init_nested_paging_core(struct guest_info *core, void *hwinfo)
573 if (is_vmx_nested()) {
574 return init_ept(core, (struct vmx_hw_info *) hwinfo);
576 // no initialization for SVM
581 int v3_deinit_nested_paging(struct v3_vm_info *vm)
583 struct nested_event_callback *cb,*temp;
585 list_for_each_entry_safe(cb,
587 &(vm->nested_impl.event_callback_list),
589 list_del(&(cb->node));
596 int v3_deinit_nested_paging_core(struct guest_info *core)
598 // nothing to do.. probably dealloc? FIXME PAD
604 int v3_register_nested_paging_event_callback(struct v3_vm_info *vm,
605 int (*callback)(struct guest_info *core,
606 struct v3_nested_pg_event *,
610 struct nested_event_callback *ec = V3_Malloc(sizeof(struct nested_event_callback));
613 PrintError(vm, VCORE_NONE, "Unable to allocate for a nested paging event callback\n");
617 ec->callback = callback;
618 ec->priv_data = priv_data;
620 list_add(&(ec->node),&(vm->nested_impl.event_callback_list));
628 int v3_unregister_nested_paging_event_callback(struct v3_vm_info *vm,
629 int (*callback)(struct guest_info *core,
630 struct v3_nested_pg_event *,
634 struct nested_event_callback *cb,*temp;
636 list_for_each_entry_safe(cb,
638 &(vm->nested_impl.event_callback_list),
640 if ((callback == cb->callback) && (priv_data == cb->priv_data)) {
641 list_del(&(cb->node));
647 PrintError(vm, VCORE_NONE, "No callback found!\n");