diff mbox series

[RFC,05/22] x86/PMUv1: report correct information in 0xa CPUID

Message ID 4a00165999a0cc250f097fc8eaab0649f1c05ac0.1698261255.git.edwin.torok@cloud.com (mailing list archive)
State New, archived
Headers show
Series vPMU bugfixes and support for PMUv5 | expand

Commit Message

Edwin Torok Oct. 25, 2023, 7:29 p.m. UTC
From: Edwin Török <edvin.torok@citrix.com>

The 0xa CPUID leaf has to report supported number of:
- fixed performance counters
- general purpose performance counters
- architectural predefined events

And the PMU version (which was already limited to 3).

Type punning is used, which should be safe due to -fno-strict-aliasing.

This limits the number of arch events supported when vpmu=arch on Icelake.

Backport: 4.0+

Signed-off-by: Edwin Török <edvin.torok@citrix.com>
---
 xen/arch/x86/cpu/vpmu_intel.c   |  2 +-
 xen/arch/x86/cpuid.c            | 39 ++++++++++++++++++++++++++++++---
 xen/arch/x86/include/asm/vpmu.h |  4 ++++
 3 files changed, 41 insertions(+), 4 deletions(-)
diff mbox series

Patch

diff --git a/xen/arch/x86/cpu/vpmu_intel.c b/xen/arch/x86/cpu/vpmu_intel.c
index fa5b40c65c..9602728f1b 100644
--- a/xen/arch/x86/cpu/vpmu_intel.c
+++ b/xen/arch/x86/cpu/vpmu_intel.c
@@ -66,7 +66,7 @@  static bool_t __read_mostly full_width_write;
 #define ARCH_CNTR_PIN_CONTROL (1ULL << 19)
 
 /* Number of general-purpose and fixed performance counters */
-static unsigned int __read_mostly arch_pmc_cnt, fixed_pmc_cnt;
+unsigned int __read_mostly arch_pmc_cnt, fixed_pmc_cnt;
 
 /* Masks used for testing whether and MSR is valid */
 #define ARCH_CTRL_MASK  (~((1ull << 32) - 1) | (1ull << 21) | ARCH_CNTR_PIN_CONTROL)
diff --git a/xen/arch/x86/cpuid.c b/xen/arch/x86/cpuid.c
index 455a09b2dd..dfbcd1b3a4 100644
--- a/xen/arch/x86/cpuid.c
+++ b/xen/arch/x86/cpuid.c
@@ -304,9 +304,42 @@  void guest_cpuid(const struct vcpu *v, uint32_t leaf,
             *res = EMPTY_LEAF;
         else
         {
-            /* Report at most v3 since that's all we currently emulate. */
-            if ( (res->a & 0xff) > 3 )
-                res->a = (res->a & ~0xff) | 3;
+            union {
+                uint32_t eax;
+                struct {
+                    uint8_t version;
+                    uint8_t general_nr;
+                    uint8_t general_width;
+                    uint8_t arch_nr;
+                };
+            } u;
+            u.eax = res->a;
+
+            /* Report at most VPMU_VERSION_MAX since that's all we currently emulate. */
+            if ( u.version >  VPMU_VERSION_MAX ) {
+                gdprintk(XENLOG_WARNING, "Limiting PMU version to %d (actual %d)", VPMU_VERSION_MAX, u.version);
+                u.version = VPMU_VERSION_MAX;
+            }
+
+            if ( u.general_nr > arch_pmc_cnt ) {
+                gdprintk(XENLOG_WARNING, "Limiting general purpose PMU count to %d (actual %d)", arch_pmc_cnt, u.general_nr);
+                u.general_nr = arch_pmc_cnt;
+            }
+
+            if ( vpmu_features & (XENPMU_FEATURE_IPC_ONLY |
+                                  XENPMU_FEATURE_ARCH_ONLY) ) {
+                unsigned limit = ( vpmu_features & XENPMU_FEATURE_ARCH_ONLY ) ? 7 : 3;
+                if (limit < u.arch_nr) {
+                    gdprintk(XENLOG_WARNING, "Limiting architectural PMU events to %d (actual %d)", limit, u.arch_nr);
+                    u.arch_nr = limit;
+                }
+            }
+
+            res->a = u.eax;
+
+            /* We only implement 3 fixed function counters */
+            if ( (res->d & 0x1f) > fixed_pmc_cnt )
+                res->d = (res->d & ~0x1f) | fixed_pmc_cnt;
         }
         break;
 
diff --git a/xen/arch/x86/include/asm/vpmu.h b/xen/arch/x86/include/asm/vpmu.h
index b165acc6c2..1ef6089ccb 100644
--- a/xen/arch/x86/include/asm/vpmu.h
+++ b/xen/arch/x86/include/asm/vpmu.h
@@ -74,6 +74,8 @@  struct vpmu_struct {
 #define VPMU_CPU_HAS_DS                     0x1000 /* Has Debug Store */
 #define VPMU_CPU_HAS_BTS                    0x2000 /* Has Branch Trace Store */
 
+#define VPMU_VERSION_MAX                    0x3
+
 static inline void vpmu_set(struct vpmu_struct *vpmu, const u32 mask)
 {
     vpmu->flags |= mask;
@@ -118,6 +120,8 @@  static inline int vpmu_do_rdmsr(unsigned int msr, uint64_t *msr_content)
 
 extern unsigned int vpmu_mode;
 extern unsigned int vpmu_features;
+extern unsigned int arch_pmc_cnt;
+extern unsigned int fixed_pmc_cnt;
 
 /* Context switch */
 static inline void vpmu_switch_from(struct vcpu *prev)