diff options
author | Mike Larkin <mlarkin@cvs.openbsd.org> | 2017-10-06 07:39:11 +0000 |
---|---|---|
committer | Mike Larkin <mlarkin@cvs.openbsd.org> | 2017-10-06 07:39:11 +0000 |
commit | 60bf7aaf9af86e3a2467097636fee65758e25310 (patch) | |
tree | 4d4107b5899524b785ec0b13a0718af1fbd6073d /sys/arch/amd64 | |
parent | 498edcf7f44462d8563305c89450d29992394fd4 (diff) |
vmm: handle 32 bit guest w/PAE when guest enables PAE first then paging,
as 32 bit linux guests do.
ok pd@
Diffstat (limited to 'sys/arch/amd64')
-rw-r--r-- | sys/arch/amd64/amd64/vmm.c | 124 |
1 files changed, 119 insertions, 5 deletions
diff --git a/sys/arch/amd64/amd64/vmm.c b/sys/arch/amd64/amd64/vmm.c index ba54292599c..42ed29275f4 100644 --- a/sys/arch/amd64/amd64/vmm.c +++ b/sys/arch/amd64/amd64/vmm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vmm.c,v 1.174 2017/10/05 23:55:29 mlarkin Exp $ */ +/* $OpenBSD: vmm.c,v 1.175 2017/10/06 07:39:10 mlarkin Exp $ */ /* * Copyright (c) 2014 Mike Larkin <mlarkin@openbsd.org> * @@ -149,6 +149,7 @@ void vm_teardown(struct vm *); int vcpu_vmx_check_cap(struct vcpu *, uint32_t, uint32_t, int); int vcpu_vmx_compute_ctrl(uint64_t, uint16_t, uint32_t, uint32_t, uint32_t *); int vmx_get_exit_info(uint64_t *, uint64_t *); +int vmx_load_pdptes(struct vcpu *); int vmx_handle_exit(struct vcpu *); int svm_handle_exit(struct vcpu *); int svm_handle_msr(struct vcpu *); @@ -1823,9 +1824,6 @@ vcpu_reset_regs_svm(struct vcpu *vcpu, struct vcpu_reg_state *vrs) ret = vcpu_writeregs_svm(vcpu, VM_RWREGS_ALL, vrs); - vmcb->v_efer |= (EFER_LME | EFER_LMA); - vmcb->v_cr4 |= CR4_PAE; - /* xcr0 power on default sets bit 0 (x87 state) */ vcpu->vc_gueststate.vg_xcr0 = XCR0_X87; @@ -4830,6 +4828,101 @@ vmx_handle_inout(struct vcpu *vcpu) } /* + * vmx_load_pdptes + * + * Update the PDPTEs in the VMCS with the values currently indicated by the + * guest CR3. This is used for 32-bit PAE guests when enabling paging. + * + * Parameters + * vcpu: The vcpu whose PDPTEs should be loaded + * + * Return values: + * 0: if successful + * EINVAL: if the PDPTEs could not be loaded + * ENOMEM: memory allocation failure + */ +int +vmx_load_pdptes(struct vcpu *vcpu) +{ + uint64_t cr3, cr3_host_phys; + vaddr_t cr3_host_virt; + pd_entry_t *pdptes; + int ret; + + if (vmread(VMCS_GUEST_IA32_CR3, &cr3)) { + printf("%s: can't read guest cr3\n", __func__); + return (EINVAL); + } + + if (!pmap_extract(vcpu->vc_parent->vm_map->pmap, (vaddr_t)cr3, + (paddr_t *)&cr3_host_phys)) { + DPRINTF("%s: nonmapped guest CR3, setting PDPTEs to 0\n", + __func__); + if (vmwrite(VMCS_GUEST_PDPTE0, 0)) { + printf("%s: can't write guest PDPTE0\n", __func__); + return (EINVAL); + } + + if (vmwrite(VMCS_GUEST_PDPTE1, 0)) { + printf("%s: can't write guest PDPTE1\n", __func__); + return (EINVAL); + } + + if (vmwrite(VMCS_GUEST_PDPTE2, 0)) { + printf("%s: can't write guest PDPTE2\n", __func__); + return (EINVAL); + } + + if (vmwrite(VMCS_GUEST_PDPTE3, 0)) { + printf("%s: can't write guest PDPTE3\n", __func__); + return (EINVAL); + } + return (0); + } + + ret = 0; + + cr3_host_virt = (vaddr_t)km_alloc(PAGE_SIZE, &kv_any, &kp_none, &kd_waitok); + if (!cr3_host_virt) { + printf("%s: can't allocate address for guest CR3 mapping\n", + __func__); + return (ENOMEM); + } + + pmap_kenter_pa(cr3_host_virt, cr3_host_phys, PROT_READ); + + pdptes = (pd_entry_t *)cr3_host_virt; + if (vmwrite(VMCS_GUEST_PDPTE0, pdptes[0])) { + printf("%s: can't write guest PDPTE0\n", __func__); + ret = EINVAL; + goto exit; + } + + if (vmwrite(VMCS_GUEST_PDPTE1, pdptes[1])) { + printf("%s: can't write guest PDPTE1\n", __func__); + ret = EINVAL; + goto exit; + } + + if (vmwrite(VMCS_GUEST_PDPTE2, pdptes[2])) { + printf("%s: can't write guest PDPTE2\n", __func__); + ret = EINVAL; + goto exit; + } + + if (vmwrite(VMCS_GUEST_PDPTE3, pdptes[3])) { + printf("%s: can't write guest PDPTE3\n", __func__); + ret = EINVAL; + goto exit; + } + +exit: + pmap_kremove(cr3_host_virt, PAGE_SIZE); + km_free((void *)cr3_host_virt, PAGE_SIZE, &kv_any, &kp_none); + return (ret); +} + +/* * vmx_handle_cr0_write * * Write handler for CR0. This function ensures valid values are written into @@ -4847,12 +4940,23 @@ int vmx_handle_cr0_write(struct vcpu *vcpu, uint64_t r) { struct vmx_msr_store *msr_store; - uint64_t ectls; + uint64_t ectls, oldcr0, cr4; + int ret; /* * XXX this is the place to place handling of the must1,must0 bits */ + if (vmread(VMCS_GUEST_IA32_CR0, &oldcr0)) { + printf("%s: can't read guest cr0\n", __func__); + return (EINVAL); + } + + if (vmread(VMCS_GUEST_IA32_CR4, &cr4)) { + printf("%s: can't read guest cr4\n", __func__); + return (EINVAL); + } + /* CR0 must always have NE set */ r |= CR0_NE; @@ -4886,6 +4990,16 @@ vmx_handle_cr0_write(struct vcpu *vcpu, uint64_t r) return (EINVAL); } + /* Load PDPTEs if PAE guest enabling paging */ + if (!(oldcr0 & CR0_PG) && (r & CR0_PG) && (cr4 & CR4_PAE)) { + ret = vmx_load_pdptes(vcpu); + + if (ret) { + printf("%s: updating PDPTEs failed\n", __func__); + return (ret); + } + } + return (0); } |