new file mode 100644
@@ -0,0 +1,9 @@
+include $(ROOT)/build/common.mk
+
+NAME := vpmu
+CATEGORY := functional
+TEST-ENVS := hvm64 hvm32
+
+obj-perenv += main.o
+
+include $(ROOT)/build/gen.mk
new file mode 100644
@@ -0,0 +1,442 @@
+/**
+ * @file tests/vpmu/main.c
+ * @ref test-vpmu
+ *
+ * @page test-vpmu vpmu
+ *
+ * Test Virtual Performance Monitoring Unit implementation, which allows guests
+ * to program fixed and general purpose performance counters to measure hardware
+ * performance.
+ *
+ * The tests read and write MSRs that are exposed by VPMU for various versions
+ * of Architectural Performance Monitoring
+ *
+ * @see tests/vpmu/main.c
+ */
+
+#include <xtf.h>
+#include <arch/msr-index.h>
+#include <arch/mm.h>
+
+#define EVENT_UOPS_RETIRED 0x004101c2
+#define EVENT_UOPS_RETIRED_ANYTHREAD 0x006101c2
+#define FIXED_CTR_CTL_BITS 4
+#define FIXED_CTR_ENABLE 0x0000000A
+#define FIXED_CTR_ENABLE_ANYTHREAD 0x0000000E
+#define PERFEVENTSELx_ENABLE_ANYTHREAD (1ull << 21)
+#define PERFEVENTSELx_ENABLE_PCB (1ull << 19)
+#define DEBUGCTL_Freeze_LBR_ON_PMI (1ull << 11)
+#define DEBUGCTL_Freeze_PerfMon_On_PMI (1ull << 12)
+#define PERF_CAPABILITIES_Full_Width (1ull << 13)
+
+const char test_title[] = "Test vpmu";
+
+static void test_valid_msr_write(uint32_t idx, uint64_t wval)
+{
+ uint64_t rval = 0;
+
+ if( wrmsr_safe(idx, wval) )
+ xtf_failure("Fail: wrmsr(0x%08x, 0x%016"PRIx64") got unexpected fault"
+ "\n", idx, wval);
+
+ /* check to see if the values were written correctly */
+ if ( rdmsr_safe(idx, &rval) )
+ xtf_failure("Fail: rdmsr(0x%08x, 0x016%"PRIxPTR") got unexpected fault"
+ "\n", idx, (uintptr_t) &rval);
+
+ if ( rval != wval )
+ xtf_failure("Fail: rdmsr mismatch idx 0x%08x, wval 0x%016"PRIx64
+ ", rval 0x%016"PRIx64"\n", idx, wval, rval);
+}
+
+static void test_invalid_msr_write(uint32_t idx, uint64_t wval)
+{
+ /* wrmsr_safe must return false after faulting */
+ if( !wrmsr_safe(idx, wval) )
+ xtf_failure("Fail: wrmsr(0x%08x, 0x%016"PRIx64") did not fault.\n",
+ idx, wval);
+}
+
+static void test_transparent_msr_write(uint32_t idx, uint64_t wval)
+{
+ uint64_t rval1 = 0, rval2 = 0;
+
+ /* read current value */
+ if ( rdmsr_safe(idx, &rval1) )
+ xtf_failure("Fail: rdmsr(0x%08x, 0x016%"PRIxPTR") got unexpected fault"
+ "\n", idx, (uintptr_t)&rval1);
+
+ /* wrmsr should not fault but should not write anything either */
+ if ( wrmsr_safe(idx, wval) )
+ xtf_failure("Fail: wrmsr(0x%08x, 0x%016"PRIx64") got unexpected fault"
+ "\n", idx, wval);
+
+ /* read new value */
+ if ( rdmsr_safe(idx, &rval2) )
+ xtf_failure("Fail: rdmsr(0x%08x, 0x016%"PRIxPTR") got unexpected fault"
+ "\n", idx, (uintptr_t) &rval2);
+
+ /* Check if the new value is the same as the one before wrmsr */
+ if ( rval1 != rval2 )
+ xtf_failure("Fail: rdmsr mismatch idx 0x%08x, wval 0x%016"PRIx64
+ ", rval 0x%016"PRIx64"\n", idx, rval1, rval2);
+}
+
+static void test_intel_pmu_ver1(uint8_t ng)
+{
+ /*
+ * Intel Sofwtare Development Manual Vol. 3B,
+ * Section 18.2.1 - Architectural Performance Monitoring Version 1
+ *
+ * MSRs made available by the VPMU
+ *
+ * PMCx (start at address 0xc1)
+ * PERFEVENTSELx (start at address 0x186)
+ */
+
+ uint32_t idx;
+ uint64_t wval = 0;
+ unsigned i;
+
+ printk("Testing version 1\n");
+
+ /* for all general purpose counters */
+ for ( i = 0; i < ng; i++ )
+ {
+ /* test writing to PMCx */
+ idx = MSR_PMC(i);
+
+ /*
+ * test we can write to all valid bits in the counters
+ * don't set bit 31 since that gets sign-extended
+ */
+ wval = ((1ull << 31) - 1) ;
+
+ test_valid_msr_write(idx, wval);
+
+ /* set all valid bits in MSR_EVENTSELx */
+ idx = MSR_PERFEVTSEL(i);
+ wval = ((1ull << 32) - 1) ^ (PERFEVENTSELx_ENABLE_ANYTHREAD |
+ PERFEVENTSELx_ENABLE_PCB);
+
+ test_valid_msr_write(idx, wval);
+
+ /* test writing an invalid value and assert that it faults */
+ wval = ~((1ull << 32) - 1);
+
+ test_invalid_msr_write(idx, wval);
+ }
+}
+
+static void test_intel_pmu_ver2(uint8_t ng, uint8_t nf)
+{
+ /*
+ * Intel Sofwtare Development Manual Vol. 3B,
+ * Section 18.2.2 - Architectural Performance Monitoring Version 2
+ *
+ * MSRs made available by the VPMU -
+ *
+ * FIXED_CTRx (start at address 0x309)
+ * FIXED_CTR_CTRL
+ * PERF_GLOBAL_CTRL
+ * PERF_GLOBAL_STATUS (read-only)
+ * PERF_GLOBAL_OVF_CTRL
+ * DEBUGCTL_Freeze_LBR_ON_PMI
+ * DEBUGCTL_Freeze_PerfMon_On_PMI
+ */
+
+ uint32_t idx;
+ uint64_t wval;
+ uint64_t rval;
+ unsigned i;
+
+ printk("Testing version 2\n");
+
+ for ( i = 0; i < nf; i++ )
+ {
+ /* test writing to FIXED_CTRx */
+ idx = MSR_FIXED_CTR(i);
+
+ wval = (1ull << 32) - 1;
+
+ test_valid_msr_write(idx, wval);
+
+ /* test invalid write to FIXED_CTRx */
+ wval = ~wval;
+
+ test_invalid_msr_write(idx, wval);
+ }
+
+ /* test FIXED_CTR_CTRL */
+ idx = MSR_FIXED_CTR_CTRL;
+
+ /* enable all fixed counters */
+ wval = 0;
+
+ for ( i = 0; i < nf; i++ )
+ wval |= (FIXED_CTR_ENABLE << (FIXED_CTR_CTL_BITS * i));
+
+ test_valid_msr_write(idx, wval);
+
+ /* invert wval to test writing an invalid value */
+ wval = ~wval;
+
+ test_invalid_msr_write(idx, wval);
+
+ /* test PERF_GLOBAL_CTRL */
+ idx = MSR_PERF_GLOBAL_CTRL;
+
+ wval = 0;
+
+ /* set all fixed function counters enable bits */
+ for ( i=0; i < nf; i ++ )
+ wval |= ((uint64_t)1 << (32 + i));
+
+ /* set all general purpose counters enable bits*/
+ for ( i = 0; i < ng; i++ )
+ wval |= (1 << i);
+
+ test_valid_msr_write(idx, wval);
+
+ /* invert wval to test invalid write to MSR_PERF_GLOBAL_CTRL*/
+ wval = ~wval;
+
+ test_invalid_msr_write(idx, wval);
+
+ /* test PERF_GLOBAL_OVF_CTRL */
+ idx = MSR_PERF_GLOBAL_OVF_CTRL;
+
+ /* set all valid bits in MSR_PERF_GLOBAL_OVF_CTRL */
+ wval = 0xC000000000000000 | (((1ULL << nf) - 1) << 32) | ((1ULL << ng) - 1);
+
+ /*
+ * Writing to MSR_PERF_GLOBAL_OVF_CTRL clears
+ * MSR_PERF_GLOBAL_STATUS but but always returns 0 when read so
+ * it is tested as a transparent write
+ */
+
+ test_transparent_msr_write(idx, wval);
+
+ /* invert wval to test invalid write to MSR_PERF_GLOBAL_OVF_CTRL*/
+ wval = ~wval;
+
+ test_invalid_msr_write(idx, wval);
+
+ /* test PERF_GLOBAL_STATUS (read-only) */
+ idx = MSR_PERF_GLOBAL_STATUS;
+
+ if ( rdmsr_safe(idx, &rval) )
+ {
+ xtf_failure("Error: test_intel_pmu_ver2: "
+ "rdmsr_safe for MSR 0x%x resulted in a fault!\n", idx);
+ }
+
+ /* try to write the PERF_GLOBAL_STATUS register and expect it to fail*/
+ test_invalid_msr_write(idx, wval);
+
+ /* test DEBUGCTL */
+ idx = MSR_DEBUGCTL;
+
+ /* Test DEBUGCTL facilities enabled by v2 */
+ wval = DEBUGCTL_Freeze_LBR_ON_PMI | DEBUGCTL_Freeze_PerfMon_On_PMI;
+
+ /*
+ * XENBUG: This test currently fails and needs to be fixed in Xen. Once
+ * fixed, call test_valid_msr_write(idx, wval) to fully test MSR write
+ */
+ if( wrmsr_safe(idx, wval) )
+ xtf_error("Fail: wrmsr(0x%08x, 0x%016"PRIx64") got unexpected fault"
+ "\n", idx, wval);
+}
+
+static void test_intel_pmu_ver3(uint8_t ng, uint8_t nf)
+{
+ /*
+ * Intel Sofwtare Development Manual Vol. 3B
+ * Section 18.2.3 Architectural Performance Monitoring Version 3
+ *
+ * MSRs made available by the VPMU
+ *
+ * PERFEVENTSELx.ANYTHREAD (Bit 21)
+ * FIXED_CTR_CTRL.ANYTHREADx (Bit 2, 6, 11)
+ *
+ * Version 3 introduces ANYTHREAD bit but VPMU does not support it to
+ * ensure that a VCPU isn't able to read values from physical resources that
+ * are not allocated to it. This test case validates that we are unable to
+ * write to .ANYTHREAD bit in PERFEVENTSELx and FIXED_CTR_CTRL
+ */
+
+ uint64_t wval;
+ uint32_t idx;
+ unsigned i;
+
+ printk("Testing version 3\n");
+
+ /* test PERFEVENTSELx.ANYTHREAD is disabled */
+ for ( i = 0; i < ng; i++ )
+ {
+ idx = MSR_PERFEVTSEL(i);
+
+ wval = EVENT_UOPS_RETIRED_ANYTHREAD;
+
+ test_invalid_msr_write(idx, wval);
+ }
+
+ /* test FIXED_CTR_CTL.ANYTHREAD is disabled */
+ idx = MSR_FIXED_CTR_CTRL;
+
+ wval = 0;
+
+ for ( i = 0; i < nf; i++ )
+ wval |= (FIXED_CTR_ENABLE_ANYTHREAD << (4 * i)) ;
+
+ test_invalid_msr_write(idx, wval);
+}
+
+static void test_full_width_cnts(uint8_t ng, uint8_t ngb, uint8_t pdcm)
+{
+ uint64_t caps, wval;
+ uint32_t idx;
+ unsigned i;
+
+ if ( rdmsr_safe(MSR_PERF_CAPABILITIES, &caps) )
+ {
+ if (pdcm) {
+ xtf_failure("Fail: Fault while reading MSR_PERF_CAPABILITIES\n");
+ }
+ return;
+ }
+ else if ( !pdcm )
+ {
+ /*
+ * XENBUG: MSR_PERF_CAPABILITIES should nto be readable without PDCM
+ * bit set in CPUID.
+ */
+ xtf_error("Error: MSR_PERF_CAPABILITIES readable while PDCM is 0\n");
+ }
+
+
+ if ( !(caps & PERF_CAPABILITIES_Full_Width) )
+ {
+ printk("Info: Full width counters not supported\n");
+ return;
+ }
+
+ /* test writes to full width counters */
+ for ( i = 0; i < ng; i++)
+ {
+ idx = MSR_A_PMC(i);
+
+ wval = ((1ull << ngb) - 1) ;
+
+ test_valid_msr_write(idx, wval);
+
+ /* invert wval to test invalid write to MSR_PERF_GLOBAL_OVF_CTRL */
+ wval = ~wval;
+
+ test_invalid_msr_write(idx, wval);
+ }
+}
+
+void test_main(void)
+{
+ /* Architectural Performance Monitoring Version */
+ uint8_t ver;
+ /* Number of general purpose counter registers */
+ uint8_t ngregs;
+ /* Number of fixed function counter registers */
+ uint8_t nfregs;
+ /* Bit width of general-purpose, performance monitoring counter */
+ uint8_t ngbits;
+ /* Performance and debug capabilties MSR*/
+ uint8_t pdcm;
+
+ /* cpuid variables */
+ uint32_t leaf = 0x0a, subleaf = 0;
+ uint32_t eax, ebx, ecx, edx;
+
+ if ( vendor_is_amd )
+ return xtf_skip("Skip: VPMU testing for AMD currently unsupported\n");
+
+ if (max_leaf < leaf)
+ return xtf_skip("Skip: Unable to retrieve PMU information from cpuid\n");
+
+ /*
+ * Inter Software Development Manual Vol 2A
+ * Table 3-8 Information Returned by CPUID Instruction
+ */
+
+ cpuid_count(leaf, subleaf, &eax, &ebx, &ecx, &edx);
+
+ /* Extract the version ID - EAX 7:0 */
+ ver = (eax & 0xff);
+
+ /* Extract number of general purpose counter regs - EAX 15:8 */
+ ngregs = (eax >> 8) & 0xff;
+
+ /* Extract number of fixed function counter regs - EDX 4:0 */
+ nfregs = edx & 0x1f;
+
+ /* Extract number of bits in general purpose counter registers bits */
+ ngbits = (eax >> 16) & 0xff;
+
+ /* Retrieve Perf & Debug Capabilties MSR availability*/
+ leaf = 0x01;
+
+ printk("Info: PMU Version %u, Genereal purpose counters %u, Fixed function "
+ "counters %u, Counter width %u\n", ver, ngregs, nfregs, ngbits);
+
+ cpuid_count(leaf, subleaf, &eax, &ebx, &ecx, &edx);
+
+ /* Extract Performance & Debug Capabilties MSR from ECX bit 15 */
+ pdcm = (ecx >> 15) & 0x01;
+
+ printk("Info: Perf & Debug Capability MSR %u\n", pdcm);
+
+ switch (ver)
+ {
+
+ default:
+ /*
+ * Version 4 facilities are not supported by Xen.
+ * VPMU emulates version 3. Fall through
+ */
+ xtf_warning("Test doesn't support version %u\n", ver);
+
+ case 3:
+ /* test version 3 facilities */
+ test_intel_pmu_ver3( ngregs, nfregs);
+
+ /* fall through */
+
+ case 2:
+ /* test version 2 facilities */
+ test_intel_pmu_ver2(ngregs, nfregs);
+
+ /* test version 1 facilities */
+ test_intel_pmu_ver1(ngregs);
+
+ /* test full width counters */
+ test_full_width_cnts(ngregs, ngbits, pdcm);
+
+ break;
+
+ case 1:
+ return xtf_skip("Skip: VPMU version 1 unsupported in Xen\n");
+
+ case 0:
+ return xtf_skip("Skip: VPMU not enabled in Xen\n");
+ }
+
+ return xtf_success(NULL);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
This patch tests VPMU functionality in the hypervisor on Intel machines. The tests write to all valid bits in the MSRs that get exposed to the guests when VPMU is enabled. The tests also write invalid values to the bits that should be masked and expect the wrmsr call to fault. The tests are currently unsupported for AMD machines and PV guests. Signed-off-by: Mohit Gambhir <mohit.gambhir@oracle.com> --- tests/vpmu/Makefile | 9 ++ tests/vpmu/main.c | 442 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 451 insertions(+) create mode 100644 tests/vpmu/Makefile create mode 100644 tests/vpmu/main.c