summaryrefslogtreecommitdiff
path: root/sys/arch/amd64
diff options
context:
space:
mode:
authorMike Larkin <mlarkin@cvs.openbsd.org>2017-05-19 06:29:22 +0000
committerMike Larkin <mlarkin@cvs.openbsd.org>2017-05-19 06:29:22 +0000
commit0291360fb4425d1aae01bcb4fee3f24fc43f3b48 (patch)
tree6ede6bddbcd3db75d1e7203965aaee850672e69e /sys/arch/amd64
parentb3f1eecc2262b7c4017843ce6bf3f405217a8544 (diff)
Respect max VPID/ASID limits. VMX VPIDs are capped at 4095, for now.
Diffstat (limited to 'sys/arch/amd64')
-rw-r--r--sys/arch/amd64/amd64/identcpu.c6
-rw-r--r--sys/arch/amd64/amd64/vmm.c120
-rw-r--r--sys/arch/amd64/include/vmmvar.h3
3 files changed, 115 insertions, 14 deletions
diff --git a/sys/arch/amd64/amd64/identcpu.c b/sys/arch/amd64/amd64/identcpu.c
index cb8ac4f3492..b3c8dce2da3 100644
--- a/sys/arch/amd64/amd64/identcpu.c
+++ b/sys/arch/amd64/amd64/identcpu.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: identcpu.c,v 1.84 2017/05/10 12:36:35 tb Exp $ */
+/* $OpenBSD: identcpu.c,v 1.85 2017/05/19 06:29:21 mlarkin Exp $ */
/* $NetBSD: identcpu.c,v 1.1 2003/04/26 18:39:28 fvdl Exp $ */
/*
@@ -950,8 +950,8 @@ cpu_check_vmm_cap(struct cpu_info *ci)
CPUID(0x8000000A, dummy, ci->ci_vmm_cap.vcc_svm.svm_max_asid,
dummy, dummy);
- if (ci->ci_vmm_cap.vcc_svm.svm_max_asid > 0xFFFF)
- ci->ci_vmm_cap.vcc_svm.svm_max_asid = 0xFFFF;
+ if (ci->ci_vmm_cap.vcc_svm.svm_max_asid > 0xFFF)
+ ci->ci_vmm_cap.vcc_svm.svm_max_asid = 0xFFF;
}
/*
diff --git a/sys/arch/amd64/amd64/vmm.c b/sys/arch/amd64/amd64/vmm.c
index 1cb58ad4f51..8b89fd503a9 100644
--- a/sys/arch/amd64/amd64/vmm.c
+++ b/sys/arch/amd64/amd64/vmm.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: vmm.c,v 1.142 2017/05/19 05:57:31 mlarkin Exp $ */
+/* $OpenBSD: vmm.c,v 1.143 2017/05/19 06:29:21 mlarkin Exp $ */
/*
* Copyright (c) 2014 Mike Larkin <mlarkin@openbsd.org>
*
@@ -100,6 +100,10 @@ struct vmm_softc {
struct rwlock vm_lock;
size_t vm_ct; /* number of in-memory VMs */
size_t vm_idx; /* next unique VM index */
+
+ struct rwlock vpid_lock;
+ uint16_t max_vpid;
+ uint8_t vpids[512]; /* bitmap of used VPID/ASIDs */
};
int vmm_enabled(void);
@@ -165,6 +169,8 @@ int svm_get_guest_faulttype(void);
int vmx_get_exit_qualification(uint64_t *);
int vmx_fault_page(struct vcpu *, paddr_t);
int vmx_handle_np_fault(struct vcpu *);
+int vmm_alloc_vpid(uint16_t *);
+void vmm_free_vpid(uint16_t);
const char *vcpu_state_decode(u_int);
const char *vmx_exit_reason_decode(uint32_t);
const char *vmx_instruction_error_decode(uint32_t);
@@ -361,6 +367,15 @@ vmm_attach(struct device *parent, struct device *self, void *aux)
sc->mode = VMM_MODE_UNKNOWN;
}
+ if (sc->mode == VMM_MODE_SVM || sc->mode == VMM_MODE_RVI) {
+ sc->max_vpid = ci->ci_vmm_cap.vcc_svm.svm_max_asid;
+ } else {
+ sc->max_vpid = 0xFFF;
+ }
+
+ bzero(&sc->vpids, sizeof(sc->vpids));
+ rw_init(&sc->vpid_lock, "vpidlock");
+
pool_init(&vm_pool, sizeof(struct vm), 0, IPL_NONE, PR_WAITOK,
"vmpool", NULL);
pool_init(&vcpu_pool, sizeof(struct vcpu), 64, IPL_NONE, PR_WAITOK,
@@ -1033,10 +1048,6 @@ vm_create(struct vm_create_params *vcp, struct proc *p)
vmm_softc->vm_ct++;
vmm_softc->vm_idx++;
- /*
- * XXX we use the vm_id for the VPID/ASID, so we need to prevent
- * wrapping around 65536/4096 entries here
- */
vm->vm_id = vmm_softc->vm_idx;
vm->vm_vcpu_ct = 0;
vm->vm_vcpus_running = 0;
@@ -1673,6 +1684,7 @@ vcpu_reset_regs_svm(struct vcpu *vcpu, struct vcpu_reg_state *vrs)
{
struct vmcb *vmcb;
int ret;
+ uint16_t asid;
vmcb = (struct vmcb *)vcpu->vc_control_va;
@@ -1728,7 +1740,14 @@ vcpu_reset_regs_svm(struct vcpu *vcpu, struct vcpu_reg_state *vrs)
svm_setmsrbr(vcpu, MSR_EFER);
/* Guest VCPU ASID */
- vmcb->v_asid = vcpu->vc_parent->vm_id;
+ if (vmm_alloc_vpid(&asid)) {
+ DPRINTF("%s: could not allocate asid\n", __func__);
+ ret = EINVAL;
+ goto exit;
+ }
+
+ vmcb->v_asid = asid;
+ vcpu->vc_vpid = asid;
/* TLB Control */
vmcb->v_tlb_control = 2; /* Flush this guest's TLB entries */
@@ -1747,6 +1766,7 @@ vcpu_reset_regs_svm(struct vcpu *vcpu, struct vcpu_reg_state *vrs)
vmcb->v_efer |= (EFER_LME | EFER_LMA);
vmcb->v_cr4 |= CR4_PAE;
+exit:
return ret;
}
@@ -1949,7 +1969,7 @@ vcpu_reset_regs_vmx(struct vcpu *vcpu, struct vcpu_reg_state *vrs)
uint32_t pinbased, procbased, procbased2, exit, entry;
uint32_t want1, want0;
uint64_t msr, ctrlval, eptp, cr3;
- uint16_t ctrl;
+ uint16_t ctrl, vpid;
struct vmx_msr_store *msr_store;
ret = 0;
@@ -2205,12 +2225,20 @@ vcpu_reset_regs_vmx(struct vcpu *vcpu, struct vcpu_reg_state *vrs)
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED_CTLS,
IA32_VMX_ACTIVATE_SECONDARY_CONTROLS, 1)) {
if (vcpu_vmx_check_cap(vcpu, IA32_VMX_PROCBASED2_CTLS,
- IA32_VMX_ENABLE_VPID, 1))
- if (vmwrite(VMCS_GUEST_VPID,
- (uint16_t)vcpu->vc_parent->vm_id)) {
+ IA32_VMX_ENABLE_VPID, 1)) {
+ if (vmm_alloc_vpid(&vpid)) {
+ DPRINTF("%s: could not allocate VPID\n",
+ __func__);
+ ret = EINVAL;
+ goto exit;
+ }
+ if (vmwrite(VMCS_GUEST_VPID, vpid)) {
ret = EINVAL;
goto exit;
}
+
+ vcpu->vc_vpid = vpid;
+ }
}
/*
@@ -2771,6 +2799,7 @@ vcpu_init(struct vcpu *vcpu)
vcpu->vc_virt_mode = vmm_softc->mode;
vcpu->vc_state = VCPU_STATE_STOPPED;
+ vcpu->vc_vpid = 0;
if (vmm_softc->mode == VMM_MODE_VMX ||
vmm_softc->mode == VMM_MODE_EPT)
ret = vcpu_init_vmx(vcpu);
@@ -2806,6 +2835,9 @@ vcpu_deinit_vmx(struct vcpu *vcpu)
if (vcpu->vc_vmx_msr_entry_load_va)
km_free((void *)vcpu->vc_vmx_msr_entry_load_va,
PAGE_SIZE, &kv_page, &kp_zero);
+
+ if (vcpu->vc_vmx_vpid_enabled)
+ vmm_free_vpid(vcpu->vc_vpid);
}
/*
@@ -2831,6 +2863,8 @@ vcpu_deinit_svm(struct vcpu *vcpu)
if (vcpu->vc_svm_ioio_va)
km_free((void *)vcpu->vc_svm_ioio_va, 3 * PAGE_SIZE, &kv_any,
&vmm_kp_contig);
+
+ vmm_free_vpid(vcpu->vc_vpid);
}
/*
@@ -4981,6 +5015,72 @@ vcpu_run_svm(struct vcpu *vcpu, struct vm_run_params *vrp)
}
/*
+ * vmm_alloc_vpid
+ *
+ * Sets the memory location pointed to by "vpid" to the next available VPID
+ * or ASID.
+ *
+ * Parameters:
+ * vpid: Pointer to location to receive the next VPID/ASID
+ *
+ * Return Values:
+ * 0: The operation completed successfully
+ * ENOMEM: No VPIDs/ASIDs were available. Content of 'vpid' is unchanged.
+ */
+int
+vmm_alloc_vpid(uint16_t *vpid)
+{
+ uint16_t i;
+ uint8_t idx, bit;
+ struct vmm_softc *sc = vmm_softc;
+
+ rw_enter_write(&vmm_softc->vpid_lock);
+ for (i = 1; i <= sc->max_vpid; i++) {
+ idx = i / 8;
+ bit = i - (idx * 8);
+
+ if (!(sc->vpids[idx] & (1 << bit))) {
+ sc->vpids[idx] |= (1 << bit);
+ *vpid = i;
+ DPRINTF("%s: allocated VPID/ASID %d\n", __func__,
+ i);
+ rw_exit_write(&vmm_softc->vpid_lock);
+ return 0;
+ }
+ }
+
+ printf("%s: no available %ss\n", __func__,
+ (sc->mode == VMM_MODE_EPT || sc->mode == VMM_MODE_VMX) ? "VPID" :
+ "ASID");
+
+ rw_exit_write(&vmm_softc->vpid_lock);
+ return ENOMEM;
+}
+
+/*
+ * vmm_free_vpid
+ *
+ * Frees the VPID/ASID id supplied in "vpid".
+ *
+ * Parameters:
+ * vpid: VPID/ASID to free.
+ */
+void
+vmm_free_vpid(uint16_t vpid)
+{
+ uint8_t idx, bit;
+ struct vmm_softc *sc = vmm_softc;
+
+ rw_enter_write(&vmm_softc->vpid_lock);
+ idx = vpid / 8;
+ bit = vpid - (idx * 8);
+ sc->vpids[idx] &= ~(1 << bit);
+
+ DPRINTF("%s: freed VPID/ASID %d\n", __func__, vpid);
+ rw_exit_write(&vmm_softc->vpid_lock);
+}
+
+/*
* vmx_exit_reason_decode
*
* Returns a human readable string describing exit type 'code'
diff --git a/sys/arch/amd64/include/vmmvar.h b/sys/arch/amd64/include/vmmvar.h
index 6c4f0b45b47..1784cc97cc3 100644
--- a/sys/arch/amd64/include/vmmvar.h
+++ b/sys/arch/amd64/include/vmmvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: vmmvar.h,v 1.37 2017/05/05 07:46:59 mlarkin Exp $ */
+/* $OpenBSD: vmmvar.h,v 1.38 2017/05/19 06:29:21 mlarkin Exp $ */
/*
* Copyright (c) 2014 Mike Larkin <mlarkin@openbsd.org>
*
@@ -682,6 +682,7 @@ struct vcpu {
struct vm *vc_parent;
uint32_t vc_id;
+ uint16_t vc_vpid;
u_int vc_state;
SLIST_ENTRY(vcpu) vc_vcpu_link;