1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2b7b7c782SAndy Lutomirski #include <linux/perf_event.h>
3dde5e720SJiri Olsa #include <linux/sysfs.h>
406ce6e9bSPeter Zijlstra #include <linux/nospec.h>
5353bf605SDave Hansen #include <asm/cpu_device_id.h>
6dde5e720SJiri Olsa #include <asm/msr.h>
7b7b7c782SAndy Lutomirski
8b7b7c782SAndy Lutomirski #include "probe.h"
9b7b7c782SAndy Lutomirski
10b7b7c782SAndy Lutomirski enum perf_msr_id {
11b7b7c782SAndy Lutomirski PERF_MSR_TSC = 0,
12b7b7c782SAndy Lutomirski PERF_MSR_APERF = 1,
13b7b7c782SAndy Lutomirski PERF_MSR_MPERF = 2,
148a224261SHuang Rui PERF_MSR_PPERF = 3,
15aaf24884SHuang Rui PERF_MSR_SMI = 4,
169ae21dd6SStephane Eranian PERF_MSR_PTSC = 5,
17b7b7c782SAndy Lutomirski PERF_MSR_IRPERF = 6,
18b7b7c782SAndy Lutomirski PERF_MSR_THERM = 7,
19b7b7c782SAndy Lutomirski PERF_MSR_EVENT_MAX,
20dde5e720SJiri Olsa };
2119b3340cSPeter Zijlstra
test_aperfmperf(int idx,void * data)2219b3340cSPeter Zijlstra static bool test_aperfmperf(int idx, void *data)
2319b3340cSPeter Zijlstra {
24b7b7c782SAndy Lutomirski return boot_cpu_has(X86_FEATURE_APERFMPERF);
25dde5e720SJiri Olsa }
268a224261SHuang Rui
test_ptsc(int idx,void * data)278a224261SHuang Rui static bool test_ptsc(int idx, void *data)
288a224261SHuang Rui {
298a224261SHuang Rui return boot_cpu_has(X86_FEATURE_PTSC);
30dde5e720SJiri Olsa }
31aaf24884SHuang Rui
test_irperf(int idx,void * data)32aaf24884SHuang Rui static bool test_irperf(int idx, void *data)
33aaf24884SHuang Rui {
34aaf24884SHuang Rui return boot_cpu_has(X86_FEATURE_IRPERF);
35dde5e720SJiri Olsa }
369ae21dd6SStephane Eranian
test_therm_status(int idx,void * data)379ae21dd6SStephane Eranian static bool test_therm_status(int idx, void *data)
389ae21dd6SStephane Eranian {
399ae21dd6SStephane Eranian return boot_cpu_has(X86_FEATURE_DTHERM);
40dde5e720SJiri Olsa }
4119b3340cSPeter Zijlstra
test_intel(int idx,void * data)4219b3340cSPeter Zijlstra static bool test_intel(int idx, void *data)
4319b3340cSPeter Zijlstra {
4419b3340cSPeter Zijlstra if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL ||
4519b3340cSPeter Zijlstra boot_cpu_data.x86 != 6)
4619b3340cSPeter Zijlstra return false;
47353bf605SDave Hansen
48b325e04eSLinus Torvalds switch (boot_cpu_data.x86_vfm) {
49353bf605SDave Hansen case INTEL_NEHALEM:
50353bf605SDave Hansen case INTEL_NEHALEM_G:
5119b3340cSPeter Zijlstra case INTEL_NEHALEM_EP:
52353bf605SDave Hansen case INTEL_NEHALEM_EX:
53353bf605SDave Hansen
54353bf605SDave Hansen case INTEL_WESTMERE:
5519b3340cSPeter Zijlstra case INTEL_WESTMERE_EP:
56353bf605SDave Hansen case INTEL_WESTMERE_EX:
57353bf605SDave Hansen
5819b3340cSPeter Zijlstra case INTEL_SANDYBRIDGE:
59353bf605SDave Hansen case INTEL_SANDYBRIDGE_X:
60353bf605SDave Hansen
6119b3340cSPeter Zijlstra case INTEL_IVYBRIDGE:
62c66f78a6SPeter Zijlstra case INTEL_IVYBRIDGE_X:
63353bf605SDave Hansen
64af239c44SPeter Zijlstra case INTEL_HASWELL:
655e741407SPeter Zijlstra case INTEL_HASWELL_X:
6619b3340cSPeter Zijlstra case INTEL_HASWELL_L:
67c66f78a6SPeter Zijlstra case INTEL_HASWELL_G:
685ebb34edSPeter Zijlstra
695e741407SPeter Zijlstra case INTEL_BROADWELL:
70353bf605SDave Hansen case INTEL_BROADWELL_D:
7171920ea9SKan Liang case INTEL_BROADWELL_G:
7269ced416SKan Liang case INTEL_BROADWELL_X:
735a796d5cSKan Liang case INTEL_SAPPHIRERAPIDS_X:
745a796d5cSKan Liang case INTEL_EMERALDRAPIDS_X:
7519b3340cSPeter Zijlstra case INTEL_GRANITERAPIDS_X:
76f2c4db1bSPeter Zijlstra case INTEL_GRANITERAPIDS_D:
775ebb34edSPeter Zijlstra
78353bf605SDave Hansen case INTEL_ATOM_SILVERMONT:
791aaccc40SKan Liang case INTEL_ATOM_SILVERMONT_D:
801aaccc40SKan Liang case INTEL_ATOM_AIRMONT:
815ebb34edSPeter Zijlstra
82f2c4db1bSPeter Zijlstra case INTEL_ATOM_GOLDMONT:
830aa0e0d6SKan Liang case INTEL_ATOM_GOLDMONT_D:
840aa0e0d6SKan Liang case INTEL_ATOM_GOLDMONT_PLUS:
85c3bb8a9fSKan Liang case INTEL_ATOM_TREMONT_D:
861aaccc40SKan Liang case INTEL_ATOM_TREMONT:
871aaccc40SKan Liang case INTEL_ATOM_TREMONT_L:
881aaccc40SKan Liang
8919b3340cSPeter Zijlstra case INTEL_XEON_PHI_KNL:
9019b3340cSPeter Zijlstra case INTEL_XEON_PHI_KNM:
9119b3340cSPeter Zijlstra if (idx == PERF_MSR_SMI)
9219b3340cSPeter Zijlstra return true;
93af239c44SPeter Zijlstra break;
94c66f78a6SPeter Zijlstra
955134596cSDave Hansen case INTEL_SKYLAKE_L:
96af239c44SPeter Zijlstra case INTEL_SKYLAKE:
97c66f78a6SPeter Zijlstra case INTEL_SKYLAKE_X:
989674b1ccSKan Liang case INTEL_KABYLAKE_L:
999674b1ccSKan Liang case INTEL_KABYLAKE:
100af239c44SPeter Zijlstra case INTEL_COMETLAKE_L:
1011a5da78dSKan Liang case INTEL_COMETLAKE:
1021a5da78dSKan Liang case INTEL_ICELAKE_L:
1031a5da78dSKan Liang case INTEL_ICELAKE:
1040917b950SKan Liang case INTEL_ICELAKE_X:
1050917b950SKan Liang case INTEL_ICELAKE_D:
106907a196fSKan Liang case INTEL_TIGERLAKE_L:
10719d3a81fSKan Liang case INTEL_TIGERLAKE:
10819d3a81fSKan Liang case INTEL_ROCKETLAKE:
109*882cdb06SPeter Zijlstra case INTEL_ALDERLAKE:
11082cd8304SKan Liang case INTEL_ALDERLAKE_L:
111d773a733SKan Liang case INTEL_ATOM_GRACEMONT:
112193c888bSKan Liang case INTEL_RAPTORLAKE:
1136887a4d3SKan Liang case INTEL_RAPTORLAKE_P:
1146887a4d3SKan Liang case INTEL_RAPTORLAKE_S:
11519b3340cSPeter Zijlstra case INTEL_METEORLAKE:
11619b3340cSPeter Zijlstra case INTEL_METEORLAKE_L:
11719b3340cSPeter Zijlstra if (idx == PERF_MSR_SMI || idx == PERF_MSR_PPERF)
11819b3340cSPeter Zijlstra return true;
11919b3340cSPeter Zijlstra break;
12019b3340cSPeter Zijlstra }
12119b3340cSPeter Zijlstra
12219b3340cSPeter Zijlstra return false;
123dde5e720SJiri Olsa }
124dde5e720SJiri Olsa
125dde5e720SJiri Olsa PMU_EVENT_ATTR_STRING(tsc, attr_tsc, "event=0x00" );
126dde5e720SJiri Olsa PMU_EVENT_ATTR_STRING(aperf, attr_aperf, "event=0x01" );
127dde5e720SJiri Olsa PMU_EVENT_ATTR_STRING(mperf, attr_mperf, "event=0x02" );
128dde5e720SJiri Olsa PMU_EVENT_ATTR_STRING(pperf, attr_pperf, "event=0x03" );
129dde5e720SJiri Olsa PMU_EVENT_ATTR_STRING(smi, attr_smi, "event=0x04" );
130dde5e720SJiri Olsa PMU_EVENT_ATTR_STRING(ptsc, attr_ptsc, "event=0x05" );
131dde5e720SJiri Olsa PMU_EVENT_ATTR_STRING(irperf, attr_irperf, "event=0x06" );
132dde5e720SJiri Olsa PMU_EVENT_ATTR_STRING(cpu_thermal_margin, attr_therm, "event=0x07" );
133dde5e720SJiri Olsa PMU_EVENT_ATTR_STRING(cpu_thermal_margin.snapshot, attr_therm_snap, "1" );
134dde5e720SJiri Olsa PMU_EVENT_ATTR_STRING(cpu_thermal_margin.unit, attr_therm_unit, "C" );
135dde5e720SJiri Olsa
136dde5e720SJiri Olsa static unsigned long msr_mask;
137dde5e720SJiri Olsa
138dde5e720SJiri Olsa PMU_EVENT_GROUP(events, aperf);
139dde5e720SJiri Olsa PMU_EVENT_GROUP(events, mperf);
140dde5e720SJiri Olsa PMU_EVENT_GROUP(events, pperf);
141dde5e720SJiri Olsa PMU_EVENT_GROUP(events, smi);
142dde5e720SJiri Olsa PMU_EVENT_GROUP(events, ptsc);
143dde5e720SJiri Olsa PMU_EVENT_GROUP(events, irperf);
144dde5e720SJiri Olsa
145dde5e720SJiri Olsa static struct attribute *attrs_therm[] = {
146dde5e720SJiri Olsa &attr_therm.attr.attr,
147dde5e720SJiri Olsa &attr_therm_snap.attr.attr,
148b7b7c782SAndy Lutomirski &attr_therm_unit.attr.attr,
149b7b7c782SAndy Lutomirski NULL,
150dde5e720SJiri Olsa };
151dde5e720SJiri Olsa
152dde5e720SJiri Olsa static struct attribute_group group_therm = {
153dde5e720SJiri Olsa .name = "events",
154b7b7c782SAndy Lutomirski .attrs = attrs_therm,
15519b3340cSPeter Zijlstra };
156dde5e720SJiri Olsa
157dde5e720SJiri Olsa static struct perf_msr msr[] = {
158dde5e720SJiri Olsa [PERF_MSR_TSC] = { .no_check = true, },
159dde5e720SJiri Olsa [PERF_MSR_APERF] = { MSR_IA32_APERF, &group_aperf, test_aperfmperf, },
160dde5e720SJiri Olsa [PERF_MSR_MPERF] = { MSR_IA32_MPERF, &group_mperf, test_aperfmperf, },
161dde5e720SJiri Olsa [PERF_MSR_PPERF] = { MSR_PPERF, &group_pperf, test_intel, },
162dde5e720SJiri Olsa [PERF_MSR_SMI] = { MSR_SMI_COUNT, &group_smi, test_intel, },
163dde5e720SJiri Olsa [PERF_MSR_PTSC] = { MSR_F15H_PTSC, &group_ptsc, test_ptsc, },
16419b3340cSPeter Zijlstra [PERF_MSR_IRPERF] = { MSR_F17H_IRPERF, &group_irperf, test_irperf, },
16519b3340cSPeter Zijlstra [PERF_MSR_THERM] = { MSR_IA32_THERM_STATUS, &group_therm, test_therm_status, },
166dde5e720SJiri Olsa };
167dde5e720SJiri Olsa
16819b3340cSPeter Zijlstra static struct attribute *events_attrs[] = {
169b7b7c782SAndy Lutomirski &attr_tsc.attr.attr,
170b7b7c782SAndy Lutomirski NULL,
171b7b7c782SAndy Lutomirski };
172b7b7c782SAndy Lutomirski
173b7b7c782SAndy Lutomirski static struct attribute_group events_attr_group = {
174b7b7c782SAndy Lutomirski .name = "events",
175b7b7c782SAndy Lutomirski .attrs = events_attrs,
176b7b7c782SAndy Lutomirski };
177b7b7c782SAndy Lutomirski
178b7b7c782SAndy Lutomirski PMU_FORMAT_ATTR(event, "config:0-63");
179b7b7c782SAndy Lutomirski static struct attribute *format_attrs[] = {
180b7b7c782SAndy Lutomirski &format_attr_event.attr,
181b7b7c782SAndy Lutomirski NULL,
182b7b7c782SAndy Lutomirski };
183b7b7c782SAndy Lutomirski static struct attribute_group format_attr_group = {
184b7b7c782SAndy Lutomirski .name = "format",
185b7b7c782SAndy Lutomirski .attrs = format_attrs,
186b7b7c782SAndy Lutomirski };
187b7b7c782SAndy Lutomirski
188b7b7c782SAndy Lutomirski static const struct attribute_group *attr_groups[] = {
189b7b7c782SAndy Lutomirski &events_attr_group,
190b7b7c782SAndy Lutomirski &format_attr_group,
191b7b7c782SAndy Lutomirski NULL,
192d9f3b450SValdis Klētnieks };
193dde5e720SJiri Olsa
194dde5e720SJiri Olsa static const struct attribute_group *attr_update[] = {
195dde5e720SJiri Olsa &group_aperf,
196dde5e720SJiri Olsa &group_mperf,
197dde5e720SJiri Olsa &group_pperf,
198dde5e720SJiri Olsa &group_smi,
199dde5e720SJiri Olsa &group_ptsc,
200dde5e720SJiri Olsa &group_irperf,
201dde5e720SJiri Olsa &group_therm,
202dde5e720SJiri Olsa NULL,
203b7b7c782SAndy Lutomirski };
204b7b7c782SAndy Lutomirski
msr_event_init(struct perf_event * event)205b7b7c782SAndy Lutomirski static int msr_event_init(struct perf_event *event)
206b7b7c782SAndy Lutomirski {
207b7b7c782SAndy Lutomirski u64 cfg = event->attr.config;
208b7b7c782SAndy Lutomirski
209b7b7c782SAndy Lutomirski if (event->attr.type != event->pmu->type)
210b7b7c782SAndy Lutomirski return -ENOENT;
2112ff40250SAndrew Murray
212b7b7c782SAndy Lutomirski /* unsupported modes and filters */
213b7b7c782SAndy Lutomirski if (event->attr.sample_period) /* no sampling */
21406ce6e9bSPeter Zijlstra return -EINVAL;
21506ce6e9bSPeter Zijlstra
21606ce6e9bSPeter Zijlstra if (cfg >= PERF_MSR_EVENT_MAX)
21706ce6e9bSPeter Zijlstra return -EINVAL;
21806ce6e9bSPeter Zijlstra
219dde5e720SJiri Olsa cfg = array_index_nospec((unsigned long)cfg, PERF_MSR_EVENT_MAX);
22019b3340cSPeter Zijlstra
22119b3340cSPeter Zijlstra if (!(msr_mask & (1 << cfg)))
222b7b7c782SAndy Lutomirski return -EINVAL;
223b7b7c782SAndy Lutomirski
224b7b7c782SAndy Lutomirski event->hw.idx = -1;
225b7b7c782SAndy Lutomirski event->hw.event_base = msr[cfg].msr;
226b7b7c782SAndy Lutomirski event->hw.config = cfg;
227b7b7c782SAndy Lutomirski
228b7b7c782SAndy Lutomirski return 0;
229b7b7c782SAndy Lutomirski }
230b7b7c782SAndy Lutomirski
msr_read_counter(struct perf_event * event)231b7b7c782SAndy Lutomirski static inline u64 msr_read_counter(struct perf_event *event)
232b7b7c782SAndy Lutomirski {
233b7b7c782SAndy Lutomirski u64 now;
234b7b7c782SAndy Lutomirski
235b7b7c782SAndy Lutomirski if (event->hw.event_base)
236ea89c065SThomas Gleixner rdmsrq(event->hw.event_base, now);
237b7b7c782SAndy Lutomirski else
238b7b7c782SAndy Lutomirski now = rdtsc_ordered();
239b7b7c782SAndy Lutomirski
240ea89c065SThomas Gleixner return now;
241b7b7c782SAndy Lutomirski }
242b7b7c782SAndy Lutomirski
msr_event_update(struct perf_event * event)243b7b7c782SAndy Lutomirski static void msr_event_update(struct perf_event *event)
244b7b7c782SAndy Lutomirski {
245b7b7c782SAndy Lutomirski u64 prev, now;
2469128d3edSIngo Molnar s64 delta;
247b7b7c782SAndy Lutomirski
2484c1c9deaSUros Bizjak /* Careful, an NMI might modify the previous event value: */
249b7b7c782SAndy Lutomirski prev = local64_read(&event->hw.prev_count);
2504c1c9deaSUros Bizjak do {
251b7b7c782SAndy Lutomirski now = msr_read_counter(event);
252b7b7c782SAndy Lutomirski } while (!local64_try_cmpxchg(&event->hw.prev_count, &prev, now));
2539ae21dd6SStephane Eranian
25478e3c795SMartin Kepplinger delta = now - prev;
2559ae21dd6SStephane Eranian if (unlikely(event->hw.event_base == MSR_SMI_COUNT)) {
2569ae21dd6SStephane Eranian delta = sign_extend64(delta, 31);
2579128d3edSIngo Molnar local64_add(delta, &event->count);
2589ae21dd6SStephane Eranian } else if (unlikely(event->hw.event_base == MSR_IA32_THERM_STATUS)) {
2599ae21dd6SStephane Eranian /* If valid, extract digital readout, otherwise set to -1: */
2609128d3edSIngo Molnar now = now & (1ULL << 31) ? (now >> 16) & 0x3f : -1;
2613c3116b7SPeter Zijlstra local64_set(&event->count, now);
262b7b7c782SAndy Lutomirski } else {
2639128d3edSIngo Molnar local64_add(delta, &event->count);
264b7b7c782SAndy Lutomirski }
265b7b7c782SAndy Lutomirski }
266b7b7c782SAndy Lutomirski
msr_event_start(struct perf_event * event,int flags)2679128d3edSIngo Molnar static void msr_event_start(struct perf_event *event, int flags)
268b7b7c782SAndy Lutomirski {
269b7b7c782SAndy Lutomirski u64 now = msr_read_counter(event);
270b7b7c782SAndy Lutomirski
271b7b7c782SAndy Lutomirski local64_set(&event->hw.prev_count, now);
272b7b7c782SAndy Lutomirski }
273b7b7c782SAndy Lutomirski
msr_event_stop(struct perf_event * event,int flags)274b7b7c782SAndy Lutomirski static void msr_event_stop(struct perf_event *event, int flags)
275b7b7c782SAndy Lutomirski {
276b7b7c782SAndy Lutomirski msr_event_update(event);
277b7b7c782SAndy Lutomirski }
278b7b7c782SAndy Lutomirski
msr_event_del(struct perf_event * event,int flags)279b7b7c782SAndy Lutomirski static void msr_event_del(struct perf_event *event, int flags)
280b7b7c782SAndy Lutomirski {
281b7b7c782SAndy Lutomirski msr_event_stop(event, PERF_EF_UPDATE);
282b7b7c782SAndy Lutomirski }
283b7b7c782SAndy Lutomirski
msr_event_add(struct perf_event * event,int flags)284b7b7c782SAndy Lutomirski static int msr_event_add(struct perf_event *event, int flags)
285b7b7c782SAndy Lutomirski {
286b7b7c782SAndy Lutomirski if (flags & PERF_EF_START)
287b7b7c782SAndy Lutomirski msr_event_start(event, flags);
288b7b7c782SAndy Lutomirski
289b7b7c782SAndy Lutomirski return 0;
290b7b7c782SAndy Lutomirski }
291b7b7c782SAndy Lutomirski
292b7b7c782SAndy Lutomirski static struct pmu pmu_msr = {
293b7b7c782SAndy Lutomirski .task_ctx_nr = perf_sw_context,
294b7b7c782SAndy Lutomirski .attr_groups = attr_groups,
295b7b7c782SAndy Lutomirski .event_init = msr_event_init,
296b7b7c782SAndy Lutomirski .add = msr_event_add,
297b7b7c782SAndy Lutomirski .del = msr_event_del,
298b7b7c782SAndy Lutomirski .start = msr_event_start,
2992ff40250SAndrew Murray .stop = msr_event_stop,
300dde5e720SJiri Olsa .read = msr_event_update,
301b7b7c782SAndy Lutomirski .capabilities = PERF_PMU_CAP_NO_INTERRUPT | PERF_PMU_CAP_NO_EXCLUDE,
302b7b7c782SAndy Lutomirski .attr_update = attr_update,
303b7b7c782SAndy Lutomirski };
304b7b7c782SAndy Lutomirski
msr_init(void)30519b3340cSPeter Zijlstra static int __init msr_init(void)
30619b3340cSPeter Zijlstra {
307b7b7c782SAndy Lutomirski if (!boot_cpu_has(X86_FEATURE_TSC)) {
308b7b7c782SAndy Lutomirski pr_cont("no MSR PMU driver.\n");
309b7b7c782SAndy Lutomirski return 0;
310dde5e720SJiri Olsa }
31119b3340cSPeter Zijlstra
312b7b7c782SAndy Lutomirski msr_mask = perf_msr_probe(msr, PERF_MSR_EVENT_MAX, true, NULL);
313b7b7c782SAndy Lutomirski
314b7b7c782SAndy Lutomirski perf_pmu_register(&pmu_msr, "msr", -1);
315b7b7c782SAndy Lutomirski
316b7b7c782SAndy Lutomirski return 0;
317 }
318 device_initcall(msr_init);
319