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

power7-pmu.c (11713B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Performance counter support for POWER7 processors.
      4 *
      5 * Copyright 2009 Paul Mackerras, IBM Corporation.
      6 */
      7#include <linux/kernel.h>
      8#include <linux/perf_event.h>
      9#include <linux/string.h>
     10#include <asm/reg.h>
     11#include <asm/cputable.h>
     12
     13#include "internal.h"
     14
     15/*
     16 * Bits in event code for POWER7
     17 */
     18#define PM_PMC_SH	16	/* PMC number (1-based) for direct events */
     19#define PM_PMC_MSK	0xf
     20#define PM_PMC_MSKS	(PM_PMC_MSK << PM_PMC_SH)
     21#define PM_UNIT_SH	12	/* TTMMUX number and setting - unit select */
     22#define PM_UNIT_MSK	0xf
     23#define PM_COMBINE_SH	11	/* Combined event bit */
     24#define PM_COMBINE_MSK	1
     25#define PM_COMBINE_MSKS	0x800
     26#define PM_L2SEL_SH	8	/* L2 event select */
     27#define PM_L2SEL_MSK	7
     28#define PM_PMCSEL_MSK	0xff
     29
     30/*
     31 * Bits in MMCR1 for POWER7
     32 */
     33#define MMCR1_TTM0SEL_SH	60
     34#define MMCR1_TTM1SEL_SH	56
     35#define MMCR1_TTM2SEL_SH	52
     36#define MMCR1_TTM3SEL_SH	48
     37#define MMCR1_TTMSEL_MSK	0xf
     38#define MMCR1_L2SEL_SH		45
     39#define MMCR1_L2SEL_MSK		7
     40#define MMCR1_PMC1_COMBINE_SH	35
     41#define MMCR1_PMC2_COMBINE_SH	34
     42#define MMCR1_PMC3_COMBINE_SH	33
     43#define MMCR1_PMC4_COMBINE_SH	32
     44#define MMCR1_PMC1SEL_SH	24
     45#define MMCR1_PMC2SEL_SH	16
     46#define MMCR1_PMC3SEL_SH	8
     47#define MMCR1_PMC4SEL_SH	0
     48#define MMCR1_PMCSEL_SH(n)	(MMCR1_PMC1SEL_SH - (n) * 8)
     49#define MMCR1_PMCSEL_MSK	0xff
     50
     51/*
     52 * Power7 event codes.
     53 */
     54#define EVENT(_name, _code) \
     55	_name = _code,
     56
     57enum {
     58#include "power7-events-list.h"
     59};
     60#undef EVENT
     61
     62/*
     63 * Layout of constraint bits:
     64 * 6666555555555544444444443333333333222222222211111111110000000000
     65 * 3210987654321098765432109876543210987654321098765432109876543210
     66 *                                              < ><  ><><><><><><>
     67 *                                              L2  NC P6P5P4P3P2P1
     68 *
     69 * L2 - 16-18 - Required L2SEL value (select field)
     70 *
     71 * NC - number of counters
     72 *     15: NC error 0x8000
     73 *     12-14: number of events needing PMC1-4 0x7000
     74 *
     75 * P6
     76 *     11: P6 error 0x800
     77 *     10-11: Count of events needing PMC6
     78 *
     79 * P1..P5
     80 *     0-9: Count of events needing PMC1..PMC5
     81 */
     82
     83static int power7_get_constraint(u64 event, unsigned long *maskp,
     84				 unsigned long *valp, u64 event_config1 __maybe_unused)
     85{
     86	int pmc, sh, unit;
     87	unsigned long mask = 0, value = 0;
     88
     89	pmc = (event >> PM_PMC_SH) & PM_PMC_MSK;
     90	if (pmc) {
     91		if (pmc > 6)
     92			return -1;
     93		sh = (pmc - 1) * 2;
     94		mask |= 2 << sh;
     95		value |= 1 << sh;
     96		if (pmc >= 5 && !(event == 0x500fa || event == 0x600f4))
     97			return -1;
     98	}
     99	if (pmc < 5) {
    100		/* need a counter from PMC1-4 set */
    101		mask  |= 0x8000;
    102		value |= 0x1000;
    103	}
    104
    105	unit = (event >> PM_UNIT_SH) & PM_UNIT_MSK;
    106	if (unit == 6) {
    107		/* L2SEL must be identical across events */
    108		int l2sel = (event >> PM_L2SEL_SH) & PM_L2SEL_MSK;
    109		mask  |= 0x7 << 16;
    110		value |= l2sel << 16;
    111	}
    112
    113	*maskp = mask;
    114	*valp = value;
    115	return 0;
    116}
    117
    118#define MAX_ALT	2	/* at most 2 alternatives for any event */
    119
    120static const unsigned int event_alternatives[][MAX_ALT] = {
    121	{ 0x200f2, 0x300f2 },		/* PM_INST_DISP */
    122	{ 0x200f4, 0x600f4 },		/* PM_RUN_CYC */
    123	{ 0x400fa, 0x500fa },		/* PM_RUN_INST_CMPL */
    124};
    125
    126/*
    127 * Scan the alternatives table for a match and return the
    128 * index into the alternatives table if found, else -1.
    129 */
    130static int find_alternative(u64 event)
    131{
    132	int i, j;
    133
    134	for (i = 0; i < ARRAY_SIZE(event_alternatives); ++i) {
    135		if (event < event_alternatives[i][0])
    136			break;
    137		for (j = 0; j < MAX_ALT && event_alternatives[i][j]; ++j)
    138			if (event == event_alternatives[i][j])
    139				return i;
    140	}
    141	return -1;
    142}
    143
    144static s64 find_alternative_decode(u64 event)
    145{
    146	int pmc, psel;
    147
    148	/* this only handles the 4x decode events */
    149	pmc = (event >> PM_PMC_SH) & PM_PMC_MSK;
    150	psel = event & PM_PMCSEL_MSK;
    151	if ((pmc == 2 || pmc == 4) && (psel & ~7) == 0x40)
    152		return event - (1 << PM_PMC_SH) + 8;
    153	if ((pmc == 1 || pmc == 3) && (psel & ~7) == 0x48)
    154		return event + (1 << PM_PMC_SH) - 8;
    155	return -1;
    156}
    157
    158static int power7_get_alternatives(u64 event, unsigned int flags, u64 alt[])
    159{
    160	int i, j, nalt = 1;
    161	s64 ae;
    162
    163	alt[0] = event;
    164	nalt = 1;
    165	i = find_alternative(event);
    166	if (i >= 0) {
    167		for (j = 0; j < MAX_ALT; ++j) {
    168			ae = event_alternatives[i][j];
    169			if (ae && ae != event)
    170				alt[nalt++] = ae;
    171		}
    172	} else {
    173		ae = find_alternative_decode(event);
    174		if (ae > 0)
    175			alt[nalt++] = ae;
    176	}
    177
    178	if (flags & PPMU_ONLY_COUNT_RUN) {
    179		/*
    180		 * We're only counting in RUN state,
    181		 * so PM_CYC is equivalent to PM_RUN_CYC
    182		 * and PM_INST_CMPL === PM_RUN_INST_CMPL.
    183		 * This doesn't include alternatives that don't provide
    184		 * any extra flexibility in assigning PMCs.
    185		 */
    186		j = nalt;
    187		for (i = 0; i < nalt; ++i) {
    188			switch (alt[i]) {
    189			case 0x1e:	/* PM_CYC */
    190				alt[j++] = 0x600f4;	/* PM_RUN_CYC */
    191				break;
    192			case 0x600f4:	/* PM_RUN_CYC */
    193				alt[j++] = 0x1e;
    194				break;
    195			case 0x2:	/* PM_PPC_CMPL */
    196				alt[j++] = 0x500fa;	/* PM_RUN_INST_CMPL */
    197				break;
    198			case 0x500fa:	/* PM_RUN_INST_CMPL */
    199				alt[j++] = 0x2;	/* PM_PPC_CMPL */
    200				break;
    201			}
    202		}
    203		nalt = j;
    204	}
    205
    206	return nalt;
    207}
    208
    209/*
    210 * Returns 1 if event counts things relating to marked instructions
    211 * and thus needs the MMCRA_SAMPLE_ENABLE bit set, or 0 if not.
    212 */
    213static int power7_marked_instr_event(u64 event)
    214{
    215	int pmc, psel;
    216	int unit;
    217
    218	pmc = (event >> PM_PMC_SH) & PM_PMC_MSK;
    219	unit = (event >> PM_UNIT_SH) & PM_UNIT_MSK;
    220	psel = event & PM_PMCSEL_MSK & ~1;	/* trim off edge/level bit */
    221	if (pmc >= 5)
    222		return 0;
    223
    224	switch (psel >> 4) {
    225	case 2:
    226		return pmc == 2 || pmc == 4;
    227	case 3:
    228		if (psel == 0x3c)
    229			return pmc == 1;
    230		if (psel == 0x3e)
    231			return pmc != 2;
    232		return 1;
    233	case 4:
    234	case 5:
    235		return unit == 0xd;
    236	case 6:
    237		if (psel == 0x64)
    238			return pmc >= 3;
    239		break;
    240	case 8:
    241		return unit == 0xd;
    242	}
    243	return 0;
    244}
    245
    246static int power7_compute_mmcr(u64 event[], int n_ev,
    247			       unsigned int hwc[], struct mmcr_regs *mmcr,
    248			       struct perf_event *pevents[],
    249			       u32 flags __maybe_unused)
    250{
    251	unsigned long mmcr1 = 0;
    252	unsigned long mmcra = MMCRA_SDAR_DCACHE_MISS | MMCRA_SDAR_ERAT_MISS;
    253	unsigned int pmc, unit, combine, l2sel, psel;
    254	unsigned int pmc_inuse = 0;
    255	int i;
    256
    257	/* First pass to count resource use */
    258	for (i = 0; i < n_ev; ++i) {
    259		pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK;
    260		if (pmc) {
    261			if (pmc > 6)
    262				return -1;
    263			if (pmc_inuse & (1 << (pmc - 1)))
    264				return -1;
    265			pmc_inuse |= 1 << (pmc - 1);
    266		}
    267	}
    268
    269	/* Second pass: assign PMCs, set all MMCR1 fields */
    270	for (i = 0; i < n_ev; ++i) {
    271		pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK;
    272		unit = (event[i] >> PM_UNIT_SH) & PM_UNIT_MSK;
    273		combine = (event[i] >> PM_COMBINE_SH) & PM_COMBINE_MSK;
    274		l2sel = (event[i] >> PM_L2SEL_SH) & PM_L2SEL_MSK;
    275		psel = event[i] & PM_PMCSEL_MSK;
    276		if (!pmc) {
    277			/* Bus event or any-PMC direct event */
    278			for (pmc = 0; pmc < 4; ++pmc) {
    279				if (!(pmc_inuse & (1 << pmc)))
    280					break;
    281			}
    282			if (pmc >= 4)
    283				return -1;
    284			pmc_inuse |= 1 << pmc;
    285		} else {
    286			/* Direct or decoded event */
    287			--pmc;
    288		}
    289		if (pmc <= 3) {
    290			mmcr1 |= (unsigned long) unit
    291				<< (MMCR1_TTM0SEL_SH - 4 * pmc);
    292			mmcr1 |= (unsigned long) combine
    293				<< (MMCR1_PMC1_COMBINE_SH - pmc);
    294			mmcr1 |= psel << MMCR1_PMCSEL_SH(pmc);
    295			if (unit == 6)	/* L2 events */
    296				mmcr1 |= (unsigned long) l2sel
    297					<< MMCR1_L2SEL_SH;
    298		}
    299		if (power7_marked_instr_event(event[i]))
    300			mmcra |= MMCRA_SAMPLE_ENABLE;
    301		hwc[i] = pmc;
    302	}
    303
    304	/* Return MMCRx values */
    305	mmcr->mmcr0 = 0;
    306	if (pmc_inuse & 1)
    307		mmcr->mmcr0 = MMCR0_PMC1CE;
    308	if (pmc_inuse & 0x3e)
    309		mmcr->mmcr0 |= MMCR0_PMCjCE;
    310	mmcr->mmcr1 = mmcr1;
    311	mmcr->mmcra = mmcra;
    312	return 0;
    313}
    314
    315static void power7_disable_pmc(unsigned int pmc, struct mmcr_regs *mmcr)
    316{
    317	if (pmc <= 3)
    318		mmcr->mmcr1 &= ~(0xffUL << MMCR1_PMCSEL_SH(pmc));
    319}
    320
    321static int power7_generic_events[] = {
    322	[PERF_COUNT_HW_CPU_CYCLES] =			PM_CYC,
    323	[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] =	PM_GCT_NOSLOT_CYC,
    324	[PERF_COUNT_HW_STALLED_CYCLES_BACKEND] =	PM_CMPLU_STALL,
    325	[PERF_COUNT_HW_INSTRUCTIONS] =			PM_INST_CMPL,
    326	[PERF_COUNT_HW_CACHE_REFERENCES] =		PM_LD_REF_L1,
    327	[PERF_COUNT_HW_CACHE_MISSES] =			PM_LD_MISS_L1,
    328	[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] =		PM_BRU_FIN,
    329	[PERF_COUNT_HW_BRANCH_MISSES] =			PM_BR_MPRED,
    330};
    331
    332#define C(x)	PERF_COUNT_HW_CACHE_##x
    333
    334/*
    335 * Table of generalized cache-related events.
    336 * 0 means not supported, -1 means nonsensical, other values
    337 * are event codes.
    338 */
    339static u64 power7_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = {
    340	[C(L1D)] = {		/* 	RESULT_ACCESS	RESULT_MISS */
    341		[C(OP_READ)] = {	0xc880,		0x400f0	},
    342		[C(OP_WRITE)] = {	0,		0x300f0	},
    343		[C(OP_PREFETCH)] = {	0xd8b8,		0	},
    344	},
    345	[C(L1I)] = {		/* 	RESULT_ACCESS	RESULT_MISS */
    346		[C(OP_READ)] = {	0,		0x200fc	},
    347		[C(OP_WRITE)] = {	-1,		-1	},
    348		[C(OP_PREFETCH)] = {	0x408a,		0	},
    349	},
    350	[C(LL)] = {		/* 	RESULT_ACCESS	RESULT_MISS */
    351		[C(OP_READ)] = {	0x16080,	0x26080	},
    352		[C(OP_WRITE)] = {	0x16082,	0x26082	},
    353		[C(OP_PREFETCH)] = {	0,		0	},
    354	},
    355	[C(DTLB)] = {		/* 	RESULT_ACCESS	RESULT_MISS */
    356		[C(OP_READ)] = {	0,		0x300fc	},
    357		[C(OP_WRITE)] = {	-1,		-1	},
    358		[C(OP_PREFETCH)] = {	-1,		-1	},
    359	},
    360	[C(ITLB)] = {		/* 	RESULT_ACCESS	RESULT_MISS */
    361		[C(OP_READ)] = {	0,		0x400fc	},
    362		[C(OP_WRITE)] = {	-1,		-1	},
    363		[C(OP_PREFETCH)] = {	-1,		-1	},
    364	},
    365	[C(BPU)] = {		/* 	RESULT_ACCESS	RESULT_MISS */
    366		[C(OP_READ)] = {	0x10068,	0x400f6	},
    367		[C(OP_WRITE)] = {	-1,		-1	},
    368		[C(OP_PREFETCH)] = {	-1,		-1	},
    369	},
    370	[C(NODE)] = {		/* 	RESULT_ACCESS	RESULT_MISS */
    371		[C(OP_READ)] = {	-1,		-1	},
    372		[C(OP_WRITE)] = {	-1,		-1	},
    373		[C(OP_PREFETCH)] = {	-1,		-1	},
    374	},
    375};
    376
    377
    378GENERIC_EVENT_ATTR(cpu-cycles,			PM_CYC);
    379GENERIC_EVENT_ATTR(stalled-cycles-frontend,	PM_GCT_NOSLOT_CYC);
    380GENERIC_EVENT_ATTR(stalled-cycles-backend,	PM_CMPLU_STALL);
    381GENERIC_EVENT_ATTR(instructions,		PM_INST_CMPL);
    382GENERIC_EVENT_ATTR(cache-references,		PM_LD_REF_L1);
    383GENERIC_EVENT_ATTR(cache-misses,		PM_LD_MISS_L1);
    384GENERIC_EVENT_ATTR(branch-instructions,		PM_BRU_FIN);
    385GENERIC_EVENT_ATTR(branch-misses,		PM_BR_MPRED);
    386
    387#define EVENT(_name, _code)     POWER_EVENT_ATTR(_name, _name);
    388#include "power7-events-list.h"
    389#undef EVENT
    390
    391#define EVENT(_name, _code)     POWER_EVENT_PTR(_name),
    392
    393static struct attribute *power7_events_attr[] = {
    394	GENERIC_EVENT_PTR(PM_CYC),
    395	GENERIC_EVENT_PTR(PM_GCT_NOSLOT_CYC),
    396	GENERIC_EVENT_PTR(PM_CMPLU_STALL),
    397	GENERIC_EVENT_PTR(PM_INST_CMPL),
    398	GENERIC_EVENT_PTR(PM_LD_REF_L1),
    399	GENERIC_EVENT_PTR(PM_LD_MISS_L1),
    400	GENERIC_EVENT_PTR(PM_BRU_FIN),
    401	GENERIC_EVENT_PTR(PM_BR_MPRED),
    402
    403	#include "power7-events-list.h"
    404	#undef EVENT
    405	NULL
    406};
    407
    408static const struct attribute_group power7_pmu_events_group = {
    409	.name = "events",
    410	.attrs = power7_events_attr,
    411};
    412
    413PMU_FORMAT_ATTR(event, "config:0-19");
    414
    415static struct attribute *power7_pmu_format_attr[] = {
    416	&format_attr_event.attr,
    417	NULL,
    418};
    419
    420static const struct attribute_group power7_pmu_format_group = {
    421	.name = "format",
    422	.attrs = power7_pmu_format_attr,
    423};
    424
    425static const struct attribute_group *power7_pmu_attr_groups[] = {
    426	&power7_pmu_format_group,
    427	&power7_pmu_events_group,
    428	NULL,
    429};
    430
    431static struct power_pmu power7_pmu = {
    432	.name			= "POWER7",
    433	.n_counter		= 6,
    434	.max_alternatives	= MAX_ALT + 1,
    435	.add_fields		= 0x1555ul,
    436	.test_adder		= 0x3000ul,
    437	.compute_mmcr		= power7_compute_mmcr,
    438	.get_constraint		= power7_get_constraint,
    439	.get_alternatives	= power7_get_alternatives,
    440	.disable_pmc		= power7_disable_pmc,
    441	.flags			= PPMU_ALT_SIPR,
    442	.attr_groups		= power7_pmu_attr_groups,
    443	.n_generic		= ARRAY_SIZE(power7_generic_events),
    444	.generic_events		= power7_generic_events,
    445	.cache_events		= &power7_cache_events,
    446};
    447
    448int __init init_power7_pmu(void)
    449{
    450	if (!cur_cpu_spec->oprofile_cpu_type ||
    451	    strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power7"))
    452		return -ENODEV;
    453
    454	if (pvr_version_is(PVR_POWER7p))
    455		power7_pmu.flags |= PPMU_SIAR_VALID;
    456
    457	return register_power_pmu(&power7_pmu);
    458}