coresight: etm4x: adding configurable address range filtering
This patch adds the capability to specify address ranges from the perf cmd line using the --filter option. If the IP falls within the range(s) program flow traces are generated. Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
f0d30cc30e
commit
2703d74c13
|
@ -45,7 +45,9 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO);
|
||||||
/* The number of ETMv4 currently registered */
|
/* The number of ETMv4 currently registered */
|
||||||
static int etm4_count;
|
static int etm4_count;
|
||||||
static struct etmv4_drvdata *etmdrvdata[NR_CPUS];
|
static struct etmv4_drvdata *etmdrvdata[NR_CPUS];
|
||||||
static void etm4_set_default(struct etmv4_config *config);
|
static void etm4_set_default_config(struct etmv4_config *config);
|
||||||
|
static int etm4_set_event_filters(struct etmv4_drvdata *drvdata,
|
||||||
|
struct perf_event *event);
|
||||||
|
|
||||||
static enum cpuhp_state hp_online;
|
static enum cpuhp_state hp_online;
|
||||||
|
|
||||||
|
@ -187,11 +189,14 @@ static void etm4_enable_hw(void *info)
|
||||||
static int etm4_parse_event_config(struct etmv4_drvdata *drvdata,
|
static int etm4_parse_event_config(struct etmv4_drvdata *drvdata,
|
||||||
struct perf_event *event)
|
struct perf_event *event)
|
||||||
{
|
{
|
||||||
|
int ret = 0;
|
||||||
struct etmv4_config *config = &drvdata->config;
|
struct etmv4_config *config = &drvdata->config;
|
||||||
struct perf_event_attr *attr = &event->attr;
|
struct perf_event_attr *attr = &event->attr;
|
||||||
|
|
||||||
if (!attr)
|
if (!attr) {
|
||||||
return -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/* Clear configuration from previous run */
|
/* Clear configuration from previous run */
|
||||||
memset(config, 0, sizeof(struct etmv4_config));
|
memset(config, 0, sizeof(struct etmv4_config));
|
||||||
|
@ -203,7 +208,12 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata,
|
||||||
config->mode = ETM_MODE_EXCL_USER;
|
config->mode = ETM_MODE_EXCL_USER;
|
||||||
|
|
||||||
/* Always start from the default config */
|
/* Always start from the default config */
|
||||||
etm4_set_default(config);
|
etm4_set_default_config(config);
|
||||||
|
|
||||||
|
/* Configure filters specified on the perf cmd line, if any. */
|
||||||
|
ret = etm4_set_event_filters(drvdata, event);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
/* Go from generic option to ETMv4 specifics */
|
/* Go from generic option to ETMv4 specifics */
|
||||||
if (attr->config & BIT(ETM_OPT_CYCACC))
|
if (attr->config & BIT(ETM_OPT_CYCACC))
|
||||||
|
@ -211,23 +221,30 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata,
|
||||||
if (attr->config & BIT(ETM_OPT_TS))
|
if (attr->config & BIT(ETM_OPT_TS))
|
||||||
config->cfg |= ETMv4_MODE_TIMESTAMP;
|
config->cfg |= ETMv4_MODE_TIMESTAMP;
|
||||||
|
|
||||||
return 0;
|
out:
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int etm4_enable_perf(struct coresight_device *csdev,
|
static int etm4_enable_perf(struct coresight_device *csdev,
|
||||||
struct perf_event *event)
|
struct perf_event *event)
|
||||||
{
|
{
|
||||||
|
int ret = 0;
|
||||||
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||||
|
|
||||||
if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id()))
|
if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) {
|
||||||
return -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/* Configure the tracer based on the session's specifics */
|
/* Configure the tracer based on the session's specifics */
|
||||||
etm4_parse_event_config(drvdata, event);
|
ret = etm4_parse_event_config(drvdata, event);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
/* And enable it */
|
/* And enable it */
|
||||||
etm4_enable_hw(drvdata);
|
etm4_enable_hw(drvdata);
|
||||||
|
|
||||||
return 0;
|
out:
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int etm4_enable_sysfs(struct coresight_device *csdev)
|
static int etm4_enable_sysfs(struct coresight_device *csdev)
|
||||||
|
@ -682,6 +699,99 @@ static void etm4_set_default(struct etmv4_config *config)
|
||||||
etm4_set_default_filter(config);
|
etm4_set_default_filter(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int etm4_get_next_comparator(struct etmv4_drvdata *drvdata, u32 type)
|
||||||
|
{
|
||||||
|
int nr_comparator, index = 0;
|
||||||
|
struct etmv4_config *config = &drvdata->config;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* nr_addr_cmp holds the number of comparator _pair_, so time 2
|
||||||
|
* for the total number of comparators.
|
||||||
|
*/
|
||||||
|
nr_comparator = drvdata->nr_addr_cmp * 2;
|
||||||
|
|
||||||
|
/* Go through the tally of comparators looking for a free one. */
|
||||||
|
while (index < nr_comparator) {
|
||||||
|
switch (type) {
|
||||||
|
case ETM_ADDR_TYPE_RANGE:
|
||||||
|
if (config->addr_type[index] == ETM_ADDR_TYPE_NONE &&
|
||||||
|
config->addr_type[index + 1] == ETM_ADDR_TYPE_NONE)
|
||||||
|
return index;
|
||||||
|
|
||||||
|
/* Address range comparators go in pairs */
|
||||||
|
index += 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we are here all the comparators have been used. */
|
||||||
|
return -ENOSPC;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int etm4_set_event_filters(struct etmv4_drvdata *drvdata,
|
||||||
|
struct perf_event *event)
|
||||||
|
{
|
||||||
|
int i, comparator, ret = 0;
|
||||||
|
struct etmv4_config *config = &drvdata->config;
|
||||||
|
struct etm_filters *filters = event->hw.addr_filters;
|
||||||
|
|
||||||
|
if (!filters)
|
||||||
|
goto default_filter;
|
||||||
|
|
||||||
|
/* Sync events with what Perf got */
|
||||||
|
perf_event_addr_filters_sync(event);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If there are no filters to deal with simply go ahead with
|
||||||
|
* the default filter, i.e the entire address range.
|
||||||
|
*/
|
||||||
|
if (!filters->nr_filters)
|
||||||
|
goto default_filter;
|
||||||
|
|
||||||
|
for (i = 0; i < filters->nr_filters; i++) {
|
||||||
|
struct etm_filter *filter = &filters->etm_filter[i];
|
||||||
|
enum etm_addr_type type = filter->type;
|
||||||
|
|
||||||
|
/* See if a comparator is free. */
|
||||||
|
comparator = etm4_get_next_comparator(drvdata, type);
|
||||||
|
if (comparator < 0) {
|
||||||
|
ret = comparator;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case ETM_ADDR_TYPE_RANGE:
|
||||||
|
etm4_set_comparator_filter(config,
|
||||||
|
filter->start_addr,
|
||||||
|
filter->stop_addr,
|
||||||
|
comparator);
|
||||||
|
/*
|
||||||
|
* TRCVICTLR::SSSTATUS == 1, the start-stop logic is
|
||||||
|
* in the started state
|
||||||
|
*/
|
||||||
|
config->vinst_ctrl |= BIT(9);
|
||||||
|
|
||||||
|
/* No start-stop filtering for ViewInst */
|
||||||
|
config->vissctlr = 0x0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
|
||||||
|
default_filter:
|
||||||
|
etm4_set_default_filter(config);
|
||||||
|
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void etm4_config_trace_mode(struct etmv4_config *config)
|
void etm4_config_trace_mode(struct etmv4_config *config)
|
||||||
{
|
{
|
||||||
u32 addr_acc, mode;
|
u32 addr_acc, mode;
|
||||||
|
|
Loading…
Reference in New Issue