[v8,26/26] KVM: selftests: Extend PMU counters test to validate RDPMC after WRMSR

Message ID 20231110021306.1269082-27-seanjc@google.com
State New
Headers
Series KVM: x86/pmu: selftests: Fixes and new tests |

Commit Message

Sean Christopherson Nov. 10, 2023, 2:13 a.m. UTC
  Extend the read/write PMU counters subtest to verify that RDPMC also reads
back the written value.  Opportunsitically verify that attempting to use
the "fast" mode of RDPMC fails, as the "fast" flag is only supported by
non-architectural PMUs, which KVM doesn't virtualize.

Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 .../selftests/kvm/x86_64/pmu_counters_test.c  | 31 +++++++++++++++++++
 1 file changed, 31 insertions(+)
  

Comments

Jinrong Liang Nov. 13, 2023, 11:41 a.m. UTC | #1
在 2023/11/10 10:13, Sean Christopherson 写道:
> Extend the read/write PMU counters subtest to verify that RDPMC also reads
> back the written value.  Opportunsitically verify that attempting to use
> the "fast" mode of RDPMC fails, as the "fast" flag is only supported by
> non-architectural PMUs, which KVM doesn't virtualize.
> 
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> ---
>   .../selftests/kvm/x86_64/pmu_counters_test.c  | 31 +++++++++++++++++++
>   1 file changed, 31 insertions(+)
> 
> diff --git a/tools/testing/selftests/kvm/x86_64/pmu_counters_test.c b/tools/testing/selftests/kvm/x86_64/pmu_counters_test.c
> index cb808ac827ba..248ebe8c0577 100644
> --- a/tools/testing/selftests/kvm/x86_64/pmu_counters_test.c
> +++ b/tools/testing/selftests/kvm/x86_64/pmu_counters_test.c
> @@ -328,6 +328,7 @@ __GUEST_ASSERT(expect_gp ? vector == GP_VECTOR : !vector,			\
>   static void guest_rd_wr_counters(uint32_t base_msr, uint8_t nr_possible_counters,
>   				 uint8_t nr_counters, uint32_t or_mask)
>   {
> +	const bool pmu_has_fast_mode = !guest_get_pmu_version();
>   	uint8_t i;
>   
>   	for (i = 0; i < nr_possible_counters; i++) {
> @@ -352,6 +353,7 @@ static void guest_rd_wr_counters(uint32_t base_msr, uint8_t nr_possible_counters
>   		const uint64_t expected_val = expect_success ? test_val : 0;
>   		const bool expect_gp = !expect_success && msr != MSR_P6_PERFCTR0 &&
>   				       msr != MSR_P6_PERFCTR1;
> +		uint32_t rdpmc_idx;
>   		uint8_t vector;
>   		uint64_t val;
>   
> @@ -365,6 +367,35 @@ static void guest_rd_wr_counters(uint32_t base_msr, uint8_t nr_possible_counters
>   		if (!expect_gp)
>   			GUEST_ASSERT_PMC_VALUE(RDMSR, msr, val, expected_val);
>   
> +		rdpmc_idx = i;
> +		if (base_msr == MSR_CORE_PERF_FIXED_CTR0)
> +			rdpmc_idx |= INTEL_RDPMC_FIXED;
> +
> +		/* Redo the read tests with RDPMC, and with forced emulation. */
> +		vector = rdpmc_safe(rdpmc_idx, &val);
> +		GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, !expect_success, vector);
> +		if (expect_success)
> +			GUEST_ASSERT_PMC_VALUE(RDPMC, rdpmc_idx, val, expected_val);
> +
> +		vector = rdpmc_safe_fep(rdpmc_idx, &val);
> +		GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, !expect_success, vector);
> +		if (expect_success)
> +			GUEST_ASSERT_PMC_VALUE(RDPMC, rdpmc_idx, val, expected_val);
> +
> +		/*
> +		 * KVM doesn't support non-architectural PMUs, i.e. it should
> +		 * impossible to have fast mode RDPMC.  Verify that attempting
> +		 * to use fast RDPMC always #GPs.
> +		 */
> +		GUEST_ASSERT(!expect_success || !pmu_has_fast_mode);
> +		rdpmc_idx |= INTEL_RDPMC_FAST;
> +
> +		vector = rdpmc_safe(rdpmc_idx, &val);
> +		GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, true, vector);
> +
> +		vector = rdpmc_safe_fep(rdpmc_idx, &val);
> +		GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, true, vector);
> +
>   		vector = wrmsr_safe(msr, 0);
>   		GUEST_ASSERT_PMC_MSR_ACCESS(WRMSR, msr, expect_gp, vector);
>   	}
> 

