xref: /linux/arch/x86/events/msr.c (revision a1c613ae4c322ddd58d5a8539dbfba2a0380a8c0)
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