The branch main has been updated by mhorne: URL: https://cgit.FreeBSD.org/src/commit/?id=cf469ab83012ee47d06bc89874b6c109f49446e0
commit cf469ab83012ee47d06bc89874b6c109f49446e0 Author: Ali Mashtizadeh <[email protected]> AuthorDate: 2026-06-10 23:20:16 +0000 Commit: Mitchell Horne <[email protected]> CommitDate: 2026-06-23 16:36:47 +0000 hwpmc_amd: Avoid using PMCs if in use by firmware Some firmwares use the PMCs to monitor OS performance. We can't be certain that the BIOS would detect any change to the counters if we reprogram them. In cases where the firmware is using the PMCs to control power management this could have dangerous side effects or unexpected performance effects. During initialization, detect if any of the counters are enabled and fail if so. Reported by: Sandipan Das Reviewed by: mhorne MFC after: 1 week Sponsored by: Netflix Pull Request: https://github.com/freebsd/freebsd-src/pull/2277 --- sys/dev/hwpmc/hwpmc_amd.c | 110 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 77 insertions(+), 33 deletions(-) diff --git a/sys/dev/hwpmc/hwpmc_amd.c b/sys/dev/hwpmc/hwpmc_amd.c index e76bdef118d5..b075837cd3e8 100644 --- a/sys/dev/hwpmc/hwpmc_amd.c +++ b/sys/dev/hwpmc/hwpmc_amd.c @@ -863,16 +863,84 @@ amd_pcpu_fini(struct pmc_mdep *md, int cpu) return (0); } +/* + * Check that the PMC hardware is safe to use. First, we check that the PMCs + * are not in use by firmware or another module. Second, if none of the PMC + * feature flags are set, we check that the event selector is working, because + * virtual machines have no way to communicate the absence of PMCs. + */ +static int +amd_hwcheck(void) +{ + uint64_t reg; + int error, i; + + /* + * Some PC vendors enable the core counters in firmware to track + * performance. The best guess is that this is being used to control + * power management from within the SMM mode. We shouldn't just take + * over the PMCs in this case. The user should try disabling any + * performance monitoring or power management functions in the BIOS to + * safely make use of the counters. + */ + for (i = 0; i < amd_core_npmcs; i++) { + error = rdmsr_safe(amd_pmcdesc[i].pm_evsel, ®); + if (error != 0) { + printf("hwpmc: AMD evsel %d rdmsr failed!\n", i); + return (-1); + } + + if ((reg & AMD_PMC_ENABLE) != 0) { + printf("hwpmc: PMCs maybe in use by firmware!\n"); + printf("hwpmc: Disable the PMC use in the BIOS before loading\n"); + return (-1); + } + } + + /* + * Unfortunately, there is no way to communicate that the original four + * core counters are disabled through CPUIDs alone. We attempt to + * write and read back the MSR to validate that it is working. + * + * Referenced the BIOS and Kernel Developer Guide for AMD Athlon 64 and + * AMD Opteron Processors 26094 Rev. 3.24 January, 2005 to ensure these + * fields are valid. + */ + if ((amd_feature2 & AMDID2_PCXC) == 0) { + error = wrmsr_safe(AMD_PMC_EVSEL_0, AMD_PMC_OS | AMD_PMC_USR); + if (error != 0) { + printf("hwpmc: AMD evsel 0 wrmsr failed!\n"); + return (-1); + } + + error = rdmsr_safe(AMD_PMC_EVSEL_0, ®); + if (error != 0) { + printf("hwpmc: AMD evsel 0 rdmsr failed!\n"); + return (-1); + } + + if (reg == 0) { + printf("hwpmc: AMD evsel returned invalid value! " + "You may be in a VM without PMC support.\n"); + return (-1); + } + + wrmsr(AMD_PMC_EVSEL_0, 0); + } + + return (0); +} + /* * Initialize ourselves. */ struct pmc_mdep * pmc_amd_initialize(void) { + u_int regs[4]; struct amd_descr *d; struct pmc_classdep *pcd; struct pmc_mdep *pmc_mdep; - uint64_t reg; enum pmc_cputype cputype; int ncpus, nclasses, i; int family, model, stepping; @@ -907,37 +975,6 @@ pmc_amd_initialize(void) return (NULL); } - /* - * Unforunately, there is no way to communicate that the original four - * core counters are disabled through CPUIDs alone. We attempt to - * write and read back the MSR to validate that it is working. - * - * Referenced the BIOS and Kernel Developer Guide for AMD Athlon 64 and - * AMD Opteron Processors 26094 Rev. 3.24 January, 2005 to ensure these - * fields are valid. - */ - if ((amd_feature2 & AMDID2_PCXC) == 0) { - error = wrmsr_safe(AMD_PMC_EVSEL_0, AMD_PMC_OS | AMD_PMC_USR); - if (error != 0) { - printf("hwpmc: AMD evsel 0 wrmsr failed!\n"); - return (NULL); - } - - error = rdmsr_safe(AMD_PMC_EVSEL_0, ®); - if (error != 0) { - printf("hwpmc: AMD evsel 0 rdmsr failed!\n"); - return (NULL); - } - - if (reg == 0) { - printf("hwpmc: AMD evsel returned invalid value! " - "You may be in a VM without PMC support.\n"); - return (NULL); - } - - wrmsr(AMD_PMC_EVSEL_0, 0); - } - /* * From PPR for AMD Family 1Ah, a new cpuid leaf specifies the maximum * number of PMCs of each type. If we do not have that leaf, we use @@ -953,7 +990,6 @@ pmc_amd_initialize(void) amd_df_npmcs = AMD_PMC_DF_DEFAULT; if (cpu_exthigh >= CPUID_EXTPERFMON) { - u_int regs[4]; do_cpuid(CPUID_EXTPERFMON, regs); if (regs[1] != 0) { amd_core_npmcs = EXTPERFMON_CORE_PMCS(regs[1]); @@ -1020,6 +1056,14 @@ pmc_amd_initialize(void) amd_npmcs += amd_df_npmcs; } + /* + * Sanity check that the hardware is safe to use. Do not read or write + * any of the PMC MSRs until after this check passes. + */ + if (amd_hwcheck() < 0) { + return (NULL); + } + /* * Allocate space for pointers to PMC HW descriptors and for * the MDEP structure used by MI code.
