cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

perf_event.c (11461B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Xtensa Performance Monitor Module driver
      4 * See Tensilica Debug User's Guide for PMU registers documentation.
      5 *
      6 * Copyright (C) 2015 Cadence Design Systems Inc.
      7 */
      8
      9#include <linux/interrupt.h>
     10#include <linux/irqdomain.h>
     11#include <linux/module.h>
     12#include <linux/of.h>
     13#include <linux/perf_event.h>
     14#include <linux/platform_device.h>
     15
     16#include <asm/processor.h>
     17#include <asm/stacktrace.h>
     18
     19/* Global control/status for all perf counters */
     20#define XTENSA_PMU_PMG			0x1000
     21/* Perf counter values */
     22#define XTENSA_PMU_PM(i)		(0x1080 + (i) * 4)
     23/* Perf counter control registers */
     24#define XTENSA_PMU_PMCTRL(i)		(0x1100 + (i) * 4)
     25/* Perf counter status registers */
     26#define XTENSA_PMU_PMSTAT(i)		(0x1180 + (i) * 4)
     27
     28#define XTENSA_PMU_PMG_PMEN		0x1
     29
     30#define XTENSA_PMU_COUNTER_MASK		0xffffffffULL
     31#define XTENSA_PMU_COUNTER_MAX		0x7fffffff
     32
     33#define XTENSA_PMU_PMCTRL_INTEN		0x00000001
     34#define XTENSA_PMU_PMCTRL_KRNLCNT	0x00000008
     35#define XTENSA_PMU_PMCTRL_TRACELEVEL	0x000000f0
     36#define XTENSA_PMU_PMCTRL_SELECT_SHIFT	8
     37#define XTENSA_PMU_PMCTRL_SELECT	0x00001f00
     38#define XTENSA_PMU_PMCTRL_MASK_SHIFT	16
     39#define XTENSA_PMU_PMCTRL_MASK		0xffff0000
     40
     41#define XTENSA_PMU_MASK(select, mask) \
     42	(((select) << XTENSA_PMU_PMCTRL_SELECT_SHIFT) | \
     43	 ((mask) << XTENSA_PMU_PMCTRL_MASK_SHIFT) | \
     44	 XTENSA_PMU_PMCTRL_TRACELEVEL | \
     45	 XTENSA_PMU_PMCTRL_INTEN)
     46
     47#define XTENSA_PMU_PMSTAT_OVFL		0x00000001
     48#define XTENSA_PMU_PMSTAT_INTASRT	0x00000010
     49
     50struct xtensa_pmu_events {
     51	/* Array of events currently on this core */
     52	struct perf_event *event[XCHAL_NUM_PERF_COUNTERS];
     53	/* Bitmap of used hardware counters */
     54	unsigned long used_mask[BITS_TO_LONGS(XCHAL_NUM_PERF_COUNTERS)];
     55};
     56static DEFINE_PER_CPU(struct xtensa_pmu_events, xtensa_pmu_events);
     57
     58static const u32 xtensa_hw_ctl[] = {
     59	[PERF_COUNT_HW_CPU_CYCLES]		= XTENSA_PMU_MASK(0, 0x1),
     60	[PERF_COUNT_HW_INSTRUCTIONS]		= XTENSA_PMU_MASK(2, 0xffff),
     61	[PERF_COUNT_HW_CACHE_REFERENCES]	= XTENSA_PMU_MASK(10, 0x1),
     62	[PERF_COUNT_HW_CACHE_MISSES]		= XTENSA_PMU_MASK(12, 0x1),
     63	/* Taken and non-taken branches + taken loop ends */
     64	[PERF_COUNT_HW_BRANCH_INSTRUCTIONS]	= XTENSA_PMU_MASK(2, 0x490),
     65	/* Instruction-related + other global stall cycles */
     66	[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND]	= XTENSA_PMU_MASK(4, 0x1ff),
     67	/* Data-related global stall cycles */
     68	[PERF_COUNT_HW_STALLED_CYCLES_BACKEND]	= XTENSA_PMU_MASK(3, 0x1ff),
     69};
     70
     71#define C(_x) PERF_COUNT_HW_CACHE_##_x
     72
     73static const u32 xtensa_cache_ctl[][C(OP_MAX)][C(RESULT_MAX)] = {
     74	[C(L1D)] = {
     75		[C(OP_READ)] = {
     76			[C(RESULT_ACCESS)]	= XTENSA_PMU_MASK(10, 0x1),
     77			[C(RESULT_MISS)]	= XTENSA_PMU_MASK(10, 0x2),
     78		},
     79		[C(OP_WRITE)] = {
     80			[C(RESULT_ACCESS)]	= XTENSA_PMU_MASK(11, 0x1),
     81			[C(RESULT_MISS)]	= XTENSA_PMU_MASK(11, 0x2),
     82		},
     83	},
     84	[C(L1I)] = {
     85		[C(OP_READ)] = {
     86			[C(RESULT_ACCESS)]	= XTENSA_PMU_MASK(8, 0x1),
     87			[C(RESULT_MISS)]	= XTENSA_PMU_MASK(8, 0x2),
     88		},
     89	},
     90	[C(DTLB)] = {
     91		[C(OP_READ)] = {
     92			[C(RESULT_ACCESS)]	= XTENSA_PMU_MASK(9, 0x1),
     93			[C(RESULT_MISS)]	= XTENSA_PMU_MASK(9, 0x8),
     94		},
     95	},
     96	[C(ITLB)] = {
     97		[C(OP_READ)] = {
     98			[C(RESULT_ACCESS)]	= XTENSA_PMU_MASK(7, 0x1),
     99			[C(RESULT_MISS)]	= XTENSA_PMU_MASK(7, 0x8),
    100		},
    101	},
    102};
    103
    104static int xtensa_pmu_cache_event(u64 config)
    105{
    106	unsigned int cache_type, cache_op, cache_result;
    107	int ret;
    108
    109	cache_type = (config >>  0) & 0xff;
    110	cache_op = (config >>  8) & 0xff;
    111	cache_result = (config >> 16) & 0xff;
    112
    113	if (cache_type >= ARRAY_SIZE(xtensa_cache_ctl) ||
    114	    cache_op >= C(OP_MAX) ||
    115	    cache_result >= C(RESULT_MAX))
    116		return -EINVAL;
    117
    118	ret = xtensa_cache_ctl[cache_type][cache_op][cache_result];
    119
    120	if (ret == 0)
    121		return -EINVAL;
    122
    123	return ret;
    124}
    125
    126static inline uint32_t xtensa_pmu_read_counter(int idx)
    127{
    128	return get_er(XTENSA_PMU_PM(idx));
    129}
    130
    131static inline void xtensa_pmu_write_counter(int idx, uint32_t v)
    132{
    133	set_er(v, XTENSA_PMU_PM(idx));
    134}
    135
    136static void xtensa_perf_event_update(struct perf_event *event,
    137				     struct hw_perf_event *hwc, int idx)
    138{
    139	uint64_t prev_raw_count, new_raw_count;
    140	int64_t delta;
    141
    142	do {
    143		prev_raw_count = local64_read(&hwc->prev_count);
    144		new_raw_count = xtensa_pmu_read_counter(event->hw.idx);
    145	} while (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
    146				 new_raw_count) != prev_raw_count);
    147
    148	delta = (new_raw_count - prev_raw_count) & XTENSA_PMU_COUNTER_MASK;
    149
    150	local64_add(delta, &event->count);
    151	local64_sub(delta, &hwc->period_left);
    152}
    153
    154static bool xtensa_perf_event_set_period(struct perf_event *event,
    155					 struct hw_perf_event *hwc, int idx)
    156{
    157	bool rc = false;
    158	s64 left;
    159
    160	if (!is_sampling_event(event)) {
    161		left = XTENSA_PMU_COUNTER_MAX;
    162	} else {
    163		s64 period = hwc->sample_period;
    164
    165		left = local64_read(&hwc->period_left);
    166		if (left <= -period) {
    167			left = period;
    168			local64_set(&hwc->period_left, left);
    169			hwc->last_period = period;
    170			rc = true;
    171		} else if (left <= 0) {
    172			left += period;
    173			local64_set(&hwc->period_left, left);
    174			hwc->last_period = period;
    175			rc = true;
    176		}
    177		if (left > XTENSA_PMU_COUNTER_MAX)
    178			left = XTENSA_PMU_COUNTER_MAX;
    179	}
    180
    181	local64_set(&hwc->prev_count, -left);
    182	xtensa_pmu_write_counter(idx, -left);
    183	perf_event_update_userpage(event);
    184
    185	return rc;
    186}
    187
    188static void xtensa_pmu_enable(struct pmu *pmu)
    189{
    190	set_er(get_er(XTENSA_PMU_PMG) | XTENSA_PMU_PMG_PMEN, XTENSA_PMU_PMG);
    191}
    192
    193static void xtensa_pmu_disable(struct pmu *pmu)
    194{
    195	set_er(get_er(XTENSA_PMU_PMG) & ~XTENSA_PMU_PMG_PMEN, XTENSA_PMU_PMG);
    196}
    197
    198static int xtensa_pmu_event_init(struct perf_event *event)
    199{
    200	int ret;
    201
    202	switch (event->attr.type) {
    203	case PERF_TYPE_HARDWARE:
    204		if (event->attr.config >= ARRAY_SIZE(xtensa_hw_ctl) ||
    205		    xtensa_hw_ctl[event->attr.config] == 0)
    206			return -EINVAL;
    207		event->hw.config = xtensa_hw_ctl[event->attr.config];
    208		return 0;
    209
    210	case PERF_TYPE_HW_CACHE:
    211		ret = xtensa_pmu_cache_event(event->attr.config);
    212		if (ret < 0)
    213			return ret;
    214		event->hw.config = ret;
    215		return 0;
    216
    217	case PERF_TYPE_RAW:
    218		/* Not 'previous counter' select */
    219		if ((event->attr.config & XTENSA_PMU_PMCTRL_SELECT) ==
    220		    (1 << XTENSA_PMU_PMCTRL_SELECT_SHIFT))
    221			return -EINVAL;
    222		event->hw.config = (event->attr.config &
    223				    (XTENSA_PMU_PMCTRL_KRNLCNT |
    224				     XTENSA_PMU_PMCTRL_TRACELEVEL |
    225				     XTENSA_PMU_PMCTRL_SELECT |
    226				     XTENSA_PMU_PMCTRL_MASK)) |
    227			XTENSA_PMU_PMCTRL_INTEN;
    228		return 0;
    229
    230	default:
    231		return -ENOENT;
    232	}
    233}
    234
    235/*
    236 * Starts/Stops a counter present on the PMU. The PMI handler
    237 * should stop the counter when perf_event_overflow() returns
    238 * !0. ->start() will be used to continue.
    239 */
    240static void xtensa_pmu_start(struct perf_event *event, int flags)
    241{
    242	struct hw_perf_event *hwc = &event->hw;
    243	int idx = hwc->idx;
    244
    245	if (WARN_ON_ONCE(idx == -1))
    246		return;
    247
    248	if (flags & PERF_EF_RELOAD) {
    249		WARN_ON_ONCE(!(event->hw.state & PERF_HES_UPTODATE));
    250		xtensa_perf_event_set_period(event, hwc, idx);
    251	}
    252
    253	hwc->state = 0;
    254
    255	set_er(hwc->config, XTENSA_PMU_PMCTRL(idx));
    256}
    257
    258static void xtensa_pmu_stop(struct perf_event *event, int flags)
    259{
    260	struct hw_perf_event *hwc = &event->hw;
    261	int idx = hwc->idx;
    262
    263	if (!(hwc->state & PERF_HES_STOPPED)) {
    264		set_er(0, XTENSA_PMU_PMCTRL(idx));
    265		set_er(get_er(XTENSA_PMU_PMSTAT(idx)),
    266		       XTENSA_PMU_PMSTAT(idx));
    267		hwc->state |= PERF_HES_STOPPED;
    268	}
    269
    270	if ((flags & PERF_EF_UPDATE) &&
    271	    !(event->hw.state & PERF_HES_UPTODATE)) {
    272		xtensa_perf_event_update(event, &event->hw, idx);
    273		event->hw.state |= PERF_HES_UPTODATE;
    274	}
    275}
    276
    277/*
    278 * Adds/Removes a counter to/from the PMU, can be done inside
    279 * a transaction, see the ->*_txn() methods.
    280 */
    281static int xtensa_pmu_add(struct perf_event *event, int flags)
    282{
    283	struct xtensa_pmu_events *ev = this_cpu_ptr(&xtensa_pmu_events);
    284	struct hw_perf_event *hwc = &event->hw;
    285	int idx = hwc->idx;
    286
    287	if (__test_and_set_bit(idx, ev->used_mask)) {
    288		idx = find_first_zero_bit(ev->used_mask,
    289					  XCHAL_NUM_PERF_COUNTERS);
    290		if (idx == XCHAL_NUM_PERF_COUNTERS)
    291			return -EAGAIN;
    292
    293		__set_bit(idx, ev->used_mask);
    294		hwc->idx = idx;
    295	}
    296	ev->event[idx] = event;
    297
    298	hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
    299
    300	if (flags & PERF_EF_START)
    301		xtensa_pmu_start(event, PERF_EF_RELOAD);
    302
    303	perf_event_update_userpage(event);
    304	return 0;
    305}
    306
    307static void xtensa_pmu_del(struct perf_event *event, int flags)
    308{
    309	struct xtensa_pmu_events *ev = this_cpu_ptr(&xtensa_pmu_events);
    310
    311	xtensa_pmu_stop(event, PERF_EF_UPDATE);
    312	__clear_bit(event->hw.idx, ev->used_mask);
    313	perf_event_update_userpage(event);
    314}
    315
    316static void xtensa_pmu_read(struct perf_event *event)
    317{
    318	xtensa_perf_event_update(event, &event->hw, event->hw.idx);
    319}
    320
    321static int callchain_trace(struct stackframe *frame, void *data)
    322{
    323	struct perf_callchain_entry_ctx *entry = data;
    324
    325	perf_callchain_store(entry, frame->pc);
    326	return 0;
    327}
    328
    329void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
    330			   struct pt_regs *regs)
    331{
    332	xtensa_backtrace_kernel(regs, entry->max_stack,
    333				callchain_trace, NULL, entry);
    334}
    335
    336void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
    337			 struct pt_regs *regs)
    338{
    339	xtensa_backtrace_user(regs, entry->max_stack,
    340			      callchain_trace, entry);
    341}
    342
    343void perf_event_print_debug(void)
    344{
    345	unsigned long flags;
    346	unsigned i;
    347
    348	local_irq_save(flags);
    349	pr_info("CPU#%d: PMG: 0x%08lx\n", smp_processor_id(),
    350		get_er(XTENSA_PMU_PMG));
    351	for (i = 0; i < XCHAL_NUM_PERF_COUNTERS; ++i)
    352		pr_info("PM%d: 0x%08lx, PMCTRL%d: 0x%08lx, PMSTAT%d: 0x%08lx\n",
    353			i, get_er(XTENSA_PMU_PM(i)),
    354			i, get_er(XTENSA_PMU_PMCTRL(i)),
    355			i, get_er(XTENSA_PMU_PMSTAT(i)));
    356	local_irq_restore(flags);
    357}
    358
    359irqreturn_t xtensa_pmu_irq_handler(int irq, void *dev_id)
    360{
    361	irqreturn_t rc = IRQ_NONE;
    362	struct xtensa_pmu_events *ev = this_cpu_ptr(&xtensa_pmu_events);
    363	unsigned i;
    364
    365	for_each_set_bit(i, ev->used_mask, XCHAL_NUM_PERF_COUNTERS) {
    366		uint32_t v = get_er(XTENSA_PMU_PMSTAT(i));
    367		struct perf_event *event = ev->event[i];
    368		struct hw_perf_event *hwc = &event->hw;
    369		u64 last_period;
    370
    371		if (!(v & XTENSA_PMU_PMSTAT_OVFL))
    372			continue;
    373
    374		set_er(v, XTENSA_PMU_PMSTAT(i));
    375		xtensa_perf_event_update(event, hwc, i);
    376		last_period = hwc->last_period;
    377		if (xtensa_perf_event_set_period(event, hwc, i)) {
    378			struct perf_sample_data data;
    379			struct pt_regs *regs = get_irq_regs();
    380
    381			perf_sample_data_init(&data, 0, last_period);
    382			if (perf_event_overflow(event, &data, regs))
    383				xtensa_pmu_stop(event, 0);
    384		}
    385
    386		rc = IRQ_HANDLED;
    387	}
    388	return rc;
    389}
    390
    391static struct pmu xtensa_pmu = {
    392	.pmu_enable = xtensa_pmu_enable,
    393	.pmu_disable = xtensa_pmu_disable,
    394	.event_init = xtensa_pmu_event_init,
    395	.add = xtensa_pmu_add,
    396	.del = xtensa_pmu_del,
    397	.start = xtensa_pmu_start,
    398	.stop = xtensa_pmu_stop,
    399	.read = xtensa_pmu_read,
    400};
    401
    402static int xtensa_pmu_setup(unsigned int cpu)
    403{
    404	unsigned i;
    405
    406	set_er(0, XTENSA_PMU_PMG);
    407	for (i = 0; i < XCHAL_NUM_PERF_COUNTERS; ++i) {
    408		set_er(0, XTENSA_PMU_PMCTRL(i));
    409		set_er(get_er(XTENSA_PMU_PMSTAT(i)), XTENSA_PMU_PMSTAT(i));
    410	}
    411	return 0;
    412}
    413
    414static int __init xtensa_pmu_init(void)
    415{
    416	int ret;
    417	int irq = irq_create_mapping(NULL, XCHAL_PROFILING_INTERRUPT);
    418
    419	ret = cpuhp_setup_state(CPUHP_AP_PERF_XTENSA_STARTING,
    420				"perf/xtensa:starting", xtensa_pmu_setup,
    421				NULL);
    422	if (ret) {
    423		pr_err("xtensa_pmu: failed to register CPU-hotplug.\n");
    424		return ret;
    425	}
    426#if XTENSA_FAKE_NMI
    427	enable_irq(irq);
    428#else
    429	ret = request_irq(irq, xtensa_pmu_irq_handler, IRQF_PERCPU,
    430			  "pmu", NULL);
    431	if (ret < 0)
    432		return ret;
    433#endif
    434
    435	ret = perf_pmu_register(&xtensa_pmu, "cpu", PERF_TYPE_RAW);
    436	if (ret)
    437		free_irq(irq, NULL);
    438
    439	return ret;
    440}
    441early_initcall(xtensa_pmu_init);