| #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(); | |
| } |