This test case failed on my Intel machine.

Error message:
Testing arch events, PMU version 0, perf_caps = 0
Testing GP counters, PMU version 0, perf_caps = 0
==== Test Assertion Failure ====
   lib/x86_64/processor.c:1100: Unhandled exception in guest
   pid=464480 tid=464480 errno=4 - Interrupted system call
      1	0x00000000004120e1: assert_on_unhandled_exception 于 
processor.c:1146
      2	0x00000000004062d9: _vcpu_run 于 kvm_util.c:1634
      3	0x00000000004062fa: vcpu_run 于 kvm_util.c:1645
      4	0x0000000000403697: run_vcpu 于 pmu_counters_test.c:56
      5	0x00000000004026fc: test_gp_counters 于 pmu_counters_test.c:434
      6	(已内连入)test_intel_counters 于 pmu_counters_test.c:580
      7	(已内连入)main 于 pmu_counters_test.c:604
      8	0x00007f7a2f03ad84: ?? ??:0
      9	0x00000000004028bd: _start 于 ??:?
   Unhandled exception '0x6' at guest RIP '0x402bab'
  
Sean Christopherson Nov. 13, 2023, 1:40 p.m. UTC | #2
On Mon, Nov 13, 2023, Jinrong Liang wrote:
> 在 2023/11/10 10:13, Sean Christopherson 写道:
> > Extend the read/write PMU counters subtest to verify that RDPMC also reads
> > back the written value.  Opportunsitically verify that attempting to use
> > the "fast" mode of RDPMC fails, as the "fast" flag is only supported by
> > non-architectural PMUs, which KVM doesn't virtualize.
> > 
> > Signed-off-by: Sean Christopherson <seanjc@google.com>
> > ---
> > +		/* Redo the read tests with RDPMC, and with forced emulation. */
> > +		vector = rdpmc_safe(rdpmc_idx, &val);
> > +		GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, !expect_success, vector);
> > +		if (expect_success)
> > +			GUEST_ASSERT_PMC_VALUE(RDPMC, rdpmc_idx, val, expected_val);
> > +
> > +		vector = rdpmc_safe_fep(rdpmc_idx, &val);
> > +		GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, !expect_success, vector);
> > +		if (expect_success)
> > +			GUEST_ASSERT_PMC_VALUE(RDPMC, rdpmc_idx, val, expected_val);

> This test case failed on my Intel machine.
> 
> Error message:
> Testing arch events, PMU version 0, perf_caps = 0
> Testing GP counters, PMU version 0, perf_caps = 0
> ==== Test Assertion Failure ====
>   lib/x86_64/processor.c:1100: Unhandled exception in guest
>   pid=464480 tid=464480 errno=4 - Interrupted system call
>      1	0x00000000004120e1: assert_on_unhandled_exception 于 processor.c:1146
>      2	0x00000000004062d9: _vcpu_run 于 kvm_util.c:1634
>      3	0x00000000004062fa: vcpu_run 于 kvm_util.c:1645
>      4	0x0000000000403697: run_vcpu 于 pmu_counters_test.c:56
>      5	0x00000000004026fc: test_gp_counters 于 pmu_counters_test.c:434
>      6	(已内连入)test_intel_counters 于 pmu_counters_test.c:580
>      7	(已内连入)main 于 pmu_counters_test.c:604
>      8	0x00007f7a2f03ad84: ?? ??:0
>      9	0x00000000004028bd: _start 于 ??:?
>   Unhandled exception '0x6' at guest RIP '0x402bab'

Argh, I didn't add a check to see if forced emulation is actually enabled (forced
emulation uses a magic "prefix" to trigger a #UD, which KVM intercepts; if forced
emulation isn't enabled, KVM ignores the magic prefix and reflects the #UD back
into the guest).

This fixes the test for me:

---
 .../selftests/kvm/x86_64/pmu_counters_test.c  | 42 ++++++++++++-------
 1 file changed, 26 insertions(+), 16 deletions(-)

diff --git a/tools/testing/selftests/kvm/x86_64/pmu_counters_test.c b/tools/testing/selftests/kvm/x86_64/pmu_counters_test.c
index 248ebe8c0577..ae5f6042f1e8 100644
--- a/tools/testing/selftests/kvm/x86_64/pmu_counters_test.c
+++ b/tools/testing/selftests/kvm/x86_64/pmu_counters_test.c
@@ -325,6 +325,26 @@ __GUEST_ASSERT(expect_gp ? vector == GP_VECTOR : !vector,			\
 		       "Expected " #insn "(0x%x) to yield 0x%lx, got 0x%lx",	\
 		       msr, expected_val, val);
 
