// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. * * Description: CoreSight Program Flow Trace driver
*/
/* * Not really modular but using module_param is the easiest way to * remain consistent with existing use cases for now.
*/ staticint boot_enable;
module_param_named(boot_enable, boot_enable, int, S_IRUGO);
staticstruct etm_drvdata *etmdrvdata[NR_CPUS];
staticenum cpuhp_state hp_online;
/* * Memory mapped writes to clear os lock are not supported on some processors * and OS lock must be unlocked before any memory mapped access on such * processors, otherwise memory mapped reads/writes will be invalid.
*/ staticvoid etm_os_unlock(struct etm_drvdata *drvdata)
{ /* Writing any value to ETMOSLAR unlocks the trace registers */
etm_writel(drvdata, 0x0, ETMOSLAR);
drvdata->os_unlock = true;
isb();
}
/** * coresight_timeout_etm - loop until a bit has changed to a specific state. * @drvdata: etm's private data structure. * @offset: address of a register, starting from @addr. * @position: the position of the bit of interest. * @value: the value the bit should have. * * Basically the same as @coresight_timeout except for the register access * method where we have to account for CP14 configurations. * * Return: 0 as soon as the bit has taken the desired state or -EAGAIN if * TIMEOUT_US has elapsed, which ever happens first.
*/
staticint coresight_timeout_etm(struct etm_drvdata *drvdata, u32 offset, int position, int value)
{ int i;
u32 val;
for (i = TIMEOUT_US; i > 0; i--) {
val = etm_readl(drvdata, offset); /* Waiting on the bit to go from 0 to 1 */ if (value) { if (val & BIT(position)) return 0; /* Waiting on the bit to go from 1 to 0 */
} else { if (!(val & BIT(position))) return 0;
}
/* * Delay is arbitrary - the specification doesn't say how long * we are expected to wait. Extra check required to make sure * we don't wait needlessly on the last iteration.
*/ if (i - 1)
udelay(1);
}
etmcr = etm_readl(drvdata, ETMCR);
etmcr &= ~ETMCR_ETM_PRG;
etm_writel(drvdata, etmcr, ETMCR); /* * Recommended by spec for cp14 accesses to ensure etmcr write is * complete before polling etmsr
*/
isb(); if (coresight_timeout_etm(drvdata, ETMSR, ETMSR_PROG_BIT, 0)) {
dev_err(&drvdata->csdev->dev, "%s: timeout observed when probing at offset %#x\n",
__func__, ETMSR);
}
}
void etm_set_default(struct etm_config *config)
{ int i;
if (WARN_ON_ONCE(!config)) return;
/* * Taken verbatim from the TRM: * * To trace all memory: * set bit [24] in register 0x009, the ETMTECR1, to 1 * set all other bits in register 0x009, the ETMTECR1, to 0 * set all bits in register 0x007, the ETMTECR2, to 0 * set register 0x008, the ETMTEEVR, to 0x6F (TRUE).
*/
config->enable_ctrl1 = ETMTECR1_INC_EXC;
config->enable_ctrl2 = 0x0;
config->enable_event = ETM_HARD_WIRE_RES_A;
/* excluding kernel AND user space doesn't make sense */ if (mode == (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER)) return;
/* nothing to do if neither flags are set */ if (!(mode & ETM_MODE_EXCL_KERN) && !(mode & ETM_MODE_EXCL_USER)) return;
flags = (1 << 0 | /* instruction execute */
3 << 3 | /* ARM instruction */
0 << 5 | /* No data value comparison */
0 << 7 | /* No exact mach */
0 << 8); /* Ignore context ID */
/* No need to worry about single address comparators. */
config->enable_ctrl2 = 0x0;
/* Bit 0 is address range comparator 1 */
config->enable_ctrl1 = ETMTECR1_ADDR_COMP_1;
/* * On ETMv3.5: * ETMACTRn[13,11] == Non-secure state comparison control * ETMACTRn[12,10] == Secure state comparison control * * b00 == Match in all modes in this state * b01 == Do not match in any more in this state * b10 == Match in all modes excepts user mode in this state * b11 == Match only in user mode in this state
*/
/* Tracing in secure mode is not supported at this time */
flags |= (0 << 12 | 1 << 10);
if (mode & ETM_MODE_EXCL_USER) { /* exclude user, match all modes except user mode */
flags |= (1 << 13 | 0 << 11);
} else { /* exclude kernel, match only in user mode */
flags |= (1 << 13 | 1 << 11);
}
/* * The ETMEEVR register is already set to "hard wire A". As such * all there is to do is setup an address comparator that spans * the entire address range and configure the state and mode bits.
*/
config->addr_val[0] = (u32) 0x0;
config->addr_val[1] = (u32) ~0x0;
config->addr_acctype[0] = flags;
config->addr_acctype[1] = flags;
config->addr_type[0] = ETM_ADDR_TYPE_RANGE;
config->addr_type[1] = ETM_ADDR_TYPE_RANGE;
}
/* Clear configuration from previous run */
memset(config, 0, sizeof(struct etm_config));
if (attr->exclude_kernel)
config->mode = ETM_MODE_EXCL_KERN;
if (attr->exclude_user)
config->mode = ETM_MODE_EXCL_USER;
/* Always start from the default config */
etm_set_default(config);
/* * By default the tracers are configured to trace the whole address * range. Narrow the field only if requested by user space.
*/ if (config->mode)
etm_config_trace_mode(config);
/* * At this time only cycle accurate, return stack and timestamp * options are available.
*/ if (attr->config & ~ETM3X_SUPPORTED_OPTIONS) return -EINVAL;
config->ctrl = attr->config;
/* Don't trace contextID when runs in non-root PID namespace */ if (!task_is_in_init_pid_ns(current))
config->ctrl &= ~ETMCR_CTXID_SIZE;
/* * Possible to have cores with PTM (supports ret stack) and ETM * (never has ret stack) on the same SoC. So if we have a request * for return stack that can't be honoured on this core then * clear the bit - trace will still continue normally
*/ if ((config->ctrl & ETMCR_RETURN_STACK) &&
!(drvdata->etmccer & ETMCCER_RETSTACK))
config->ctrl &= ~ETMCR_RETURN_STACK;
return 0;
}
staticint etm_enable_hw(struct etm_drvdata *drvdata)
{ int i, rc;
u32 etmcr; struct etm_config *config = &drvdata->config; struct coresight_device *csdev = drvdata->csdev;
CS_UNLOCK(drvdata->csa.base);
rc = coresight_claim_device_unlocked(csdev); if (rc) goto done;
/* Turn engine on */
etm_clr_pwrdwn(drvdata); /* Apply power to trace registers */
etm_set_pwrup(drvdata); /* Make sure all registers are accessible */
etm_os_unlock(drvdata);
etm_set_prog(drvdata);
etmcr = etm_readl(drvdata, ETMCR); /* Clear setting from a previous run if need be */
etmcr &= ~ETM3X_SUPPORTED_OPTIONS;
etmcr |= drvdata->port_size;
etmcr |= ETMCR_ETM_EN;
etm_writel(drvdata, config->ctrl | etmcr, ETMCR);
etm_writel(drvdata, config->trigger_event, ETMTRIGGER);
etm_writel(drvdata, config->startstop_ctrl, ETMTSSCR);
etm_writel(drvdata, config->enable_event, ETMTEEVR);
etm_writel(drvdata, config->enable_ctrl1, ETMTECR1);
etm_writel(drvdata, config->fifofull_level, ETMFFLR); for (i = 0; i < drvdata->nr_addr_cmp; i++) {
etm_writel(drvdata, config->addr_val[i], ETMACVRn(i));
etm_writel(drvdata, config->addr_acctype[i], ETMACTRn(i));
} for (i = 0; i < drvdata->nr_cntr; i++) {
etm_writel(drvdata, config->cntr_rld_val[i], ETMCNTRLDVRn(i));
etm_writel(drvdata, config->cntr_event[i], ETMCNTENRn(i));
etm_writel(drvdata, config->cntr_rld_event[i],
ETMCNTRLDEVRn(i));
etm_writel(drvdata, config->cntr_val[i], ETMCNTVRn(i));
}
etm_writel(drvdata, config->seq_12_event, ETMSQ12EVR);
etm_writel(drvdata, config->seq_21_event, ETMSQ21EVR);
etm_writel(drvdata, config->seq_23_event, ETMSQ23EVR);
etm_writel(drvdata, config->seq_31_event, ETMSQ31EVR);
etm_writel(drvdata, config->seq_32_event, ETMSQ32EVR);
etm_writel(drvdata, config->seq_13_event, ETMSQ13EVR);
etm_writel(drvdata, config->seq_curr_state, ETMSQR); for (i = 0; i < drvdata->nr_ext_out; i++)
etm_writel(drvdata, ETM_DEFAULT_EVENT_VAL, ETMEXTOUTEVRn(i)); for (i = 0; i < drvdata->nr_ctxid_cmp; i++)
etm_writel(drvdata, config->ctxid_pid[i], ETMCIDCVRn(i));
etm_writel(drvdata, config->ctxid_mask, ETMCIDCMR);
etm_writel(drvdata, config->sync_freq, ETMSYNCFR); /* No external input selected */
etm_writel(drvdata, 0x0, ETMEXTINSELR);
etm_writel(drvdata, config->timestamp_event, ETMTSEVR); /* No auxiliary control selected */
etm_writel(drvdata, 0x0, ETMAUXCR);
etm_writel(drvdata, drvdata->traceid, ETMTRACEIDR); /* No VMID comparator value selected */
etm_writel(drvdata, 0x0, ETMVMIDCVR);
/* * Configure the ETM only if the CPU is online. If it isn't online * hw configuration will take place on the local CPU during bring up.
*/ if (cpu_online(drvdata->cpu)) {
arg.drvdata = drvdata;
ret = smp_call_function_single(drvdata->cpu,
etm_enable_hw_smp_call, &arg, 1); if (!ret)
ret = arg.rc; if (!ret)
drvdata->sticky_enable = true;
} else {
ret = -ENODEV;
}
if (ret)
etm_release_trace_id(drvdata);
spin_unlock(&drvdata->spinlock);
if (!ret)
dev_dbg(&csdev->dev, "ETM tracing enabled\n"); return ret;
}
if (!coresight_take_mode(csdev, mode)) { /* Someone is already using the tracer */ return -EBUSY;
}
switch (mode) { case CS_MODE_SYSFS:
ret = etm_enable_sysfs(csdev, path); break; case CS_MODE_PERF:
ret = etm_enable_perf(csdev, event, path); break; default:
ret = -EINVAL;
}
/* The tracer didn't start */ if (ret)
coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED);
if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) return;
CS_UNLOCK(drvdata->csa.base);
/* Setting the prog bit disables tracing immediately */
etm_set_prog(drvdata);
/* * There is no way to know when the tracer will be used again so * power down the tracer.
*/
etm_set_pwrdwn(drvdata);
coresight_disclaim_device_unlocked(csdev);
CS_LOCK(drvdata->csa.base);
/* * perf will release trace ids when _free_aux() * is called at the end of the session
*/
/* * Taking hotplug lock here protects from clocks getting disabled * with tracing being left on (crash scenario) if user disable occurs * after cpu online mask indicates the cpu is offline but before the * DYING hotplug callback is serviced by the ETM driver.
*/
cpus_read_lock();
spin_lock(&drvdata->spinlock);
/* * Executing etm_disable_hw on the cpu whose ETM is being disabled * ensures that register writes occur when cpu is powered.
*/
smp_call_function_single(drvdata->cpu, etm_disable_hw, drvdata, 1);
/* * we only release trace IDs when resetting sysfs. * This permits sysfs users to read the trace ID after the trace * session has completed. This maintains operational behaviour with * prior trace id allocation method
*/
/* * For as long as the tracer isn't disabled another entity can't * change its status. As such we can read the status here without * fearing it will change under us.
*/
mode = coresight_get_mode(csdev);
switch (mode) { case CS_MODE_DISABLED: break; case CS_MODE_SYSFS:
etm_disable_sysfs(csdev); break; case CS_MODE_PERF:
etm_disable_perf(csdev); break; default:
WARN_ON_ONCE(mode); return;
}
if (mode)
coresight_set_mode(csdev, CS_MODE_DISABLED);
}
/* Make sure all registers are accessible */
etm_os_unlock(drvdata);
CS_UNLOCK(drvdata->csa.base);
/* First dummy read */
(void)etm_readl(drvdata, ETMPDSR); /* Provide power to ETM: ETMPDCR[3] == 1 */
etm_set_pwrup(drvdata); /* * Clear power down bit since when this bit is set writes to * certain registers might be ignored.
*/
etm_clr_pwrdwn(drvdata); /* * Set prog bit. It will be set from reset but this is included to * ensure it is set
*/
etm_set_prog(drvdata);
/* * Taking hotplug lock here to avoid racing between etm_remove and * CPU hotplug call backs.
*/
cpus_read_lock(); /* * The readers for etmdrvdata[] are CPU hotplug call backs * and PM notification call backs. Change etmdrvdata[i] on * CPU i ensures these call backs has consistent view * inside one call back function.
*/ if (smp_call_function_single(drvdata->cpu, clear_etmdrvdata, &drvdata->cpu, 1))
etmdrvdata[drvdata->cpu] = NULL;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.