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

coresight-cti-platform.c (13032B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (c) 2019, The Linaro Limited. All rights reserved.
      4 */
      5#include <linux/coresight.h>
      6#include <linux/device.h>
      7#include <linux/err.h>
      8#include <linux/of.h>
      9#include <linux/property.h>
     10#include <linux/slab.h>
     11
     12#include <dt-bindings/arm/coresight-cti-dt.h>
     13
     14#include "coresight-cti.h"
     15#include "coresight-priv.h"
     16
     17/* Number of CTI signals in the v8 architecturally defined connection */
     18#define NR_V8PE_IN_SIGS		2
     19#define NR_V8PE_OUT_SIGS	3
     20#define NR_V8ETM_INOUT_SIGS	4
     21
     22/* CTI device tree trigger connection node keyword */
     23#define CTI_DT_CONNS		"trig-conns"
     24
     25/* CTI device tree connection property keywords */
     26#define CTI_DT_V8ARCH_COMPAT	"arm,coresight-cti-v8-arch"
     27#define CTI_DT_CSDEV_ASSOC	"arm,cs-dev-assoc"
     28#define CTI_DT_TRIGIN_SIGS	"arm,trig-in-sigs"
     29#define CTI_DT_TRIGOUT_SIGS	"arm,trig-out-sigs"
     30#define CTI_DT_TRIGIN_TYPES	"arm,trig-in-types"
     31#define CTI_DT_TRIGOUT_TYPES	"arm,trig-out-types"
     32#define CTI_DT_FILTER_OUT_SIGS	"arm,trig-filters"
     33#define CTI_DT_CONN_NAME	"arm,trig-conn-name"
     34#define CTI_DT_CTM_ID		"arm,cti-ctm-id"
     35
     36#ifdef CONFIG_OF
     37/*
     38 * CTI can be bound to a CPU, or a system device.
     39 * CPU can be declared at the device top level or in a connections node
     40 * so need to check relative to node not device.
     41 */
     42static int of_cti_get_cpu_at_node(const struct device_node *node)
     43{
     44	int cpu;
     45	struct device_node *dn;
     46
     47	if (node == NULL)
     48		return -1;
     49
     50	dn = of_parse_phandle(node, "cpu", 0);
     51	/* CTI affinity defaults to no cpu */
     52	if (!dn)
     53		return -1;
     54	cpu = of_cpu_node_to_id(dn);
     55	of_node_put(dn);
     56
     57	/* No Affinity  if no cpu nodes are found */
     58	return (cpu < 0) ? -1 : cpu;
     59}
     60
     61#else
     62static int of_cti_get_cpu_at_node(const struct device_node *node)
     63{
     64	return -1;
     65}
     66
     67#endif
     68
     69/*
     70 * CTI can be bound to a CPU, or a system device.
     71 * CPU can be declared at the device top level or in a connections node
     72 * so need to check relative to node not device.
     73 */
     74static int cti_plat_get_cpu_at_node(struct fwnode_handle *fwnode)
     75{
     76	if (is_of_node(fwnode))
     77		return of_cti_get_cpu_at_node(to_of_node(fwnode));
     78	return -1;
     79}
     80
     81const char *cti_plat_get_node_name(struct fwnode_handle *fwnode)
     82{
     83	if (is_of_node(fwnode))
     84		return of_node_full_name(to_of_node(fwnode));
     85	return "unknown";
     86}
     87
     88/*
     89 * Extract a name from the fwnode.
     90 * If the device associated with the node is a coresight_device, then return
     91 * that name and the coresight_device pointer, otherwise return the node name.
     92 */
     93static const char *
     94cti_plat_get_csdev_or_node_name(struct fwnode_handle *fwnode,
     95				struct coresight_device **csdev)
     96{
     97	const char *name = NULL;
     98	*csdev = coresight_find_csdev_by_fwnode(fwnode);
     99	if (*csdev)
    100		name = dev_name(&(*csdev)->dev);
    101	else
    102		name = cti_plat_get_node_name(fwnode);
    103	return name;
    104}
    105
    106static bool cti_plat_node_name_eq(struct fwnode_handle *fwnode,
    107				  const char *name)
    108{
    109	if (is_of_node(fwnode))
    110		return of_node_name_eq(to_of_node(fwnode), name);
    111	return false;
    112}
    113
    114static int cti_plat_create_v8_etm_connection(struct device *dev,
    115					     struct cti_drvdata *drvdata)
    116{
    117	int ret = -ENOMEM, i;
    118	struct fwnode_handle *root_fwnode, *cs_fwnode;
    119	const char *assoc_name = NULL;
    120	struct coresight_device *csdev;
    121	struct cti_trig_con *tc = NULL;
    122
    123	root_fwnode = dev_fwnode(dev);
    124	if (IS_ERR_OR_NULL(root_fwnode))
    125		return -EINVAL;
    126
    127	/* Can optionally have an etm node - return if not  */
    128	cs_fwnode = fwnode_find_reference(root_fwnode, CTI_DT_CSDEV_ASSOC, 0);
    129	if (IS_ERR(cs_fwnode))
    130		return 0;
    131
    132	/* allocate memory */
    133	tc = cti_allocate_trig_con(dev, NR_V8ETM_INOUT_SIGS,
    134				   NR_V8ETM_INOUT_SIGS);
    135	if (!tc)
    136		goto create_v8_etm_out;
    137
    138	/* build connection data */
    139	tc->con_in->used_mask = 0xF0; /* sigs <4,5,6,7> */
    140	tc->con_out->used_mask = 0xF0; /* sigs <4,5,6,7> */
    141
    142	/*
    143	 * The EXTOUT type signals from the ETM are connected to a set of input
    144	 * triggers on the CTI, the EXTIN being connected to output triggers.
    145	 */
    146	for (i = 0; i < NR_V8ETM_INOUT_SIGS; i++) {
    147		tc->con_in->sig_types[i] = ETM_EXTOUT;
    148		tc->con_out->sig_types[i] = ETM_EXTIN;
    149	}
    150
    151	/*
    152	 * We look to see if the ETM coresight device associated with this
    153	 * handle has been registered with the system - i.e. probed before
    154	 * this CTI. If so csdev will be non NULL and we can use the device
    155	 * name and pass the csdev to the connection entry function where
    156	 * the association will be recorded.
    157	 * If not, then simply record the name in the connection data, the
    158	 * probing of the ETM will call into the CTI driver API to update the
    159	 * association then.
    160	 */
    161	assoc_name = cti_plat_get_csdev_or_node_name(cs_fwnode, &csdev);
    162	ret = cti_add_connection_entry(dev, drvdata, tc, csdev, assoc_name);
    163
    164create_v8_etm_out:
    165	fwnode_handle_put(cs_fwnode);
    166	return ret;
    167}
    168
    169/*
    170 * Create an architecturally defined v8 connection
    171 * must have a cpu, can have an ETM.
    172 */
    173static int cti_plat_create_v8_connections(struct device *dev,
    174					  struct cti_drvdata *drvdata)
    175{
    176	struct cti_device *cti_dev = &drvdata->ctidev;
    177	struct cti_trig_con *tc = NULL;
    178	int cpuid = 0;
    179	char cpu_name_str[16];
    180	int ret = -ENOMEM;
    181
    182	/* Must have a cpu node */
    183	cpuid = cti_plat_get_cpu_at_node(dev_fwnode(dev));
    184	if (cpuid < 0) {
    185		dev_warn(dev,
    186			 "ARM v8 architectural CTI connection: missing cpu\n");
    187		return -EINVAL;
    188	}
    189	cti_dev->cpu = cpuid;
    190
    191	/* Allocate the v8 cpu connection memory */
    192	tc = cti_allocate_trig_con(dev, NR_V8PE_IN_SIGS, NR_V8PE_OUT_SIGS);
    193	if (!tc)
    194		goto of_create_v8_out;
    195
    196	/* Set the v8 PE CTI connection data */
    197	tc->con_in->used_mask = 0x3; /* sigs <0 1> */
    198	tc->con_in->sig_types[0] = PE_DBGTRIGGER;
    199	tc->con_in->sig_types[1] = PE_PMUIRQ;
    200	tc->con_out->used_mask = 0x7; /* sigs <0 1 2 > */
    201	tc->con_out->sig_types[0] = PE_EDBGREQ;
    202	tc->con_out->sig_types[1] = PE_DBGRESTART;
    203	tc->con_out->sig_types[2] = PE_CTIIRQ;
    204	scnprintf(cpu_name_str, sizeof(cpu_name_str), "cpu%d", cpuid);
    205
    206	ret = cti_add_connection_entry(dev, drvdata, tc, NULL, cpu_name_str);
    207	if (ret)
    208		goto of_create_v8_out;
    209
    210	/* Create the v8 ETM associated connection */
    211	ret = cti_plat_create_v8_etm_connection(dev, drvdata);
    212	if (ret)
    213		goto of_create_v8_out;
    214
    215	/* filter pe_edbgreq - PE trigout sig <0> */
    216	drvdata->config.trig_out_filter |= 0x1;
    217
    218of_create_v8_out:
    219	return ret;
    220}
    221
    222static int cti_plat_check_v8_arch_compatible(struct device *dev)
    223{
    224	struct fwnode_handle *fwnode = dev_fwnode(dev);
    225
    226	if (is_of_node(fwnode))
    227		return of_device_is_compatible(to_of_node(fwnode),
    228					       CTI_DT_V8ARCH_COMPAT);
    229	return 0;
    230}
    231
    232static int cti_plat_count_sig_elements(const struct fwnode_handle *fwnode,
    233				       const char *name)
    234{
    235	int nr_elem = fwnode_property_count_u32(fwnode, name);
    236
    237	return (nr_elem < 0 ? 0 : nr_elem);
    238}
    239
    240static int cti_plat_read_trig_group(struct cti_trig_grp *tgrp,
    241				    const struct fwnode_handle *fwnode,
    242				    const char *grp_name)
    243{
    244	int idx, err = 0;
    245	u32 *values;
    246
    247	if (!tgrp->nr_sigs)
    248		return 0;
    249
    250	values = kcalloc(tgrp->nr_sigs, sizeof(u32), GFP_KERNEL);
    251	if (!values)
    252		return -ENOMEM;
    253
    254	err = fwnode_property_read_u32_array(fwnode, grp_name,
    255					     values, tgrp->nr_sigs);
    256
    257	if (!err) {
    258		/* set the signal usage mask */
    259		for (idx = 0; idx < tgrp->nr_sigs; idx++)
    260			tgrp->used_mask |= BIT(values[idx]);
    261	}
    262
    263	kfree(values);
    264	return err;
    265}
    266
    267static int cti_plat_read_trig_types(struct cti_trig_grp *tgrp,
    268				    const struct fwnode_handle *fwnode,
    269				    const char *type_name)
    270{
    271	int items, err = 0, nr_sigs;
    272	u32 *values = NULL, i;
    273
    274	/* allocate an array according to number of signals in connection */
    275	nr_sigs = tgrp->nr_sigs;
    276	if (!nr_sigs)
    277		return 0;
    278
    279	/* see if any types have been included in the device description */
    280	items = cti_plat_count_sig_elements(fwnode, type_name);
    281	if (items > nr_sigs)
    282		return -EINVAL;
    283
    284	/* need an array to store the values iff there are any */
    285	if (items) {
    286		values = kcalloc(items, sizeof(u32), GFP_KERNEL);
    287		if (!values)
    288			return -ENOMEM;
    289
    290		err = fwnode_property_read_u32_array(fwnode, type_name,
    291						     values, items);
    292		if (err)
    293			goto read_trig_types_out;
    294	}
    295
    296	/*
    297	 * Match type id to signal index, 1st type to 1st index etc.
    298	 * If fewer types than signals default remainder to GEN_IO.
    299	 */
    300	for (i = 0; i < nr_sigs; i++) {
    301		if (i < items) {
    302			tgrp->sig_types[i] =
    303				values[i] < CTI_TRIG_MAX ? values[i] : GEN_IO;
    304		} else {
    305			tgrp->sig_types[i] = GEN_IO;
    306		}
    307	}
    308
    309read_trig_types_out:
    310	kfree(values);
    311	return err;
    312}
    313
    314static int cti_plat_process_filter_sigs(struct cti_drvdata *drvdata,
    315					const struct fwnode_handle *fwnode)
    316{
    317	struct cti_trig_grp *tg = NULL;
    318	int err = 0, nr_filter_sigs;
    319
    320	nr_filter_sigs = cti_plat_count_sig_elements(fwnode,
    321						     CTI_DT_FILTER_OUT_SIGS);
    322	if (nr_filter_sigs == 0)
    323		return 0;
    324
    325	if (nr_filter_sigs > drvdata->config.nr_trig_max)
    326		return -EINVAL;
    327
    328	tg = kzalloc(sizeof(*tg), GFP_KERNEL);
    329	if (!tg)
    330		return -ENOMEM;
    331
    332	err = cti_plat_read_trig_group(tg, fwnode, CTI_DT_FILTER_OUT_SIGS);
    333	if (!err)
    334		drvdata->config.trig_out_filter |= tg->used_mask;
    335
    336	kfree(tg);
    337	return err;
    338}
    339
    340static int cti_plat_create_connection(struct device *dev,
    341				      struct cti_drvdata *drvdata,
    342				      struct fwnode_handle *fwnode)
    343{
    344	struct cti_trig_con *tc = NULL;
    345	int cpuid = -1, err = 0;
    346	struct coresight_device *csdev = NULL;
    347	const char *assoc_name = "unknown";
    348	char cpu_name_str[16];
    349	int nr_sigs_in, nr_sigs_out;
    350
    351	/* look to see how many in and out signals we have */
    352	nr_sigs_in = cti_plat_count_sig_elements(fwnode, CTI_DT_TRIGIN_SIGS);
    353	nr_sigs_out = cti_plat_count_sig_elements(fwnode, CTI_DT_TRIGOUT_SIGS);
    354
    355	if ((nr_sigs_in > drvdata->config.nr_trig_max) ||
    356	    (nr_sigs_out > drvdata->config.nr_trig_max))
    357		return -EINVAL;
    358
    359	tc = cti_allocate_trig_con(dev, nr_sigs_in, nr_sigs_out);
    360	if (!tc)
    361		return -ENOMEM;
    362
    363	/* look for the signals properties. */
    364	err = cti_plat_read_trig_group(tc->con_in, fwnode,
    365				       CTI_DT_TRIGIN_SIGS);
    366	if (err)
    367		goto create_con_err;
    368
    369	err = cti_plat_read_trig_types(tc->con_in, fwnode,
    370				       CTI_DT_TRIGIN_TYPES);
    371	if (err)
    372		goto create_con_err;
    373
    374	err = cti_plat_read_trig_group(tc->con_out, fwnode,
    375				       CTI_DT_TRIGOUT_SIGS);
    376	if (err)
    377		goto create_con_err;
    378
    379	err = cti_plat_read_trig_types(tc->con_out, fwnode,
    380				       CTI_DT_TRIGOUT_TYPES);
    381	if (err)
    382		goto create_con_err;
    383
    384	err = cti_plat_process_filter_sigs(drvdata, fwnode);
    385	if (err)
    386		goto create_con_err;
    387
    388	/* read the connection name if set - may be overridden by later */
    389	fwnode_property_read_string(fwnode, CTI_DT_CONN_NAME, &assoc_name);
    390
    391	/* associated cpu ? */
    392	cpuid = cti_plat_get_cpu_at_node(fwnode);
    393	if (cpuid >= 0) {
    394		drvdata->ctidev.cpu = cpuid;
    395		scnprintf(cpu_name_str, sizeof(cpu_name_str), "cpu%d", cpuid);
    396		assoc_name = cpu_name_str;
    397	} else {
    398		/* associated device ? */
    399		struct fwnode_handle *cs_fwnode = fwnode_find_reference(fwnode,
    400									CTI_DT_CSDEV_ASSOC,
    401									0);
    402		if (!IS_ERR(cs_fwnode)) {
    403			assoc_name = cti_plat_get_csdev_or_node_name(cs_fwnode,
    404								     &csdev);
    405			fwnode_handle_put(cs_fwnode);
    406		}
    407	}
    408	/* set up a connection */
    409	err = cti_add_connection_entry(dev, drvdata, tc, csdev, assoc_name);
    410
    411create_con_err:
    412	return err;
    413}
    414
    415static int cti_plat_create_impdef_connections(struct device *dev,
    416					      struct cti_drvdata *drvdata)
    417{
    418	int rc = 0;
    419	struct fwnode_handle *fwnode = dev_fwnode(dev);
    420	struct fwnode_handle *child = NULL;
    421
    422	if (IS_ERR_OR_NULL(fwnode))
    423		return -EINVAL;
    424
    425	fwnode_for_each_child_node(fwnode, child) {
    426		if (cti_plat_node_name_eq(child, CTI_DT_CONNS))
    427			rc = cti_plat_create_connection(dev, drvdata,
    428							child);
    429		if (rc != 0)
    430			break;
    431	}
    432	fwnode_handle_put(child);
    433
    434	return rc;
    435}
    436
    437/* get the hardware configuration & connection data. */
    438static int cti_plat_get_hw_data(struct device *dev, struct cti_drvdata *drvdata)
    439{
    440	int rc = 0;
    441	struct cti_device *cti_dev = &drvdata->ctidev;
    442
    443	/* get any CTM ID - defaults to 0 */
    444	device_property_read_u32(dev, CTI_DT_CTM_ID, &cti_dev->ctm_id);
    445
    446	/* check for a v8 architectural CTI device */
    447	if (cti_plat_check_v8_arch_compatible(dev))
    448		rc = cti_plat_create_v8_connections(dev, drvdata);
    449	else
    450		rc = cti_plat_create_impdef_connections(dev, drvdata);
    451	if (rc)
    452		return rc;
    453
    454	/* if no connections, just add a single default based on max IN-OUT */
    455	if (cti_dev->nr_trig_con == 0)
    456		rc = cti_add_default_connection(dev, drvdata);
    457	return rc;
    458}
    459
    460struct coresight_platform_data *
    461coresight_cti_get_platform_data(struct device *dev)
    462{
    463	int ret = -ENOENT;
    464	struct coresight_platform_data *pdata = NULL;
    465	struct fwnode_handle *fwnode = dev_fwnode(dev);
    466	struct cti_drvdata *drvdata = dev_get_drvdata(dev);
    467
    468	if (IS_ERR_OR_NULL(fwnode))
    469		goto error;
    470
    471	/*
    472	 * Alloc platform data but leave it zero init. CTI does not use the
    473	 * same connection infrastructuree as trace path components but an
    474	 * empty struct enables us to use the standard coresight component
    475	 * registration code.
    476	 */
    477	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
    478	if (!pdata) {
    479		ret = -ENOMEM;
    480		goto error;
    481	}
    482
    483	/* get some CTI specifics */
    484	ret = cti_plat_get_hw_data(dev, drvdata);
    485
    486	if (!ret)
    487		return pdata;
    488error:
    489	return ERR_PTR(ret);
    490}