hwpmc: Disable AMD PMCs if in an unsupported VM

AMD does not have a CPUID bit to indicate the lack of K8 PMCs.  If all
other PMC features are not present we should test an event selector to
see if it stores and returns a value.  If the VM is implemented
correctly, this should result in a #GP on the initial wrmsr_safe.  Bhyve
and a few other VMs ignore writes, so I got one step further and test
that it retains the OS and USR bits.

Tested on Zen 5 native and a Zen 5 Bhyve virtual machine.  This code
should not run on any recent hardware, except in a VM, as it checks that
the core counter extension is missing.

PR:		268943
Reported by:	Sandipan Das, John F. Carr <jfc@mit.edu>
Reviewed by:	mhorne, imp
Sponsored by:	Netflix
MFC after:	1 week
Pull Request:	https://github.com/freebsd/freebsd-src/pull/2272/changes
This commit is contained in:
Ali Mashtizadeh
2026-06-05 16:48:53 -07:00
committed by Mitchell Horne
parent 8f9aabbdbc
commit dded0ab415
+35 -2
View File
@@ -869,12 +869,14 @@ amd_pcpu_fini(struct pmc_mdep *md, int cpu)
struct pmc_mdep *
pmc_amd_initialize(void)
{
struct amd_descr *d;
struct pmc_classdep *pcd;
struct pmc_mdep *pmc_mdep;
uint64_t reg;
enum pmc_cputype cputype;
int error, i, ncpus, nclasses;
int ncpus, nclasses, i;
int family, model, stepping;
struct amd_descr *d;
int error;
/*
* The presence of hardware performance counters on the AMD
@@ -905,6 +907,37 @@ 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, &reg);
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