+static void guest_test_rdpmc(uint32_t rdpmc_idx, bool expect_success,
+			     uint64_t expected_val)
+{
+	uint8_t vector;
+	uint64_t val;
+
+	vector = rdpmc_safe(rdpmc_idx, &val);
+	GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, !expect_success, vector);
+	if (expect_success)
+		GUEST_ASSERT_PMC_VALUE(RDPMC, rdpmc_idx, val, expected_val);
+
+	if (!is_forced_emulation_enabled)
+		return;
+
+	vector = rdpmc_safe_fep(rdpmc_idx, &val);
+	GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, !expect_success, vector);
+	if (expect_success)
+		GUEST_ASSERT_PMC_VALUE(RDPMC, rdpmc_idx, val, expected_val);
+}
+
 static void guest_rd_wr_counters(uint32_t base_msr, uint8_t nr_possible_counters,
 				 uint8_t nr_counters, uint32_t or_mask)
 {
@@ -367,20 +387,15 @@ static void guest_rd_wr_counters(uint32_t base_msr, uint8_t nr_possible_counters
 		if (!expect_gp)
 			GUEST_ASSERT_PMC_VALUE(RDMSR, msr, val, expected_val);
 
+		/*
+		 * Redo the read tests with RDPMC, which has different indexing
+		 * semantics and additional capabilities.
+		 */
 		rdpmc_idx = i;
 		if (base_msr == MSR_CORE_PERF_FIXED_CTR0)
 			rdpmc_idx |= INTEL_RDPMC_FIXED;
 
-		/* Redo the read tests with RDPMC, and with forced emulation. */
-		vector = rdpmc_safe(rdpmc_idx, &val);
-		GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, !expect_success, vector);
-		if (expect_success)
-			GUEST_ASSERT_PMC_VALUE(RDPMC, rdpmc_idx, val, expected_val);
-
-		vector = rdpmc_safe_fep(rdpmc_idx, &val);
-		GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, !expect_success, vector);
-		if (expect_success)
-			GUEST_ASSERT_PMC_VALUE(RDPMC, rdpmc_idx, val, expected_val);
+		guest_test_rdpmc(rdpmc_idx, expect_success, expected_val);
 
 		/*
 		 * KVM doesn't support non-architectural PMUs, i.e. it should
@@ -389,12 +404,7 @@ static void guest_rd_wr_counters(uint32_t base_msr, uint8_t nr_possible_counters
 		 */
 		GUEST_ASSERT(!expect_success || !pmu_has_fast_mode);
 		rdpmc_idx |= INTEL_RDPMC_FAST;
-
-		vector = rdpmc_safe(rdpmc_idx, &val);
-		GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, true, vector);
-
-		vector = rdpmc_safe_fep(rdpmc_idx, &val);
-		GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, true, vector);
+		guest_test_rdpmc(rdpmc_idx, false, -1ull);
 
 		vector = wrmsr_safe(msr, 0);
 		GUEST_ASSERT_PMC_MSR_ACCESS(WRMSR, msr, expect_gp, vector);

base-commit: 743a1a6d106931691be32e081e929d9b3de5777f
--
  
