staticint boot_enable;
module_param(boot_enable, int, 0444);
MODULE_PARM_DESC(boot_enable, "Enable tracing on boot");
#define PARAM_PM_SAVE_FIRMWARE 0 /* save self-hosted state as per firmware */ #define PARAM_PM_SAVE_NEVER 1 /* never save any state */ #define PARAM_PM_SAVE_SELF_HOSTED 2 /* save self-hosted state only */
staticint pm_save_enable = PARAM_PM_SAVE_FIRMWARE;
module_param(pm_save_enable, int, 0444);
MODULE_PARM_DESC(pm_save_enable, "Save/restore state on power down: 1 = never, 2 = self-hosted");
/* * Check if TRCSSPCICRn(i) is implemented for a given instance. * * TRCSSPCICRn is implemented only if : * TRCSSPCICR<n> is present only if all of the following are true: * TRCIDR4.NUMSSCC > n. * TRCIDR4.NUMPC > 0b0000 . * TRCSSCSR<n>.PC == 0b1
*/ staticbool etm4x_sspcicrn_present(struct etmv4_drvdata *drvdata, int n)
{ return (n < drvdata->nr_ss_cmp) &&
drvdata->nr_pe &&
(drvdata->config.ss_status[n] & TRCSSCSRn_PC);
}
struct etm4_enable_arg { struct etmv4_drvdata *drvdata; int rc;
};
/* * etm4x_prohibit_trace - Prohibit the CPU from tracing at all ELs. * When the CPU supports FEAT_TRF, we could move the ETM to a trace * prohibited state by filtering the Exception levels via TRFCR_EL1.
*/ staticvoid etm4x_prohibit_trace(struct etmv4_drvdata *drvdata)
{
u64 trfcr;
/* If the CPU doesn't support FEAT_TRF, nothing to do */ if (!drvdata->trfcr) return;
if (drvdata->config.mode & ETM_MODE_EXCL_KERN)
trfcr &= ~TRFCR_EL1_ExTRE; if (drvdata->config.mode & ETM_MODE_EXCL_USER)
trfcr &= ~TRFCR_EL1_E0TRE;
return trfcr;
}
/* * etm4x_allow_trace - Allow CPU tracing in the respective ELs, * as configured by the drvdata->config.mode for the current * session. Even though we have TRCVICTLR bits to filter the * trace in the ELs, it doesn't prevent the ETM from generating * a packet (e.g, TraceInfo) that might contain the addresses from * the excluded levels. Thus we use the additional controls provided * via the Trace Filtering controls (FEAT_TRF) to make sure no trace * is generated for the excluded ELs.
*/ staticvoid etm4x_allow_trace(struct etmv4_drvdata *drvdata)
{
u64 trfcr, guest_trfcr;
/* If the CPU doesn't support FEAT_TRF, nothing to do */ if (!drvdata->trfcr) return;
/* Set filters for guests and pass to KVM */ if (drvdata->config.mode & ETM_MODE_EXCL_GUEST)
guest_trfcr = drvdata->trfcr & ~(TRFCR_EL1_ExTRE | TRFCR_EL1_E0TRE); else
guest_trfcr = etm4x_get_kern_user_filter(drvdata);
/* TRFCR_EL1 doesn't have CX so mask it out. */
guest_trfcr &= ~TRFCR_EL2_CX;
kvm_tracing_set_el1_configuration(guest_trfcr);
}
/* * bit 12 and 13 of HISI_HIP08_CORE_COMMIT_REG are used together * to set core-commit, 2'b00 means cpu is at full speed, 2'b01, * 2'b10, 2'b11 mean reduce pipeline speed, and 2'b01 means level-1 * speed(minimun value). So bit 12 and 13 should be cleared together.
*/
val = read_sysreg_s(HISI_HIP08_CORE_COMMIT_REG);
val &= ~HISI_HIP08_CORE_COMMIT_MASK;
val |= commit << HISI_HIP08_CORE_COMMIT_SHIFT;
write_sysreg_s(val, HISI_HIP08_CORE_COMMIT_REG);
}
if (ftr->arch_callback)
ftr->arch_callback(false);
}
}
staticvoid etm4_check_arch_features(struct etmv4_drvdata *drvdata, struct csdev_access *csa)
{ /* * TRCPIDR* registers are not required for ETMs with system * instructions. They must be identified by the MIDR+REVIDRs. * Skip the TRCPID checks for now.
*/ if (!csa->io_mem) return;
staticvoid etm4x_sys_ins_barrier(struct csdev_access *csa, u32 offset, int pos, int val)
{ if (!csa->io_mem)
isb();
}
/* * etm4x_wait_status: Poll for TRCSTATR.<pos> == <val>. While using system * instruction to access the trace unit, each access must be separated by a * synchronization barrier. See ARM IHI0064H.b section "4.3.7 Synchronization of * register updates", for system instructions section, in "Notes": * * "In particular, whenever disabling or enabling the trace unit, a poll of * TRCSTATR needs explicit synchronization between each read of TRCSTATR"
*/ staticint etm4x_wait_status(struct csdev_access *csa, int pos, int val)
{ if (!csa->io_mem) return coresight_timeout_action(csa, TRCSTATR, pos, val,
etm4x_sys_ins_barrier); return coresight_timeout(csa, TRCSTATR, pos, val);
}
/* * ETE mandates that the TRCRSR is written to before * enabling it.
*/ if (etm4x_is_ete(drvdata))
etm4x_relaxed_write32(csa, TRCRSR_TA, TRCRSR);
etm4x_allow_trace(drvdata); /* Enable the trace unit */
etm4x_relaxed_write32(csa, 1, TRCPRGCTLR);
/* Synchronize the register updates for sysreg access */ if (!csa->io_mem)
isb();
/* wait for TRCSTATR.IDLE to go back down to '0' */ if (etm4x_wait_status(csa, TRCSTATR_IDLE_BIT, 0)) {
dev_err(etm_dev, "timeout while waiting for Idle Trace Status\n"); return -ETIME;
}
/* * As recommended by section 4.3.7 ("Synchronization when using the * memory-mapped interface") of ARM IHI 0064D
*/
dsb(sy);
isb();
rc = coresight_claim_device_unlocked(csdev); if (rc) goto done;
/* Disable the trace unit before programming trace registers */
etm4x_relaxed_write32(csa, 0, TRCPRGCTLR);
/* * If we use system instructions, we need to synchronize the * write to the TRCPRGCTLR, before accessing the TRCSTATR. * See ARM IHI0064F, section * "4.3.7 Synchronization of register updates"
*/ if (!csa->io_mem)
isb();
/* wait for TRCSTATR.IDLE to go up */ if (etm4x_wait_status(csa, TRCSTATR_IDLE_BIT, 1))
dev_err(etm_dev, "timeout while waiting for Idle Trace Status\n"); if (drvdata->nr_pe)
etm4x_relaxed_write32(csa, config->pe_sel, TRCPROCSELR);
etm4x_relaxed_write32(csa, config->cfg, TRCCONFIGR); /* nothing specific implemented */
etm4x_relaxed_write32(csa, 0x0, TRCAUXCTLR);
etm4x_relaxed_write32(csa, config->eventctrl0, TRCEVENTCTL0R);
etm4x_relaxed_write32(csa, config->eventctrl1, TRCEVENTCTL1R); if (drvdata->stallctl)
etm4x_relaxed_write32(csa, config->stall_ctrl, TRCSTALLCTLR);
etm4x_relaxed_write32(csa, config->ts_ctrl, TRCTSCTLR);
etm4x_relaxed_write32(csa, config->syncfreq, TRCSYNCPR);
etm4x_relaxed_write32(csa, config->ccctlr, TRCCCCTLR);
etm4x_relaxed_write32(csa, config->bb_ctrl, TRCBBCTLR);
etm4x_relaxed_write32(csa, drvdata->trcid, TRCTRACEIDR);
etm4x_relaxed_write32(csa, config->vinst_ctrl, TRCVICTLR);
etm4x_relaxed_write32(csa, config->viiectlr, TRCVIIECTLR);
etm4x_relaxed_write32(csa, config->vissctlr, TRCVISSCTLR); if (drvdata->nr_pe_cmp)
etm4x_relaxed_write32(csa, config->vipcssctlr, TRCVIPCSSCTLR); for (i = 0; i < drvdata->nrseqstate - 1; i++)
etm4x_relaxed_write32(csa, config->seq_ctrl[i], TRCSEQEVRn(i)); if (drvdata->nrseqstate) {
etm4x_relaxed_write32(csa, config->seq_rst, TRCSEQRSTEVR);
etm4x_relaxed_write32(csa, config->seq_state, TRCSEQSTR);
} if (drvdata->numextinsel)
etm4x_relaxed_write32(csa, config->ext_inp, TRCEXTINSELR); for (i = 0; i < drvdata->nr_cntr; i++) {
etm4x_relaxed_write32(csa, config->cntrldvr[i], TRCCNTRLDVRn(i));
etm4x_relaxed_write32(csa, config->cntr_ctrl[i], TRCCNTCTLRn(i));
etm4x_relaxed_write32(csa, config->cntr_val[i], TRCCNTVRn(i));
}
/* * Resource selector pair 0 is always implemented and reserved. As * such start at 2.
*/ for (i = 2; i < drvdata->nr_resource * 2; i++)
etm4x_relaxed_write32(csa, config->res_ctrl[i], TRCRSCTLRn(i));
for (i = 0; i < drvdata->nr_ss_cmp; i++) { /* always clear status bit on restart if using single-shot */ if (config->ss_ctrl[i] || config->ss_pe_cmp[i])
config->ss_status[i] &= ~TRCSSCSRn_STATUS;
etm4x_relaxed_write32(csa, config->ss_ctrl[i], TRCSSCCRn(i));
etm4x_relaxed_write32(csa, config->ss_status[i], TRCSSCSRn(i)); if (etm4x_sspcicrn_present(drvdata, i))
etm4x_relaxed_write32(csa, config->ss_pe_cmp[i], TRCSSPCICRn(i));
} for (i = 0; i < drvdata->nr_addr_cmp * 2; i++) {
etm4x_relaxed_write64(csa, config->addr_val[i], TRCACVRn(i));
etm4x_relaxed_write64(csa, config->addr_acc[i], TRCACATRn(i));
} for (i = 0; i < drvdata->numcidc; i++)
etm4x_relaxed_write64(csa, config->ctxid_pid[i], TRCCIDCVRn(i));
etm4x_relaxed_write32(csa, config->ctxid_mask0, TRCCIDCCTLR0); if (drvdata->numcidc > 4)
etm4x_relaxed_write32(csa, config->ctxid_mask1, TRCCIDCCTLR1);
for (i = 0; i < drvdata->numvmidc; i++)
etm4x_relaxed_write64(csa, config->vmid_val[i], TRCVMIDCVRn(i));
etm4x_relaxed_write32(csa, config->vmid_mask0, TRCVMIDCCTLR0); if (drvdata->numvmidc > 4)
etm4x_relaxed_write32(csa, config->vmid_mask1, TRCVMIDCCTLR1);
if (!drvdata->skip_power_up) {
u32 trcpdcr = etm4x_relaxed_read32(csa, TRCPDCR);
/* * Request to keep the trace unit powered and also * emulation of powerdown
*/
etm4x_relaxed_write32(csa, trcpdcr | TRCPDCR_PU, TRCPDCR);
}
if (!drvdata->paused)
rc = etm4_enable_trace_unit(drvdata);
done:
etm4_cs_lock(drvdata, csa);
if (WARN_ON(!arg)) return;
arg->rc = etm4_enable_hw(arg->drvdata);
}
/* * The goal of function etm4_config_timestamp_event() is to configure a * counter that will tell the tracer to emit a timestamp packet when it * reaches zero. This is done in order to get a more fine grained idea * of when instructions are executed so that they can be correlated * with execution on other CPUs. * * To do this the counter itself is configured to self reload and * TRCRSCTLR1 (always true) used to get the counter to decrement. From * there a resource selector is configured with the counter and the * timestamp control register to use the resource selector to trigger the * event that will insert a timestamp packet in the stream.
*/ staticint etm4_config_timestamp_event(struct etmv4_drvdata *drvdata)
{ int ctridx, ret = -EINVAL; int counter, rselector;
u32 val = 0; struct etmv4_config *config = &drvdata->config;
/* No point in trying if we don't have at least one counter */ if (!drvdata->nr_cntr) goto out;
/* Find a counter that hasn't been initialised */ for (ctridx = 0; ctridx < drvdata->nr_cntr; ctridx++) if (config->cntr_val[ctridx] == 0) break;
/* All the counters have been configured already, bail out */ if (ctridx == drvdata->nr_cntr) {
pr_debug("%s: no available counter found\n", __func__);
ret = -ENOSPC; goto out;
}
/* * Searching for an available resource selector to use, starting at * '2' since every implementation has at least 2 resource selector. * ETMIDR4 gives the number of resource selector _pairs_, * hence multiply by 2.
*/ for (rselector = 2; rselector < drvdata->nr_resource * 2; rselector++) if (!config->res_ctrl[rselector]) break;
if (rselector == drvdata->nr_resource * 2) {
pr_debug("%s: no available resource selector found\n",
__func__);
ret = -ENOSPC; goto out;
}
/* Remember what counter we used */
counter = 1 << ctridx;
/* * Initialise original and reload counter value to the smallest * possible value in order to get as much precision as we can.
*/
config->cntr_val[ctridx] = 1;
config->cntrldvr[ctridx] = 1;
/* Set the trace counter control register */
val = 0x1 << 16 | /* Bit 16, reload counter automatically */
0x0 << 7 | /* Select single resource selector */
0x1; /* Resource selector 1, i.e always true */
config->cntr_ctrl[ctridx] = val;
val = 0x2 << 16 | /* Group 0b0010 - Counter and sequencers */
counter << 0; /* Counter to use */
config->res_ctrl[rselector] = val;
val = 0x0 << 7 | /* Select single resource selector */
rselector; /* Resource selector */
/* Clear configuration from previous run */
memset(config, 0, sizeof(struct etmv4_config));
if (attr->exclude_kernel)
config->mode = ETM_MODE_EXCL_KERN;
if (attr->exclude_user)
config->mode = ETM_MODE_EXCL_USER;
if (attr->exclude_host)
config->mode |= ETM_MODE_EXCL_HOST;
if (attr->exclude_guest)
config->mode |= ETM_MODE_EXCL_GUEST;
/* Always start from the 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 */ if (attr->config & BIT(ETM_OPT_CYCACC)) {
config->cfg |= TRCCONFIGR_CCI; /* TRM: Must program this for cycacc to work */
cc_threshold = attr->config3 & ETM_CYC_THRESHOLD_MASK; if (!cc_threshold)
cc_threshold = ETM_CYC_THRESHOLD_DEFAULT; if (cc_threshold < drvdata->ccitmin)
cc_threshold = drvdata->ccitmin;
config->ccctlr = cc_threshold;
} if (attr->config & BIT(ETM_OPT_TS)) { /* * Configure timestamps to be emitted at regular intervals in * order to correlate instructions executed on different CPUs * (CPU-wide trace scenarios).
*/
ret = etm4_config_timestamp_event(drvdata);
/* * No need to go further if timestamp intervals can't * be configured.
*/ if (ret) goto out;
/* bit[11], Global timestamp tracing bit */
config->cfg |= TRCCONFIGR_TS;
}
/* Only trace contextID when runs in root PID namespace */ if ((attr->config & BIT(ETM_OPT_CTXTID)) &&
task_is_in_init_pid_ns(current)) /* bit[6], Context ID tracing bit */
config->cfg |= TRCCONFIGR_CID;
/* * If set bit ETM_OPT_CTXTID2 in perf config, this asks to trace VMID * for recording CONTEXTIDR_EL2. Do not enable VMID tracing if the * kernel is not running in EL2.
*/ if (attr->config & BIT(ETM_OPT_CTXTID2)) { if (!is_kernel_in_hyp_mode()) {
ret = -EINVAL; goto out;
} /* Only trace virtual contextID when runs in root PID namespace */ if (task_is_in_init_pid_ns(current))
config->cfg |= TRCCONFIGR_VMID | TRCCONFIGR_VMIDOPT;
}
/* return stack - enable if selected and supported */ if ((attr->config & BIT(ETM_OPT_RETSTK)) && drvdata->retstack) /* bit[12], Return stack enable bit */
config->cfg |= TRCCONFIGR_RS;
/* * Set any selected configuration and preset. * * This extracts the values of PMU_FORMAT_ATTR(configid) and PMU_FORMAT_ATTR(preset) * in the perf attributes defined in coresight-etm-perf.c. * configid uses bits 63:32 of attr->config2, preset uses bits 3:0 of attr->config. * A zero configid means no configuration active, preset = 0 means no preset selected.
*/ if (attr->config2 & GENMASK_ULL(63, 32)) {
cfg_hash = (u32)(attr->config2 >> 32);
preset = attr->config & 0xF;
ret = cscfg_csdev_enable_active_config(csdev, cfg_hash, preset);
}
/* branch broadcast - enable if selected and supported */ if (attr->config & BIT(ETM_OPT_BRANCH_BROADCAST)) { if (!drvdata->trcbb) { /* * Missing BB support could cause silent decode errors * so fail to open if it's not supported.
*/
ret = -EINVAL; goto out;
} else {
config->cfg |= BIT(ETM4_CFG_BIT_BB);
}
}
out: return ret;
}
staticint etm4_enable_perf(struct coresight_device *csdev, struct perf_event *event, struct coresight_path *path)
{ int ret = 0; struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) {
ret = -EINVAL; goto out;
}
/* Configure the tracer based on the session's specifics */
ret = etm4_parse_event_config(csdev, event); if (ret) goto out;
drvdata->trcid = path->trace_id;
/* Populate pause state */
drvdata->paused = !!READ_ONCE(event->hw.aux_paused);
/* And enable it */
ret = etm4_enable_hw(drvdata);
/* enable any config activated by configfs */
cscfg_config_sysfs_get_active_cfg(&cfg_hash, &preset); if (cfg_hash) {
ret = cscfg_csdev_enable_active_config(csdev, cfg_hash, preset); if (ret) return ret;
}
raw_spin_lock(&drvdata->spinlock);
drvdata->trcid = path->trace_id;
/* Tracer will never be paused in sysfs mode */
drvdata->paused = false;
/* * Executing etm4_enable_hw on the cpu whose ETM is being enabled * ensures that register writes occur when cpu is powered.
*/
arg.drvdata = drvdata;
ret = smp_call_function_single(drvdata->cpu,
etm4_enable_hw_smp_call, &arg, 1); if (!ret)
ret = arg.rc; if (!ret)
drvdata->sticky_enable = true;
if (ret)
etm4_release_trace_id(drvdata);
raw_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 = etm4_enable_sysfs(csdev, path); break; case CS_MODE_PERF:
ret = etm4_enable_perf(csdev, event, path); break; default:
ret = -EINVAL;
}
/* The tracer didn't start */ if (ret)
coresight_set_mode(csdev, CS_MODE_DISABLED);
/* EN, bit[0] Trace unit enable bit */
control &= ~0x1;
/* * If the CPU supports v8.4 Trace filter Control, * set the ETM to trace prohibited region.
*/
etm4x_prohibit_trace(drvdata); /* * Make sure everything completes before disabling, as recommended * by section 7.3.77 ("TRCVICTLR, ViewInst Main Control Register, * SSTATUS") of ARM IHI 0064D
*/
dsb(sy);
isb(); /* Trace synchronization barrier, is a nop if not supported */
tsb_csync();
etm4x_relaxed_write32(csa, control, TRCPRGCTLR);
/* * As recommended by section 4.3.7 ("Synchronization when using system * instructions to progrom the trace unit") of ARM IHI 0064H.b, the * self-hosted trace analyzer must perform a Context synchronization * event between writing to the TRCPRGCTLR and reading the TRCSTATR.
*/ if (!csa->io_mem)
isb();
/* wait for TRCSTATR.PMSTABLE to go to '1' */ if (etm4x_wait_status(csa, TRCSTATR_PMSTABLE_BIT, 1))
dev_err(etm_dev, "timeout while waiting for PM stable Trace Status\n"); /* * As recommended by section 4.3.7 (Synchronization of register updates) * of ARM IHI 0064H.b.
*/
isb();
}
if (!drvdata->skip_power_up) { /* power can be removed from the trace unit now */
control = etm4x_relaxed_read32(csa, TRCPDCR);
control &= ~TRCPDCR_PU;
etm4x_relaxed_write32(csa, control, TRCPDCR);
}
etm4_disable_trace_unit(drvdata);
/* read the status of the single shot comparators */ for (i = 0; i < drvdata->nr_ss_cmp; i++) {
config->ss_status[i] =
etm4x_relaxed_read32(csa, TRCSSCSRn(i));
}
/* read back the current counter values */ for (i = 0; i < drvdata->nr_cntr; i++) {
config->cntr_val[i] =
etm4x_relaxed_read32(csa, TRCCNTVRn(i));
}
if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) return -EINVAL;
etm4_disable_hw(drvdata); /* * The config_id occupies bits 63:32 of the config2 perf event attr * field. If this is non-zero then we will have enabled a config.
*/ if (attr->config2 & GENMASK_ULL(63, 32))
cscfg_csdev_disable_active_config(csdev);
/* * Check if the start/stop logic was active when the unit was stopped. * That way we can re-enable the start/stop logic when the process is * scheduled again. Configuration of the start/stop logic happens in * function etm4_set_event_filters().
*/
control = etm4x_relaxed_read32(&csdev->access, TRCVICTLR); /* TRCVICTLR::SSSTATUS, bit[9] */
filters->ssstatus = (control & BIT(9));
/* * 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();
raw_spin_lock(&drvdata->spinlock);
/* * Executing etm4_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, etm4_disable_hw, drvdata, 1);
raw_spin_unlock(&drvdata->spinlock);
cscfg_csdev_disable_active_config(csdev);
cpus_read_unlock();
/* * 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:
etm4_disable_sysfs(csdev); break; case CS_MODE_PERF:
etm4_disable_perf(csdev, event); break;
}
if (mode)
coresight_set_mode(csdev, CS_MODE_DISABLED);
}
if (!is_coresight_device(drvdata->base) || !is_devtype_cpu_trace(drvdata->base)) returnfalse;
/* * All ETMs must implement TRCDEVARCH to indicate that * the component is an ETMv4. Even though TRCIDR1 also * contains the information, it is part of the "Trace" * register and must be accessed with the OSLK cleared, * with MMIO. But we cannot touch the OSLK until we are * sure this is an ETM. So rely only on the TRCDEVARCH.
*/ if ((devarch & ETM_DEVARCH_ID_MASK) != ETM_DEVARCH_ETMv4x_ARCH) {
pr_warn_once("TRCDEVARCH doesn't match ETMv4 architecture\n"); returnfalse;
}
staticbool etm4_init_csdev_access(struct etmv4_drvdata *drvdata, struct csdev_access *csa)
{ /* * Always choose the memory mapped io, if there is * a memory map to prevent sysreg access on broken * systems.
*/ if (drvdata->base) return etm4_init_iomem_access(drvdata, csa);
if (etm4_init_sysreg_access(drvdata, csa)) returntrue;
drvdata->trfcr = 0; if (!cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_TraceFilt_SHIFT)) return;
/* * If the CPU supports v8.4 SelfHosted Tracing, enable * tracing at the kernel EL and EL0, forcing to use the * virtual time as the timestamp.
*/
trfcr = (FIELD_PREP(TRFCR_EL1_TS_MASK, TRFCR_EL1_TS_VIRTUAL) |
TRFCR_EL1_ExTRE |
TRFCR_EL1_E0TRE);
/* If we are running at EL2, allow tracing the CONTEXTIDR_EL2. */ if (is_kernel_in_hyp_mode())
trfcr |= TRFCR_EL2_CX;
drvdata->trfcr = trfcr;
}
/* * The following errata on applicable cpu ranges, affect the CCITMIN filed * in TCRIDR3 register. Software read for the field returns 0x100 limiting * the cycle threshold granularity, whereas the right value should have * been 0x4, which is well supported in the hardware.
*/ staticstruct midr_range etm_wrong_ccitmin_cpus[] = { /* Erratum #1490853 - Cortex-A76 */
MIDR_RANGE(MIDR_CORTEX_A76, 0, 0, 4, 0), /* Erratum #1490853 - Neoverse-N1 */
MIDR_RANGE(MIDR_NEOVERSE_N1, 0, 0, 4, 0), /* Erratum #1491015 - Cortex-A77 */
MIDR_RANGE(MIDR_CORTEX_A77, 0, 0, 1, 0), /* Erratum #1502854 - Cortex-X1 */
MIDR_REV(MIDR_CORTEX_X1, 0, 0), /* Erratum #1619801 - Neoverse-V1 */
MIDR_REV(MIDR_NEOVERSE_V1, 0, 0),
{},
};
staticvoid etm4_fixup_wrong_ccitmin(struct etmv4_drvdata *drvdata)
{ /* * Erratum affected cpus will read 256 as the minimum * instruction trace cycle counting threshold whereas * the correct value should be 4 instead. Override the * recorded value for 'drvdata->ccitmin' to workaround * this problem.
*/ if (is_midr_in_range_list(etm_wrong_ccitmin_cpus)) { if (drvdata->ccitmin == 256)
drvdata->ccitmin = 4;
}
}
/* * If we are unable to detect the access mechanism, * or unable to detect the trace unit type, fail * early.
*/ if (!etm4_init_csdev_access(drvdata, csa)) return;
if (!csa->io_mem ||
fwnode_property_present(dev_fwnode(dev), "qcom,skip-power-up"))
drvdata->skip_power_up = true;
/* Detect the support for OS Lock before we actually use it */
etm_detect_os_lock(drvdata, csa);
/* Make sure all registers are accessible */
etm4_os_unlock_csa(drvdata, csa);
etm4_cs_unlock(drvdata, csa);
etm4_check_arch_features(drvdata, csa);
/* find all capabilities of the tracing unit */
etmidr0 = etm4x_relaxed_read32(csa, TRCIDR0);
/* INSTP0, bits[2:1] P0 tracing support field */
drvdata->instrp0 = !!(FIELD_GET(TRCIDR0_INSTP0_MASK, etmidr0) == 0b11); /* TRCBB, bit[5] Branch broadcast tracing support bit */
drvdata->trcbb = !!(etmidr0 & TRCIDR0_TRCBB); /* TRCCOND, bit[6] Conditional instruction tracing support bit */
drvdata->trccond = !!(etmidr0 & TRCIDR0_TRCCOND); /* TRCCCI, bit[7] Cycle counting instruction bit */
drvdata->trccci = !!(etmidr0 & TRCIDR0_TRCCCI); /* RETSTACK, bit[9] Return stack bit */
drvdata->retstack = !!(etmidr0 & TRCIDR0_RETSTACK); /* NUMEVENT, bits[11:10] Number of events field */
drvdata->nr_event = FIELD_GET(TRCIDR0_NUMEVENT_MASK, etmidr0); /* QSUPP, bits[16:15] Q element support field */
drvdata->q_support = FIELD_GET(TRCIDR0_QSUPP_MASK, etmidr0); if (drvdata->q_support)
drvdata->q_filt = !!(etmidr0 & TRCIDR0_QFILT); /* TSSIZE, bits[28:24] Global timestamp size field */
drvdata->ts_size = FIELD_GET(TRCIDR0_TSSIZE_MASK, etmidr0);
/* maximum size of resources */
etmidr2 = etm4x_relaxed_read32(csa, TRCIDR2); /* CIDSIZE, bits[9:5] Indicates the Context ID size */
drvdata->ctxid_size = FIELD_GET(TRCIDR2_CIDSIZE_MASK, etmidr2); /* VMIDSIZE, bits[14:10] Indicates the VMID size */
drvdata->vmid_size = FIELD_GET(TRCIDR2_VMIDSIZE_MASK, etmidr2); /* CCSIZE, bits[28:25] size of the cycle counter in bits minus 12 */
drvdata->ccsize = FIELD_GET(TRCIDR2_CCSIZE_MASK, etmidr2);
etmidr3 = etm4x_relaxed_read32(csa, TRCIDR3); /* CCITMIN, bits[11:0] minimum threshold value that can be programmed */
drvdata->ccitmin = FIELD_GET(TRCIDR3_CCITMIN_MASK, etmidr3);
etm4_fixup_wrong_ccitmin(drvdata);
/* EXLEVEL_S, bits[19:16] Secure state instruction tracing */
drvdata->s_ex_level = FIELD_GET(TRCIDR3_EXLEVEL_S_MASK, etmidr3);
drvdata->config.s_ex_level = drvdata->s_ex_level; /* EXLEVEL_NS, bits[23:20] Non-secure state instruction tracing */
drvdata->ns_ex_level = FIELD_GET(TRCIDR3_EXLEVEL_NS_MASK, etmidr3); /* * TRCERR, bit[24] whether a trace unit can trace a * system error exception.
*/
drvdata->trc_error = !!(etmidr3 & TRCIDR3_TRCERR); /* SYNCPR, bit[25] implementation has a fixed synchronization period? */
drvdata->syncpr = !!(etmidr3 & TRCIDR3_SYNCPR); /* STALLCTL, bit[26] is stall control implemented? */
drvdata->stallctl = !!(etmidr3 & TRCIDR3_STALLCTL); /* SYSSTALL, bit[27] implementation can support stall control? */
drvdata->sysstall = !!(etmidr3 & TRCIDR3_SYSSTALL); /* * NUMPROC - the number of PEs available for tracing, 5bits * = TRCIDR3.bits[13:12]bits[30:28] * bits[4:3] = TRCIDR3.bits[13:12] (since etm-v4.2, otherwise RES0) * bits[3:0] = TRCIDR3.bits[30:28]
*/
drvdata->nr_pe = (FIELD_GET(TRCIDR3_NUMPROC_HI_MASK, etmidr3) << 3) |
FIELD_GET(TRCIDR3_NUMPROC_LO_MASK, etmidr3); /* NOOVERFLOW, bit[31] is trace overflow prevention supported */
drvdata->nooverflow = !!(etmidr3 & TRCIDR3_NOOVERFLOW);
/* number of resources trace unit supports */
etmidr4 = etm4x_relaxed_read32(csa, TRCIDR4); /* NUMACPAIRS, bits[0:3] number of addr comparator pairs for tracing */
drvdata->nr_addr_cmp = FIELD_GET(TRCIDR4_NUMACPAIRS_MASK, etmidr4); /* NUMPC, bits[15:12] number of PE comparator inputs for tracing */
drvdata->nr_pe_cmp = FIELD_GET(TRCIDR4_NUMPC_MASK, etmidr4); /* * NUMRSPAIR, bits[19:16] * The number of resource pairs conveyed by the HW starts at 0, i.e a * value of 0x0 indicate 1 resource pair, 0x1 indicate two and so on. * As such add 1 to the value of NUMRSPAIR for a better representation. * * For ETM v4.3 and later, 0x0 means 0, and no pairs are available - * the default TRUE and FALSE resource selectors are omitted. * Otherwise for values 0x1 and above the number is N + 1 as per v4.2.
*/
drvdata->nr_resource = FIELD_GET(TRCIDR4_NUMRSPAIR_MASK, etmidr4); if ((drvdata->arch < ETM_ARCH_V4_3) || (drvdata->nr_resource > 0))
drvdata->nr_resource += 1; /* * NUMSSCC, bits[23:20] the number of single-shot * comparator control for tracing. Read any status regs as these * also contain RO capability data.
*/
drvdata->nr_ss_cmp = FIELD_GET(TRCIDR4_NUMSSCC_MASK, etmidr4); for (i = 0; i < drvdata->nr_ss_cmp; i++) {
drvdata->config.ss_status[i] =
etm4x_relaxed_read32(csa, TRCSSCSRn(i));
} /* NUMCIDC, bits[27:24] number of Context ID comparators for tracing */
drvdata->numcidc = FIELD_GET(TRCIDR4_NUMCIDC_MASK, etmidr4); /* NUMVMIDC, bits[31:28] number of VMID comparators for tracing */
drvdata->numvmidc = FIELD_GET(TRCIDR4_NUMVMIDC_MASK, etmidr4);
etmidr5 = etm4x_relaxed_read32(csa, TRCIDR5); /* NUMEXTIN, bits[8:0] number of external inputs implemented */
drvdata->nr_ext_inp = FIELD_GET(TRCIDR5_NUMEXTIN_MASK, etmidr5);
drvdata->numextinsel = FIELD_GET(TRCIDR5_NUMEXTINSEL_MASK, etmidr5); /* TRACEIDSIZE, bits[21:16] indicates the trace ID width */
drvdata->trcid_size = FIELD_GET(TRCIDR5_TRACEIDSIZE_MASK, etmidr5); /* ATBTRIG, bit[22] implementation can support ATB triggers? */
drvdata->atbtrig = !!(etmidr5 & TRCIDR5_ATBTRIG); /* * LPOVERRIDE, bit[23] implementation supports * low-power state override
*/
drvdata->lpoverride = (etmidr5 & TRCIDR5_LPOVERRIDE) && (!drvdata->skip_power_up); /* NUMSEQSTATE, bits[27:25] number of sequencer states implemented */
drvdata->nrseqstate = FIELD_GET(TRCIDR5_NUMSEQSTATE_MASK, etmidr5); /* NUMCNTR, bits[30:28] number of counters available for tracing */
drvdata->nr_cntr = FIELD_GET(TRCIDR5_NUMCNTR_MASK, etmidr5);
/* * EXLEVEL_NS, for NonSecure Exception levels. * The mask here is a generic value and must be * shifted to the corresponding field for the registers
*/ if (!is_kernel_in_hyp_mode()) { /* Stay away from hypervisor mode for non-VHE */
access_type = ETM_EXLEVEL_NS_HYP; if (config->mode & ETM_MODE_EXCL_KERN)
access_type |= ETM_EXLEVEL_NS_OS;
} elseif (config->mode & ETM_MODE_EXCL_KERN) {
access_type = ETM_EXLEVEL_NS_HYP;
}
if (config->mode & ETM_MODE_EXCL_USER)
access_type |= ETM_EXLEVEL_NS_APP;
return access_type;
}
/* * Construct the exception level masks for a given config. * This must be shifted to the corresponding register field * for usage.
*/ static u64 etm4_get_access_type(struct etmv4_config *config)
{ /* All Secure exception levels are excluded from the trace */ return etm4_get_ns_access_type(config) | (u64)config->s_ex_level;
}
/* * Configure the ViewInst function to include this address range * comparator. * * @comparator is divided by two since it is the index in the * etmv4_config::addr_val array but register TRCVIIECTLR deals with * address range comparator _pairs_. * * Therefore: * index 0 -> compatator pair 0 * index 2 -> comparator pair 1 * index 4 -> comparator pair 2 * ... * index 14 -> comparator pair 7
*/
config->viiectlr |= BIT(comparator / 2);
}
staticvoid etm4_set_start_stop_filter(struct etmv4_config *config,
u64 address, int comparator, enum etm_addr_type type)
{ int shift;
u64 access_type = etm4_get_comparator_access_type(config);
/* * Configure ViewInst Start-Stop control register. * Addresses configured to start tracing go from bit 0 to n-1, * while those configured to stop tracing from 16 to 16 + n-1.
*/
shift = (type == ETM_ADDR_TYPE_START ? 0 : 16);
config->vissctlr |= BIT(shift + comparator);
}
staticvoid etm4_set_default_filter(struct etmv4_config *config)
{ /* Trace everything 'default' filter achieved by no filtering */
config->viiectlr = 0x0;
/* * TRCVICTLR::SSSTATUS == 1, the start-stop logic is * in the started state
*/
config->vinst_ctrl |= TRCVICTLR_SSSTATUS;
config->mode |= ETM_MODE_VIEWINST_STARTSTOP;
/* No start-stop filtering for ViewInst */
config->vissctlr = 0x0;
}
staticvoid etm4_set_default(struct etmv4_config *config)
{ if (WARN_ON_ONCE(!config)) return;
/* * Make default initialisation trace everything * * This is done by a minimum default config sufficient to enable * full instruction trace - with a default filter for trace all * achieved by having no filtering.
*/
etm4_set_default_config(config);
etm4_set_default_filter(config);
}
staticint 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; case ETM_ADDR_TYPE_START: case ETM_ADDR_TYPE_STOP: if (config->addr_type[index] == ETM_ADDR_TYPE_NONE) return index;
/* Start/stop address can have odd indexes */
index += 1; break; default: return -EINVAL;
}
}
/* If we are here all the comparators have been used. */ return -ENOSPC;
}
staticint etm4_set_event_filters(struct etmv4_drvdata *drvdata, struct perf_event *event)
{ int i, comparator, ret = 0;
u64 address; 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 |= TRCVICTLR_SSSTATUS;
/* No start-stop filtering for ViewInst */
config->vissctlr = 0x0; break; case ETM_ADDR_TYPE_START: case ETM_ADDR_TYPE_STOP: /* Get the right start or stop address */
address = (type == ETM_ADDR_TYPE_START ?
filter->start_addr :
filter->stop_addr);
/* * If filters::ssstatus == 1, trace acquisition was * started but the process was yanked away before the * stop address was hit. As such the start/stop * logic needs to be re-started so that tracing can * resume where it left. * * The start/stop logic status when a process is * scheduled out is checked in function * etm4_disable_perf().
*/ if (filters->ssstatus)
config->vinst_ctrl |= TRCVICTLR_SSSTATUS;
/* No include/exclude filtering for ViewInst */
config->viiectlr = 0x0; break; default:
ret = -EINVAL; goto out;
}
}
/* excluding kernel AND user space doesn't make sense */
WARN_ON_ONCE(mode == (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER));
/* nothing to do if neither flags are set */ if (!(mode & ETM_MODE_EXCL_KERN) && !(mode & ETM_MODE_EXCL_USER)) return;
etm4_set_victlr_access(config);
}
staticint etm4_online_cpu(unsignedint cpu)
{ if (!etmdrvdata[cpu]) return etm4_probe_cpu(cpu);
if (etmdrvdata[cpu]->boot_enable && !etmdrvdata[cpu]->sticky_enable)
coresight_enable_sysfs(etmdrvdata[cpu]->csdev); return 0;
}
staticint etm4_starting_cpu(unsignedint cpu)
{ if (!etmdrvdata[cpu]) return 0;
raw_spin_lock(&etmdrvdata[cpu]->spinlock); if (!etmdrvdata[cpu]->os_unlock)
etm4_os_unlock(etmdrvdata[cpu]);
if (coresight_get_mode(etmdrvdata[cpu]->csdev))
etm4_enable_hw(etmdrvdata[cpu]);
raw_spin_unlock(&etmdrvdata[cpu]->spinlock); return 0;
}
staticint etm4_dying_cpu(unsignedint cpu)
{ if (!etmdrvdata[cpu]) return 0;
raw_spin_lock(&etmdrvdata[cpu]->spinlock); if (coresight_get_mode(etmdrvdata[cpu]->csdev))
etm4_disable_hw(etmdrvdata[cpu]);
raw_spin_unlock(&etmdrvdata[cpu]->spinlock); return 0;
}
staticint __etm4_cpu_save(struct etmv4_drvdata *drvdata)
{ int i, ret = 0; struct etmv4_save_state *state; struct coresight_device *csdev = drvdata->csdev; struct csdev_access *csa; struct device *etm_dev;
if (WARN_ON(!csdev)) return -ENODEV;
etm_dev = &csdev->dev;
csa = &csdev->access;
/* * As recommended by 3.4.1 ("The procedure when powering down the PE") * of ARM IHI 0064D
*/
dsb(sy);
isb();
etm4_cs_unlock(drvdata, csa); /* Lock the OS lock to disable trace and external debugger access */
etm4_os_lock(drvdata);
/* wait for TRCSTATR.PMSTABLE to go up */ if (etm4x_wait_status(csa, TRCSTATR_PMSTABLE_BIT, 1)) {
dev_err(etm_dev, "timeout while waiting for PM Stable Status\n");
etm4_os_unlock(drvdata);
ret = -EBUSY; goto out;
}
if (drvdata->numextinsel)
state->trcextinselr = etm4x_read32(csa, TRCEXTINSELR);
for (i = 0; i < drvdata->nr_cntr; i++) {
state->trccntrldvr[i] = etm4x_read32(csa, TRCCNTRLDVRn(i));
state->trccntctlr[i] = etm4x_read32(csa, TRCCNTCTLRn(i));
state->trccntvr[i] = etm4x_read32(csa, TRCCNTVRn(i));
}
/* Resource selector pair 0 is reserved */ for (i = 2; i < drvdata->nr_resource * 2; i++)
state->trcrsctlr[i] = etm4x_read32(csa, TRCRSCTLRn(i));
for (i = 0; i < drvdata->nr_ss_cmp; i++) {
state->trcssccr[i] = etm4x_read32(csa, TRCSSCCRn(i));
state->trcsscsr[i] = etm4x_read32(csa, TRCSSCSRn(i)); if (etm4x_sspcicrn_present(drvdata, i))
state->trcsspcicr[i] = etm4x_read32(csa, TRCSSPCICRn(i));
}
for (i = 0; i < drvdata->nr_addr_cmp * 2; i++) {
state->trcacvr[i] = etm4x_read64(csa, TRCACVRn(i));
state->trcacatr[i] = etm4x_read64(csa, TRCACATRn(i));
}
/* * Data trace stream is architecturally prohibited for A profile cores * so we don't save (or later restore) trcdvcvr and trcdvcmr - As per * section 1.3.4 ("Possible functional configurations of an ETMv4 trace * unit") of ARM IHI 0064D.
*/
for (i = 0; i < drvdata->numcidc; i++)
state->trccidcvr[i] = etm4x_read64(csa, TRCCIDCVRn(i));
for (i = 0; i < drvdata->numvmidc; i++)
state->trcvmidcvr[i] = etm4x_read64(csa, TRCVMIDCVRn(i));
if (!drvdata->skip_power_up)
state->trcpdcr = etm4x_read32(csa, TRCPDCR);
/* wait for TRCSTATR.IDLE to go up */ if (etm4x_wait_status(csa, TRCSTATR_PMSTABLE_BIT, 1)) {
dev_err(etm_dev, "timeout while waiting for Idle Trace Status\n");
etm4_os_unlock(drvdata);
ret = -EBUSY; goto out;
}
drvdata->state_needs_restore = true;
/* * Power can be removed from the trace unit now. We do this to * potentially save power on systems that respect the TRCPDCR_PU * despite requesting software to save/restore state.
*/ if (!drvdata->skip_power_up)
etm4x_relaxed_write32(csa, (state->trcpdcr & ~TRCPDCR_PU),
TRCPDCR);
out:
etm4_cs_lock(drvdata, csa); return ret;
}
staticint etm4_cpu_save(struct etmv4_drvdata *drvdata)
{ int ret = 0;
/* Save the TRFCR irrespective of whether the ETM is ON */ if (drvdata->trfcr)
drvdata->save_trfcr = read_trfcr(); /* * Save and restore the ETM Trace registers only if * the ETM is active.
*/ if (coresight_get_mode(drvdata->csdev) && drvdata->save_state)
ret = __etm4_cpu_save(drvdata); return ret;
}
if (WARN_ON_ONCE(drvdata->cpu != cpu)) return NOTIFY_BAD;
switch (cmd) { case CPU_PM_ENTER: if (etm4_cpu_save(drvdata)) return NOTIFY_BAD; break; case CPU_PM_EXIT: case CPU_PM_ENTER_FAILED:
etm4_cpu_restore(drvdata); break; default: return NOTIFY_DONE;
}
major = ETM_ARCH_MAJOR_VERSION(drvdata->arch);
minor = ETM_ARCH_MINOR_VERSION(drvdata->arch);
if (etm4x_is_ete(drvdata)) {
type_name = "ete"; /* ETE v1 has major version == 0b101. Adjust this for logging.*/
major -= 4;
} else {
type_name = "etm";
}
ret = etm_perf_symlink(drvdata->csdev, true); if (ret) {
coresight_unregister(drvdata->csdev); return ret;
}
/* register with config infrastructure & load any current features */
ret = etm4_cscfg_register(drvdata->csdev); if (ret) {
coresight_unregister(drvdata->csdev); return ret;
}
if (pm_save_enable != PARAM_PM_SAVE_NEVER) {
drvdata->save_state = devm_kmalloc(dev, sizeof(struct etmv4_save_state), GFP_KERNEL); if (!drvdata->save_state) return -ENOMEM;
}
raw_spin_lock_init(&drvdata->spinlock);
drvdata->cpu = coresight_get_cpu(dev); if (drvdata->cpu < 0) return drvdata->cpu;
init_arg.dev = dev;
init_arg.csa = &access;
/* * Serialize against CPUHP callbacks to avoid race condition * between the smp call and saving the delayed probe.
*/
cpus_read_lock(); if (smp_call_function_single(drvdata->cpu,
etm4_init_arch_data, &init_arg, 1)) { /* The CPU was offline, try again once it comes online. */
delayed = devm_kmalloc(dev, sizeof(*delayed), GFP_KERNEL); if (!delayed) {
cpus_read_unlock(); return -ENOMEM;
}
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.