[PATCH 18/23] KVM: x86/pmu: Handle GLOBAL_STATUS MSRs under PerfMon masking

Zide Chen <[email protected]>
Newsgroups org.kernel.vger.kvm,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
For IA32_PERF_GLOBAL_STATUS{,_SET,_RESET}, the effective mask under
PerfMon masking is perfmon_mask. Writes that set bits outside the
effective mask must #GP, so use ~perfmon_mask as the effective
reserved-bit mask instead of global_status_rsvd.

At guest context load, keep host-owned GLOBAL_STATUS bits unchanged
because system-wide events may be scheduled on host-owned resources
while running in VMX non-root mode.

Likewise, at guest context put, preserve host-owned bits and clear
only the guest-owned subset from hardware GLOBAL_STATUS, leaving
pmu->global_status containing only guest-owned bits.

Signed-off-by: Zide Chen <[email protected]>
---
 arch/x86/kvm/pmu.c           | 13 ++++++++-----
 arch/x86/kvm/vmx/pmu_intel.c | 17 ++++++++++++++++-
 2 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index b051ba66edce..23177c38f286 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -922,12 +922,15 @@ int kvm_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
 	u32 msr = msr_info->index;
 	u64 data = msr_info->data;
-	u64 diff;
+	u64 global_status_rsvd, diff;
 
 	/*
 	 * Note, AMD ignores writes to reserved bits and read-only PMU MSRs,
 	 * whereas Intel generates #GP on attempts to write reserved/RO MSRs.
 	 */
+	global_status_rsvd = kvm_vcpu_has_perfmon_mask(vcpu) ?
+			     ~pmu->perfmon_mask : pmu->global_status_rsvd;
+
 	switch (msr) {
 	case MSR_CORE_PERF_GLOBAL_STATUS:
 		if (!msr_info->host_initiated)
@@ -938,7 +941,7 @@ int kvm_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 		if (!msr_info->host_initiated)
 			break;
 
-		if (data & pmu->global_status_rsvd)
+		if (data & global_status_rsvd)
 			return 1;
 
 		pmu->global_status = data;
@@ -967,7 +970,7 @@ int kvm_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 		 * GLOBAL_OVF_CTRL, a.k.a. GLOBAL STATUS_RESET, clears bits in
 		 * GLOBAL_STATUS, and so the set of reserved bits is the same.
 		 */
-		if (data & pmu->global_status_rsvd)
+		if (data & global_status_rsvd)
 			return 1;
 		fallthrough;
 	case MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR:
@@ -975,14 +978,14 @@ int kvm_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 			pmu->global_status &= ~data;
 		break;
 	case MSR_CORE_PERF_GLOBAL_STATUS_SET:
-		if (data & pmu->global_status_rsvd)
+		if (data & global_status_rsvd)
 			return 1;
 		if (!msr_info->host_initiated)
 			pmu->global_status |= data;
 		break;
 	case MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_SET:
 		if (!msr_info->host_initiated)
-			pmu->global_status |= data & ~pmu->global_status_rsvd;
+			pmu->global_status |= data & ~global_status_rsvd;
 		break;
 	default:
 		kvm_pmu_mark_pmc_in_use(vcpu, msr_info->index);
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index d0373de951d5..e43380f79c72 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -968,6 +968,14 @@ static void intel_mediated_pmu_load(struct kvm_vcpu *vcpu, u64 host_global_ctrl)
 
 	rdmsrq(MSR_CORE_PERF_GLOBAL_STATUS, global_status);
 	toggle = pmu->global_status ^ global_status;
+
+	/*
+	 * Restrict OVF_CTRL/STATUS_SET writes to guest-owned bits under
+	 * PerfMon masking.
+	 */
+	if (kvm_vcpu_has_perfmon_mask(vcpu))
+		toggle &= pmu->perfmon_mask;
+
 	if (global_status & toggle)
 		wrmsrq(MSR_CORE_PERF_GLOBAL_OVF_CTRL, global_status & toggle);
 	if (pmu->global_status & toggle)
@@ -984,7 +992,14 @@ static void intel_mediated_pmu_put(struct kvm_vcpu *vcpu)
 	/* MSR_CORE_PERF_GLOBAL_CTRL is already saved at VM-exit. */
 	rdmsrq(MSR_CORE_PERF_GLOBAL_STATUS, pmu->global_status);
 
-	/* Clear hardware MSR_CORE_PERF_GLOBAL_STATUS MSR, if non-zero. */
+	/*
+	 * Clear only the guest-owned bits from the hardware GLOBAL_STATUS
+	 * if any are set. pmu->global_status is then left holding just the
+	 * guest-owned subset.
+	 */
+	if (kvm_vcpu_has_perfmon_mask(vcpu))
+		pmu->global_status &= pmu->perfmon_mask;
+
 	if (pmu->global_status)
 		wrmsrq(MSR_CORE_PERF_GLOBAL_OVF_CTRL, pmu->global_status);
 
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.