From 298988e3993ce75455aaaa652dc55d3ee0e384d3 Mon Sep 17 00:00:00 2001 From: Alexander Kudryavtsev Date: Fri, 23 Sep 2011 23:18:17 +0400 Subject: [PATCH 09/32] EPT fixes --- palacios/include/palacios/vmx_ctrl_regs.h | 3 +- palacios/src/palacios/vm_guest.c | 20 +++++- palacios/src/palacios/vmm_ctrl_regs.c | 6 +- palacios/src/palacios/vmx.c | 8 +- palacios/src/palacios/vmx_ctrl_regs.c | 111 ++++++++++++++++++++++++----- palacios/src/palacios/vmx_handler.c | 4 +- 6 files changed, 121 insertions(+), 31 deletions(-) diff --git a/palacios/include/palacios/vmx_ctrl_regs.h b/palacios/include/palacios/vmx_ctrl_regs.h index 789ff4e..aed31de 100644 --- a/palacios/include/palacios/vmx_ctrl_regs.h +++ b/palacios/include/palacios/vmx_ctrl_regs.h @@ -25,6 +25,7 @@ #include struct guest_info; +struct vmx_exit_info; int v3_vmx_handle_cr0_access(struct guest_info * info, struct vmx_exit_cr_qual * cr_qual, @@ -32,7 +33,7 @@ int v3_vmx_handle_cr0_access(struct guest_info * 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); + struct vmx_exit_info * exit_info); #endif diff --git a/palacios/src/palacios/vm_guest.c b/palacios/src/palacios/vm_guest.c index 7bd275f..d7aa301 100644 --- a/palacios/src/palacios/vm_guest.c +++ b/palacios/src/palacios/vm_guest.c @@ -32,6 +32,7 @@ #include #include +extern v3_cpu_arch_t v3_cpu_types[V3_CONFIG_MAX_CPUS]; v3_cpu_mode_t v3_get_vm_cpu_mode(struct guest_info * info) { struct cr0_32 * cr0; @@ -44,8 +45,18 @@ v3_cpu_mode_t v3_get_vm_cpu_mode(struct guest_info * info) { cr0 = (struct cr0_32 *)&(info->shdw_pg_state.guest_cr0); efer = (struct efer_64 *)&(info->shdw_pg_state.guest_efer); } else if (info->shdw_pg_mode == NESTED_PAGING) { - cr0 = (struct cr0_32 *)&(info->ctrl_regs.cr0); - efer = (struct efer_64 *)&(guest_state->efer); + // XXX + if(v3_cpu_types[info->pcpu_id] == V3_VMX_EPT_CPU) { + // with EPT, we use VMXASSIST + cr0 = (struct cr0_32 *)&(info->shdw_pg_state.guest_cr0); + } else { + cr0 = (struct cr0_32 *)&(info->ctrl_regs.cr0); + } + if(v3_cpu_types[info->pcpu_id] == V3_VMX_EPT_CPU || v3_cpu_types[info->pcpu_id] == V3_VMX_EPT_UG_CPU) { + efer = (struct efer_64 *)&(info->shdw_pg_state.guest_efer); + } else { + efer = (struct efer_64 *)&(guest_state->efer); + } } else { PrintError("Invalid Paging Mode...\n"); V3_ASSERT(0); @@ -133,7 +144,10 @@ v3_mem_mode_t v3_get_vm_mem_mode(struct guest_info * info) { if (info->shdw_pg_mode == SHADOW_PAGING) { cr0 = (struct cr0_32 *)&(info->shdw_pg_state.guest_cr0); } else if (info->shdw_pg_mode == NESTED_PAGING) { - cr0 = (struct cr0_32 *)&(info->ctrl_regs.cr0); + if(v3_cpu_types[info->pcpu_id] == V3_VMX_EPT_CPU) // XXX + cr0 = (struct cr0_32 *)&(info->shdw_pg_state.guest_cr0); + else + cr0 = (struct cr0_32 *)&(info->ctrl_regs.cr0); } else { PrintError("Invalid Paging Mode...\n"); V3_ASSERT(0); diff --git a/palacios/src/palacios/vmm_ctrl_regs.c b/palacios/src/palacios/vmm_ctrl_regs.c index 3616ae8..8d5216e 100644 --- a/palacios/src/palacios/vmm_ctrl_regs.c +++ b/palacios/src/palacios/vmm_ctrl_regs.c @@ -513,6 +513,7 @@ int v3_handle_cr4_write(struct guest_info * info) { *cr4 = *new_cr4; PrintDebug("New CR4=%x\n", *(uint_t *)cr4); + } else if ((cpu_mode == LONG) || (cpu_mode == LONG_32_COMPAT)) { struct cr4_64 * new_cr4 = (struct cr4_64 *)(dec_instr.src_operand.operand); struct cr4_64 * cr4 = (struct cr4_64 *)&(info->ctrl_regs.cr4); @@ -532,8 +533,8 @@ int v3_handle_cr4_write(struct guest_info * info) { PrintError("CR4 write not supported in CPU_MODE: %s\n", v3_cpu_mode_to_str(cpu_mode)); return -1; } - - + + if (flush_tlb) { PrintDebug("Handling PSE/PGE/PAE -> TLBFlush (doing flush now!)\n"); if (v3_activate_shadow_pt(info) == -1) { @@ -586,6 +587,7 @@ int v3_handle_efer_write(struct guest_info * core, uint_t msr, struct v3_msr src // 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; + ((struct efer_64 *)vm_efer)->lma = 1; } return 0; diff --git a/palacios/src/palacios/vmx.c b/palacios/src/palacios/vmx.c index 4e0afaa..11bfa3b 100644 --- a/palacios/src/palacios/vmx.c +++ b/palacios/src/palacios/vmx.c @@ -263,7 +263,7 @@ static int init_vmcs_bios(struct guest_info * core, struct vmx_data * vmx_state) // Setup VMX Assist v3_vmxassist_init(core, vmx_state); - } else if ((core->shdw_pg_mode == NESTED_PAGING) && + } else if ((core->shdw_pg_mode == NESTED_PAGING) && (v3_cpu_types[core->pcpu_id] == V3_VMX_EPT_CPU)) { #define CR0_PE 0x00000001 @@ -273,9 +273,9 @@ static int init_vmcs_bios(struct guest_info * core, struct vmx_data * vmx_state) // vmx_state->pinbased_ctrls |= NMI_EXIT; - /* Disable CR exits */ - vmx_state->pri_proc_ctrls.cr3_ld_exit = 0; - vmx_state->pri_proc_ctrls.cr3_str_exit = 0; + /* Do not disable CR3 exits until guest enables paging */ + //vmx_state->pri_proc_ctrls.cr3_ld_exit = 0; + //vmx_state->pri_proc_ctrls.cr3_str_exit = 0; vmx_state->pri_proc_ctrls.invlpg_exit = 0; diff --git a/palacios/src/palacios/vmx_ctrl_regs.c b/palacios/src/palacios/vmx_ctrl_regs.c index d83d51f..7f95b9c 100644 --- a/palacios/src/palacios/vmx_ctrl_regs.c +++ b/palacios/src/palacios/vmx_ctrl_regs.c @@ -27,7 +27,7 @@ #include #include -#ifndef V3_CONFIG_DEBUG_VMX +#ifndef V3_CONFIG_DEBUG_CTRL_REGS #undef PrintDebug #define PrintDebug(fmt, args...) #endif @@ -77,21 +77,44 @@ 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) { + +static int ept_handle_cr4_write(struct guest_info * info, v3_reg_t * reg); +int v3_vmx_handle_cr4_access(struct guest_info * info, struct vmx_exit_info * exit_info) { + struct vmx_exit_cr_qual * cr_qual = (struct vmx_exit_cr_qual *)&(exit_info->exit_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; - } - } + + if(info->shdw_pg_mode == SHADOW_PAGING) { + if (cr_qual->access_type == 0) { + if (v3_handle_cr4_write(info) != 0) { + PrintError("Could not handle CR4 write\n"); + return -1; + } + + // need to update shadow CR4 since it is not updated inside v3_handle_cr4_write + struct vmx_data * vmx_info = (struct vmx_data *)info->vmm_data; + vmx_info->guest_cr4 = info->ctrl_regs.cr4 &= ~CR4_VMXE; + + info->ctrl_regs.cr4 |= CR4_VMXE; // 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; + } + } + } else { + v3_reg_t * reg = get_reg_ptr(info, cr_qual); + if(cr_qual->access_type == 0) { + if (ept_handle_cr4_write(info, reg) != 0) { + PrintError("Could not handle CR4 write\n"); + return -1; + } + info->rip += exit_info->instr_len; + } else { + PrintError("Impossible exit due to read of CR4 in VMX!\n"); + return -1; + } + } return 0; } @@ -100,6 +123,30 @@ int v3_vmx_handle_cr4_access(struct guest_info * info, struct vmx_exit_cr_qual * return -1; } +static int ept_handle_cr4_write(struct guest_info * info, v3_reg_t * reg) { + struct vmx_data * vmx_info = (struct vmx_data *)info->vmm_data; + struct cr4_32 *guest_cr4 = (struct cr4_32 *)&vmx_info->guest_cr4; + struct cr4_32 *hw_cr4 = (struct cr4_32 *)&info->ctrl_regs.cr4; + struct cr4_32 *new_cr4 = (struct cr4_32 *)reg; + + // mask is set to VMXE and PAE, so their change is only possible reason of VMEXIT + + PrintDebug("Guest CR4 Write. HW CR4 %x, shadow CR4 %x, new CR4 %x\n", + *(uint32_t*)hw_cr4, *(uint32_t*)guest_cr4, *(uint32_t*)new_cr4); + // update shadow anyway + *guest_cr4 = *new_cr4; + if(info->mem_mode == VIRTUAL_MEM) { + // we have paging enabled, so only care about VMXE + *hw_cr4 = *new_cr4; + *(uint32_t*)hw_cr4 |= CR4_VMXE; + PrintDebug("CR4 update: new value %x\n", *(uint32_t*)hw_cr4); + } else { + // if paging is disabled, we cannot enable PAE, disable PSE or VME since VMXASSIST is still working + PrintDebug("Paging disabled, CR4 update pending\n"); + } + return 0; +} + static int handle_mov_to_cr3(struct guest_info * info, v3_reg_t * cr3_reg) { if (info->shdw_pg_mode == SHADOW_PAGING) { @@ -129,8 +176,12 @@ static int handle_mov_to_cr3(struct guest_info * info, v3_reg_t * cr3_reg) { (void *)info->shdw_pg_state.guest_cr3); */ } else if (info->shdw_pg_mode == NESTED_PAGING) { - PrintError("Nested paging not available in VMX right now!\n"); - return -1; + if(info->mem_mode == VIRTUAL_MEM) { + PrintError("Mov to CR3 with paging enabled in EPT mode is impossible!\n"); + return -1; + } + info->shdw_pg_state.guest_cr3 = (uint32_t)*cr3_reg; + PrintDebug("Guest CR3 value %x cached until paging is enabled.\n", (uint32_t)*cr3_reg); } @@ -152,8 +203,8 @@ static int handle_mov_from_cr3(struct guest_info * info, v3_reg_t * cr3_reg) { } } else { - PrintError("Unhandled paging mode\n"); - return -1; + *cr3_reg = (uint32_t)info->shdw_pg_state.guest_cr3; + PrintDebug("Guest reads cached value in EPT mode.\n"); } @@ -224,7 +275,7 @@ static int handle_mov_to_cr0(struct guest_info * info, v3_reg_t * new_cr0, struc if (vm_efer->lme) { // PrintDebug("Enabling long mode\n"); - + vm_efer->lma = 1; hw_efer->lma = 1; hw_efer->lme = 1; @@ -238,6 +289,22 @@ static int handle_mov_to_cr0(struct guest_info * info, v3_reg_t * new_cr0, struc PrintError("Failed to activate shadow page tables\n"); return -1; } + } else { + // EPT mode, need to update cached CR3, CR4 + info->ctrl_regs.cr3 = info->shdw_pg_state.guest_cr3; + // Disable CR3 exits as guest enabled paging + vmx_info->pri_proc_ctrls.cr3_ld_exit = 0; + vmx_info->pri_proc_ctrls.cr3_str_exit = 0; + if(v3_update_vmcs_ctrl_fields(info)) return -1; + // write guest CR4 + struct cr4_32 *guest_cr4 = (struct cr4_32 *)&vmx_info->guest_cr4; + struct cr4_32 *hw_cr4 = (struct cr4_32 *)&info->ctrl_regs.cr4; + *hw_cr4 = *guest_cr4; + *(uint32_t*)hw_cr4 |= CR4_VMXE; + PrintDebug("Guest enables paging. Restored cached CR3 (%x), CR4 (%x)." + " Guest EFER %x, guest HW EFER %x\n", (uint32_t)info->ctrl_regs.cr3, *(uint32_t*)guest_cr4, + *(uint32_t*)vm_efer, *(uint32_t*)hw_efer); + } } else { @@ -248,8 +315,14 @@ static int handle_mov_to_cr0(struct guest_info * info, v3_reg_t * new_cr0, struc return -1; } } else { + // we need to restore VMXASSIST. XXX +#define GUEST_CR0 0x80010031 +#define GUEST_CR4 0x00002010 + *(uint32_t*)guest_cr0 = GUEST_CR0; + info->ctrl_regs.cr4 = GUEST_CR4; // This is hideous... Let's hope that the 1to1 page table has not been nuked... info->ctrl_regs.cr3 = VMXASSIST_1to1_PT; + PrintError("Guest disables paging. Is everything OK?\n"); } } } diff --git a/palacios/src/palacios/vmx_handler.c b/palacios/src/palacios/vmx_handler.c index be86fe2..63c8960 100644 --- a/palacios/src/palacios/vmx_handler.c +++ b/palacios/src/palacios/vmx_handler.c @@ -99,7 +99,7 @@ int v3_handle_vmx_exit(struct guest_info * info, struct vmx_exit_info * exit_inf } } else { - PrintError("Page fault in unimplemented paging mode\n"); + PrintError("Page fault in unimplemented paging mode (address %p error code %x).\n", (void*)(addr_t)exit_info->exit_qual, *(unsigned*)&error_code); return -1; } } else if ((uint8_t)exit_info->int_info == 2) { @@ -229,7 +229,7 @@ int v3_handle_vmx_exit(struct guest_info * info, struct vmx_exit_info * exit_inf break; case 4: //PrintDebug("Handling CR4 Access\n"); - if (v3_vmx_handle_cr4_access(info, cr_qual) == -1) { + if (v3_vmx_handle_cr4_access(info, exit_info) == -1) { PrintError("Error in CR4 access handler\n"); return -1; } -- 1.7.5.4