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.


VMX 64-bit guest support. Add exit handling for CR4 and EFER accesses.
Kevin Pedretti [Thu, 7 Jul 2011 16:09:22 +0000 (10:09 -0600)]
palacios/include/palacios/vmcs.h
palacios/include/palacios/vmx_ctrl_regs.h
palacios/src/palacios/svm.c
palacios/src/palacios/vmcs.c
palacios/src/palacios/vmm_ctrl_regs.c
palacios/src/palacios/vmx.c
palacios/src/palacios/vmx_ctrl_regs.c
palacios/src/palacios/vmx_handler.c

index f8028b5..5d50355 100644 (file)
@@ -36,6 +36,7 @@
 
 /* Control register exit masks */
 #define   CR4_VMXE      0x00002000
+#define   CR4_PAE       0x00000020
 
 
 
index 4a8f0d8..789ff4e 100644 (file)
@@ -31,7 +31,8 @@ int v3_vmx_handle_cr0_access(struct guest_info * info,
                             struct vmx_exit_info * exit_info);
 int v3_vmx_handle_cr3_access(struct guest_info * info, 
                             struct vmx_exit_cr_qual * cr_qual);
-
+int v3_vmx_handle_cr4_access(struct guest_info * info, 
+                            struct vmx_exit_cr_qual * cr_qual);
 
 #endif
 
index bdb9862..f366a76 100644 (file)
@@ -81,6 +81,24 @@ static vmcb_t * Allocate_VMCB() {
 }
 
 
