[PATCH v2 10/36] KVM: nVMX: Supports VMX tertiary controls and GUEST_APIC_TIMER bit

isaku.yamahata@intel.com posted 36 patches 3 weeks, 6 days ago
[PATCH v2 10/36] KVM: nVMX: Supports VMX tertiary controls and GUEST_APIC_TIMER bit
Posted by isaku.yamahata@intel.com 3 weeks, 6 days ago
From: Isaku Yamahata <isaku.yamahata@intel.com>

Emulate MSR_IA32_VMX_PROCBASED_CTLS3 to advertise APIC timer virtualization
feature to the L2 guest.

Reported-by: syzbot+ci66a37fb2e2f8de71@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/kvm/6982f952.050a0220.3b3015.0012.GAE@google.com/
Signed-off-by: Isaku Yamahata <isaku.yamahata@intel.com>
---
Changes:
v1 -> v2:
- When in-kernel lapic is disabled, disable nested apic timer
  virtualization.
- disable apic timer virtualization bit in nested tertiary vm exec control
  when in-kernel lapic is disabled.
---
 arch/x86/kvm/vmx/capabilities.h |  1 +
 arch/x86/kvm/vmx/hyperv.c       |  7 +++++
 arch/x86/kvm/vmx/nested.c       | 48 +++++++++++++++++++++++++++++++++
 arch/x86/kvm/vmx/nested.h       | 11 ++++++++
 arch/x86/kvm/vmx/vmx.c          |  6 ++++-
 arch/x86/kvm/x86.h              |  2 +-
 6 files changed, 73 insertions(+), 2 deletions(-)

diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h
index c5cb098f579b..8d67be77f02c 100644
--- a/arch/x86/kvm/vmx/capabilities.h
+++ b/arch/x86/kvm/vmx/capabilities.h
@@ -47,6 +47,7 @@ struct nested_vmx_msrs {
 	u64 cr4_fixed1;
 	u64 vmcs_enum;
 	u64 vmfunc_controls;
+	u64 tertiary_ctls;
 };
 
 struct vmcs_config {
diff --git a/arch/x86/kvm/vmx/hyperv.c b/arch/x86/kvm/vmx/hyperv.c
index fa41d036acd4..2731c2e4b0e5 100644
--- a/arch/x86/kvm/vmx/hyperv.c
+++ b/arch/x86/kvm/vmx/hyperv.c
@@ -141,6 +141,13 @@ void nested_evmcs_filter_control_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 *
 	case MSR_IA32_VMX_PROCBASED_CTLS2:
 		ctl_high &= evmcs_get_supported_ctls(EVMCS_2NDEXEC);
 		break;
+	case MSR_IA32_VMX_PROCBASED_CTLS3:
+		/*
+		 * tertiary procbased controls are 64-bit. 0 means unsupported,
+		 * 1 supported.
+		 */
+		*pdata &= evmcs_get_supported_ctls(EVMCS_3RDEXEC);
+		return;
 	case MSR_IA32_VMX_TRUE_PINBASED_CTLS:
 	case MSR_IA32_VMX_PINBASED_CTLS:
 		ctl_high &= evmcs_get_supported_ctls(EVMCS_PINCTRL);
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 5f0ac8acd768..be6b92b3c66a 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -218,6 +218,11 @@ static inline bool vmx_control_verify(u32 control, u32 low, u32 high)
 	return fixed_bits_valid(control, low, high);
 }
 
+static inline bool vmx_control64_verify(u64 control, u64 msr)
+{
+	return !(control & ~msr);
+}
+
 static inline u64 vmx_control_msr(u32 low, u32 high)
 {
 	return low | ((u64)high << 32);
@@ -1511,6 +1516,25 @@ int vmx_set_vmx_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 	case MSR_IA32_VMX_TRUE_ENTRY_CTLS:
 	case MSR_IA32_VMX_PROCBASED_CTLS2:
 		return vmx_restore_control_msr(vmx, msr_index, data);
+	case MSR_IA32_VMX_PROCBASED_CTLS3: {
+		u64 ctls3;
+
+		if (!__nested_cpu_supports_tertiary_ctls(&vmcs_config.nested))
+			return -EINVAL;
+
+		/* read-only for guest. */
+		if (!msr_info->host_initiated)
+			return -EINVAL;
+
+		ctls3 = vmcs_config.nested.tertiary_ctls;
+		if (!nested_cpu_can_support_apic_virt_timer(vcpu))
+			ctls3 &= ~TERTIARY_EXEC_GUEST_APIC_TIMER;
+
+		if (!vmx_control64_verify(data, ctls3))
+			return -EINVAL;
+		vmx->nested.msrs.tertiary_ctls = data;
+		return 0;
+	}
 	case MSR_IA32_VMX_MISC:
 		return vmx_restore_vmx_misc(vmx, data);
 	case MSR_IA32_VMX_CR0_FIXED0:
@@ -1608,6 +1632,16 @@ int vmx_get_vmx_msr(struct nested_vmx_msrs *msrs, struct msr_data *msr_info)
 			msrs->secondary_ctls_low,
 			msrs->secondary_ctls_high);
 		break;