Jinrong Liang Nov. 14, 2023, 3:07 a.m. UTC | #3
Sean Christopherson <seanjc@google.com> 于2023年11月13日周一 21:40写道:
>
> On Mon, Nov 13, 2023, Jinrong Liang wrote:
> > 在 2023/11/10 10:13, Sean Christopherson 写道:
> > > Extend the read/write PMU counters subtest to verify that RDPMC also reads
> > > back the written value.  Opportunsitically verify that attempting to use
> > > the "fast" mode of RDPMC fails, as the "fast" flag is only supported by
> > > non-architectural PMUs, which KVM doesn't virtualize.
> > >
> > > Signed-off-by: Sean Christopherson <seanjc@google.com>
> > > ---
> > > +           /* Redo the read tests with RDPMC, and with forced emulation. */
> > > +           vector = rdpmc_safe(rdpmc_idx, &val);
> > > +           GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, !expect_success, vector);
> > > +           if (expect_success)
> > > +                   GUEST_ASSERT_PMC_VALUE(RDPMC, rdpmc_idx, val, expected_val);
> > > +
> > > +           vector = rdpmc_safe_fep(rdpmc_idx, &val);
> > > +           GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, !expect_success, vector);
> > > +           if (expect_success)
> > > +                   GUEST_ASSERT_PMC_VALUE(RDPMC, rdpmc_idx, val, expected_val);
>
> > This test case failed on my Intel machine.
> >
> > Error message:
> > Testing arch events, PMU version 0, perf_caps = 0
> > Testing GP counters, PMU version 0, perf_caps = 0
> > ==== Test Assertion Failure ====
> >   lib/x86_64/processor.c:1100: Unhandled exception in guest
> >   pid=464480 tid=464480 errno=4 - Interrupted system call
> >      1        0x00000000004120e1: assert_on_unhandled_exception 于 processor.c:1146
> >      2        0x00000000004062d9: _vcpu_run 于 kvm_util.c:1634
> >      3        0x00000000004062fa: vcpu_run 于 kvm_util.c:1645
> >      4        0x0000000000403697: run_vcpu 于 pmu_counters_test.c:56
> >      5        0x00000000004026fc: test_gp_counters 于 pmu_counters_test.c:434
> >      6        (已内连入)test_intel_counters 于 pmu_counters_test.c:580
> >      7        (已内连入)main 于 pmu_counters_test.c:604
> >      8        0x00007f7a2f03ad84: ?? ??:0
> >      9        0x00000000004028bd: _start 于 ??:?
> >   Unhandled exception '0x6' at guest RIP '0x402bab'
>
> Argh, I didn't add a check to see if forced emulation is actually enabled (forced
> emulation uses a magic "prefix" to trigger a #UD, which KVM intercepts; if forced
> emulation isn't enabled, KVM ignores the magic prefix and reflects the #UD back
> into the guest).
>
> This fixes the test for me:
>
> ---
>  .../selftests/kvm/x86_64/pmu_counters_test.c  | 42 ++++++++++++-------
>  1 file changed, 26 insertions(+), 16 deletions(-)
>
> diff --git a/tools/testing/selftests/kvm/x86_64/pmu_counters_test.c b/tools/testing/selftests/kvm/x86_64/pmu_counters_test.c
> index 248ebe8c0577..ae5f6042f1e8 100644
> --- a/tools/testing/selftests/kvm/x86_64/pmu_counters_test.c
> +++ b/tools/testing/selftests/kvm/x86_64/pmu_counters_test.c
> @@ -325,6 +325,26 @@ __GUEST_ASSERT(expect_gp ? vector == GP_VECTOR : !vector,                  \
>                        "Expected " #insn "(0x%x) to yield 0x%lx, got 0x%lx",    \
>                        msr, expected_val, val);
>
> +static void guest_test_rdpmc(uint32_t rdpmc_idx, bool expect_success,
> +                            uint64_t expected_val)
> +{
> +       uint8_t vector;
> +       uint64_t val;
> +
> +       vector = rdpmc_safe(rdpmc_idx, &val);
> +       GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, !expect_success, vector);
> +       if (expect_success)
> +               GUEST_ASSERT_PMC_VALUE(RDPMC, rdpmc_idx, val, expected_val);
> +
> +       if (!is_forced_emulation_enabled)
> +               return;
> +
> +       vector = rdpmc_safe_fep(rdpmc_idx, &val);
> +       GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, !expect_success, vector);
> +       if (expect_success)
> +               GUEST_ASSERT_PMC_VALUE(RDPMC, rdpmc_idx, val, expected_val);
> +}
> +
>  static void guest_rd_wr_counters(uint32_t base_msr, uint8_t nr_possible_counters,
>                                  uint8_t nr_counters, uint32_t or_mask)
>  {
> @@ -367,20 +387,15 @@ static void guest_rd_wr_counters(uint32_t base_msr, uint8_t nr_possible_counters
>                 if (!expect_gp)
>                         GUEST_ASSERT_PMC_VALUE(RDMSR, msr, val, expected_val);
>
> +               /*
> +                * Redo the read tests with RDPMC, which has different indexing
> +                * semantics and additional capabilities.
> +                */
>                 rdpmc_idx = i;
>                 if (base_msr == MSR_CORE_PERF_FIXED_CTR0)
>                         rdpmc_idx |= INTEL_RDPMC_FIXED;
>
> -               /* Redo the read tests with RDPMC, and with forced emulation. */
> -               vector = rdpmc_safe(rdpmc_idx, &val);
> -               GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, !expect_success, vector);
> -               if (expect_success)
> -                       GUEST_ASSERT_PMC_VALUE(RDPMC, rdpmc_idx, val, expected_val);
> -
> -               vector = rdpmc_safe_fep(rdpmc_idx, &val);
> -               GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, !expect_success, vector);
> -               if (expect_success)
> -                       GUEST_ASSERT_PMC_VALUE(RDPMC, rdpmc_idx, val, expected_val);
> +               guest_test_rdpmc(rdpmc_idx, expect_success, expected_val);
>
>                 /*
>                  * KVM doesn't support non-architectural PMUs, i.e. it should
> @@ -389,12 +404,7 @@ static void guest_rd_wr_counters(uint32_t base_msr, uint8_t nr_possible_counters
>                  */
>                 GUEST_ASSERT(!expect_success || !pmu_has_fast_mode);
>                 rdpmc_idx |= INTEL_RDPMC_FAST;
> -
> -               vector = rdpmc_safe(rdpmc_idx, &val);
> -               GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, true, vector);
> -
> -               vector = rdpmc_safe_fep(rdpmc_idx, &val);
> -               GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, true, vector);
> +               guest_test_rdpmc(rdpmc_idx, false, -1ull);
>
>                 vector = wrmsr_safe(msr, 0);
>                 GUEST_ASSERT_PMC_MSR_ACCESS(WRMSR, msr, expect_gp, vector);
>
> base-commit: 743a1a6d106931691be32e081e929d9b3de5777f
> --

