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

riscv_pmu.c (8412B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * RISC-V performance counter support.
      4 *
      5 * Copyright (C) 2021 Western Digital Corporation or its affiliates.
      6 *
      7 * This implementation is based on old RISC-V perf and ARM perf event code
      8 * which are in turn based on sparc64 and x86 code.
      9 */
     10
     11#include <linux/cpumask.h>
     12#include <linux/irq.h>
     13#include <linux/irqdesc.h>
     14#include <linux/perf/riscv_pmu.h>
     15#include <linux/printk.h>
     16#include <linux/smp.h>
     17
     18#include <asm/sbi.h>
     19
     20static unsigned long csr_read_num(int csr_num)
     21{
     22#define switchcase_csr_read(__csr_num, __val)		{\
     23	case __csr_num:					\
     24		__val = csr_read(__csr_num);		\
     25		break; }
     26#define switchcase_csr_read_2(__csr_num, __val)		{\
     27	switchcase_csr_read(__csr_num + 0, __val)	 \
     28	switchcase_csr_read(__csr_num + 1, __val)}
     29#define switchcase_csr_read_4(__csr_num, __val)		{\
     30	switchcase_csr_read_2(__csr_num + 0, __val)	 \
     31	switchcase_csr_read_2(__csr_num + 2, __val)}
     32#define switchcase_csr_read_8(__csr_num, __val)		{\
     33	switchcase_csr_read_4(__csr_num + 0, __val)	 \
     34	switchcase_csr_read_4(__csr_num + 4, __val)}
     35#define switchcase_csr_read_16(__csr_num, __val)	{\
     36	switchcase_csr_read_8(__csr_num + 0, __val)	 \
     37	switchcase_csr_read_8(__csr_num + 8, __val)}
     38#define switchcase_csr_read_32(__csr_num, __val)	{\
     39	switchcase_csr_read_16(__csr_num + 0, __val)	 \
     40	switchcase_csr_read_16(__csr_num + 16, __val)}
     41
     42	unsigned long ret = 0;
     43
     44	switch (csr_num) {
     45	switchcase_csr_read_32(CSR_CYCLE, ret)
     46	switchcase_csr_read_32(CSR_CYCLEH, ret)
     47	default :
     48		break;
     49	}
     50
     51	return ret;
     52#undef switchcase_csr_read_32
     53#undef switchcase_csr_read_16
     54#undef switchcase_csr_read_8
     55#undef switchcase_csr_read_4
     56#undef switchcase_csr_read_2
     57#undef switchcase_csr_read
     58}
     59
     60/*
     61 * Read the CSR of a corresponding counter.
     62 */
     63unsigned long riscv_pmu_ctr_read_csr(unsigned long csr)
     64{
     65	if (csr < CSR_CYCLE || csr > CSR_HPMCOUNTER31H ||
     66	   (csr > CSR_HPMCOUNTER31 && csr < CSR_CYCLEH)) {
     67		pr_err("Invalid performance counter csr %lx\n", csr);
     68		return -EINVAL;
     69	}
     70
     71	return csr_read_num(csr);
     72}
     73
     74u64 riscv_pmu_ctr_get_width_mask(struct perf_event *event)
     75{
     76	int cwidth;
     77	struct riscv_pmu *rvpmu = to_riscv_pmu(event->pmu);
     78	struct hw_perf_event *hwc = &event->hw;
     79
     80	if (!rvpmu->ctr_get_width)
     81	/**
     82	 * If the pmu driver doesn't support counter width, set it to default
     83	 * maximum allowed by the specification.
     84	 */
     85		cwidth = 63;
     86	else {
     87		if (hwc->idx == -1)
     88			/* Handle init case where idx is not initialized yet */
     89			cwidth = rvpmu->ctr_get_width(0);
     90		else
     91			cwidth = rvpmu->ctr_get_width(hwc->idx);
     92	}
     93
     94	return GENMASK_ULL(cwidth, 0);
     95}
     96
     97u64 riscv_pmu_event_update(struct perf_event *event)
     98{
     99	struct riscv_pmu *rvpmu = to_riscv_pmu(event->pmu);
    100	struct hw_perf_event *hwc = &event->hw;
    101	u64 prev_raw_count, new_raw_count;
    102	unsigned long cmask;
    103	u64 oldval, delta;
    104
    105	if (!rvpmu->ctr_read)
    106		return 0;
    107
    108	cmask = riscv_pmu_ctr_get_width_mask(event);
    109
    110	do {
    111		prev_raw_count = local64_read(&hwc->prev_count);
    112		new_raw_count = rvpmu->ctr_read(event);
    113		oldval = local64_cmpxchg(&hwc->prev_count, prev_raw_count,
    114					 new_raw_count);
    115	} while (oldval != prev_raw_count);
    116
    117	delta = (new_raw_count - prev_raw_count) & cmask;
    118	local64_add(delta, &event->count);
    119	local64_sub(delta, &hwc->period_left);
    120
    121	return delta;
    122}
    123
    124static void riscv_pmu_stop(struct perf_event *event, int flags)
    125{
    126	struct hw_perf_event *hwc = &event->hw;
    127	struct riscv_pmu *rvpmu = to_riscv_pmu(event->pmu);
    128
    129	WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
    130
    131	if (!(hwc->state & PERF_HES_STOPPED)) {
    132		if (rvpmu->ctr_stop) {
    133			rvpmu->ctr_stop(event, 0);
    134			hwc->state |= PERF_HES_STOPPED;
    135		}
    136		riscv_pmu_event_update(event);
    137		hwc->state |= PERF_HES_UPTODATE;
    138	}
    139}
    140
    141int riscv_pmu_event_set_period(struct perf_event *event)
    142{
    143	struct hw_perf_event *hwc = &event->hw;
    144	s64 left = local64_read(&hwc->period_left);
    145	s64 period = hwc->sample_period;
    146	int overflow = 0;
    147	uint64_t max_period = riscv_pmu_ctr_get_width_mask(event);
    148
    149	if (unlikely(left <= -period)) {
    150		left = period;
    151		local64_set(&hwc->period_left, left);
    152		hwc->last_period = period;
    153		overflow = 1;
    154	}
    155
    156	if (unlikely(left <= 0)) {
    157		left += period;
    158		local64_set(&hwc->period_left, left);
    159		hwc->last_period = period;
    160		overflow = 1;
    161	}
    162
    163	/*
    164	 * Limit the maximum period to prevent the counter value
    165	 * from overtaking the one we are about to program. In
    166	 * effect we are reducing max_period to account for
    167	 * interrupt latency (and we are being very conservative).
    168	 */
    169	if (left > (max_period >> 1))
    170		left = (max_period >> 1);
    171
    172	local64_set(&hwc->prev_count, (u64)-left);
    173	perf_event_update_userpage(event);
    174
    175	return overflow;
    176}
    177
    178static void riscv_pmu_start(struct perf_event *event, int flags)
    179{
    180	struct hw_perf_event *hwc = &event->hw;
    181	struct riscv_pmu *rvpmu = to_riscv_pmu(event->pmu);
    182	uint64_t max_period = riscv_pmu_ctr_get_width_mask(event);
    183	u64 init_val;
    184
    185	if (WARN_ON_ONCE(!(event->hw.state & PERF_HES_STOPPED)))
    186		return;
    187
    188	if (flags & PERF_EF_RELOAD)
    189		WARN_ON_ONCE(!(event->hw.state & PERF_HES_UPTODATE));
    190
    191	hwc->state = 0;
    192	riscv_pmu_event_set_period(event);
    193	init_val = local64_read(&hwc->prev_count) & max_period;
    194	rvpmu->ctr_start(event, init_val);
    195	perf_event_update_userpage(event);
    196}
    197
    198static int riscv_pmu_add(struct perf_event *event, int flags)
    199{
    200	struct riscv_pmu *rvpmu = to_riscv_pmu(event->pmu);
    201	struct cpu_hw_events *cpuc = this_cpu_ptr(rvpmu->hw_events);
    202	struct hw_perf_event *hwc = &event->hw;
    203	int idx;
    204
    205	idx = rvpmu->ctr_get_idx(event);
    206	if (idx < 0)
    207		return idx;
    208
    209	hwc->idx = idx;
    210	cpuc->events[idx] = event;
    211	cpuc->n_events++;
    212	hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
    213	if (flags & PERF_EF_START)
    214		riscv_pmu_start(event, PERF_EF_RELOAD);
    215
    216	/* Propagate our changes to the userspace mapping. */
    217	perf_event_update_userpage(event);
    218
    219	return 0;
    220}
    221
    222static void riscv_pmu_del(struct perf_event *event, int flags)
    223{
    224	struct riscv_pmu *rvpmu = to_riscv_pmu(event->pmu);
    225	struct cpu_hw_events *cpuc = this_cpu_ptr(rvpmu->hw_events);
    226	struct hw_perf_event *hwc = &event->hw;
    227
    228	riscv_pmu_stop(event, PERF_EF_UPDATE);
    229	cpuc->events[hwc->idx] = NULL;
    230	/* The firmware need to reset the counter mapping */
    231	if (rvpmu->ctr_stop)
    232		rvpmu->ctr_stop(event, RISCV_PMU_STOP_FLAG_RESET);
    233	cpuc->n_events--;
    234	if (rvpmu->ctr_clear_idx)
    235		rvpmu->ctr_clear_idx(event);
    236	perf_event_update_userpage(event);
    237	hwc->idx = -1;
    238}
    239
    240static void riscv_pmu_read(struct perf_event *event)
    241{
    242	riscv_pmu_event_update(event);
    243}
    244
    245static int riscv_pmu_event_init(struct perf_event *event)
    246{
    247	struct hw_perf_event *hwc = &event->hw;
    248	struct riscv_pmu *rvpmu = to_riscv_pmu(event->pmu);
    249	int mapped_event;
    250	u64 event_config = 0;
    251	uint64_t cmask;
    252
    253	hwc->flags = 0;
    254	mapped_event = rvpmu->event_map(event, &event_config);
    255	if (mapped_event < 0) {
    256		pr_debug("event %x:%llx not supported\n", event->attr.type,
    257			 event->attr.config);
    258		return mapped_event;
    259	}
    260
    261	/*
    262	 * idx is set to -1 because the index of a general event should not be
    263	 * decided until binding to some counter in pmu->add().
    264	 * config will contain the information about counter CSR
    265	 * the idx will contain the counter index
    266	 */
    267	hwc->config = event_config;
    268	hwc->idx = -1;
    269	hwc->event_base = mapped_event;
    270
    271	if (!is_sampling_event(event)) {
    272		/*
    273		 * For non-sampling runs, limit the sample_period to half
    274		 * of the counter width. That way, the new counter value
    275		 * is far less likely to overtake the previous one unless
    276		 * you have some serious IRQ latency issues.
    277		 */
    278		cmask = riscv_pmu_ctr_get_width_mask(event);
    279		hwc->sample_period  =  cmask >> 1;
    280		hwc->last_period    = hwc->sample_period;
    281		local64_set(&hwc->period_left, hwc->sample_period);
    282	}
    283
    284	return 0;
    285}
    286
    287struct riscv_pmu *riscv_pmu_alloc(void)
    288{
    289	struct riscv_pmu *pmu;
    290	int cpuid, i;
    291	struct cpu_hw_events *cpuc;
    292
    293	pmu = kzalloc(sizeof(*pmu), GFP_KERNEL);
    294	if (!pmu)
    295		goto out;
    296
    297	pmu->hw_events = alloc_percpu_gfp(struct cpu_hw_events, GFP_KERNEL);
    298	if (!pmu->hw_events) {
    299		pr_info("failed to allocate per-cpu PMU data.\n");
    300		goto out_free_pmu;
    301	}
    302
    303	for_each_possible_cpu(cpuid) {
    304		cpuc = per_cpu_ptr(pmu->hw_events, cpuid);
    305		cpuc->n_events = 0;
    306		for (i = 0; i < RISCV_MAX_COUNTERS; i++)
    307			cpuc->events[i] = NULL;
    308	}
    309	pmu->pmu = (struct pmu) {
    310		.event_init	= riscv_pmu_event_init,
    311		.add		= riscv_pmu_add,
    312		.del		= riscv_pmu_del,
    313		.start		= riscv_pmu_start,
    314		.stop		= riscv_pmu_stop,
    315		.read		= riscv_pmu_read,
    316	};
    317
    318	return pmu;
    319
    320out_free_pmu:
    321	kfree(pmu);
    322out:
    323	return NULL;
    324}