blob: 67f3b23ef14ca5047ede1d05fdef07a4a3717f7c [file] [log] [blame] [edit]
#include "pmu.h"
struct pmu_caps pmu;
/*
* For Intel Atom CPUs, the PMU events "Instruction Retired" or
* "Branch Instruction Retired" may be overcounted for some certain
* instructions, like FAR CALL/JMP, RETF, IRET, VMENTRY/VMEXIT/VMPTRLD
* and complex SGX/SMX/CSTATE instructions/flows.
*
* The detailed information can be found in the errata (section SRF7):
* https://edc.intel.com/content/www/us/en/design/products-and-solutions/processors-and-chipsets/sierra-forest/xeon-6700-series-processor-with-e-cores-specification-update/errata-details/
*
* For the Atom platforms before Sierra Forest (including Sierra Forest),
* Both 2 events "Instruction Retired" and "Branch Instruction Retired" would
* be overcounted on these certain instructions, but for Clearwater Forest
* only "Instruction Retired" event is overcounted on these instructions.
*/
static void pmu_detect_intel_overcount_errata(void)
{
struct cpuid c = cpuid(1);
if (x86_family(c.a) == 0x6) {
switch (x86_model(c.a)) {
case 0xDD: /* Clearwater Forest */
pmu.errata.instructions_retired_overcount = true;
break;
case 0xAF: /* Sierra Forest */
case 0x4D: /* Avaton, Rangely */
case 0x5F: /* Denverton */
case 0x86: /* Jacobsville */
pmu.errata.instructions_retired_overcount = true;
pmu.errata.branches_retired_overcount = true;
break;
default:
break;
}
}
}
void pmu_init(void)
{
pmu.is_intel = is_intel();
if (pmu.is_intel) {
pmu_detect_intel_overcount_errata();
pmu.version = this_cpu_property(X86_PROPERTY_PMU_VERSION);
if (pmu.version > 1) {
pmu.nr_fixed_counters = this_cpu_property(X86_PROPERTY_PMU_NR_FIXED_COUNTERS);
pmu.fixed_counter_width = this_cpu_property(X86_PROPERTY_PMU_FIXED_COUNTERS_BIT_WIDTH);
}
pmu.nr_gp_counters = this_cpu_property(X86_PROPERTY_PMU_NR_GP_COUNTERS);
pmu.gp_counter_width = this_cpu_property(X86_PROPERTY_PMU_GP_COUNTERS_BIT_WIDTH);
pmu.arch_event_mask_length = this_cpu_property(X86_PROPERTY_PMU_EBX_BIT_VECTOR_LENGTH);
/* CPUID.0xA.EBX bit is '1' if an arch event is NOT available. */
pmu.arch_event_available = ~this_cpu_property(X86_PROPERTY_PMU_EVENTS_MASK) &
(BIT(pmu.arch_event_mask_length) - 1);
if (this_cpu_has(X86_FEATURE_PDCM))
pmu.perf_cap = rdmsr(MSR_IA32_PERF_CAPABILITIES);
pmu.msr_gp_counter_base = MSR_IA32_PERFCTR0;
pmu.msr_gp_event_select_base = MSR_P6_EVNTSEL0;
if (this_cpu_has_perf_global_status()) {
pmu.msr_global_status = MSR_CORE_PERF_GLOBAL_STATUS;
pmu.msr_global_ctl = MSR_CORE_PERF_GLOBAL_CTRL;
pmu.msr_global_status_clr = MSR_CORE_PERF_GLOBAL_OVF_CTRL;
}
} else {
if (this_cpu_has(X86_FEATURE_PERFCTR_CORE)) {
/* Performance Monitoring Version 2 Supported */
if (this_cpu_has(X86_FEATURE_AMD_PMU_V2)) {
pmu.version = 2;
pmu.nr_gp_counters = this_cpu_property(X86_PROPERTY_NR_PERFCTR_CORE);
} else {
pmu.nr_gp_counters = AMD64_NUM_COUNTERS_CORE;
}
pmu.msr_gp_counter_base = MSR_F15H_PERF_CTR0;
pmu.msr_gp_event_select_base = MSR_F15H_PERF_CTL0;
} else {
pmu.nr_gp_counters = AMD64_NUM_COUNTERS;
pmu.msr_gp_counter_base = MSR_K7_PERFCTR0;
pmu.msr_gp_event_select_base = MSR_K7_EVNTSEL0;
}
pmu.gp_counter_width = PMC_DEFAULT_WIDTH;
pmu.arch_event_mask_length = 32;
pmu.arch_event_available = -1u;
if (this_cpu_has_perf_global_status()) {
pmu.msr_global_status = MSR_AMD64_PERF_CNTR_GLOBAL_STATUS;
pmu.msr_global_ctl = MSR_AMD64_PERF_CNTR_GLOBAL_CTL;
pmu.msr_global_status_clr = MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR;
}
}
pmu_reset_all_counters();
}