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");
/* 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);
}
/* *ETEmandatesthattheTRCRSRiswrittentobefore *enablingit.
*/ 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;
}
rc = coresight_claim_device_unlocked(csdev); if (rc) goto done;
/* Disable the trace unit before programming trace registers */
etm4x_relaxed_write32(csa, 0, TRCPRGCTLR);
/* *Ifweusesysteminstructions,weneedtosynchronizethe *writetotheTRCPRGCTLR,beforeaccessingtheTRCSTATR. *SeeARMIHI0064F,section *"4.3.7Synchronizationofregisterupdates"
*/ 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));
}
/* *Resourceselectorpair0isalwaysimplementedandreserved.As *suchstartat2.
*/ 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);
if (WARN_ON(!arg)) return;
arg->rc = etm4_enable_hw(arg->drvdata);
}
/* *Thegoaloffunctionetm4_config_timestamp_event()istoconfigurea *counterthatwilltellthetracertoemitatimestamppacketwhenit *reacheszero.Thisisdoneinordertogetamorefinegrainedidea *ofwheninstructionsareexecutedsothattheycanbecorrelated *withexecutiononotherCPUs. * *Todothisthecounteritselfisconfiguredtoselfreloadand *TRCRSCTLR1(alwaystrue)usedtogetthecountertodecrement.From *therearesourceselectorisconfiguredwiththecounterandthe *timestampcontrolregistertousetheresourceselectortotriggerthe *eventthatwillinsertatimestamppacketinthestream.
*/ 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;
}
/* *Searchingforanavailableresourceselectortouse,startingat *'2'sinceeveryimplementationhasatleast2resourceselector. *ETMIDR4givesthenumberofresourceselector_pairs_, *hencemultiplyby2.
*/ 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;
/* 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)) { /* *Configuretimestampstobeemittedatregularintervalsin *ordertocorrelateinstructionsexecutedondifferentCPUs *(CPU-widetracescenarios).
*/
ret = etm4_config_timestamp_event(drvdata);
/* *Noneedtogofurtheriftimestampintervalscan't *beconfigured.
*/ 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;
/* *IfsetbitETM_OPT_CTXTID2inperfconfig,thisaskstotraceVMID *forrecordingCONTEXTIDR_EL2.DonotenableVMIDtracingifthe *kernelisnotrunninginEL2.
*/ 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;
/* 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;
/* *Executingetm4_enable_hwonthecpuwhoseETMisbeingenabled *ensuresthatregisterwritesoccurwhencpuispowered.
*/
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;
/* *IftheCPUsupportsv8.4TracefilterControl, *settheETMtotraceprohibitedregion.
*/
etm4x_prohibit_trace(drvdata); /* *Makesureeverythingcompletesbeforedisabling,asrecommended *bysection7.3.77("TRCVICTLR,ViewInstMainControlRegister, *SSTATUS")ofARMIHI0064D
*/
dsb(sy);
isb(); /* Trace synchronization barrier, is a nop if not supported */
tsb_csync();
etm4x_relaxed_write32(csa, control, TRCPRGCTLR);
/* *Asrecommendedbysection4.3.7("Synchronizationwhenusingsystem *instructionstoprogromthetraceunit")ofARMIHI0064H.b,the *self-hostedtraceanalyzermustperformaContextsynchronization *eventbetweenwritingtotheTRCPRGCTLRandreadingtheTRCSTATR.
*/ 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"); /* *Asrecommendedbysection4.3.7(Synchronizationofregisterupdates) *ofARMIHI0064H.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));
}
/* *Ifweareunabletodetecttheaccessmechanism, *orunabletodetectthetraceunittype,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);
/* 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);
/* *Iftherearenofilterstodealwithsimplygoaheadwith *thedefaultfilter,i.etheentireaddressrange.
*/ 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;
}
/* 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);
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->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;
}
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(); /* *SaveandrestoretheETMTraceregistersonlyif *theETMisactive.
*/ 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;
/* *SerializeagainstCPUHPcallbackstoavoidracecondition *betweenthesmpcallandsavingthedelayedprobe.
*/
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.