[PATCH 1/2] hwpmc_amd: fix amd_get_msr() MSR offset for newer counter, bases

From: Paulo Fragoso <paulo_at_nlink.com.br>
Date: Sun, 01 Mar 2026 13:53:24 UTC
The previous code subtracted AMD_PMC_PERFCTR_0 (0xC0010004) from all
perfctr MSR addresses to compute a relative offset. This is incorrect
for counters using AMD_PMC_CORE_BASE (0xC0010200), AMD_PMC_L3_BASE
(0xC0010230), and AMD_PMC_DF_BASE (0xC0010240), producing wrong offsets.

For example, core counter 4 (pm_perfctr = 0xC0010209) would produce:
   0xC0010209 - 0xC0010004 = 517  (wrong)
instead of:
   (0xC0010209 - 0xC0010200) / 2 = 4  (correct)

Fix by computing the MSR offset relative to the correct base address
for each counter sub-class using a switch on pm_subclass field added
by the Netflix hwpmc commit (37bba2ad92d8).

The CORE case handles both legacy processors (using AMD_PMC_PERFCTR_0)
and newer processors with AMDID2_PCXC feature bit (using AMD_PMC_CORE_BASE)
as documented in AMD PPR 57930-A0 page 220:
   MSRC001_0200 [Performance Event Select 0] (Core::X86::Msr::PERF_CTL0)

Verified on: AMD Ryzen 5600X (Family 19h, Zen 3), FreeBSD 16.0-CURRENT
   - 6 core counters (K8-0..K8-5) ENABLED
   - 6 L3 counters  (K8-L3-0..K8-L3-5) ENABLED
   - 4 DF counters  (K8-DF-0..K8-DF-3) ENABLED
   Total: 16 K8 PMCs correctly registered

Sponsored by: NLINK (https://nlink.com.br), Recife, Brazil
---
  sys/dev/hwpmc/hwpmc_amd.c | 19 ++++++++++++++++++-
  1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/sys/dev/hwpmc/hwpmc_amd.c b/sys/dev/hwpmc/hwpmc_amd.c
index 801b75b39595..b7dbbca18da2 100644
--- a/sys/dev/hwpmc/hwpmc_amd.c
+++ b/sys/dev/hwpmc/hwpmc_amd.c
@@ -655,7 +655,24 @@ amd_get_msr(int ri, uint32_t *msr)
  	KASSERT(ri >= 0 && ri < amd_npmcs,
  	    ("[amd,%d] ri %d out of range", __LINE__, ri));

-	*msr = amd_pmcdesc[ri].pm_perfctr - AMD_PMC_PERFCTR_0;
+	switch (amd_pmcdesc[ri].pm_subclass) {
+	case PMC_AMD_SUB_CLASS_CORE:
+		if (amd_pmcdesc[ri].pm_perfctr >= AMD_PMC_CORE_BASE)
+			*msr = (amd_pmcdesc[ri].pm_perfctr -
+			    AMD_PMC_CORE_BASE) / 2;
+		else
+			*msr = amd_pmcdesc[ri].pm_perfctr -
+			    AMD_PMC_PERFCTR_0;
+		break;
+	case PMC_AMD_SUB_CLASS_L3_CACHE:
+		*msr = (amd_pmcdesc[ri].pm_perfctr - AMD_PMC_L3_BASE) / 2;
+		break;
+	case PMC_AMD_SUB_CLASS_DATA_FABRIC:
+		*msr = (amd_pmcdesc[ri].pm_perfctr - AMD_PMC_DF_BASE) / 2;
+		break;
+	default:
+		return (EINVAL);
+	}
  	return (0);
  }

--
2.53.0