summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/arch/amd64/amd64/vmm.c109
1 files changed, 108 insertions, 1 deletions
diff --git a/sys/arch/amd64/amd64/vmm.c b/sys/arch/amd64/amd64/vmm.c
index 27d46b65c4f..e146c7fa7c0 100644
--- a/sys/arch/amd64/amd64/vmm.c
+++ b/sys/arch/amd64/amd64/vmm.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: vmm.c,v 1.205 2018/07/02 05:37:18 mlarkin Exp $ */
+/* $OpenBSD: vmm.c,v 1.206 2018/07/05 03:48:45 mlarkin Exp $ */
/*
* Copyright (c) 2014 Mike Larkin <mlarkin@openbsd.org>
*
@@ -175,6 +175,8 @@ int vmm_get_guest_faulttype(void);
int vmx_get_guest_faulttype(void);
int svm_get_guest_faulttype(struct vmcb *);
int vmx_get_exit_qualification(uint64_t *);
+int vmm_get_guest_cpu_cpl(struct vcpu *);
+int vmm_get_guest_cpu_mode(struct vcpu *);
int svm_fault_page(struct vcpu *, paddr_t);
int vmx_fault_page(struct vcpu *, paddr_t);
int vmx_handle_np_fault(struct vcpu *);
@@ -4894,6 +4896,111 @@ vmx_handle_np_fault(struct vcpu *vcpu)
}
/*
+ * vmm_get_guest_cpu_cpl
+ *
+ * Determines current CPL of 'vcpu'. On VMX/Intel, this is gathered from the
+ * VMCS field for the DPL of SS (this seems odd, but is documented that way
+ * in the SDM). For SVM/AMD, this is gathered directly from the VMCB's 'cpl'
+ * field, as per the APM.
+ *
+ * Parameters:
+ * vcpu: guest VCPU for which CPL is to be checked
+ *
+ * Return Values:
+ * -1: the CPL could not be determined
+ * 0-3 indicating the current CPL. For real mode operation, 0 is returned.
+ */
+int
+vmm_get_guest_cpu_cpl(struct vcpu *vcpu)
+{
+ int mode;
+ struct vmcb *vmcb;
+ uint64_t ss_ar;
+
+ mode = vmm_get_guest_cpu_mode(vcpu);
+
+ if (mode == VMM_CPU_MODE_UNKNOWN)
+ return (-1);
+
+ if (mode == VMM_CPU_MODE_REAL)
+ return (0);
+
+ if (vmm_softc->mode == VMM_MODE_SVM ||
+ vmm_softc->mode == VMM_MODE_RVI) {
+ vmcb = (struct vmcb *)vcpu->vc_control_va;
+ return (vmcb->v_cpl);
+ } else if (vmm_softc->mode == VMM_MODE_VMX ||
+ vmm_softc->mode == VMM_MODE_EPT) {
+ if (vmread(VMCS_GUEST_IA32_SS_AR, &ss_ar))
+ return (-1);
+ return ((ss_ar & 0x60) >> 5);
+ } else
+ return (-1);
+}
+
+/*
+ * vmm_get_guest_cpu_mode
+ *
+ * Determines current CPU mode of 'vcpu'.
+ *
+ * Parameters:
+ * vcpu: guest VCPU for which mode is to be checked
+ *
+ * Return Values:
+ * One of VMM_CPU_MODE_*, or VMM_CPU_MODE_UNKNOWN if the mode could not be
+ * ascertained.
+ */
+int
+vmm_get_guest_cpu_mode(struct vcpu *vcpu)
+{
+ uint64_t cr0, efer, cs_ar;
+ uint8_t l, dib;
+ struct vmcb *vmcb;
+ struct vmx_msr_store *msr_store;
+
+ if (vmm_softc->mode == VMM_MODE_SVM ||
+ vmm_softc->mode == VMM_MODE_RVI) {
+ vmcb = (struct vmcb *)vcpu->vc_control_va;
+ cr0 = vmcb->v_cr0;
+ efer = vmcb->v_efer;
+ cs_ar = vmcb->v_cs.vs_attr;
+ cs_ar = (cs_ar & 0xff) | ((cs_ar << 4) & 0xf000);
+ } else if (vmm_softc->mode == VMM_MODE_VMX ||
+ vmm_softc->mode == VMM_MODE_EPT) {
+ if (vmread(VMCS_GUEST_IA32_CR0, &cr0))
+ return (VMM_CPU_MODE_UNKNOWN);
+ if (vmread(VMCS_GUEST_IA32_CS_AR, &cs_ar))
+ return (VMM_CPU_MODE_UNKNOWN);
+ msr_store =
+ (struct vmx_msr_store *)vcpu->vc_vmx_msr_exit_save_va;
+ efer = msr_store[VCPU_REGS_EFER].vms_data;
+ } else
+ return (VMM_CPU_MODE_UNKNOWN);
+
+ l = (cs_ar & 0x2000) >> 13;
+ dib = (cs_ar & 0x4000) >> 14;
+
+ /* Check CR0.PE */
+ if (!(cr0 & CR0_PE))
+ return (VMM_CPU_MODE_REAL);
+
+ /* Check EFER */
+ if (efer & EFER_LMA) {
+ /* Could be compat or long mode, check CS.L */
+ if (l)
+ return (VMM_CPU_MODE_LONG);
+ else
+ return (VMM_CPU_MODE_COMPAT);
+ }
+
+ /* Check prot vs prot32 */
+ if (dib)
+ return (VMM_CPU_MODE_PROT32);
+ else
+ return (VMM_CPU_MODE_PROT);
+}
+
+/*
* svm_handle_inout
*
* Exit handler for IN/OUT instructions.