This fix worked perfectly, thanks.

>
  

Patch

diff --git a/tools/testing/selftests/kvm/x86_64/pmu_counters_test.c b/tools/testing/selftests/kvm/x86_64/pmu_counters_test.c
index cb808ac827ba..248ebe8c0577 100644
--- a/tools/testing/selftests/kvm/x86_64/pmu_counters_test.c
+++ b/tools/testing/selftests/kvm/x86_64/pmu_counters_test.c
@@ -328,6 +328,7 @@  __GUEST_ASSERT(expect_gp ? vector == GP_VECTOR : !vector,			\
 static void guest_rd_wr_counters(uint32_t base_msr, uint8_t nr_possible_counters,
 				 uint8_t nr_counters, uint32_t or_mask)
 {
+	const bool pmu_has_fast_mode = !guest_get_pmu_version();
 	uint8_t i;
 
 	for (i = 0; i < nr_possible_counters; i++) {
@@ -352,6 +353,7 @@  static void guest_rd_wr_counters(uint32_t base_msr, uint8_t nr_possible_counters
 		const uint64_t expected_val = expect_success ? test_val : 0;
 		const bool expect_gp = !expect_success && msr != MSR_P6_PERFCTR0 &&
 				       msr != MSR_P6_PERFCTR1;
+		uint32_t rdpmc_idx;
 		uint8_t vector;
 		uint64_t val;
 
@@ -365,6 +367,35 @@  static void guest_rd_wr_counters(uint32_t base_msr, uint8_t nr_possible_counters
 		if (!expect_gp)
 			GUEST_ASSERT_PMC_VALUE(RDMSR, msr, val, expected_val);
 
+		rdpmc_idx = i;
+		if (base_msr == MSR_CORE_PERF_FIXED_CTR0)
+			rdpmc_idx |= INTEL_RDPMC_FIXED;
+
+		/* Redo the read tests with RDPMC, and with forced emulation. */
+		vector = rdpmc_safe(rdpmc_idx, &val);
+		GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, !expect_success, vector);
+		if (expect_success)
+			GUEST_ASSERT_PMC_VALUE(RDPMC, rdpmc_idx, val, expected_val);
+
+		vector = rdpmc_safe_fep(rdpmc_idx, &val);
+		GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, !expect_success, vector);
+		if (expect_success)
+			GUEST_ASSERT_PMC_VALUE(RDPMC, rdpmc_idx, val, expected_val);
+
+		/*
+		 * KVM doesn't support non-architectural PMUs, i.e. it should
+		 * impossible to have fast mode RDPMC.  Verify that attempting
+		 * to use fast RDPMC always #GPs.
+		 */
+		GUEST_ASSERT(!expect_success || !pmu_has_fast_mode);
+		rdpmc_idx |= INTEL_RDPMC_FAST;
+
+		vector = rdpmc_safe(rdpmc_idx, &val);
+		GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, true, vector);
+
+		vector = rdpmc_safe_fep(rdpmc_idx, &val);
+		GUEST_ASSERT_PMC_MSR_ACCESS(RDPMC, rdpmc_idx, true, vector);
+
 		vector = wrmsr_safe(msr, 0);
 		GUEST_ASSERT_PMC_MSR_ACCESS(WRMSR, msr, expect_gp, vector);
 	}