[RFC PATCH v6 09/36] KVM: nVMX: Enforce unsupported eVMCS in VMX MSRs for host accesses

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Sean Christopherson <seanjc@xxxxxxxxxx>
---
 arch/x86/include/asm/kvm_host.h |  4 +-
 arch/x86/kvm/vmx/evmcs.c        | 91 ++++++++++++++++++++-------------
 arch/x86/kvm/vmx/evmcs.h        | 19 +++++--
 arch/x86/kvm/vmx/nested.c       | 15 ++++--
 arch/x86/kvm/vmx/vmx.c          | 12 ++---
 arch/x86/kvm/vmx/vmx.h          |  2 +
 arch/x86/kvm/x86.c              |  8 ++-
 include/uapi/linux/kvm.h        |  1 +
 8 files changed, 99 insertions(+), 53 deletions(-)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 2c96c43c313a..2209724b765e 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1647,8 +1647,8 @@ struct kvm_x86_nested_ops {
 	bool (*get_nested_state_pages)(struct kvm_vcpu *vcpu);
 	int (*write_log_dirty)(struct kvm_vcpu *vcpu, gpa_t l2_gpa);
 
-	int (*enable_evmcs)(struct kvm_vcpu *vcpu,
-			    uint16_t *vmcs_version);
+	int (*enable_evmcs)(struct kvm_vcpu *vcpu, uint16_t *vmcs_version,
+			    bool enforce_evmcs);
 	uint16_t (*get_evmcs_version)(struct kvm_vcpu *vcpu);
 };
 
diff --git a/arch/x86/kvm/vmx/evmcs.c b/arch/x86/kvm/vmx/evmcs.c
index 3bf8681e5239..c0cb68ce7b1b 100644
--- a/arch/x86/kvm/vmx/evmcs.c
+++ b/arch/x86/kvm/vmx/evmcs.c
@@ -349,6 +349,7 @@ uint16_t nested_get_evmcs_version(struct kvm_vcpu *vcpu)
 
 enum evmcs_revision {
 	EVMCSv1_LEGACY,
+	EVMCSv1_ENFORCED,
 	NR_EVMCS_REVISIONS,
 };
 
