162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/perf_event.h> 362306a36Sopenharmony_ci#include <linux/sysfs.h> 462306a36Sopenharmony_ci#include <linux/nospec.h> 562306a36Sopenharmony_ci#include <asm/intel-family.h> 662306a36Sopenharmony_ci#include "probe.h" 762306a36Sopenharmony_ci 862306a36Sopenharmony_cienum perf_msr_id { 962306a36Sopenharmony_ci PERF_MSR_TSC = 0, 1062306a36Sopenharmony_ci PERF_MSR_APERF = 1, 1162306a36Sopenharmony_ci PERF_MSR_MPERF = 2, 1262306a36Sopenharmony_ci PERF_MSR_PPERF = 3, 1362306a36Sopenharmony_ci PERF_MSR_SMI = 4, 1462306a36Sopenharmony_ci PERF_MSR_PTSC = 5, 1562306a36Sopenharmony_ci PERF_MSR_IRPERF = 6, 1662306a36Sopenharmony_ci PERF_MSR_THERM = 7, 1762306a36Sopenharmony_ci PERF_MSR_EVENT_MAX, 1862306a36Sopenharmony_ci}; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic bool test_aperfmperf(int idx, void *data) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci return boot_cpu_has(X86_FEATURE_APERFMPERF); 2362306a36Sopenharmony_ci} 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic bool test_ptsc(int idx, void *data) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci return boot_cpu_has(X86_FEATURE_PTSC); 2862306a36Sopenharmony_ci} 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic bool test_irperf(int idx, void *data) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci return boot_cpu_has(X86_FEATURE_IRPERF); 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic bool test_therm_status(int idx, void *data) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci return boot_cpu_has(X86_FEATURE_DTHERM); 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic bool test_intel(int idx, void *data) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL || 4362306a36Sopenharmony_ci boot_cpu_data.x86 != 6) 4462306a36Sopenharmony_ci return false; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci switch (boot_cpu_data.x86_model) { 4762306a36Sopenharmony_ci case INTEL_FAM6_NEHALEM: 4862306a36Sopenharmony_ci case INTEL_FAM6_NEHALEM_G: 4962306a36Sopenharmony_ci case INTEL_FAM6_NEHALEM_EP: 5062306a36Sopenharmony_ci case INTEL_FAM6_NEHALEM_EX: 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci case INTEL_FAM6_WESTMERE: 5362306a36Sopenharmony_ci case INTEL_FAM6_WESTMERE_EP: 5462306a36Sopenharmony_ci case INTEL_FAM6_WESTMERE_EX: 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci case INTEL_FAM6_SANDYBRIDGE: 5762306a36Sopenharmony_ci case INTEL_FAM6_SANDYBRIDGE_X: 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci case INTEL_FAM6_IVYBRIDGE: 6062306a36Sopenharmony_ci case INTEL_FAM6_IVYBRIDGE_X: 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci case INTEL_FAM6_HASWELL: 6362306a36Sopenharmony_ci case INTEL_FAM6_HASWELL_X: 6462306a36Sopenharmony_ci case INTEL_FAM6_HASWELL_L: 6562306a36Sopenharmony_ci case INTEL_FAM6_HASWELL_G: 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci case INTEL_FAM6_BROADWELL: 6862306a36Sopenharmony_ci case INTEL_FAM6_BROADWELL_D: 6962306a36Sopenharmony_ci case INTEL_FAM6_BROADWELL_G: 7062306a36Sopenharmony_ci case INTEL_FAM6_BROADWELL_X: 7162306a36Sopenharmony_ci case INTEL_FAM6_SAPPHIRERAPIDS_X: 7262306a36Sopenharmony_ci case INTEL_FAM6_EMERALDRAPIDS_X: 7362306a36Sopenharmony_ci case INTEL_FAM6_GRANITERAPIDS_X: 7462306a36Sopenharmony_ci case INTEL_FAM6_GRANITERAPIDS_D: 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci case INTEL_FAM6_ATOM_SILVERMONT: 7762306a36Sopenharmony_ci case INTEL_FAM6_ATOM_SILVERMONT_D: 7862306a36Sopenharmony_ci case INTEL_FAM6_ATOM_AIRMONT: 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci case INTEL_FAM6_ATOM_GOLDMONT: 8162306a36Sopenharmony_ci case INTEL_FAM6_ATOM_GOLDMONT_D: 8262306a36Sopenharmony_ci case INTEL_FAM6_ATOM_GOLDMONT_PLUS: 8362306a36Sopenharmony_ci case INTEL_FAM6_ATOM_TREMONT_D: 8462306a36Sopenharmony_ci case INTEL_FAM6_ATOM_TREMONT: 8562306a36Sopenharmony_ci case INTEL_FAM6_ATOM_TREMONT_L: 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci case INTEL_FAM6_XEON_PHI_KNL: 8862306a36Sopenharmony_ci case INTEL_FAM6_XEON_PHI_KNM: 8962306a36Sopenharmony_ci if (idx == PERF_MSR_SMI) 9062306a36Sopenharmony_ci return true; 9162306a36Sopenharmony_ci break; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci case INTEL_FAM6_SKYLAKE_L: 9462306a36Sopenharmony_ci case INTEL_FAM6_SKYLAKE: 9562306a36Sopenharmony_ci case INTEL_FAM6_SKYLAKE_X: 9662306a36Sopenharmony_ci case INTEL_FAM6_KABYLAKE_L: 9762306a36Sopenharmony_ci case INTEL_FAM6_KABYLAKE: 9862306a36Sopenharmony_ci case INTEL_FAM6_COMETLAKE_L: 9962306a36Sopenharmony_ci case INTEL_FAM6_COMETLAKE: 10062306a36Sopenharmony_ci case INTEL_FAM6_ICELAKE_L: 10162306a36Sopenharmony_ci case INTEL_FAM6_ICELAKE: 10262306a36Sopenharmony_ci case INTEL_FAM6_ICELAKE_X: 10362306a36Sopenharmony_ci case INTEL_FAM6_ICELAKE_D: 10462306a36Sopenharmony_ci case INTEL_FAM6_TIGERLAKE_L: 10562306a36Sopenharmony_ci case INTEL_FAM6_TIGERLAKE: 10662306a36Sopenharmony_ci case INTEL_FAM6_ROCKETLAKE: 10762306a36Sopenharmony_ci case INTEL_FAM6_ALDERLAKE: 10862306a36Sopenharmony_ci case INTEL_FAM6_ALDERLAKE_L: 10962306a36Sopenharmony_ci case INTEL_FAM6_ATOM_GRACEMONT: 11062306a36Sopenharmony_ci case INTEL_FAM6_RAPTORLAKE: 11162306a36Sopenharmony_ci case INTEL_FAM6_RAPTORLAKE_P: 11262306a36Sopenharmony_ci case INTEL_FAM6_RAPTORLAKE_S: 11362306a36Sopenharmony_ci case INTEL_FAM6_METEORLAKE: 11462306a36Sopenharmony_ci case INTEL_FAM6_METEORLAKE_L: 11562306a36Sopenharmony_ci if (idx == PERF_MSR_SMI || idx == PERF_MSR_PPERF) 11662306a36Sopenharmony_ci return true; 11762306a36Sopenharmony_ci break; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return false; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ciPMU_EVENT_ATTR_STRING(tsc, attr_tsc, "event=0x00" ); 12462306a36Sopenharmony_ciPMU_EVENT_ATTR_STRING(aperf, attr_aperf, "event=0x01" ); 12562306a36Sopenharmony_ciPMU_EVENT_ATTR_STRING(mperf, attr_mperf, "event=0x02" ); 12662306a36Sopenharmony_ciPMU_EVENT_ATTR_STRING(pperf, attr_pperf, "event=0x03" ); 12762306a36Sopenharmony_ciPMU_EVENT_ATTR_STRING(smi, attr_smi, "event=0x04" ); 12862306a36Sopenharmony_ciPMU_EVENT_ATTR_STRING(ptsc, attr_ptsc, "event=0x05" ); 12962306a36Sopenharmony_ciPMU_EVENT_ATTR_STRING(irperf, attr_irperf, "event=0x06" ); 13062306a36Sopenharmony_ciPMU_EVENT_ATTR_STRING(cpu_thermal_margin, attr_therm, "event=0x07" ); 13162306a36Sopenharmony_ciPMU_EVENT_ATTR_STRING(cpu_thermal_margin.snapshot, attr_therm_snap, "1" ); 13262306a36Sopenharmony_ciPMU_EVENT_ATTR_STRING(cpu_thermal_margin.unit, attr_therm_unit, "C" ); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic unsigned long msr_mask; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ciPMU_EVENT_GROUP(events, aperf); 13762306a36Sopenharmony_ciPMU_EVENT_GROUP(events, mperf); 13862306a36Sopenharmony_ciPMU_EVENT_GROUP(events, pperf); 13962306a36Sopenharmony_ciPMU_EVENT_GROUP(events, smi); 14062306a36Sopenharmony_ciPMU_EVENT_GROUP(events, ptsc); 14162306a36Sopenharmony_ciPMU_EVENT_GROUP(events, irperf); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic struct attribute *attrs_therm[] = { 14462306a36Sopenharmony_ci &attr_therm.attr.attr, 14562306a36Sopenharmony_ci &attr_therm_snap.attr.attr, 14662306a36Sopenharmony_ci &attr_therm_unit.attr.attr, 14762306a36Sopenharmony_ci NULL, 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic struct attribute_group group_therm = { 15162306a36Sopenharmony_ci .name = "events", 15262306a36Sopenharmony_ci .attrs = attrs_therm, 15362306a36Sopenharmony_ci}; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic struct perf_msr msr[] = { 15662306a36Sopenharmony_ci [PERF_MSR_TSC] = { .no_check = true, }, 15762306a36Sopenharmony_ci [PERF_MSR_APERF] = { MSR_IA32_APERF, &group_aperf, test_aperfmperf, }, 15862306a36Sopenharmony_ci [PERF_MSR_MPERF] = { MSR_IA32_MPERF, &group_mperf, test_aperfmperf, }, 15962306a36Sopenharmony_ci [PERF_MSR_PPERF] = { MSR_PPERF, &group_pperf, test_intel, }, 16062306a36Sopenharmony_ci [PERF_MSR_SMI] = { MSR_SMI_COUNT, &group_smi, test_intel, }, 16162306a36Sopenharmony_ci [PERF_MSR_PTSC] = { MSR_F15H_PTSC, &group_ptsc, test_ptsc, }, 16262306a36Sopenharmony_ci [PERF_MSR_IRPERF] = { MSR_F17H_IRPERF, &group_irperf, test_irperf, }, 16362306a36Sopenharmony_ci [PERF_MSR_THERM] = { MSR_IA32_THERM_STATUS, &group_therm, test_therm_status, }, 16462306a36Sopenharmony_ci}; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic struct attribute *events_attrs[] = { 16762306a36Sopenharmony_ci &attr_tsc.attr.attr, 16862306a36Sopenharmony_ci NULL, 16962306a36Sopenharmony_ci}; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic struct attribute_group events_attr_group = { 17262306a36Sopenharmony_ci .name = "events", 17362306a36Sopenharmony_ci .attrs = events_attrs, 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ciPMU_FORMAT_ATTR(event, "config:0-63"); 17762306a36Sopenharmony_cistatic struct attribute *format_attrs[] = { 17862306a36Sopenharmony_ci &format_attr_event.attr, 17962306a36Sopenharmony_ci NULL, 18062306a36Sopenharmony_ci}; 18162306a36Sopenharmony_cistatic struct attribute_group format_attr_group = { 18262306a36Sopenharmony_ci .name = "format", 18362306a36Sopenharmony_ci .attrs = format_attrs, 18462306a36Sopenharmony_ci}; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic const struct attribute_group *attr_groups[] = { 18762306a36Sopenharmony_ci &events_attr_group, 18862306a36Sopenharmony_ci &format_attr_group, 18962306a36Sopenharmony_ci NULL, 19062306a36Sopenharmony_ci}; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic const struct attribute_group *attr_update[] = { 19362306a36Sopenharmony_ci &group_aperf, 19462306a36Sopenharmony_ci &group_mperf, 19562306a36Sopenharmony_ci &group_pperf, 19662306a36Sopenharmony_ci &group_smi, 19762306a36Sopenharmony_ci &group_ptsc, 19862306a36Sopenharmony_ci &group_irperf, 19962306a36Sopenharmony_ci &group_therm, 20062306a36Sopenharmony_ci NULL, 20162306a36Sopenharmony_ci}; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic int msr_event_init(struct perf_event *event) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci u64 cfg = event->attr.config; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (event->attr.type != event->pmu->type) 20862306a36Sopenharmony_ci return -ENOENT; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* unsupported modes and filters */ 21162306a36Sopenharmony_ci if (event->attr.sample_period) /* no sampling */ 21262306a36Sopenharmony_ci return -EINVAL; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (cfg >= PERF_MSR_EVENT_MAX) 21562306a36Sopenharmony_ci return -EINVAL; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci cfg = array_index_nospec((unsigned long)cfg, PERF_MSR_EVENT_MAX); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (!(msr_mask & (1 << cfg))) 22062306a36Sopenharmony_ci return -EINVAL; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci event->hw.idx = -1; 22362306a36Sopenharmony_ci event->hw.event_base = msr[cfg].msr; 22462306a36Sopenharmony_ci event->hw.config = cfg; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return 0; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic inline u64 msr_read_counter(struct perf_event *event) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci u64 now; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (event->hw.event_base) 23462306a36Sopenharmony_ci rdmsrl(event->hw.event_base, now); 23562306a36Sopenharmony_ci else 23662306a36Sopenharmony_ci now = rdtsc_ordered(); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return now; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic void msr_event_update(struct perf_event *event) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci u64 prev, now; 24462306a36Sopenharmony_ci s64 delta; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* Careful, an NMI might modify the previous event value: */ 24762306a36Sopenharmony_ci prev = local64_read(&event->hw.prev_count); 24862306a36Sopenharmony_ci do { 24962306a36Sopenharmony_ci now = msr_read_counter(event); 25062306a36Sopenharmony_ci } while (!local64_try_cmpxchg(&event->hw.prev_count, &prev, now)); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci delta = now - prev; 25362306a36Sopenharmony_ci if (unlikely(event->hw.event_base == MSR_SMI_COUNT)) { 25462306a36Sopenharmony_ci delta = sign_extend64(delta, 31); 25562306a36Sopenharmony_ci local64_add(delta, &event->count); 25662306a36Sopenharmony_ci } else if (unlikely(event->hw.event_base == MSR_IA32_THERM_STATUS)) { 25762306a36Sopenharmony_ci /* If valid, extract digital readout, otherwise set to -1: */ 25862306a36Sopenharmony_ci now = now & (1ULL << 31) ? (now >> 16) & 0x3f : -1; 25962306a36Sopenharmony_ci local64_set(&event->count, now); 26062306a36Sopenharmony_ci } else { 26162306a36Sopenharmony_ci local64_add(delta, &event->count); 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic void msr_event_start(struct perf_event *event, int flags) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci u64 now = msr_read_counter(event); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci local64_set(&event->hw.prev_count, now); 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic void msr_event_stop(struct perf_event *event, int flags) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci msr_event_update(event); 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic void msr_event_del(struct perf_event *event, int flags) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci msr_event_stop(event, PERF_EF_UPDATE); 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic int msr_event_add(struct perf_event *event, int flags) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci if (flags & PERF_EF_START) 28562306a36Sopenharmony_ci msr_event_start(event, flags); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci return 0; 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic struct pmu pmu_msr = { 29162306a36Sopenharmony_ci .task_ctx_nr = perf_sw_context, 29262306a36Sopenharmony_ci .attr_groups = attr_groups, 29362306a36Sopenharmony_ci .event_init = msr_event_init, 29462306a36Sopenharmony_ci .add = msr_event_add, 29562306a36Sopenharmony_ci .del = msr_event_del, 29662306a36Sopenharmony_ci .start = msr_event_start, 29762306a36Sopenharmony_ci .stop = msr_event_stop, 29862306a36Sopenharmony_ci .read = msr_event_update, 29962306a36Sopenharmony_ci .capabilities = PERF_PMU_CAP_NO_INTERRUPT | PERF_PMU_CAP_NO_EXCLUDE, 30062306a36Sopenharmony_ci .attr_update = attr_update, 30162306a36Sopenharmony_ci}; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic int __init msr_init(void) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci if (!boot_cpu_has(X86_FEATURE_TSC)) { 30662306a36Sopenharmony_ci pr_cont("no MSR PMU driver.\n"); 30762306a36Sopenharmony_ci return 0; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci msr_mask = perf_msr_probe(msr, PERF_MSR_EVENT_MAX, true, NULL); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci perf_pmu_register(&pmu_msr, "msr", -1); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return 0; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_cidevice_initcall(msr_init); 317