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

hisi_uncore_sllc_pmu.c (14541B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * HiSilicon SLLC uncore Hardware event counters support
      4 *
      5 * Copyright (C) 2020 HiSilicon Limited
      6 * Author: Shaokun Zhang <zhangshaokun@hisilicon.com>
      7 *
      8 * This code is based on the uncore PMUs like arm-cci and arm-ccn.
      9 */
     10#include <linux/acpi.h>
     11#include <linux/cpuhotplug.h>
     12#include <linux/interrupt.h>
     13#include <linux/irq.h>
     14#include <linux/list.h>
     15#include <linux/smp.h>
     16
     17#include "hisi_uncore_pmu.h"
     18
     19/* SLLC register definition */
     20#define SLLC_INT_MASK			0x0814
     21#define SLLC_INT_STATUS			0x0818
     22#define SLLC_INT_CLEAR			0x081c
     23#define SLLC_PERF_CTRL			0x1c00
     24#define SLLC_SRCID_CTRL			0x1c04
     25#define SLLC_TGTID_CTRL			0x1c08
     26#define SLLC_EVENT_CTRL			0x1c14
     27#define SLLC_EVENT_TYPE0		0x1c18
     28#define SLLC_VERSION			0x1cf0
     29#define SLLC_EVENT_CNT0_L		0x1d00
     30
     31#define SLLC_EVTYPE_MASK		0xff
     32#define SLLC_PERF_CTRL_EN		BIT(0)
     33#define SLLC_FILT_EN			BIT(1)
     34#define SLLC_TRACETAG_EN		BIT(2)
     35#define SLLC_SRCID_EN			BIT(4)
     36#define SLLC_SRCID_NONE			0x0
     37#define SLLC_TGTID_EN			BIT(5)
     38#define SLLC_TGTID_NONE			0x0
     39#define SLLC_TGTID_MIN_SHIFT		1
     40#define SLLC_TGTID_MAX_SHIFT		12
     41#define SLLC_SRCID_CMD_SHIFT		1
     42#define SLLC_SRCID_MSK_SHIFT		12
     43#define SLLC_NR_EVENTS			0x80
     44
     45HISI_PMU_EVENT_ATTR_EXTRACTOR(tgtid_min, config1, 10, 0);
     46HISI_PMU_EVENT_ATTR_EXTRACTOR(tgtid_max, config1, 21, 11);
     47HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_cmd, config1, 32, 22);
     48HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_msk, config1, 43, 33);
     49HISI_PMU_EVENT_ATTR_EXTRACTOR(tracetag_en, config1, 44, 44);
     50
     51static bool tgtid_is_valid(u32 max, u32 min)
     52{
     53	return max > 0 && max >= min;
     54}
     55
     56static void hisi_sllc_pmu_enable_tracetag(struct perf_event *event)
     57{
     58	struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
     59	u32 tt_en = hisi_get_tracetag_en(event);
     60
     61	if (tt_en) {
     62		u32 val;
     63
     64		val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
     65		val |= SLLC_TRACETAG_EN | SLLC_FILT_EN;
     66		writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
     67	}
     68}
     69
     70static void hisi_sllc_pmu_disable_tracetag(struct perf_event *event)
     71{
     72	struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
     73	u32 tt_en = hisi_get_tracetag_en(event);
     74
     75	if (tt_en) {
     76		u32 val;
     77
     78		val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
     79		val &= ~(SLLC_TRACETAG_EN | SLLC_FILT_EN);
     80		writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
     81	}
     82}
     83
     84static void hisi_sllc_pmu_config_tgtid(struct perf_event *event)
     85{
     86	struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
     87	u32 min = hisi_get_tgtid_min(event);
     88	u32 max = hisi_get_tgtid_max(event);
     89
     90	if (tgtid_is_valid(max, min)) {
     91		u32 val = (max << SLLC_TGTID_MAX_SHIFT) | (min << SLLC_TGTID_MIN_SHIFT);
     92
     93		writel(val, sllc_pmu->base + SLLC_TGTID_CTRL);
     94		/* Enable the tgtid */
     95		val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
     96		val |= SLLC_TGTID_EN | SLLC_FILT_EN;
     97		writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
     98	}
     99}
    100
    101static void hisi_sllc_pmu_clear_tgtid(struct perf_event *event)
    102{
    103	struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
    104	u32 min = hisi_get_tgtid_min(event);
    105	u32 max = hisi_get_tgtid_max(event);
    106
    107	if (tgtid_is_valid(max, min)) {
    108		u32 val;
    109
    110		writel(SLLC_TGTID_NONE, sllc_pmu->base + SLLC_TGTID_CTRL);
    111		/* Disable the tgtid */
    112		val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
    113		val &= ~(SLLC_TGTID_EN | SLLC_FILT_EN);
    114		writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
    115	}
    116}
    117
    118static void hisi_sllc_pmu_config_srcid(struct perf_event *event)
    119{
    120	struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
    121	u32 cmd = hisi_get_srcid_cmd(event);
    122
    123	if (cmd) {
    124		u32 val, msk;
    125
    126		msk = hisi_get_srcid_msk(event);
    127		val = (cmd << SLLC_SRCID_CMD_SHIFT) | (msk << SLLC_SRCID_MSK_SHIFT);
    128		writel(val, sllc_pmu->base + SLLC_SRCID_CTRL);
    129		/* Enable the srcid */
    130		val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
    131		val |= SLLC_SRCID_EN | SLLC_FILT_EN;
    132		writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
    133	}
    134}
    135
    136static void hisi_sllc_pmu_clear_srcid(struct perf_event *event)
    137{
    138	struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu);
    139	u32 cmd = hisi_get_srcid_cmd(event);
    140
    141	if (cmd) {
    142		u32 val;
    143
    144		writel(SLLC_SRCID_NONE, sllc_pmu->base + SLLC_SRCID_CTRL);
    145		/* Disable the srcid */
    146		val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
    147		val &= ~(SLLC_SRCID_EN | SLLC_FILT_EN);
    148		writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
    149	}
    150}
    151
    152static void hisi_sllc_pmu_enable_filter(struct perf_event *event)
    153{
    154	if (event->attr.config1 != 0x0) {
    155		hisi_sllc_pmu_enable_tracetag(event);
    156		hisi_sllc_pmu_config_srcid(event);
    157		hisi_sllc_pmu_config_tgtid(event);
    158	}
    159}
    160
    161static void hisi_sllc_pmu_clear_filter(struct perf_event *event)
    162{
    163	if (event->attr.config1 != 0x0) {
    164		hisi_sllc_pmu_disable_tracetag(event);
    165		hisi_sllc_pmu_clear_srcid(event);
    166		hisi_sllc_pmu_clear_tgtid(event);
    167	}
    168}
    169
    170static u32 hisi_sllc_pmu_get_counter_offset(int idx)
    171{
    172	return (SLLC_EVENT_CNT0_L + idx * 8);
    173}
    174
    175static u64 hisi_sllc_pmu_read_counter(struct hisi_pmu *sllc_pmu,
    176				      struct hw_perf_event *hwc)
    177{
    178	return readq(sllc_pmu->base +
    179		     hisi_sllc_pmu_get_counter_offset(hwc->idx));
    180}
    181
    182static void hisi_sllc_pmu_write_counter(struct hisi_pmu *sllc_pmu,
    183					struct hw_perf_event *hwc, u64 val)
    184{
    185	writeq(val, sllc_pmu->base +
    186	       hisi_sllc_pmu_get_counter_offset(hwc->idx));
    187}
    188
    189static void hisi_sllc_pmu_write_evtype(struct hisi_pmu *sllc_pmu, int idx,
    190				       u32 type)
    191{
    192	u32 reg, reg_idx, shift, val;
    193
    194	/*
    195	 * Select the appropriate event select register(SLLC_EVENT_TYPE0/1).
    196	 * There are 2 event select registers for the 8 hardware counters.
    197	 * Event code is 8-bits and for the former 4 hardware counters,
    198	 * SLLC_EVENT_TYPE0 is chosen. For the latter 4 hardware counters,
    199	 * SLLC_EVENT_TYPE1 is chosen.
    200	 */
    201	reg = SLLC_EVENT_TYPE0 + (idx / 4) * 4;
    202	reg_idx = idx % 4;
    203	shift = 8 * reg_idx;
    204
    205	/* Write event code to SLLC_EVENT_TYPEx Register */
    206	val = readl(sllc_pmu->base + reg);
    207	val &= ~(SLLC_EVTYPE_MASK << shift);
    208	val |= (type << shift);
    209	writel(val, sllc_pmu->base + reg);
    210}
    211
    212static void hisi_sllc_pmu_start_counters(struct hisi_pmu *sllc_pmu)
    213{
    214	u32 val;
    215
    216	val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
    217	val |= SLLC_PERF_CTRL_EN;
    218	writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
    219}
    220
    221static void hisi_sllc_pmu_stop_counters(struct hisi_pmu *sllc_pmu)
    222{
    223	u32 val;
    224
    225	val = readl(sllc_pmu->base + SLLC_PERF_CTRL);
    226	val &= ~(SLLC_PERF_CTRL_EN);
    227	writel(val, sllc_pmu->base + SLLC_PERF_CTRL);
    228}
    229
    230static void hisi_sllc_pmu_enable_counter(struct hisi_pmu *sllc_pmu,
    231					 struct hw_perf_event *hwc)
    232{
    233	u32 val;
    234
    235	val = readl(sllc_pmu->base + SLLC_EVENT_CTRL);
    236	val |= 1 << hwc->idx;
    237	writel(val, sllc_pmu->base + SLLC_EVENT_CTRL);
    238}
    239
    240static void hisi_sllc_pmu_disable_counter(struct hisi_pmu *sllc_pmu,
    241					  struct hw_perf_event *hwc)
    242{
    243	u32 val;
    244
    245	val = readl(sllc_pmu->base + SLLC_EVENT_CTRL);
    246	val &= ~(1 << hwc->idx);
    247	writel(val, sllc_pmu->base + SLLC_EVENT_CTRL);
    248}
    249
    250static void hisi_sllc_pmu_enable_counter_int(struct hisi_pmu *sllc_pmu,
    251					     struct hw_perf_event *hwc)
    252{
    253	u32 val;
    254
    255	val = readl(sllc_pmu->base + SLLC_INT_MASK);
    256	/* Write 0 to enable interrupt */
    257	val &= ~(1 << hwc->idx);
    258	writel(val, sllc_pmu->base + SLLC_INT_MASK);
    259}
    260
    261static void hisi_sllc_pmu_disable_counter_int(struct hisi_pmu *sllc_pmu,
    262					      struct hw_perf_event *hwc)
    263{
    264	u32 val;
    265
    266	val = readl(sllc_pmu->base + SLLC_INT_MASK);
    267	/* Write 1 to mask interrupt */
    268	val |= 1 << hwc->idx;
    269	writel(val, sllc_pmu->base + SLLC_INT_MASK);
    270}
    271
    272static u32 hisi_sllc_pmu_get_int_status(struct hisi_pmu *sllc_pmu)
    273{
    274	return readl(sllc_pmu->base + SLLC_INT_STATUS);
    275}
    276
    277static void hisi_sllc_pmu_clear_int_status(struct hisi_pmu *sllc_pmu, int idx)
    278{
    279	writel(1 << idx, sllc_pmu->base + SLLC_INT_CLEAR);
    280}
    281
    282static const struct acpi_device_id hisi_sllc_pmu_acpi_match[] = {
    283	{ "HISI0263", },
    284	{}
    285};
    286MODULE_DEVICE_TABLE(acpi, hisi_sllc_pmu_acpi_match);
    287
    288static int hisi_sllc_pmu_init_data(struct platform_device *pdev,
    289				   struct hisi_pmu *sllc_pmu)
    290{
    291	/*
    292	 * Use the SCCL_ID and the index ID to identify the SLLC PMU,
    293	 * while SCCL_ID is from MPIDR_EL1 by CPU.
    294	 */
    295	if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id",
    296				     &sllc_pmu->sccl_id)) {
    297		dev_err(&pdev->dev, "Cannot read sccl-id!\n");
    298		return -EINVAL;
    299	}
    300
    301	if (device_property_read_u32(&pdev->dev, "hisilicon,idx-id",
    302				     &sllc_pmu->index_id)) {
    303		dev_err(&pdev->dev, "Cannot read idx-id!\n");
    304		return -EINVAL;
    305	}
    306
    307	/* SLLC PMUs only share the same SCCL */
    308	sllc_pmu->ccl_id = -1;
    309
    310	sllc_pmu->base = devm_platform_ioremap_resource(pdev, 0);
    311	if (IS_ERR(sllc_pmu->base)) {
    312		dev_err(&pdev->dev, "ioremap failed for sllc_pmu resource.\n");
    313		return PTR_ERR(sllc_pmu->base);
    314	}
    315
    316	sllc_pmu->identifier = readl(sllc_pmu->base + SLLC_VERSION);
    317
    318	return 0;
    319}
    320
    321static struct attribute *hisi_sllc_pmu_v2_format_attr[] = {
    322	HISI_PMU_FORMAT_ATTR(event, "config:0-7"),
    323	HISI_PMU_FORMAT_ATTR(tgtid_min, "config1:0-10"),
    324	HISI_PMU_FORMAT_ATTR(tgtid_max, "config1:11-21"),
    325	HISI_PMU_FORMAT_ATTR(srcid_cmd, "config1:22-32"),
    326	HISI_PMU_FORMAT_ATTR(srcid_msk, "config1:33-43"),
    327	HISI_PMU_FORMAT_ATTR(tracetag_en, "config1:44"),
    328	NULL
    329};
    330
    331static const struct attribute_group hisi_sllc_pmu_v2_format_group = {
    332	.name = "format",
    333	.attrs = hisi_sllc_pmu_v2_format_attr,
    334};
    335
    336static struct attribute *hisi_sllc_pmu_v2_events_attr[] = {
    337	HISI_PMU_EVENT_ATTR(rx_req,             0x30),
    338	HISI_PMU_EVENT_ATTR(rx_data,            0x31),
    339	HISI_PMU_EVENT_ATTR(tx_req,             0x34),
    340	HISI_PMU_EVENT_ATTR(tx_data,            0x35),
    341	HISI_PMU_EVENT_ATTR(cycles,             0x09),
    342	NULL
    343};
    344
    345static const struct attribute_group hisi_sllc_pmu_v2_events_group = {
    346	.name = "events",
    347	.attrs = hisi_sllc_pmu_v2_events_attr,
    348};
    349
    350static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL);
    351
    352static struct attribute *hisi_sllc_pmu_cpumask_attrs[] = {
    353	&dev_attr_cpumask.attr,
    354	NULL
    355};
    356
    357static const struct attribute_group hisi_sllc_pmu_cpumask_attr_group = {
    358	.attrs = hisi_sllc_pmu_cpumask_attrs,
    359};
    360
    361static struct device_attribute hisi_sllc_pmu_identifier_attr =
    362	__ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL);
    363
    364static struct attribute *hisi_sllc_pmu_identifier_attrs[] = {
    365	&hisi_sllc_pmu_identifier_attr.attr,
    366	NULL
    367};
    368
    369static const struct attribute_group hisi_sllc_pmu_identifier_group = {
    370	.attrs = hisi_sllc_pmu_identifier_attrs,
    371};
    372
    373static const struct attribute_group *hisi_sllc_pmu_v2_attr_groups[] = {
    374	&hisi_sllc_pmu_v2_format_group,
    375	&hisi_sllc_pmu_v2_events_group,
    376	&hisi_sllc_pmu_cpumask_attr_group,
    377	&hisi_sllc_pmu_identifier_group,
    378	NULL
    379};
    380
    381static const struct hisi_uncore_ops hisi_uncore_sllc_ops = {
    382	.write_evtype		= hisi_sllc_pmu_write_evtype,
    383	.get_event_idx		= hisi_uncore_pmu_get_event_idx,
    384	.start_counters		= hisi_sllc_pmu_start_counters,
    385	.stop_counters		= hisi_sllc_pmu_stop_counters,
    386	.enable_counter		= hisi_sllc_pmu_enable_counter,
    387	.disable_counter	= hisi_sllc_pmu_disable_counter,
    388	.enable_counter_int	= hisi_sllc_pmu_enable_counter_int,
    389	.disable_counter_int	= hisi_sllc_pmu_disable_counter_int,
    390	.write_counter		= hisi_sllc_pmu_write_counter,
    391	.read_counter		= hisi_sllc_pmu_read_counter,
    392	.get_int_status		= hisi_sllc_pmu_get_int_status,
    393	.clear_int_status	= hisi_sllc_pmu_clear_int_status,
    394	.enable_filter		= hisi_sllc_pmu_enable_filter,
    395	.disable_filter		= hisi_sllc_pmu_clear_filter,
    396};
    397
    398static int hisi_sllc_pmu_dev_probe(struct platform_device *pdev,
    399				   struct hisi_pmu *sllc_pmu)
    400{
    401	int ret;
    402
    403	ret = hisi_sllc_pmu_init_data(pdev, sllc_pmu);
    404	if (ret)
    405		return ret;
    406
    407	ret = hisi_uncore_pmu_init_irq(sllc_pmu, pdev);
    408	if (ret)
    409		return ret;
    410
    411	sllc_pmu->pmu_events.attr_groups = hisi_sllc_pmu_v2_attr_groups;
    412	sllc_pmu->ops = &hisi_uncore_sllc_ops;
    413	sllc_pmu->check_event = SLLC_NR_EVENTS;
    414	sllc_pmu->counter_bits = 64;
    415	sllc_pmu->num_counters = 8;
    416	sllc_pmu->dev = &pdev->dev;
    417	sllc_pmu->on_cpu = -1;
    418
    419	return 0;
    420}
    421
    422static int hisi_sllc_pmu_probe(struct platform_device *pdev)
    423{
    424	struct hisi_pmu *sllc_pmu;
    425	char *name;
    426	int ret;
    427
    428	sllc_pmu = devm_kzalloc(&pdev->dev, sizeof(*sllc_pmu), GFP_KERNEL);
    429	if (!sllc_pmu)
    430		return -ENOMEM;
    431
    432	ret = hisi_sllc_pmu_dev_probe(pdev, sllc_pmu);
    433	if (ret)
    434		return ret;
    435
    436	name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%u_sllc%u",
    437			      sllc_pmu->sccl_id, sllc_pmu->index_id);
    438	if (!name)
    439		return -ENOMEM;
    440
    441	ret = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE,
    442				       &sllc_pmu->node);
    443	if (ret) {
    444		dev_err(&pdev->dev, "Error %d registering hotplug\n", ret);
    445		return ret;
    446	}
    447
    448	sllc_pmu->pmu = (struct pmu) {
    449		.module		= THIS_MODULE,
    450		.task_ctx_nr	= perf_invalid_context,
    451		.event_init	= hisi_uncore_pmu_event_init,
    452		.pmu_enable	= hisi_uncore_pmu_enable,
    453		.pmu_disable	= hisi_uncore_pmu_disable,
    454		.add		= hisi_uncore_pmu_add,
    455		.del		= hisi_uncore_pmu_del,
    456		.start		= hisi_uncore_pmu_start,
    457		.stop		= hisi_uncore_pmu_stop,
    458		.read		= hisi_uncore_pmu_read,
    459		.attr_groups    = sllc_pmu->pmu_events.attr_groups,
    460		.capabilities	= PERF_PMU_CAP_NO_EXCLUDE,
    461	};
    462
    463	ret = perf_pmu_register(&sllc_pmu->pmu, name, -1);
    464	if (ret) {
    465		dev_err(sllc_pmu->dev, "PMU register failed, ret = %d\n", ret);
    466		cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE,
    467					    &sllc_pmu->node);
    468		return ret;
    469	}
    470
    471	platform_set_drvdata(pdev, sllc_pmu);
    472
    473	return ret;
    474}
    475
    476static int hisi_sllc_pmu_remove(struct platform_device *pdev)
    477{
    478	struct hisi_pmu *sllc_pmu = platform_get_drvdata(pdev);
    479
    480	perf_pmu_unregister(&sllc_pmu->pmu);
    481	cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE,
    482					    &sllc_pmu->node);
    483	return 0;
    484}
    485
    486static struct platform_driver hisi_sllc_pmu_driver = {
    487	.driver = {
    488		.name = "hisi_sllc_pmu",
    489		.acpi_match_table = hisi_sllc_pmu_acpi_match,
    490		.suppress_bind_attrs = true,
    491	},
    492	.probe = hisi_sllc_pmu_probe,
    493	.remove = hisi_sllc_pmu_remove,
    494};
    495
    496static int __init hisi_sllc_pmu_module_init(void)
    497{
    498	int ret;
    499
    500	ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE,
    501				      "AP_PERF_ARM_HISI_SLLC_ONLINE",
    502				      hisi_uncore_pmu_online_cpu,
    503				      hisi_uncore_pmu_offline_cpu);
    504	if (ret) {
    505		pr_err("SLLC PMU: cpuhp state setup failed, ret = %d\n", ret);
    506		return ret;
    507	}
    508
    509	ret = platform_driver_register(&hisi_sllc_pmu_driver);
    510	if (ret)
    511		cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE);
    512
    513	return ret;
    514}
    515module_init(hisi_sllc_pmu_module_init);
    516
    517static void __exit hisi_sllc_pmu_module_exit(void)
    518{
    519	platform_driver_unregister(&hisi_sllc_pmu_driver);
    520	cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_SLLC_ONLINE);
    521}
    522module_exit(hisi_sllc_pmu_module_exit);
    523
    524MODULE_DESCRIPTION("HiSilicon SLLC uncore PMU driver");
    525MODULE_LICENSE("GPL v2");
    526MODULE_AUTHOR("Shaokun Zhang <zhangshaokun@hisilicon.com>");
    527MODULE_AUTHOR("Qi Liu <liuqi115@huawei.com>");