@@ -363,99 +364,119 @@ enum evmcs_ctrl_type {
 
 static const u32 evmcs_unsupported_ctrls[NR_EVMCS_CTRLS][NR_EVMCS_REVISIONS] = {
 	[EVMCS_EXIT_CTRLS] = {
-		[EVMCSv1_LEGACY] = EVMCS1_UNSUPPORTED_VMEXIT_CTRL | VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL,
+		[EVMCSv1_LEGACY]   = EVMCS1_UNSUPPORTED_VMEXIT_CTRL_LEGACY,
+		[EVMCSv1_ENFORCED] = EVMCS1_UNSUPPORTED_VMEXIT_CTRL,
 	},
 	[EVMCS_ENTRY_CTRLS] = {
-		[EVMCSv1_LEGACY] = EVMCS1_UNSUPPORTED_VMENTRY_CTRL | VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL,
+		[EVMCSv1_LEGACY]   = EVMCS1_UNSUPPORTED_VMENTRY_CTRL_LEGACY,
+		[EVMCSv1_ENFORCED] = EVMCS1_UNSUPPORTED_VMENTRY_CTRL,
 	},
 	[EVMCS_2NDEXEC] = {
-		[EVMCSv1_LEGACY] = EVMCS1_UNSUPPORTED_2NDEXEC | SECONDARY_EXEC_TSC_SCALING,
+		[EVMCSv1_LEGACY]   = EVMCS1_UNSUPPORTED_2NDEXEC_LEGACY,
+		[EVMCSv1_ENFORCED] = EVMCS1_UNSUPPORTED_2NDEXEC,
 	},
 	[EVMCS_PINCTRL] = {
-		[EVMCSv1_LEGACY] = EVMCS1_UNSUPPORTED_PINCTRL,
+		[EVMCSv1_LEGACY]   = EVMCS1_UNSUPPORTED_PINCTRL_LEGACY,
+		[EVMCSv1_ENFORCED] = EVMCS1_UNSUPPORTED_PINCTRL,
 	},
 	[EVMCS_VMFUNC] = {
-		[EVMCSv1_LEGACY] = EVMCS1_UNSUPPORTED_VMFUNC,
+		[EVMCSv1_LEGACY]   = EVMCS1_UNSUPPORTED_VMFUNC_LEGACY,
+		[EVMCSv1_ENFORCED] = EVMCS1_UNSUPPORTED_VMFUNC,
 	},
 };
 
-static u32 evmcs_get_unsupported_ctls(enum evmcs_ctrl_type ctrl_type)
+static u32 evmcs_get_unsupported_ctls(struct vcpu_vmx *vmx,
+				      enum evmcs_ctrl_type ctrl_type)
 {
 	enum evmcs_revision evmcs_rev = EVMCSv1_LEGACY;
 
+	if (vmx->nested.enforce_evmcs)
+		evmcs_rev = EVMCSv1_ENFORCED;
+
 	return evmcs_unsupported_ctrls[ctrl_type][evmcs_rev];
 }
 
-void nested_evmcs_filter_control_msr(u32 msr_index, u64 *pdata)
+u64 nested_evmcs_get_unsupported_ctrls(struct vcpu_vmx *vmx, u32 msr_index)
 {
-	u32 ctl_low = (u32)*pdata;
-	u32 ctl_high = (u32)(*pdata >> 32);
-
-	/*
-	 * Hyper-V 2016 and 2019 try using these features even when eVMCS
-	 * is enabled but there are no corresponding fields.
-	 */
 	switch (msr_index) {
 	case MSR_IA32_VMX_EXIT_CTLS:
 	case MSR_IA32_VMX_TRUE_EXIT_CTLS:
-		ctl_high &= ~evmcs_get_unsupported_ctls(EVMCS_EXIT_CTRLS);
-		break;
+		return evmcs_get_unsupported_ctls(vmx, EVMCS_EXIT_CTRLS);
 	case MSR_IA32_VMX_ENTRY_CTLS:
 	case MSR_IA32_VMX_TRUE_ENTRY_CTLS:
-		ctl_high &= ~evmcs_get_unsupported_ctls(EVMCS_ENTRY_CTRLS);
-		break;
+		return evmcs_get_unsupported_ctls(vmx, EVMCS_ENTRY_CTRLS);
 	case MSR_IA32_VMX_PROCBASED_CTLS2:
-		ctl_high &= ~evmcs_get_unsupported_ctls(EVMCS_2NDEXEC);
-		break;
+		return evmcs_get_unsupported_ctls(vmx, EVMCS_2NDEXEC);
 	case MSR_IA32_VMX_TRUE_PINBASED_CTLS:
 	case MSR_IA32_VMX_PINBASED_CTLS:
-		ctl_high &= ~evmcs_get_unsupported_ctls(EVMCS_PINCTRL);
-		break;
+		return evmcs_get_unsupported_ctls(vmx, EVMCS_PINCTRL);
 	case MSR_IA32_VMX_VMFUNC:
-		ctl_low &= ~evmcs_get_unsupported_ctls(EVMCS_VMFUNC);
-		break;
+		return evmcs_get_unsupported_ctls(vmx, EVMCS_VMFUNC);
 	}
+	return 0;
+}
+
+void nested_evmcs_filter_control_msr(struct kvm_vcpu *vcpu,
+				     struct msr_data *msr_info)
+{
+	struct vcpu_vmx *vmx = to_vmx(vcpu);
+	u64 unsupported_ctrls;
+
+	if (!msr_info->host_initiated && !guest_cpuid_has_evmcs(vcpu))
+		return;
+
+	if (msr_info->host_initiated && !vmx->nested.enforce_evmcs)
+		return;
 
-	*pdata = ctl_low | ((u64)ctl_high << 32);
+	unsupported_ctrls = nested_evmcs_get_unsupported_ctrls(vmx, msr_info->index);
+	if (msr_info->index == MSR_IA32_VMX_VMFUNC)
+		msr_info->data &= ~unsupported_ctrls;
+	else
+		msr_info->data &= ~(unsupported_ctrls << 32);
 }
 
-static bool nested_evmcs_is_valid_controls(enum evmcs_ctrl_type ctrl_type,
+static bool nested_evmcs_is_valid_controls(struct kvm_vcpu *vcpu,
+					   enum evmcs_ctrl_type ctrl_type,
 					   u32 val)
 {
-	return !(val & evmcs_get_unsupported_ctls(ctrl_type));
+	return !(val & evmcs_get_unsupported_ctls(to_vmx(vcpu), ctrl_type));
 }
 
-int nested_evmcs_check_controls(struct vmcs12 *vmcs12)
+int nested_evmcs_check_controls(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
 {
-	if (CC(!nested_evmcs_is_valid_controls(EVMCS_PINCTRL,
+	if (CC(!nested_evmcs_is_valid_controls(vcpu, EVMCS_PINCTRL,
 					       vmcs12->pin_based_vm_exec_control)))
 		return -EINVAL;
 
-	if (CC(!nested_evmcs_is_valid_controls(EVMCS_2NDEXEC,
+	if (CC(!nested_evmcs_is_valid_controls(vcpu, EVMCS_2NDEXEC,
 					       vmcs12->secondary_vm_exec_control)))
 		return -EINVAL;
 
-	if (CC(!nested_evmcs_is_valid_controls(EVMCS_EXIT_CTRLS,
+	if (CC(!nested_evmcs_is_valid_controls(vcpu, EVMCS_EXIT_CTRLS,
 					       vmcs12->vm_exit_controls)))
 		return -EINVAL;
 
-	if (CC(!nested_evmcs_is_valid_controls(EVMCS_ENTRY_CTRLS,
+	if (CC(!nested_evmcs_is_valid_controls(vcpu, EVMCS_ENTRY_CTRLS,
 					       vmcs12->vm_entry_controls)))
 		return -EINVAL;
 
-	if (CC(!nested_evmcs_is_valid_controls(EVMCS_VMFUNC,
+	if (CC(!nested_evmcs_is_valid_controls(vcpu, EVMCS_VMFUNC,
 					       vmcs12->vm_function_control)))
 		return -EINVAL;
 
 	return 0;
 }
 
-int nested_enable_evmcs(struct kvm_vcpu *vcpu,
-			uint16_t *vmcs_version)
+int nested_enable_evmcs(struct kvm_vcpu *vcpu, uint16_t *vmcs_version,
+			bool enforce_evmcs)
 {
 	struct vcpu_vmx *vmx = to_vmx(vcpu);
 
+	if (vmx->nested.enlightened_vmcs_enabled && enforce_evmcs)
+		return -EINVAL;
+
 	vmx->nested.enlightened_vmcs_enabled = true;
+	vmx->nested.enforce_evmcs = enforce_evmcs;
 
 	if (vmcs_version)
 		*vmcs_version = nested_get_evmcs_version(vcpu);
diff --git a/arch/x86/kvm/vmx/evmcs.h b/arch/x86/kvm/vmx/evmcs.h
index f886a8ff0342..e2b3aeee57ac 100644
--- a/arch/x86/kvm/vmx/evmcs.h
+++ b/arch/x86/kvm/vmx/evmcs.h
@@ -13,6 +13,7 @@
 #include "vmcs12.h"
 
 struct vmcs_config;
+struct vcpu_vmx;
 
 DECLARE_STATIC_KEY_FALSE(enable_evmcs);
 
@@ -66,6 +67,14 @@ DECLARE_STATIC_KEY_FALSE(enable_evmcs);
 #define EVMCS1_UNSUPPORTED_VMENTRY_CTRL (VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL)
 #define EVMCS1_UNSUPPORTED_VMFUNC (VMX_VMFUNC_EPTP_SWITCHING)
 
+/* TODO: explicitly define these */
+#define EVMCS1_UNSUPPORTED_PINCTRL_LEGACY	EVMCS1_UNSUPPORTED_PINCTRL
+#define EVMCS1_UNSUPPORTED_EXEC_CTRL_LEGACY	EVMCS1_UNSUPPORTED_EXEC_CTRL
+#define EVMCS1_UNSUPPORTED_2NDEXEC_LEGACY	EVMCS1_UNSUPPORTED_2NDEXEC
+#define EVMCS1_UNSUPPORTED_VMEXIT_CTRL_LEGACY	EVMCS1_UNSUPPORTED_VMEXIT_CTRL
+#define EVMCS1_UNSUPPORTED_VMENTRY_CTRL_LEGACY	EVMCS1_UNSUPPORTED_VMENTRY_CTRL
+#define EVMCS1_UNSUPPORTED_VMFUNC_LEGACY	EVMCS1_UNSUPPORTED_VMFUNC
+
 struct evmcs_field {
 	u16 offset;
 	u16 clean_field;
@@ -241,9 +250,11 @@ enum nested_evmptrld_status {
 
 bool nested_enlightened_vmentry(struct kvm_vcpu *vcpu, u64 *evmcs_gpa);
 uint16_t nested_get_evmcs_version(struct kvm_vcpu *vcpu);
-int nested_enable_evmcs(struct kvm_vcpu *vcpu,
-			uint16_t *vmcs_version);
-void nested_evmcs_filter_control_msr(u32 msr_index, u64 *pdata);
-int nested_evmcs_check_controls(struct vmcs12 *vmcs12);
+int nested_enable_evmcs(struct kvm_vcpu *vcpu, uint16_t *vmcs_version,
+			bool enforce_evmcs);
+u64 nested_evmcs_get_unsupported_ctrls(struct vcpu_vmx *vmx, u32 msr_index);
+void nested_evmcs_filter_control_msr(struct kvm_vcpu *vcpu,
+				     struct msr_data *msr_info);
+int nested_evmcs_check_controls(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12);
 
 #endif /* __KVM_X86_VMX_EVMCS_H */
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 28f9d64851b3..52d299b9263b 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -1279,12 +1279,17 @@ static void vmx_get_control_msr(struct nested_vmx_msrs *msrs, u32 msr_index,
 static int
 vmx_restore_control_msr(struct vcpu_vmx *vmx, u32 msr_index, u64 data)
 {
-	u32 *lowp, *highp;
+	u32 *lowp, *highp, high;
 	u64 supported;
 
 	vmx_get_control_msr(&vmcs_config.nested, msr_index, &lowp, &highp);
 
-	supported = vmx_control_msr(*lowp, *highp);
+	/* Do not overwrite the global vmcs_config.nested! */
+	high = *highp;
+	if (vmx->nested.enforce_evmcs)
+		high &= ~nested_evmcs_get_unsupported_ctrls(vmx, msr_index);
+
+	supported = vmx_control_msr(*lowp, high);
 
 	/* Check must-be-1 bits are still 1. */
 	if (!is_bitwise_subset(data, supported, GENMASK_ULL(31, 0)))
@@ -1435,6 +1440,10 @@ int vmx_set_vmx_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 data)
 	case MSR_IA32_VMX_VMFUNC:
 		if (data & ~vmcs_config.nested.vmfunc_controls)
 			return -EINVAL;
+		if (vmx->nested.enforce_evmcs &&
+		    (data & nested_evmcs_get_unsupported_ctrls(vmx, MSR_IA32_VMX_VMFUNC)))
+			return -EINVAL;
+
 		vmx->nested.msrs.vmfunc_controls = data;
 		return 0;
 	default:
@@ -2864,7 +2873,7 @@ static int nested_vmx_check_controls(struct kvm_vcpu *vcpu,
 		return -EINVAL;
 
 	if (guest_cpuid_has_evmcs(vcpu))
-		return nested_evmcs_check_controls(vmcs12);
+		return nested_evmcs_check_controls(vcpu, vmcs12);
 
 	return 0;
 }
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index d4ed802947d7..73f9074efc61 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -1924,15 +1924,11 @@ static int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 				    &msr_info->data))
 			return 1;
 		/*
-		 * Enlightened VMCS v1 doesn't have certain VMCS fields but
-		 * instead of just ignoring the features, different Hyper-V
-		 * versions are either trying to use them and fail or do some
-		 * sanity checking and refuse to boot. Filter all unsupported
-		 * features out.
+		 * New Enlightened VMCS fields always lag behind their hardware
+		 * counterparts, filter out fields that are not yet defined.
 		 */
-		if (!msr_info->host_initiated && guest_cpuid_has_evmcs(vcpu))
-			nested_evmcs_filter_control_msr(msr_info->index,
-							&msr_info->data);
+		if (vmx->nested.enlightened_vmcs_enabled)
+			nested_evmcs_filter_control_msr(vcpu, msr_info);
 		break;
 	case MSR_IA32_RTIT_CTL:
 		if (!vmx_pt_mode_is_host_guest())
diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h
index 35c7e6aef301..a7a05b5e41d2 100644
--- a/arch/x86/kvm/vmx/vmx.h
+++ b/arch/x86/kvm/vmx/vmx.h
@@ -197,6 +197,8 @@ struct nested_vmx {
 	 */
 	bool enlightened_vmcs_enabled;
 
+	bool enforce_evmcs;
+
 	/* L2 must run next, and mustn't decide to exit to L1. */
 	bool nested_run_pending;
 
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index d7374d768296..fb5cecb19cf5 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -4452,6 +4452,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 		r = kvm_x86_ops.enable_direct_tlbflush != NULL;
 		break;
 	case KVM_CAP_HYPERV_ENLIGHTENED_VMCS:
+	case KVM_CAP_HYPERV_ENLIGHTENED_VMCS2:
 		r = kvm_x86_ops.nested_ops->enable_evmcs != NULL;
 		break;
 	case KVM_CAP_SMALLER_MAXPHYADDR:
@@ -5429,9 +5430,13 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
 		return kvm_hv_activate_synic(vcpu, cap->cap ==
 					     KVM_CAP_HYPERV_SYNIC2);
 	case KVM_CAP_HYPERV_ENLIGHTENED_VMCS:
+	case KVM_CAP_HYPERV_ENLIGHTENED_VMCS2: {
+		bool enforce_evmcs = cap->cap == KVM_CAP_HYPERV_ENLIGHTENED_VMCS2;
+
 		if (!kvm_x86_ops.nested_ops->enable_evmcs)
 			return -ENOTTY;
-		r = kvm_x86_ops.nested_ops->enable_evmcs(vcpu, &vmcs_version);
+		r = kvm_x86_ops.nested_ops->enable_evmcs(vcpu, &vmcs_version,
+							 enforce_evmcs);
 		if (!r) {
 			user_ptr = (void __user *)(uintptr_t)cap->args[0];
 			if (copy_to_user(user_ptr, &vmcs_version,
@@ -5439,6 +5444,7 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
 				r = -EFAULT;
 		}
 		return r;
+	}
 	case KVM_CAP_HYPERV_DIRECT_TLBFLUSH:
 		if (!kvm_x86_ops.enable_direct_tlbflush)
 			return -ENOTTY;
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index eed0315a77a6..ba08d6f74267 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1177,6 +1177,7 @@ struct kvm_ppc_resize_hpt {
 #define KVM_CAP_VM_DISABLE_NX_HUGE_PAGES 220
 #define KVM_CAP_S390_ZPCI_OP 221
 #define KVM_CAP_S390_CPU_TOPOLOGY 222
+#define KVM_CAP_HYPERV_ENLIGHTENED_VMCS2 223
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
-- 
2.37.1.595.g718a3a8f04-goog




[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux