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_cpum_cf_common.c (5505B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * CPU-Measurement Counter Facility Support - Common Layer
      4 *
      5 *  Copyright IBM Corp. 2019
      6 *  Author(s): Hendrik Brueckner <brueckner@linux.ibm.com>
      7 */
      8#define KMSG_COMPONENT	"cpum_cf_common"
      9#define pr_fmt(fmt)	KMSG_COMPONENT ": " fmt
     10
     11#include <linux/kernel.h>
     12#include <linux/kernel_stat.h>
     13#include <linux/percpu.h>
     14#include <linux/notifier.h>
     15#include <linux/init.h>
     16#include <linux/export.h>
     17#include <asm/ctl_reg.h>
     18#include <asm/irq.h>
     19#include <asm/cpu_mcf.h>
     20
     21/* Per-CPU event structure for the counter facility */
     22DEFINE_PER_CPU(struct cpu_cf_events, cpu_cf_events) = {
     23	.ctr_set = {
     24		[CPUMF_CTR_SET_BASIC]	= ATOMIC_INIT(0),
     25		[CPUMF_CTR_SET_USER]	= ATOMIC_INIT(0),
     26		[CPUMF_CTR_SET_CRYPTO]	= ATOMIC_INIT(0),
     27		[CPUMF_CTR_SET_EXT]	= ATOMIC_INIT(0),
     28		[CPUMF_CTR_SET_MT_DIAG] = ATOMIC_INIT(0),
     29	},
     30	.alert = ATOMIC64_INIT(0),
     31	.state = 0,
     32	.dev_state = 0,
     33	.flags = 0,
     34	.used = 0,
     35	.usedss = 0,
     36	.sets = 0
     37};
     38/* Indicator whether the CPU-Measurement Counter Facility Support is ready */
     39static bool cpum_cf_initalized;
     40
     41/* CPU-measurement alerts for the counter facility */
     42static void cpumf_measurement_alert(struct ext_code ext_code,
     43				    unsigned int alert, unsigned long unused)
     44{
     45	struct cpu_cf_events *cpuhw;
     46
     47	if (!(alert & CPU_MF_INT_CF_MASK))
     48		return;
     49
     50	inc_irq_stat(IRQEXT_CMC);
     51	cpuhw = this_cpu_ptr(&cpu_cf_events);
     52
     53	/* Measurement alerts are shared and might happen when the PMU
     54	 * is not reserved.  Ignore these alerts in this case. */
     55	if (!(cpuhw->flags & PMU_F_RESERVED))
     56		return;
     57
     58	/* counter authorization change alert */
     59	if (alert & CPU_MF_INT_CF_CACA)
     60		qctri(&cpuhw->info);
     61
     62	/* loss of counter data alert */
     63	if (alert & CPU_MF_INT_CF_LCDA)
     64		pr_err("CPU[%i] Counter data was lost\n", smp_processor_id());
     65
     66	/* loss of MT counter data alert */
     67	if (alert & CPU_MF_INT_CF_MTDA)
     68		pr_warn("CPU[%i] MT counter data was lost\n",
     69			smp_processor_id());
     70
     71	/* store alert for special handling by in-kernel users */
     72	atomic64_or(alert, &cpuhw->alert);
     73}
     74
     75#define PMC_INIT      0
     76#define PMC_RELEASE   1
     77static void cpum_cf_setup_cpu(void *flags)
     78{
     79	struct cpu_cf_events *cpuhw = this_cpu_ptr(&cpu_cf_events);
     80
     81	switch (*((int *) flags)) {
     82	case PMC_INIT:
     83		memset(&cpuhw->info, 0, sizeof(cpuhw->info));
     84		qctri(&cpuhw->info);
     85		cpuhw->flags |= PMU_F_RESERVED;
     86		break;
     87
     88	case PMC_RELEASE:
     89		cpuhw->flags &= ~PMU_F_RESERVED;
     90		break;
     91	}
     92
     93	/* Disable CPU counter sets */
     94	lcctl(0);
     95}
     96
     97bool kernel_cpumcf_avail(void)
     98{
     99	return cpum_cf_initalized;
    100}
    101EXPORT_SYMBOL(kernel_cpumcf_avail);
    102
    103/* Initialize the CPU-measurement counter facility */
    104int __kernel_cpumcf_begin(void)
    105{
    106	int flags = PMC_INIT;
    107
    108	on_each_cpu(cpum_cf_setup_cpu, &flags, 1);
    109	irq_subclass_register(IRQ_SUBCLASS_MEASUREMENT_ALERT);
    110
    111	return 0;
    112}
    113EXPORT_SYMBOL(__kernel_cpumcf_begin);
    114
    115/* Obtain the CPU-measurement alerts for the counter facility */
    116unsigned long kernel_cpumcf_alert(int clear)
    117{
    118	struct cpu_cf_events *cpuhw = this_cpu_ptr(&cpu_cf_events);
    119	unsigned long alert;
    120
    121	alert = atomic64_read(&cpuhw->alert);
    122	if (clear)
    123		atomic64_set(&cpuhw->alert, 0);
    124
    125	return alert;
    126}
    127EXPORT_SYMBOL(kernel_cpumcf_alert);
    128
    129/* Release the CPU-measurement counter facility */
    130void __kernel_cpumcf_end(void)
    131{
    132	int flags = PMC_RELEASE;
    133
    134	on_each_cpu(cpum_cf_setup_cpu, &flags, 1);
    135	irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT);
    136}
    137EXPORT_SYMBOL(__kernel_cpumcf_end);
    138
    139static int cpum_cf_setup(unsigned int cpu, int flags)
    140{
    141	local_irq_disable();
    142	cpum_cf_setup_cpu(&flags);
    143	local_irq_enable();
    144	return 0;
    145}
    146
    147static int cpum_cf_online_cpu(unsigned int cpu)
    148{
    149	cpum_cf_setup(cpu, PMC_INIT);
    150	return cfset_online_cpu(cpu);
    151}
    152
    153static int cpum_cf_offline_cpu(unsigned int cpu)
    154{
    155	cfset_offline_cpu(cpu);
    156	return cpum_cf_setup(cpu, PMC_RELEASE);
    157}
    158
    159/* Return the maximum possible counter set size (in number of 8 byte counters)
    160 * depending on type and model number.
    161 */
    162size_t cpum_cf_ctrset_size(enum cpumf_ctr_set ctrset,
    163			   struct cpumf_ctr_info *info)
    164{
    165	size_t ctrset_size = 0;
    166
    167	switch (ctrset) {
    168	case CPUMF_CTR_SET_BASIC:
    169		if (info->cfvn >= 1)
    170			ctrset_size = 6;
    171		break;
    172	case CPUMF_CTR_SET_USER:
    173		if (info->cfvn == 1)
    174			ctrset_size = 6;
    175		else if (info->cfvn >= 3)
    176			ctrset_size = 2;
    177		break;
    178	case CPUMF_CTR_SET_CRYPTO:
    179		if (info->csvn >= 1 && info->csvn <= 5)
    180			ctrset_size = 16;
    181		else if (info->csvn == 6 || info->csvn == 7)
    182			ctrset_size = 20;
    183		break;
    184	case CPUMF_CTR_SET_EXT:
    185		if (info->csvn == 1)
    186			ctrset_size = 32;
    187		else if (info->csvn == 2)
    188			ctrset_size = 48;
    189		else if (info->csvn >= 3 && info->csvn <= 5)
    190			ctrset_size = 128;
    191		else if (info->csvn == 6 || info->csvn == 7)
    192			ctrset_size = 160;
    193		break;
    194	case CPUMF_CTR_SET_MT_DIAG:
    195		if (info->csvn > 3)
    196			ctrset_size = 48;
    197		break;
    198	case CPUMF_CTR_SET_MAX:
    199		break;
    200	}
    201
    202	return ctrset_size;
    203}
    204
    205static int __init cpum_cf_init(void)
    206{
    207	int rc;
    208
    209	if (!cpum_cf_avail())
    210		return -ENODEV;
    211
    212	/* clear bit 15 of cr0 to unauthorize problem-state to
    213	 * extract measurement counters */
    214	ctl_clear_bit(0, 48);
    215
    216	/* register handler for measurement-alert interruptions */
    217	rc = register_external_irq(EXT_IRQ_MEASURE_ALERT,
    218				   cpumf_measurement_alert);
    219	if (rc) {
    220		pr_err("Registering for CPU-measurement alerts "
    221		       "failed with rc=%i\n", rc);
    222		return rc;
    223	}
    224
    225	rc = cpuhp_setup_state(CPUHP_AP_PERF_S390_CF_ONLINE,
    226				"perf/s390/cf:online",
    227				cpum_cf_online_cpu, cpum_cf_offline_cpu);
    228	if (!rc)
    229		cpum_cf_initalized = true;
    230
    231	return rc;
    232}
    233early_initcall(cpum_cf_init);