+static int v3_svm_handle_efer_write(struct guest_info * core, uint_t msr, struct v3_msr src, void * priv_data)
+{
+    int status;
+
+    // Call arch-independent handler
+    if ((status = v3_handle_efer_write(core, msr, src, priv_data)) != 0)
+       return status;
+
+    // SVM-specific code
+    if (core->shdw_pg_mode == NESTED_PAGING) {
+       // Ensure that hardware visible EFER.SVME bit is set (SVM Enable)
+       struct efer_64 * hw_efer = (struct efer_64 *)&(core->ctrl_regs.efer);
+       hw_efer->svme = 1;
+    }
+
+    return 0;
+}
+
 
 static void Init_VMCB_BIOS(vmcb_t * vmcb, struct guest_info * core) {
     vmcb_ctrl_t * ctrl_area = GET_VMCB_CTRL_AREA(vmcb);
@@ -221,7 +239,7 @@ static void Init_VMCB_BIOS(vmcb_t * vmcb, struct guest_info * core) {
 
     v3_hook_msr(core->vm_info, EFER_MSR, 
                &v3_handle_efer_read,
-               &v3_handle_efer_write, 
+               &v3_svm_handle_efer_write, 
                core);
 
     if (core->shdw_pg_mode == SHADOW_PAGING) {
index 9e5bd77..2d36a3f 100644 (file)
@@ -260,6 +260,7 @@ int v3_vmx_restore_vmcs(struct guest_info * info) {
 
 #ifdef __V3_64BIT__
     check_vmcs_write(VMCS_GUEST_EFER, info->ctrl_regs.efer);
+    check_vmcs_write(VMCS_ENTRY_CTRLS, vmx_info->entry_ctrls.value);
 #endif
 
 
index d5a8650..3616ae8 100644 (file)
@@ -557,28 +557,37 @@ int v3_handle_efer_read(struct guest_info * core, uint_t msr, struct v3_msr * ds
 }
 
 
-
-// TODO: this is a disaster we need to clean this up...
 int v3_handle_efer_write(struct guest_info * core, uint_t msr, struct v3_msr src, void * priv_data) {
-    //struct efer_64 * new_efer = (struct efer_64 *)&(src.value);
-    struct efer_64 * shadow_efer = (struct efer_64 *)&(core->ctrl_regs.efer);
-    struct v3_msr * guest_efer = &(core->shdw_pg_state.guest_efer);
+    struct v3_msr *  vm_efer     = &(core->shdw_pg_state.guest_efer);
+    struct efer_64 * hw_efer     = (struct efer_64 *)&(core->ctrl_regs.efer);
+    struct efer_64   old_hw_efer = *((struct efer_64 *)&core->ctrl_regs.efer);
     
-    PrintDebug("EFER Write\n");
-    PrintDebug("EFER Write Values: HI=%x LO=%x\n", src.hi, src.lo);
+    PrintDebug("EFER Write HI=%x LO=%x\n", src.hi, src.lo);
 
-    //PrintDebug("Old EFER=%p\n", (void *)*(addr_t*)(shadow_efer));
-    
-    // We virtualize the guests efer to hide the SVME and LMA bits
-    guest_efer->value = src.value;
-    
-    if (core->shdw_pg_mode == SHADOW_PAGING) {
-       // Enable/Disable Syscall
-       shadow_efer->sce = src.value & 0x1;
-    } else if (core->shdw_pg_mode == NESTED_PAGING) {
-       *(uint64_t *)shadow_efer = src.value;
-       shadow_efer->svme = 1;
+    // Set EFER value seen by guest if it reads EFER
+    vm_efer->value = src.value;
+
+    // Set EFER value seen by hardware while the guest is running
+    *(uint64_t *)hw_efer = src.value;
+
+    // Catch unsupported features
+    if ((old_hw_efer.lme == 1) && (hw_efer->lme == 0)) {
+       PrintError("Disabling long mode once it has been enabled is not supported\n");
+       return -1;
     }
+
+    // Set LME and LMA bits seen by hardware
+    if (old_hw_efer.lme == 0) {
+       // Long mode was not previously enabled, so the lme bit cannot
+       // be set yet. It will be set later when the guest sets CR0.PG
+       // to enable paging.
+       hw_efer->lme = 0;
+    } else {
+       // Long mode was previously enabled. Ensure LMA bit is set.
+       // VMX does not automatically set LMA, and this should not affect SVM.
+       hw_efer->lma = 1;
+    }
+
     return 0;
 }
 
index 5963d93..20c0c5f 100644 (file)
@@ -212,23 +212,25 @@ static int init_vmcs_bios(struct guest_info * core, struct vmx_data * vmx_state)
 
 
 #ifdef __V3_64BIT__
+    // Ensure host runs in 64-bit mode at each VM EXIT
     vmx_state->exit_ctrls.host_64_on = 1;
 #endif
 
-
-    /* Not sure how exactly to handle this... */
+    // Hook all accesses to EFER register
     v3_hook_msr(core->vm_info, EFER_MSR, 
                &v3_handle_efer_read,
                &v3_handle_efer_write, 
                core);
 
-    // Or is it this??? 
-    vmx_state->entry_ctrls.ld_efer = 1;
+    // Restore host's EFER register on each VM EXIT
     vmx_state->exit_ctrls.ld_efer = 1;
+
+    // Save/restore guest's EFER register to/from VMCS on VM EXIT/ENTRY
     vmx_state->exit_ctrls.save_efer = 1;
-    /*   ***   */
+    vmx_state->entry_ctrls.ld_efer  = 1;
 
-    vmx_ret |= check_vmcs_write(VMCS_CR4_MASK, CR4_VMXE);
+    // Cause VM_EXIT whenever CR4.VMXE or CR4.PAE bits are written
+    vmx_ret |= check_vmcs_write(VMCS_CR4_MASK, CR4_VMXE | CR4_PAE);
 
 
     /* Setup paging */
index 6bbde36..d83d51f 100644 (file)
@@ -77,6 +77,29 @@ int v3_vmx_handle_cr3_access(struct guest_info * info, struct vmx_exit_cr_qual *
     return -1;
 }
 
+int v3_vmx_handle_cr4_access(struct guest_info * info, struct vmx_exit_cr_qual * cr_qual) {
+    if (cr_qual->access_type < 2) {
+
+       if (cr_qual->access_type == 0) {
+           if (v3_handle_cr4_write(info) != 0) {
+               PrintError("Could not handle CR4 write\n");
+               return -1;
+           }
+           info->ctrl_regs.cr4 |= 0x2000; // no VMX allowed in guest, so mask CR4.VMXE
+       } else {
+           if (v3_handle_cr4_read(info) != 0) {
+               PrintError("Could not handle CR4 read\n");
+               return -1;
+           }
+       }
+
+       return 0;
+    }
+
+    PrintError("Invalid CR4 Access type?? (type=%d)\n", cr_qual->access_type);
+    return -1;
+}
+
 static int handle_mov_to_cr3(struct guest_info * info, v3_reg_t * cr3_reg) {
 
     if (info->shdw_pg_mode == SHADOW_PAGING) {
@@ -196,13 +219,14 @@ static int handle_mov_to_cr0(struct guest_info * info, v3_reg_t * new_cr0, struc
            // Paging transition
            
            if (v3_get_vm_mem_mode(info) == VIRTUAL_MEM) {
-               struct efer_64 * guest_efer = (struct efer_64 *)&(info->ctrl_regs.efer);
+               struct efer_64 * vm_efer = (struct efer_64 *)&(info->shdw_pg_state.guest_efer);
+               struct efer_64 * hw_efer = (struct efer_64 *)&(info->ctrl_regs.efer);
                
-               if (guest_efer->lme == 1) {
+               if (vm_efer->lme) {
                    //     PrintDebug("Enabling long mode\n");
                    
-                   guest_efer->lma = 1;
-                   guest_efer->lme = 1;
+                   hw_efer->lma = 1;
+                   hw_efer->lme = 1;
                    
                    vmx_info->entry_ctrls.guest_ia32e = 1;
                }
index 71c1eab..5af3122 100644 (file)
@@ -224,12 +224,22 @@ int v3_handle_vmx_exit(struct guest_info * info, struct vmx_exit_info * exit_inf
                        return -1;
                    }
                    break;
+               case 4:
+                   //PrintDebug("Handling CR4 Access\n");
+                   if (v3_vmx_handle_cr4_access(info, cr_qual) == -1) {
+                       PrintError("Error in CR4 access handler\n");
+                       return -1;
+                   }
+                   break;
                default:
                    PrintError("Unhandled CR access: %d\n", cr_qual->cr_id);
                    return -1;
            }
            
-           info->rip += exit_info->instr_len;
+           // TODO: move RIP increment into all of the above individual CR
+           //       handlers, not just v3_vmx_handle_cr4_access()
+           if (cr_qual->cr_id != 4)
+               info->rip += exit_info->instr_len;
 
            break;
        }