+	case MSR_IA32_VMX_PROCBASED_CTLS3:
+		if (!__nested_cpu_supports_tertiary_ctls(&vmcs_config.nested))
+			return KVM_MSR_RET_UNSUPPORTED;
+
+		if (!msr_info->host_initiated &&
+		    !__nested_cpu_supports_tertiary_ctls(msrs))
+			return -EINVAL;
+
+		*pdata = msrs->tertiary_ctls;
+		break;
 	case MSR_IA32_VMX_EPT_VPID_CAP:
 		*pdata = msrs->ept_caps |
 			((u64)msrs->vpid_caps << 32);
@@ -7285,6 +7319,18 @@ static void nested_vmx_setup_secondary_ctls(u32 ept_caps,
 		msrs->secondary_ctls_high |= SECONDARY_EXEC_ENCLS_EXITING;
 }
 
+static void nested_vmx_setup_tertiary_ctls(struct vmcs_config *vmcs_conf,
+					   struct nested_vmx_msrs *msrs)
+{
+	msrs->tertiary_ctls = vmcs_conf->cpu_based_3rd_exec_ctrl;
+
+	msrs->tertiary_ctls &= TERTIARY_EXEC_GUEST_APIC_TIMER;
+
+	if (msrs->tertiary_ctls)
+		msrs->procbased_ctls_high |=
+			CPU_BASED_ACTIVATE_TERTIARY_CONTROLS;
+}
+
 static void nested_vmx_setup_misc_data(struct vmcs_config *vmcs_conf,
 				       struct nested_vmx_msrs *msrs)
 {
@@ -7373,6 +7419,8 @@ void nested_vmx_setup_ctls_msrs(struct vmcs_config *vmcs_conf, u32 ept_caps)
 
 	nested_vmx_setup_secondary_ctls(ept_caps, vmcs_conf, msrs);
 
+	nested_vmx_setup_tertiary_ctls(vmcs_conf, msrs);
+
 	nested_vmx_setup_misc_data(vmcs_conf, msrs);
 
 	nested_vmx_setup_basic(msrs);
diff --git a/arch/x86/kvm/vmx/nested.h b/arch/x86/kvm/vmx/nested.h
index d0257447b7cb..8c25054a710e 100644
--- a/arch/x86/kvm/vmx/nested.h
+++ b/arch/x86/kvm/vmx/nested.h
@@ -152,6 +152,17 @@ static inline bool nested_cpu_has_vmx_shadow_vmcs(struct kvm_vcpu *vcpu)
 		SECONDARY_EXEC_SHADOW_VMCS;
 }
 
+static inline bool __nested_cpu_supports_tertiary_ctls(struct nested_vmx_msrs *msrs)
+{
+	return msrs->procbased_ctls_high & CPU_BASED_ACTIVATE_TERTIARY_CONTROLS;
+}
+
+/* APIC TIMER VIRTUALIZATION requires in-kernel lapic. */
+static inline bool nested_cpu_can_support_apic_virt_timer(struct kvm_vcpu *vcpu)
+{
+	return cpu_has_vmx_apic_timer_virt() && lapic_in_kernel(vcpu);
+}
+
 static inline bool nested_cpu_has(struct vmcs12 *vmcs12, u32 bit)
 {
 	return vmcs12->cpu_based_vm_exec_control & bit;
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 9177b693df1b..25f31103cb21 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -4985,9 +4985,13 @@ static void __vmx_vcpu_reset(struct kvm_vcpu *vcpu)
 	init_vmcs(vmx);
 
 	if (nested &&
-	    kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_STUFF_FEATURE_MSRS))
+	    kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_STUFF_FEATURE_MSRS)) {
 		memcpy(&vmx->nested.msrs, &vmcs_config.nested, sizeof(vmx->nested.msrs));
 
+		if (!nested_cpu_can_support_apic_virt_timer(vcpu))
+			vmx->nested.msrs.tertiary_ctls &= ~TERTIARY_EXEC_GUEST_APIC_TIMER;
+	}
+
 	vcpu_setup_sgx_lepubkeyhash(vcpu);
 
 	vmx->nested.posted_intr_nv = -1;
diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h
index 44a28d343d40..1de31aae9c6c 100644
--- a/arch/x86/kvm/x86.h
+++ b/arch/x86/kvm/x86.h
@@ -92,7 +92,7 @@ do {											\
  * associated feature that KVM supports for nested virtualization.
  */
 #define KVM_FIRST_EMULATED_VMX_MSR	MSR_IA32_VMX_BASIC
-#define KVM_LAST_EMULATED_VMX_MSR	MSR_IA32_VMX_VMFUNC
+#define KVM_LAST_EMULATED_VMX_MSR	MSR_IA32_VMX_PROCBASED_CTLS3
 
 #define KVM_DEFAULT_PLE_GAP		128
 #define KVM_VMX_DEFAULT_PLE_WINDOW	4096
-- 
2.45.2