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.
[palacios.git] / palacios / src / palacios / vmm_ctrl_regs.c
index 6f99e39..3616ae8 100644 (file)
@@ -24,8 +24,9 @@
 #include <palacios/vm_guest_mem.h>
 #include <palacios/vmm_ctrl_regs.h>
 #include <palacios/vmm_direct_paging.h>
+#include <palacios/svm.h>
 
-#ifndef DEBUG_CTRL_REGS
+#ifndef V3_CONFIG_DEBUG_CTRL_REGS
 #undef PrintDebug
 #define PrintDebug(fmt, args...)
 #endif
@@ -44,9 +45,9 @@ int v3_handle_cr0_write(struct guest_info * info) {
     struct x86_instr dec_instr;
     
     if (info->mem_mode == PHYSICAL_MEM) { 
-       ret = read_guest_pa_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
+       ret = v3_read_gpa_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
     } else { 
-       ret = read_guest_va_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
+       ret = v3_read_gva_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
     }
     
     if (v3_decode(info, (addr_t)instr, &dec_instr) == -1) {
@@ -107,20 +108,25 @@ static int handle_mov_to_cr0(struct guest_info * info, struct x86_instr * dec_in
     *guest_cr0 = *new_cr0;
     
     // This value must always be set to 1 
-    guest_cr0->et = 1;    
+    guest_cr0->et = 1;
     
     // Set the shadow register to catch non-virtualized flags
     *shadow_cr0 = *guest_cr0;
     
     // Paging is always enabled
-    shadow_cr0->pg = 1;  
+    shadow_cr0->pg = 1;
+
+    if (guest_cr0->pg == 0) {
+       // If paging is not enabled by the guest, then we always enable write-protect to catch memory hooks
+       shadow_cr0->wp = 1;
+    }
     
     // Was there a paging transition
     // Meaning we need to change the page tables
     if (paging_transition) {
-       if (v3_get_mem_mode(info) == VIRTUAL_MEM) {
+       if (v3_get_vm_mem_mode(info) == VIRTUAL_MEM) {
            
-           struct efer_64 * guest_efer  = (struct efer_64 *)&(info->guest_efer);
+           struct efer_64 * guest_efer  = (struct efer_64 *)&(info->shdw_pg_state.guest_efer);
            struct efer_64 * shadow_efer = (struct efer_64 *)&(info->ctrl_regs.efer);
            
            // Check long mode LME to set LME
@@ -140,7 +146,9 @@ static int handle_mov_to_cr0(struct guest_info * info, struct x86_instr * dec_in
                PrintError("Failed to activate shadow page tables\n");
                return -1;
            }
-       } else  {
+       } else {
+
+           shadow_cr0->wp = 1;
            
            if (v3_activate_passthrough_pt(info) == -1) {
                PrintError("Failed to activate passthrough page tables\n");
@@ -217,9 +225,9 @@ int v3_handle_cr0_read(struct guest_info * info) {
     struct x86_instr dec_instr;
     
     if (info->mem_mode == PHYSICAL_MEM) { 
-       ret = read_guest_pa_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
+       ret = v3_read_gpa_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
     } else { 
-       ret = read_guest_va_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
+       ret = v3_read_gva_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
     }
     
     
@@ -231,8 +239,8 @@ int v3_handle_cr0_read(struct guest_info * info) {
     if (dec_instr.op_type == V3_OP_MOVCR2) {
        PrintDebug("MOVCR2 (mode=%s)\n", v3_cpu_mode_to_str(info->cpu_mode));
 
-       if ((v3_get_cpu_mode(info) == LONG) || 
-           (v3_get_cpu_mode(info) == LONG_32_COMPAT)) {
+       if ((v3_get_vm_cpu_mode(info) == LONG) || 
+           (v3_get_vm_cpu_mode(info) == LONG_32_COMPAT)) {
            struct cr0_64 * dst_reg = (struct cr0_64 *)(dec_instr.dst_operand.operand);
        
            if (info->shdw_pg_mode == SHADOW_PAGING) {
@@ -291,9 +299,9 @@ int v3_handle_cr3_write(struct guest_info * info) {
     struct x86_instr dec_instr;
     
     if (info->mem_mode == PHYSICAL_MEM) { 
-       ret = read_guest_pa_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
+       ret = v3_read_gpa_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
     } else { 
-       ret = read_guest_va_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
+       ret = v3_read_gva_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
     }
     
     if (v3_decode(info, (addr_t)instr, &dec_instr) == -1) {
@@ -320,14 +328,15 @@ int v3_handle_cr3_write(struct guest_info * info) {
                struct cr3_32 * guest_cr3 = (struct cr3_32 *)&(info->shdw_pg_state.guest_cr3);
                *guest_cr3 = *new_cr3;
            }
-           
+
+
            // If Paging is enabled in the guest then we need to change the shadow page tables
            if (info->mem_mode == VIRTUAL_MEM) {
                if (v3_activate_shadow_pt(info) == -1) {
                    PrintError("Failed to activate 32 bit shadow page table\n");
                    return -1;
                }
-           } 
+           }
            
            PrintDebug("New Shadow CR3=%p; New Guest CR3=%p\n", 
                       (void *)(addr_t)(info->ctrl_regs.cr3), 
@@ -367,9 +376,9 @@ int v3_handle_cr3_read(struct guest_info * info) {
     struct x86_instr dec_instr;
     
     if (info->mem_mode == PHYSICAL_MEM) { 
-       ret = read_guest_pa_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
+       ret = v3_read_gpa_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
     } else { 
-       ret = read_guest_va_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
+       ret = v3_read_gva_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
     }
     
     if (v3_decode(info, (addr_t)instr, &dec_instr) == -1) {
@@ -382,8 +391,8 @@ int v3_handle_cr3_read(struct guest_info * info) {
        
        if (info->shdw_pg_mode == SHADOW_PAGING) {
            
-           if ((v3_get_cpu_mode(info) == LONG) || 
-               (v3_get_cpu_mode(info) == LONG_32_COMPAT)) {
+           if ((v3_get_vm_cpu_mode(info) == LONG) || 
+               (v3_get_vm_cpu_mode(info) == LONG_32_COMPAT)) {
                struct cr3_64 * dst_reg = (struct cr3_64 *)(dec_instr.dst_operand.operand);
                struct cr3_64 * guest_cr3 = (struct cr3_64 *)&(info->shdw_pg_state.guest_cr3);
                *dst_reg = *guest_cr3;
@@ -396,8 +405,8 @@ int v3_handle_cr3_read(struct guest_info * info) {
        } else if (info->shdw_pg_mode == NESTED_PAGING) {
            
            // This is just a passthrough operation which we probably don't need here
-           if ((v3_get_cpu_mode(info) == LONG) || 
-               (v3_get_cpu_mode(info) == LONG_32_COMPAT)) {
+           if ((v3_get_vm_cpu_mode(info) == LONG) || 
+               (v3_get_vm_cpu_mode(info) == LONG_32_COMPAT)) {
                struct cr3_64 * dst_reg = (struct cr3_64 *)(dec_instr.dst_operand.operand);
                struct cr3_64 * guest_cr3 = (struct cr3_64 *)&(info->ctrl_regs.cr3);
                *dst_reg = *guest_cr3;
@@ -431,12 +440,12 @@ int v3_handle_cr4_write(struct guest_info * info) {
     int ret;
     int flush_tlb=0;
     struct x86_instr dec_instr;
-    v3_cpu_mode_t cpu_mode = v3_get_cpu_mode(info);
+    v3_cpu_mode_t cpu_mode = v3_get_vm_cpu_mode(info);
     
     if (info->mem_mode == PHYSICAL_MEM) { 
-       ret = read_guest_pa_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
+       ret = v3_read_gpa_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
     } else { 
-       ret = read_guest_va_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
+       ret = v3_read_gva_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
     }
     
     if (v3_decode(info, (addr_t)instr, &dec_instr) == -1) {
@@ -451,7 +460,7 @@ int v3_handle_cr4_write(struct guest_info * info) {
     
     // Check to see if we need to flush the tlb
     
-    if (v3_get_mem_mode(info) == VIRTUAL_MEM) { 
+    if (v3_get_vm_mem_mode(info) == VIRTUAL_MEM) { 
        struct cr4_32 * new_cr4 = (struct cr4_32 *)(dec_instr.src_operand.operand);
        struct cr4_32 * cr4 = (struct cr4_32 *)&(info->ctrl_regs.cr4);
        
@@ -465,7 +474,7 @@ int v3_handle_cr4_write(struct guest_info * info) {
            (cr4->pge != new_cr4->pge) || 
            (cr4->pae != new_cr4->pae)) { 
            PrintDebug("Handling PSE/PGE/PAE -> TLBFlush case, flag set\n");
-           flush_tlb=1;
+           flush_tlb = 1;
            
        }
     }
@@ -479,7 +488,7 @@ int v3_handle_cr4_write(struct guest_info * info) {
        PrintDebug("Old CR4=%x\n", *(uint_t *)cr4);
        
        if ((info->shdw_pg_mode == SHADOW_PAGING)) { 
-           if (v3_get_mem_mode(info) == PHYSICAL_MEM) {
+           if (v3_get_vm_mem_mode(info) == PHYSICAL_MEM) {
                
                if ((cr4->pae == 0) && (new_cr4->pae == 1)) {
                    PrintDebug("Creating PAE passthrough tables\n");
@@ -495,6 +504,7 @@ int v3_handle_cr4_write(struct guest_info * info) {
                    
                } else if ((cr4->pae == 1) && (new_cr4->pae == 0)) {
                    // Create passthrough standard 32bit pagetables
+                   PrintError("Switching From PAE to Protected mode not supported\n");
                    return -1;
                } 
            }
@@ -512,7 +522,7 @@ int v3_handle_cr4_write(struct guest_info * info) {
        
        if (new_cr4->pae == 0) {
            // cannot turn off PAE in long mode GPF the guest
-           PrintError("Cannot disable PAE in long mode, sending GPF\n");
+           PrintError("Cannot disable PAE in long mode, should send GPF\n");
            return -1;
        }
        
@@ -538,37 +548,71 @@ int v3_handle_cr4_write(struct guest_info * info) {
 }
 
 
-int v3_handle_efer_read(uint_t msr, struct v3_msr * dst, void * priv_data) {
-    struct guest_info * info = (struct guest_info *)(priv_data);
-    PrintDebug("EFER Read HI=%x LO=%x\n", info->guest_efer.hi, info->guest_efer.lo);
+int v3_handle_efer_read(struct guest_info * core, uint_t msr, struct v3_msr * dst, void * priv_data) {
+    PrintDebug("EFER Read HI=%x LO=%x\n", core->shdw_pg_state.guest_efer.hi, core->shdw_pg_state.guest_efer.lo);
     
-    dst->value = info->guest_efer.value;
+    dst->value = core->shdw_pg_state.guest_efer.value;
     
-    info->rip += 2; // WRMSR/RDMSR are two byte operands
     return 0;
 }
 
 
-
-// TODO: this is a disaster we need to clean this up...
-int v3_handle_efer_write(uint_t msr, struct v3_msr src, void * priv_data) {
-    struct guest_info * info = (struct guest_info *)(priv_data);
-    //struct efer_64 * new_efer = (struct efer_64 *)&(src.value);
-    struct efer_64 * shadow_efer = (struct efer_64 *)&(info->ctrl_regs.efer);
-    struct v3_msr * guest_efer = &(info->guest_efer);
-    
-    PrintDebug("EFER Write\n");
-    PrintDebug("EFER Write Values: 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;
-    
+int v3_handle_efer_write(struct guest_info * core, uint_t msr, struct v3_msr src, void * priv_data) {
+    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);
     
-    // Enable/Disable Syscall
-    shadow_efer->sce = src.value & 0x1;
-    
-    info->rip += 2; // WRMSR/RDMSR are two byte operands
+    PrintDebug("EFER Write HI=%x LO=%x\n", src.hi, src.lo);
+
+    // 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;
+}
+
+int v3_handle_vm_cr_read(struct guest_info * core, uint_t msr, struct v3_msr * dst, void * priv_data) {
+    /* tell the guest that the BIOS disabled SVM, that way it doesn't get 
+     * confused by the fact that CPUID reports SVM as available but it still
+     * cannot be used 
+     */
+    dst->value = SVM_VM_CR_MSR_lock | SVM_VM_CR_MSR_svmdis;
+    PrintDebug("VM_CR Read HI=%x LO=%x\n", dst->hi, dst->lo);
+    return 0;
+}
+
+int v3_handle_vm_cr_write(struct guest_info * core, uint_t msr, struct v3_msr src, void * priv_data) {
+    PrintDebug("VM_CR Write\n");
+    PrintDebug("VM_CR Write Values: HI=%x LO=%x\n", src.hi, src.lo);
+
+    /* writes to LOCK and SVMDIS are silently ignored (according to the spec), 
+     * other writes indicate the guest wants to use some feature we haven't
+     * implemented
+     */
+    if (src.value & ~(SVM_VM_CR_MSR_lock | SVM_VM_CR_MSR_svmdis)) {
+       PrintDebug("VM_CR write sets unsupported bits: HI=%x LO=%x\n", src.hi, src.lo);
+       return -1;
+    }
     
     return 0;
 }