AMD Zen 2 introduces the RDPRU instruction which can be used to access some
processor registers which are typically only accessible in privilege level
0. ECX specifies the register to read and EDX:EAX will contain the value read.

ECX: 0 - Register MPERF
     1 - Register APERF

This has the added advantage of not having to use the msr module, since the
userspace to kernel transitions which occur during each read_msr() might
cause APERF and MPERF to go out of sync.

Signed-off-by: Janakarajan Natarajan <[email protected]>
---
 tools/power/cpupower/utils/helpers/cpuid.c    |  4 ++++
 tools/power/cpupower/utils/helpers/helpers.h  |  1 +
 .../utils/idle_monitor/mperf_monitor.c        | 20 +++++++++++++++++++
 3 files changed, 25 insertions(+)

diff --git a/tools/power/cpupower/utils/helpers/cpuid.c 
b/tools/power/cpupower/utils/helpers/cpuid.c
index 5cc39d4e23ed..73bfafc60e9b 100644
--- a/tools/power/cpupower/utils/helpers/cpuid.c
+++ b/tools/power/cpupower/utils/helpers/cpuid.c
@@ -131,6 +131,10 @@ int get_cpu_info(struct cpupower_cpu_info *cpu_info)
                if (ext_cpuid_level >= 0x80000007 &&
                    (cpuid_edx(0x80000007) & (1 << 9)))
                        cpu_info->caps |= CPUPOWER_CAP_AMD_CBP;
+
+               if (ext_cpuid_level >= 0x80000008 &&
+                   cpuid_ebx(0x80000008) & (1 << 4))
+                       cpu_info->caps |= CPUPOWER_CAP_AMD_RDPRU;
        }
 
        if (cpu_info->vendor == X86_VENDOR_INTEL) {
diff --git a/tools/power/cpupower/utils/helpers/helpers.h 
b/tools/power/cpupower/utils/helpers/helpers.h
index 357b19bb136e..c258eeccd05f 100644
--- a/tools/power/cpupower/utils/helpers/helpers.h
+++ b/tools/power/cpupower/utils/helpers/helpers.h
@@ -69,6 +69,7 @@ enum cpupower_cpu_vendor {X86_VENDOR_UNKNOWN = 0, 
X86_VENDOR_INTEL,
 #define CPUPOWER_CAP_HAS_TURBO_RATIO   0x00000010
 #define CPUPOWER_CAP_IS_SNB            0x00000020
 #define CPUPOWER_CAP_INTEL_IDA         0x00000040
+#define CPUPOWER_CAP_AMD_RDPRU         0x00000080
 
 #define CPUPOWER_AMD_CPBDIS            0x02000000
 
diff --git a/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c 
b/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c
index afb2e6f8edd3..e7d48cb563c0 100644
--- a/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c
+++ b/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c
@@ -19,6 +19,10 @@
 #define MSR_APERF      0xE8
 #define MSR_MPERF      0xE7
 
+#define RDPRU ".byte 0x0f, 0x01, 0xfd"
+#define RDPRU_ECX_MPERF        0
+#define RDPRU_ECX_APERF        1
+
 #define MSR_TSC        0x10
 
 #define MSR_AMD_HWCR 0xc0010015
@@ -89,6 +93,8 @@ static int mperf_get_tsc(unsigned long long *tsc)
 static int get_aperf_mperf(int cpu, unsigned long long *aval,
                                    unsigned long long *mval)
 {
+       unsigned long low_a, high_a;
+       unsigned long low_m, high_m;
        int ret;
 
        /*
@@ -101,6 +107,20 @@ static int get_aperf_mperf(int cpu, unsigned long long 
*aval,
                        return 1;
        }
 
+       if (cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_RDPRU) {
+               asm volatile(RDPRU
+                            : "=a" (low_a), "=d" (high_a)
+                            : "c" (RDPRU_ECX_APERF));
+               asm volatile(RDPRU
+                            : "=a" (low_m), "=d" (high_m)
+                            : "c" (RDPRU_ECX_MPERF));
+
+               *aval = ((low_a) | (high_a) << 32);
+               *mval = ((low_m) | (high_m) << 32);
+
+               return 0;
+       }
+
        ret  = read_msr(cpu, MSR_APERF, aval);
        ret |= read_msr(cpu, MSR_MPERF, mval);
 
-- 
2.17.1

Reply via email to