18a9fd832SMathieu Poirier // SPDX-License-Identifier: GPL-2.0 2a818c563SMathieu Poirier /* 3a818c563SMathieu Poirier * Copyright(C) 2015 Linaro Limited. All rights reserved. 4a818c563SMathieu Poirier * Author: Mathieu Poirier <mathieu.poirier@linaro.org> 5a818c563SMathieu Poirier */ 6a818c563SMathieu Poirier 7a818c563SMathieu Poirier #include <api/fs/fs.h> 8fa4e819bSMathieu Poirier #include <linux/bits.h> 9a818c563SMathieu Poirier #include <linux/bitops.h> 10afaed6d3SArnaldo Carvalho de Melo #include <linux/compiler.h> 11a818c563SMathieu Poirier #include <linux/coresight-pmu.h> 12a818c563SMathieu Poirier #include <linux/kernel.h> 13a818c563SMathieu Poirier #include <linux/log2.h> 14a818c563SMathieu Poirier #include <linux/types.h> 15a818c563SMathieu Poirier 16a818c563SMathieu Poirier #include "cs-etm.h" 17a818c563SMathieu Poirier #include "../../perf.h" 18a818c563SMathieu Poirier #include "../../util/auxtrace.h" 19a818c563SMathieu Poirier #include "../../util/cpumap.h" 20a818c563SMathieu Poirier #include "../../util/evlist.h" 213becf452SMathieu Poirier #include "../../util/evsel.h" 22a818c563SMathieu Poirier #include "../../util/pmu.h" 23a818c563SMathieu Poirier #include "../../util/thread_map.h" 24a818c563SMathieu Poirier #include "../../util/cs-etm.h" 25a818c563SMathieu Poirier 26fa4e819bSMathieu Poirier #include <errno.h> 27a818c563SMathieu Poirier #include <stdlib.h> 287a8ef4c4SArnaldo Carvalho de Melo #include <sys/stat.h> 29a818c563SMathieu Poirier 30a818c563SMathieu Poirier struct cs_etm_recording { 31a818c563SMathieu Poirier struct auxtrace_record itr; 32a818c563SMathieu Poirier struct perf_pmu *cs_etm_pmu; 33a818c563SMathieu Poirier struct perf_evlist *evlist; 34a818c563SMathieu Poirier bool snapshot_mode; 35a818c563SMathieu Poirier size_t snapshot_size; 36a818c563SMathieu Poirier }; 37a818c563SMathieu Poirier 38*3399ad9aSMathieu Poirier static const char *metadata_etmv3_ro[CS_ETM_PRIV_MAX] = { 39*3399ad9aSMathieu Poirier [CS_ETM_ETMCCER] = "mgmt/etmccer", 40*3399ad9aSMathieu Poirier [CS_ETM_ETMIDR] = "mgmt/etmidr", 41*3399ad9aSMathieu Poirier }; 42*3399ad9aSMathieu Poirier 43*3399ad9aSMathieu Poirier static const char *metadata_etmv4_ro[CS_ETMV4_PRIV_MAX] = { 44*3399ad9aSMathieu Poirier [CS_ETMV4_TRCIDR0] = "trcidr/trcidr0", 45*3399ad9aSMathieu Poirier [CS_ETMV4_TRCIDR1] = "trcidr/trcidr1", 46*3399ad9aSMathieu Poirier [CS_ETMV4_TRCIDR2] = "trcidr/trcidr2", 47*3399ad9aSMathieu Poirier [CS_ETMV4_TRCIDR8] = "trcidr/trcidr8", 48*3399ad9aSMathieu Poirier [CS_ETMV4_TRCAUTHSTATUS] = "mgmt/trcauthstatus", 49*3399ad9aSMathieu Poirier }; 50*3399ad9aSMathieu Poirier 51a818c563SMathieu Poirier static bool cs_etm_is_etmv4(struct auxtrace_record *itr, int cpu); 52a818c563SMathieu Poirier 53*3399ad9aSMathieu Poirier static int cs_etm_set_context_id(struct auxtrace_record *itr, 54*3399ad9aSMathieu Poirier struct perf_evsel *evsel, int cpu) 55*3399ad9aSMathieu Poirier { 56*3399ad9aSMathieu Poirier struct cs_etm_recording *ptr; 57*3399ad9aSMathieu Poirier struct perf_pmu *cs_etm_pmu; 58*3399ad9aSMathieu Poirier char path[PATH_MAX]; 59*3399ad9aSMathieu Poirier int err = -EINVAL; 60*3399ad9aSMathieu Poirier u32 val; 61*3399ad9aSMathieu Poirier 62*3399ad9aSMathieu Poirier ptr = container_of(itr, struct cs_etm_recording, itr); 63*3399ad9aSMathieu Poirier cs_etm_pmu = ptr->cs_etm_pmu; 64*3399ad9aSMathieu Poirier 65*3399ad9aSMathieu Poirier if (!cs_etm_is_etmv4(itr, cpu)) 66*3399ad9aSMathieu Poirier goto out; 67*3399ad9aSMathieu Poirier 68*3399ad9aSMathieu Poirier /* Get a handle on TRCIRD2 */ 69*3399ad9aSMathieu Poirier snprintf(path, PATH_MAX, "cpu%d/%s", 70*3399ad9aSMathieu Poirier cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR2]); 71*3399ad9aSMathieu Poirier err = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val); 72*3399ad9aSMathieu Poirier 73*3399ad9aSMathieu Poirier /* There was a problem reading the file, bailing out */ 74*3399ad9aSMathieu Poirier if (err != 1) { 75*3399ad9aSMathieu Poirier pr_err("%s: can't read file %s\n", 76*3399ad9aSMathieu Poirier CORESIGHT_ETM_PMU_NAME, path); 77*3399ad9aSMathieu Poirier goto out; 78*3399ad9aSMathieu Poirier } 79*3399ad9aSMathieu Poirier 80*3399ad9aSMathieu Poirier /* 81*3399ad9aSMathieu Poirier * TRCIDR2.CIDSIZE, bit [9-5], indicates whether contextID tracing 82*3399ad9aSMathieu Poirier * is supported: 83*3399ad9aSMathieu Poirier * 0b00000 Context ID tracing is not supported. 84*3399ad9aSMathieu Poirier * 0b00100 Maximum of 32-bit Context ID size. 85*3399ad9aSMathieu Poirier * All other values are reserved. 86*3399ad9aSMathieu Poirier */ 87*3399ad9aSMathieu Poirier val = BMVAL(val, 5, 9); 88*3399ad9aSMathieu Poirier if (!val || val != 0x4) { 89*3399ad9aSMathieu Poirier err = -EINVAL; 90*3399ad9aSMathieu Poirier goto out; 91*3399ad9aSMathieu Poirier } 92*3399ad9aSMathieu Poirier 93*3399ad9aSMathieu Poirier /* All good, let the kernel know */ 94*3399ad9aSMathieu Poirier evsel->attr.config |= (1 << ETM_OPT_CTXTID); 95*3399ad9aSMathieu Poirier err = 0; 96*3399ad9aSMathieu Poirier 97*3399ad9aSMathieu Poirier out: 98*3399ad9aSMathieu Poirier 99*3399ad9aSMathieu Poirier return err; 100*3399ad9aSMathieu Poirier } 101*3399ad9aSMathieu Poirier 102*3399ad9aSMathieu Poirier static int cs_etm_set_option(struct auxtrace_record *itr, 103*3399ad9aSMathieu Poirier struct perf_evsel *evsel, u32 option) 104*3399ad9aSMathieu Poirier { 105*3399ad9aSMathieu Poirier int i, err = -EINVAL; 106*3399ad9aSMathieu Poirier struct cpu_map *event_cpus = evsel->evlist->cpus; 107*3399ad9aSMathieu Poirier struct cpu_map *online_cpus = cpu_map__new(NULL); 108*3399ad9aSMathieu Poirier 109*3399ad9aSMathieu Poirier /* Set option of each CPU we have */ 110*3399ad9aSMathieu Poirier for (i = 0; i < cpu__max_cpu(); i++) { 111*3399ad9aSMathieu Poirier if (!cpu_map__has(event_cpus, i) || 112*3399ad9aSMathieu Poirier !cpu_map__has(online_cpus, i)) 113*3399ad9aSMathieu Poirier continue; 114*3399ad9aSMathieu Poirier 115*3399ad9aSMathieu Poirier switch (option) { 116*3399ad9aSMathieu Poirier case ETM_OPT_CTXTID: 117*3399ad9aSMathieu Poirier err = cs_etm_set_context_id(itr, evsel, i); 118*3399ad9aSMathieu Poirier if (err) 119*3399ad9aSMathieu Poirier goto out; 120*3399ad9aSMathieu Poirier break; 121*3399ad9aSMathieu Poirier default: 122*3399ad9aSMathieu Poirier goto out; 123*3399ad9aSMathieu Poirier } 124*3399ad9aSMathieu Poirier } 125*3399ad9aSMathieu Poirier 126*3399ad9aSMathieu Poirier err = 0; 127*3399ad9aSMathieu Poirier out: 128*3399ad9aSMathieu Poirier cpu_map__put(online_cpus); 129*3399ad9aSMathieu Poirier return err; 130*3399ad9aSMathieu Poirier } 131*3399ad9aSMathieu Poirier 132a818c563SMathieu Poirier static int cs_etm_parse_snapshot_options(struct auxtrace_record *itr, 133a818c563SMathieu Poirier struct record_opts *opts, 134a818c563SMathieu Poirier const char *str) 135a818c563SMathieu Poirier { 136a818c563SMathieu Poirier struct cs_etm_recording *ptr = 137a818c563SMathieu Poirier container_of(itr, struct cs_etm_recording, itr); 138a818c563SMathieu Poirier unsigned long long snapshot_size = 0; 139a818c563SMathieu Poirier char *endptr; 140a818c563SMathieu Poirier 141a818c563SMathieu Poirier if (str) { 142a818c563SMathieu Poirier snapshot_size = strtoull(str, &endptr, 0); 143a818c563SMathieu Poirier if (*endptr || snapshot_size > SIZE_MAX) 144a818c563SMathieu Poirier return -1; 145a818c563SMathieu Poirier } 146a818c563SMathieu Poirier 147a818c563SMathieu Poirier opts->auxtrace_snapshot_mode = true; 148a818c563SMathieu Poirier opts->auxtrace_snapshot_size = snapshot_size; 149a818c563SMathieu Poirier ptr->snapshot_size = snapshot_size; 150a818c563SMathieu Poirier 151a818c563SMathieu Poirier return 0; 152a818c563SMathieu Poirier } 153a818c563SMathieu Poirier 154fa4e819bSMathieu Poirier static int cs_etm_set_sink_attr(struct perf_pmu *pmu, 155fa4e819bSMathieu Poirier struct perf_evsel *evsel) 156fa4e819bSMathieu Poirier { 157fa4e819bSMathieu Poirier char msg[BUFSIZ], path[PATH_MAX], *sink; 158fa4e819bSMathieu Poirier struct perf_evsel_config_term *term; 159fa4e819bSMathieu Poirier int ret = -EINVAL; 160fa4e819bSMathieu Poirier u32 hash; 161fa4e819bSMathieu Poirier 162fa4e819bSMathieu Poirier if (evsel->attr.config2 & GENMASK(31, 0)) 163fa4e819bSMathieu Poirier return 0; 164fa4e819bSMathieu Poirier 165fa4e819bSMathieu Poirier list_for_each_entry(term, &evsel->config_terms, list) { 166fa4e819bSMathieu Poirier if (term->type != PERF_EVSEL__CONFIG_TERM_DRV_CFG) 167fa4e819bSMathieu Poirier continue; 168fa4e819bSMathieu Poirier 169fa4e819bSMathieu Poirier sink = term->val.drv_cfg; 170fa4e819bSMathieu Poirier snprintf(path, PATH_MAX, "sinks/%s", sink); 171fa4e819bSMathieu Poirier 172fa4e819bSMathieu Poirier ret = perf_pmu__scan_file(pmu, path, "%x", &hash); 173fa4e819bSMathieu Poirier if (ret != 1) { 174fa4e819bSMathieu Poirier pr_err("failed to set sink \"%s\" on event %s with %d (%s)\n", 175fa4e819bSMathieu Poirier sink, perf_evsel__name(evsel), errno, 176fa4e819bSMathieu Poirier str_error_r(errno, msg, sizeof(msg))); 177fa4e819bSMathieu Poirier return ret; 178fa4e819bSMathieu Poirier } 179fa4e819bSMathieu Poirier 180fa4e819bSMathieu Poirier evsel->attr.config2 |= hash; 181fa4e819bSMathieu Poirier return 0; 182fa4e819bSMathieu Poirier } 183fa4e819bSMathieu Poirier 184fa4e819bSMathieu Poirier /* 185fa4e819bSMathieu Poirier * No sink was provided on the command line - for _now_ treat 186fa4e819bSMathieu Poirier * this as an error. 187fa4e819bSMathieu Poirier */ 188fa4e819bSMathieu Poirier return ret; 189fa4e819bSMathieu Poirier } 190fa4e819bSMathieu Poirier 191a818c563SMathieu Poirier static int cs_etm_recording_options(struct auxtrace_record *itr, 192a818c563SMathieu Poirier struct perf_evlist *evlist, 193a818c563SMathieu Poirier struct record_opts *opts) 194a818c563SMathieu Poirier { 195fa4e819bSMathieu Poirier int ret; 196a818c563SMathieu Poirier struct cs_etm_recording *ptr = 197a818c563SMathieu Poirier container_of(itr, struct cs_etm_recording, itr); 198a818c563SMathieu Poirier struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; 199a818c563SMathieu Poirier struct perf_evsel *evsel, *cs_etm_evsel = NULL; 200*3399ad9aSMathieu Poirier struct cpu_map *cpus = evlist->cpus; 201a818c563SMathieu Poirier bool privileged = (geteuid() == 0 || perf_event_paranoid() < 0); 202*3399ad9aSMathieu Poirier int err = 0; 203a818c563SMathieu Poirier 204a818c563SMathieu Poirier ptr->evlist = evlist; 205a818c563SMathieu Poirier ptr->snapshot_mode = opts->auxtrace_snapshot_mode; 206a818c563SMathieu Poirier 207a818c563SMathieu Poirier evlist__for_each_entry(evlist, evsel) { 208a818c563SMathieu Poirier if (evsel->attr.type == cs_etm_pmu->type) { 209a818c563SMathieu Poirier if (cs_etm_evsel) { 210a818c563SMathieu Poirier pr_err("There may be only one %s event\n", 211a818c563SMathieu Poirier CORESIGHT_ETM_PMU_NAME); 212a818c563SMathieu Poirier return -EINVAL; 213a818c563SMathieu Poirier } 214a818c563SMathieu Poirier evsel->attr.freq = 0; 215a818c563SMathieu Poirier evsel->attr.sample_period = 1; 216a818c563SMathieu Poirier cs_etm_evsel = evsel; 217a818c563SMathieu Poirier opts->full_auxtrace = true; 218a818c563SMathieu Poirier } 219a818c563SMathieu Poirier } 220a818c563SMathieu Poirier 221a818c563SMathieu Poirier /* no need to continue if at least one event of interest was found */ 222a818c563SMathieu Poirier if (!cs_etm_evsel) 223a818c563SMathieu Poirier return 0; 224a818c563SMathieu Poirier 225fa4e819bSMathieu Poirier ret = cs_etm_set_sink_attr(cs_etm_pmu, cs_etm_evsel); 226fa4e819bSMathieu Poirier if (ret) 227fa4e819bSMathieu Poirier return ret; 228fa4e819bSMathieu Poirier 229a818c563SMathieu Poirier if (opts->use_clockid) { 230a818c563SMathieu Poirier pr_err("Cannot use clockid (-k option) with %s\n", 231a818c563SMathieu Poirier CORESIGHT_ETM_PMU_NAME); 232a818c563SMathieu Poirier return -EINVAL; 233a818c563SMathieu Poirier } 234a818c563SMathieu Poirier 235a818c563SMathieu Poirier /* we are in snapshot mode */ 236a818c563SMathieu Poirier if (opts->auxtrace_snapshot_mode) { 237a818c563SMathieu Poirier /* 238a818c563SMathieu Poirier * No size were given to '-S' or '-m,', so go with 239a818c563SMathieu Poirier * the default 240a818c563SMathieu Poirier */ 241a818c563SMathieu Poirier if (!opts->auxtrace_snapshot_size && 242a818c563SMathieu Poirier !opts->auxtrace_mmap_pages) { 243a818c563SMathieu Poirier if (privileged) { 244a818c563SMathieu Poirier opts->auxtrace_mmap_pages = MiB(4) / page_size; 245a818c563SMathieu Poirier } else { 246a818c563SMathieu Poirier opts->auxtrace_mmap_pages = 247a818c563SMathieu Poirier KiB(128) / page_size; 248a818c563SMathieu Poirier if (opts->mmap_pages == UINT_MAX) 249a818c563SMathieu Poirier opts->mmap_pages = KiB(256) / page_size; 250a818c563SMathieu Poirier } 251a818c563SMathieu Poirier } else if (!opts->auxtrace_mmap_pages && !privileged && 252a818c563SMathieu Poirier opts->mmap_pages == UINT_MAX) { 253a818c563SMathieu Poirier opts->mmap_pages = KiB(256) / page_size; 254a818c563SMathieu Poirier } 255a818c563SMathieu Poirier 256a818c563SMathieu Poirier /* 257a818c563SMathieu Poirier * '-m,xyz' was specified but no snapshot size, so make the 258a818c563SMathieu Poirier * snapshot size as big as the auxtrace mmap area. 259a818c563SMathieu Poirier */ 260a818c563SMathieu Poirier if (!opts->auxtrace_snapshot_size) { 261a818c563SMathieu Poirier opts->auxtrace_snapshot_size = 262a818c563SMathieu Poirier opts->auxtrace_mmap_pages * (size_t)page_size; 263a818c563SMathieu Poirier } 264a818c563SMathieu Poirier 265a818c563SMathieu Poirier /* 266a818c563SMathieu Poirier * -Sxyz was specified but no auxtrace mmap area, so make the 267a818c563SMathieu Poirier * auxtrace mmap area big enough to fit the requested snapshot 268a818c563SMathieu Poirier * size. 269a818c563SMathieu Poirier */ 270a818c563SMathieu Poirier if (!opts->auxtrace_mmap_pages) { 271a818c563SMathieu Poirier size_t sz = opts->auxtrace_snapshot_size; 272a818c563SMathieu Poirier 273a818c563SMathieu Poirier sz = round_up(sz, page_size) / page_size; 274a818c563SMathieu Poirier opts->auxtrace_mmap_pages = roundup_pow_of_two(sz); 275a818c563SMathieu Poirier } 276a818c563SMathieu Poirier 277a818c563SMathieu Poirier /* Snapshost size can't be bigger than the auxtrace area */ 278a818c563SMathieu Poirier if (opts->auxtrace_snapshot_size > 279a818c563SMathieu Poirier opts->auxtrace_mmap_pages * (size_t)page_size) { 280a818c563SMathieu Poirier pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n", 281a818c563SMathieu Poirier opts->auxtrace_snapshot_size, 282a818c563SMathieu Poirier opts->auxtrace_mmap_pages * (size_t)page_size); 283a818c563SMathieu Poirier return -EINVAL; 284a818c563SMathieu Poirier } 285a818c563SMathieu Poirier 286a818c563SMathieu Poirier /* Something went wrong somewhere - this shouldn't happen */ 287a818c563SMathieu Poirier if (!opts->auxtrace_snapshot_size || 288a818c563SMathieu Poirier !opts->auxtrace_mmap_pages) { 289a818c563SMathieu Poirier pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n"); 290a818c563SMathieu Poirier return -EINVAL; 291a818c563SMathieu Poirier } 292a818c563SMathieu Poirier } 293a818c563SMathieu Poirier 294a818c563SMathieu Poirier /* We are in full trace mode but '-m,xyz' wasn't specified */ 295a818c563SMathieu Poirier if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) { 296a818c563SMathieu Poirier if (privileged) { 297a818c563SMathieu Poirier opts->auxtrace_mmap_pages = MiB(4) / page_size; 298a818c563SMathieu Poirier } else { 299a818c563SMathieu Poirier opts->auxtrace_mmap_pages = KiB(128) / page_size; 300a818c563SMathieu Poirier if (opts->mmap_pages == UINT_MAX) 301a818c563SMathieu Poirier opts->mmap_pages = KiB(256) / page_size; 302a818c563SMathieu Poirier } 303a818c563SMathieu Poirier 304a818c563SMathieu Poirier } 305a818c563SMathieu Poirier 306a818c563SMathieu Poirier /* Validate auxtrace_mmap_pages provided by user */ 307a818c563SMathieu Poirier if (opts->auxtrace_mmap_pages) { 308a818c563SMathieu Poirier unsigned int max_page = (KiB(128) / page_size); 309a818c563SMathieu Poirier size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size; 310a818c563SMathieu Poirier 311a818c563SMathieu Poirier if (!privileged && 312a818c563SMathieu Poirier opts->auxtrace_mmap_pages > max_page) { 313a818c563SMathieu Poirier opts->auxtrace_mmap_pages = max_page; 314a818c563SMathieu Poirier pr_err("auxtrace too big, truncating to %d\n", 315a818c563SMathieu Poirier max_page); 316a818c563SMathieu Poirier } 317a818c563SMathieu Poirier 318a818c563SMathieu Poirier if (!is_power_of_2(sz)) { 319a818c563SMathieu Poirier pr_err("Invalid mmap size for %s: must be a power of 2\n", 320a818c563SMathieu Poirier CORESIGHT_ETM_PMU_NAME); 321a818c563SMathieu Poirier return -EINVAL; 322a818c563SMathieu Poirier } 323a818c563SMathieu Poirier } 324a818c563SMathieu Poirier 325a818c563SMathieu Poirier if (opts->auxtrace_snapshot_mode) 326a818c563SMathieu Poirier pr_debug2("%s snapshot size: %zu\n", CORESIGHT_ETM_PMU_NAME, 327a818c563SMathieu Poirier opts->auxtrace_snapshot_size); 328a818c563SMathieu Poirier 329a818c563SMathieu Poirier /* 330a818c563SMathieu Poirier * To obtain the auxtrace buffer file descriptor, the auxtrace 331a818c563SMathieu Poirier * event must come first. 332a818c563SMathieu Poirier */ 333a818c563SMathieu Poirier perf_evlist__to_front(evlist, cs_etm_evsel); 3340c788d47SKim Phillips 335a818c563SMathieu Poirier /* 336a818c563SMathieu Poirier * In the case of per-cpu mmaps, we need the CPU on the 337*3399ad9aSMathieu Poirier * AUX event. We also need the contextID in order to be notified 338*3399ad9aSMathieu Poirier * when a context switch happened. 339a818c563SMathieu Poirier */ 340*3399ad9aSMathieu Poirier if (!cpu_map__empty(cpus)) { 341a818c563SMathieu Poirier perf_evsel__set_sample_bit(cs_etm_evsel, CPU); 342a818c563SMathieu Poirier 343*3399ad9aSMathieu Poirier err = cs_etm_set_option(itr, cs_etm_evsel, ETM_OPT_CTXTID); 344*3399ad9aSMathieu Poirier if (err) 345*3399ad9aSMathieu Poirier goto out; 346*3399ad9aSMathieu Poirier } 347*3399ad9aSMathieu Poirier 348a818c563SMathieu Poirier /* Add dummy event to keep tracking */ 349a818c563SMathieu Poirier if (opts->full_auxtrace) { 350a818c563SMathieu Poirier struct perf_evsel *tracking_evsel; 351a818c563SMathieu Poirier 352a818c563SMathieu Poirier err = parse_events(evlist, "dummy:u", NULL); 353a818c563SMathieu Poirier if (err) 354*3399ad9aSMathieu Poirier goto out; 355a818c563SMathieu Poirier 356a818c563SMathieu Poirier tracking_evsel = perf_evlist__last(evlist); 357a818c563SMathieu Poirier perf_evlist__set_tracking_event(evlist, tracking_evsel); 358a818c563SMathieu Poirier 359a818c563SMathieu Poirier tracking_evsel->attr.freq = 0; 360a818c563SMathieu Poirier tracking_evsel->attr.sample_period = 1; 361a818c563SMathieu Poirier 362a818c563SMathieu Poirier /* In per-cpu case, always need the time of mmap events etc */ 363a818c563SMathieu Poirier if (!cpu_map__empty(cpus)) 364a818c563SMathieu Poirier perf_evsel__set_sample_bit(tracking_evsel, TIME); 365a818c563SMathieu Poirier } 366a818c563SMathieu Poirier 367*3399ad9aSMathieu Poirier out: 368*3399ad9aSMathieu Poirier return err; 369a818c563SMathieu Poirier } 370a818c563SMathieu Poirier 371a818c563SMathieu Poirier static u64 cs_etm_get_config(struct auxtrace_record *itr) 372a818c563SMathieu Poirier { 373a818c563SMathieu Poirier u64 config = 0; 374a818c563SMathieu Poirier struct cs_etm_recording *ptr = 375a818c563SMathieu Poirier container_of(itr, struct cs_etm_recording, itr); 376a818c563SMathieu Poirier struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; 377a818c563SMathieu Poirier struct perf_evlist *evlist = ptr->evlist; 378a818c563SMathieu Poirier struct perf_evsel *evsel; 379a818c563SMathieu Poirier 380a818c563SMathieu Poirier evlist__for_each_entry(evlist, evsel) { 381a818c563SMathieu Poirier if (evsel->attr.type == cs_etm_pmu->type) { 382a818c563SMathieu Poirier /* 383a818c563SMathieu Poirier * Variable perf_event_attr::config is assigned to 384a818c563SMathieu Poirier * ETMv3/PTM. The bit fields have been made to match 385a818c563SMathieu Poirier * the ETMv3.5 ETRMCR register specification. See the 386a818c563SMathieu Poirier * PMU_FORMAT_ATTR() declarations in 387a818c563SMathieu Poirier * drivers/hwtracing/coresight/coresight-perf.c for 388a818c563SMathieu Poirier * details. 389a818c563SMathieu Poirier */ 390a818c563SMathieu Poirier config = evsel->attr.config; 391a818c563SMathieu Poirier break; 392a818c563SMathieu Poirier } 393a818c563SMathieu Poirier } 394a818c563SMathieu Poirier 395a818c563SMathieu Poirier return config; 396a818c563SMathieu Poirier } 397a818c563SMathieu Poirier 398df770ff0SMike Leach #ifndef BIT 399df770ff0SMike Leach #define BIT(N) (1UL << (N)) 400df770ff0SMike Leach #endif 401df770ff0SMike Leach 402df770ff0SMike Leach static u64 cs_etmv4_get_config(struct auxtrace_record *itr) 403df770ff0SMike Leach { 404df770ff0SMike Leach u64 config = 0; 405df770ff0SMike Leach u64 config_opts = 0; 406df770ff0SMike Leach 407df770ff0SMike Leach /* 408df770ff0SMike Leach * The perf event variable config bits represent both 409df770ff0SMike Leach * the command line options and register programming 410df770ff0SMike Leach * bits in ETMv3/PTM. For ETMv4 we must remap options 411df770ff0SMike Leach * to real bits 412df770ff0SMike Leach */ 413df770ff0SMike Leach config_opts = cs_etm_get_config(itr); 414df770ff0SMike Leach if (config_opts & BIT(ETM_OPT_CYCACC)) 415df770ff0SMike Leach config |= BIT(ETM4_CFG_BIT_CYCACC); 416*3399ad9aSMathieu Poirier if (config_opts & BIT(ETM_OPT_CTXTID)) 417*3399ad9aSMathieu Poirier config |= BIT(ETM4_CFG_BIT_CTXTID); 418df770ff0SMike Leach if (config_opts & BIT(ETM_OPT_TS)) 419df770ff0SMike Leach config |= BIT(ETM4_CFG_BIT_TS); 420df770ff0SMike Leach if (config_opts & BIT(ETM_OPT_RETSTK)) 421df770ff0SMike Leach config |= BIT(ETM4_CFG_BIT_RETSTK); 422df770ff0SMike Leach 423df770ff0SMike Leach return config; 424df770ff0SMike Leach } 425df770ff0SMike Leach 426a818c563SMathieu Poirier static size_t 427a818c563SMathieu Poirier cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused, 428a818c563SMathieu Poirier struct perf_evlist *evlist __maybe_unused) 429a818c563SMathieu Poirier { 430a818c563SMathieu Poirier int i; 431a818c563SMathieu Poirier int etmv3 = 0, etmv4 = 0; 432796bfaddSMathieu Poirier struct cpu_map *event_cpus = evlist->cpus; 433796bfaddSMathieu Poirier struct cpu_map *online_cpus = cpu_map__new(NULL); 434a818c563SMathieu Poirier 435a818c563SMathieu Poirier /* cpu map is not empty, we have specific CPUs to work with */ 436796bfaddSMathieu Poirier if (!cpu_map__empty(event_cpus)) { 437796bfaddSMathieu Poirier for (i = 0; i < cpu__max_cpu(); i++) { 438796bfaddSMathieu Poirier if (!cpu_map__has(event_cpus, i) || 439796bfaddSMathieu Poirier !cpu_map__has(online_cpus, i)) 440796bfaddSMathieu Poirier continue; 441796bfaddSMathieu Poirier 442796bfaddSMathieu Poirier if (cs_etm_is_etmv4(itr, i)) 443a818c563SMathieu Poirier etmv4++; 444a818c563SMathieu Poirier else 445a818c563SMathieu Poirier etmv3++; 446a818c563SMathieu Poirier } 447a818c563SMathieu Poirier } else { 448a818c563SMathieu Poirier /* get configuration for all CPUs in the system */ 449a818c563SMathieu Poirier for (i = 0; i < cpu__max_cpu(); i++) { 450796bfaddSMathieu Poirier if (!cpu_map__has(online_cpus, i)) 451796bfaddSMathieu Poirier continue; 452796bfaddSMathieu Poirier 453a818c563SMathieu Poirier if (cs_etm_is_etmv4(itr, i)) 454a818c563SMathieu Poirier etmv4++; 455a818c563SMathieu Poirier else 456a818c563SMathieu Poirier etmv3++; 457a818c563SMathieu Poirier } 458a818c563SMathieu Poirier } 459a818c563SMathieu Poirier 460796bfaddSMathieu Poirier cpu_map__put(online_cpus); 461796bfaddSMathieu Poirier 462a818c563SMathieu Poirier return (CS_ETM_HEADER_SIZE + 463a818c563SMathieu Poirier (etmv4 * CS_ETMV4_PRIV_SIZE) + 464a818c563SMathieu Poirier (etmv3 * CS_ETMV3_PRIV_SIZE)); 465a818c563SMathieu Poirier } 466a818c563SMathieu Poirier 467a818c563SMathieu Poirier static bool cs_etm_is_etmv4(struct auxtrace_record *itr, int cpu) 468a818c563SMathieu Poirier { 469a818c563SMathieu Poirier bool ret = false; 470a818c563SMathieu Poirier char path[PATH_MAX]; 471a818c563SMathieu Poirier int scan; 472a818c563SMathieu Poirier unsigned int val; 473a818c563SMathieu Poirier struct cs_etm_recording *ptr = 474a818c563SMathieu Poirier container_of(itr, struct cs_etm_recording, itr); 475a818c563SMathieu Poirier struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; 476a818c563SMathieu Poirier 477a818c563SMathieu Poirier /* Take any of the RO files for ETMv4 and see if it present */ 478a818c563SMathieu Poirier snprintf(path, PATH_MAX, "cpu%d/%s", 479a818c563SMathieu Poirier cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR0]); 480a818c563SMathieu Poirier scan = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val); 481a818c563SMathieu Poirier 482a818c563SMathieu Poirier /* The file was read successfully, we have a winner */ 483a818c563SMathieu Poirier if (scan == 1) 484a818c563SMathieu Poirier ret = true; 485a818c563SMathieu Poirier 486a818c563SMathieu Poirier return ret; 487a818c563SMathieu Poirier } 488a818c563SMathieu Poirier 489a818c563SMathieu Poirier static int cs_etm_get_ro(struct perf_pmu *pmu, int cpu, const char *path) 490a818c563SMathieu Poirier { 491a818c563SMathieu Poirier char pmu_path[PATH_MAX]; 492a818c563SMathieu Poirier int scan; 493a818c563SMathieu Poirier unsigned int val = 0; 494a818c563SMathieu Poirier 495a818c563SMathieu Poirier /* Get RO metadata from sysfs */ 496a818c563SMathieu Poirier snprintf(pmu_path, PATH_MAX, "cpu%d/%s", cpu, path); 497a818c563SMathieu Poirier 498a818c563SMathieu Poirier scan = perf_pmu__scan_file(pmu, pmu_path, "%x", &val); 499a818c563SMathieu Poirier if (scan != 1) 500a818c563SMathieu Poirier pr_err("%s: error reading: %s\n", __func__, pmu_path); 501a818c563SMathieu Poirier 502a818c563SMathieu Poirier return val; 503a818c563SMathieu Poirier } 504a818c563SMathieu Poirier 505a818c563SMathieu Poirier static void cs_etm_get_metadata(int cpu, u32 *offset, 506a818c563SMathieu Poirier struct auxtrace_record *itr, 507a818c563SMathieu Poirier struct auxtrace_info_event *info) 508a818c563SMathieu Poirier { 509a818c563SMathieu Poirier u32 increment; 510a818c563SMathieu Poirier u64 magic; 511a818c563SMathieu Poirier struct cs_etm_recording *ptr = 512a818c563SMathieu Poirier container_of(itr, struct cs_etm_recording, itr); 513a818c563SMathieu Poirier struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; 514a818c563SMathieu Poirier 515a818c563SMathieu Poirier /* first see what kind of tracer this cpu is affined to */ 516a818c563SMathieu Poirier if (cs_etm_is_etmv4(itr, cpu)) { 517a818c563SMathieu Poirier magic = __perf_cs_etmv4_magic; 518a818c563SMathieu Poirier /* Get trace configuration register */ 519a818c563SMathieu Poirier info->priv[*offset + CS_ETMV4_TRCCONFIGR] = 520df770ff0SMike Leach cs_etmv4_get_config(itr); 521a818c563SMathieu Poirier /* Get traceID from the framework */ 522a818c563SMathieu Poirier info->priv[*offset + CS_ETMV4_TRCTRACEIDR] = 523a818c563SMathieu Poirier coresight_get_trace_id(cpu); 524a818c563SMathieu Poirier /* Get read-only information from sysFS */ 525a818c563SMathieu Poirier info->priv[*offset + CS_ETMV4_TRCIDR0] = 526a818c563SMathieu Poirier cs_etm_get_ro(cs_etm_pmu, cpu, 527a818c563SMathieu Poirier metadata_etmv4_ro[CS_ETMV4_TRCIDR0]); 528a818c563SMathieu Poirier info->priv[*offset + CS_ETMV4_TRCIDR1] = 529a818c563SMathieu Poirier cs_etm_get_ro(cs_etm_pmu, cpu, 530a818c563SMathieu Poirier metadata_etmv4_ro[CS_ETMV4_TRCIDR1]); 531a818c563SMathieu Poirier info->priv[*offset + CS_ETMV4_TRCIDR2] = 532a818c563SMathieu Poirier cs_etm_get_ro(cs_etm_pmu, cpu, 533a818c563SMathieu Poirier metadata_etmv4_ro[CS_ETMV4_TRCIDR2]); 534a818c563SMathieu Poirier info->priv[*offset + CS_ETMV4_TRCIDR8] = 535a818c563SMathieu Poirier cs_etm_get_ro(cs_etm_pmu, cpu, 536a818c563SMathieu Poirier metadata_etmv4_ro[CS_ETMV4_TRCIDR8]); 537a818c563SMathieu Poirier info->priv[*offset + CS_ETMV4_TRCAUTHSTATUS] = 538a818c563SMathieu Poirier cs_etm_get_ro(cs_etm_pmu, cpu, 539a818c563SMathieu Poirier metadata_etmv4_ro 540a818c563SMathieu Poirier [CS_ETMV4_TRCAUTHSTATUS]); 541a818c563SMathieu Poirier 542a818c563SMathieu Poirier /* How much space was used */ 543a818c563SMathieu Poirier increment = CS_ETMV4_PRIV_MAX; 544a818c563SMathieu Poirier } else { 545a818c563SMathieu Poirier magic = __perf_cs_etmv3_magic; 546a818c563SMathieu Poirier /* Get configuration register */ 547a818c563SMathieu Poirier info->priv[*offset + CS_ETM_ETMCR] = cs_etm_get_config(itr); 548a818c563SMathieu Poirier /* Get traceID from the framework */ 549a818c563SMathieu Poirier info->priv[*offset + CS_ETM_ETMTRACEIDR] = 550a818c563SMathieu Poirier coresight_get_trace_id(cpu); 551a818c563SMathieu Poirier /* Get read-only information from sysFS */ 552a818c563SMathieu Poirier info->priv[*offset + CS_ETM_ETMCCER] = 553a818c563SMathieu Poirier cs_etm_get_ro(cs_etm_pmu, cpu, 554a818c563SMathieu Poirier metadata_etmv3_ro[CS_ETM_ETMCCER]); 555a818c563SMathieu Poirier info->priv[*offset + CS_ETM_ETMIDR] = 556a818c563SMathieu Poirier cs_etm_get_ro(cs_etm_pmu, cpu, 557a818c563SMathieu Poirier metadata_etmv3_ro[CS_ETM_ETMIDR]); 558a818c563SMathieu Poirier 559a818c563SMathieu Poirier /* How much space was used */ 560a818c563SMathieu Poirier increment = CS_ETM_PRIV_MAX; 561a818c563SMathieu Poirier } 562a818c563SMathieu Poirier 563a818c563SMathieu Poirier /* Build generic header portion */ 564a818c563SMathieu Poirier info->priv[*offset + CS_ETM_MAGIC] = magic; 565a818c563SMathieu Poirier info->priv[*offset + CS_ETM_CPU] = cpu; 566a818c563SMathieu Poirier /* Where the next CPU entry should start from */ 567a818c563SMathieu Poirier *offset += increment; 568a818c563SMathieu Poirier } 569a818c563SMathieu Poirier 570a818c563SMathieu Poirier static int cs_etm_info_fill(struct auxtrace_record *itr, 571a818c563SMathieu Poirier struct perf_session *session, 572a818c563SMathieu Poirier struct auxtrace_info_event *info, 573a818c563SMathieu Poirier size_t priv_size) 574a818c563SMathieu Poirier { 575a818c563SMathieu Poirier int i; 576a818c563SMathieu Poirier u32 offset; 577a818c563SMathieu Poirier u64 nr_cpu, type; 578796bfaddSMathieu Poirier struct cpu_map *cpu_map; 579796bfaddSMathieu Poirier struct cpu_map *event_cpus = session->evlist->cpus; 580796bfaddSMathieu Poirier struct cpu_map *online_cpus = cpu_map__new(NULL); 581a818c563SMathieu Poirier struct cs_etm_recording *ptr = 582a818c563SMathieu Poirier container_of(itr, struct cs_etm_recording, itr); 583a818c563SMathieu Poirier struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; 584a818c563SMathieu Poirier 585a818c563SMathieu Poirier if (priv_size != cs_etm_info_priv_size(itr, session->evlist)) 586a818c563SMathieu Poirier return -EINVAL; 587a818c563SMathieu Poirier 588a818c563SMathieu Poirier if (!session->evlist->nr_mmaps) 589a818c563SMathieu Poirier return -EINVAL; 590a818c563SMathieu Poirier 591796bfaddSMathieu Poirier /* If the cpu_map is empty all online CPUs are involved */ 592796bfaddSMathieu Poirier if (cpu_map__empty(event_cpus)) { 593796bfaddSMathieu Poirier cpu_map = online_cpus; 594796bfaddSMathieu Poirier } else { 595796bfaddSMathieu Poirier /* Make sure all specified CPUs are online */ 596796bfaddSMathieu Poirier for (i = 0; i < cpu_map__nr(event_cpus); i++) { 597796bfaddSMathieu Poirier if (cpu_map__has(event_cpus, i) && 598796bfaddSMathieu Poirier !cpu_map__has(online_cpus, i)) 599796bfaddSMathieu Poirier return -EINVAL; 600796bfaddSMathieu Poirier } 601796bfaddSMathieu Poirier 602796bfaddSMathieu Poirier cpu_map = event_cpus; 603796bfaddSMathieu Poirier } 604796bfaddSMathieu Poirier 605796bfaddSMathieu Poirier nr_cpu = cpu_map__nr(cpu_map); 606a818c563SMathieu Poirier /* Get PMU type as dynamically assigned by the core */ 607a818c563SMathieu Poirier type = cs_etm_pmu->type; 608a818c563SMathieu Poirier 609a818c563SMathieu Poirier /* First fill out the session header */ 610a818c563SMathieu Poirier info->type = PERF_AUXTRACE_CS_ETM; 611a818c563SMathieu Poirier info->priv[CS_HEADER_VERSION_0] = 0; 612a818c563SMathieu Poirier info->priv[CS_PMU_TYPE_CPUS] = type << 32; 613a818c563SMathieu Poirier info->priv[CS_PMU_TYPE_CPUS] |= nr_cpu; 614a818c563SMathieu Poirier info->priv[CS_ETM_SNAPSHOT] = ptr->snapshot_mode; 615a818c563SMathieu Poirier 616a818c563SMathieu Poirier offset = CS_ETM_SNAPSHOT + 1; 617a818c563SMathieu Poirier 618796bfaddSMathieu Poirier for (i = 0; i < cpu__max_cpu() && offset < priv_size; i++) 619796bfaddSMathieu Poirier if (cpu_map__has(cpu_map, i)) 620a818c563SMathieu Poirier cs_etm_get_metadata(i, &offset, itr, info); 621796bfaddSMathieu Poirier 622796bfaddSMathieu Poirier cpu_map__put(online_cpus); 623a818c563SMathieu Poirier 624a818c563SMathieu Poirier return 0; 625a818c563SMathieu Poirier } 626a818c563SMathieu Poirier 627a818c563SMathieu Poirier static int cs_etm_find_snapshot(struct auxtrace_record *itr __maybe_unused, 628a818c563SMathieu Poirier int idx, struct auxtrace_mmap *mm, 629a818c563SMathieu Poirier unsigned char *data __maybe_unused, 630a818c563SMathieu Poirier u64 *head, u64 *old) 631a818c563SMathieu Poirier { 632a818c563SMathieu Poirier pr_debug3("%s: mmap index %d old head %zu new head %zu size %zu\n", 633a818c563SMathieu Poirier __func__, idx, (size_t)*old, (size_t)*head, mm->len); 634a818c563SMathieu Poirier 635a818c563SMathieu Poirier *old = *head; 636a818c563SMathieu Poirier *head += mm->len; 637a818c563SMathieu Poirier 638a818c563SMathieu Poirier return 0; 639a818c563SMathieu Poirier } 640a818c563SMathieu Poirier 641a818c563SMathieu Poirier static int cs_etm_snapshot_start(struct auxtrace_record *itr) 642a818c563SMathieu Poirier { 643a818c563SMathieu Poirier struct cs_etm_recording *ptr = 644a818c563SMathieu Poirier container_of(itr, struct cs_etm_recording, itr); 645a818c563SMathieu Poirier struct perf_evsel *evsel; 646a818c563SMathieu Poirier 647a818c563SMathieu Poirier evlist__for_each_entry(ptr->evlist, evsel) { 648a818c563SMathieu Poirier if (evsel->attr.type == ptr->cs_etm_pmu->type) 649a818c563SMathieu Poirier return perf_evsel__disable(evsel); 650a818c563SMathieu Poirier } 651a818c563SMathieu Poirier return -EINVAL; 652a818c563SMathieu Poirier } 653a818c563SMathieu Poirier 654a818c563SMathieu Poirier static int cs_etm_snapshot_finish(struct auxtrace_record *itr) 655a818c563SMathieu Poirier { 656a818c563SMathieu Poirier struct cs_etm_recording *ptr = 657a818c563SMathieu Poirier container_of(itr, struct cs_etm_recording, itr); 658a818c563SMathieu Poirier struct perf_evsel *evsel; 659a818c563SMathieu Poirier 660a818c563SMathieu Poirier evlist__for_each_entry(ptr->evlist, evsel) { 661a818c563SMathieu Poirier if (evsel->attr.type == ptr->cs_etm_pmu->type) 662a818c563SMathieu Poirier return perf_evsel__enable(evsel); 663a818c563SMathieu Poirier } 664a818c563SMathieu Poirier return -EINVAL; 665a818c563SMathieu Poirier } 666a818c563SMathieu Poirier 667a818c563SMathieu Poirier static u64 cs_etm_reference(struct auxtrace_record *itr __maybe_unused) 668a818c563SMathieu Poirier { 669a818c563SMathieu Poirier return (((u64) rand() << 0) & 0x00000000FFFFFFFFull) | 670a818c563SMathieu Poirier (((u64) rand() << 32) & 0xFFFFFFFF00000000ull); 671a818c563SMathieu Poirier } 672a818c563SMathieu Poirier 673a818c563SMathieu Poirier static void cs_etm_recording_free(struct auxtrace_record *itr) 674a818c563SMathieu Poirier { 675a818c563SMathieu Poirier struct cs_etm_recording *ptr = 676a818c563SMathieu Poirier container_of(itr, struct cs_etm_recording, itr); 677a818c563SMathieu Poirier free(ptr); 678a818c563SMathieu Poirier } 679a818c563SMathieu Poirier 680a818c563SMathieu Poirier static int cs_etm_read_finish(struct auxtrace_record *itr, int idx) 681a818c563SMathieu Poirier { 682a818c563SMathieu Poirier struct cs_etm_recording *ptr = 683a818c563SMathieu Poirier container_of(itr, struct cs_etm_recording, itr); 684a818c563SMathieu Poirier struct perf_evsel *evsel; 685a818c563SMathieu Poirier 686a818c563SMathieu Poirier evlist__for_each_entry(ptr->evlist, evsel) { 687a818c563SMathieu Poirier if (evsel->attr.type == ptr->cs_etm_pmu->type) 688a818c563SMathieu Poirier return perf_evlist__enable_event_idx(ptr->evlist, 689a818c563SMathieu Poirier evsel, idx); 690a818c563SMathieu Poirier } 691a818c563SMathieu Poirier 692a818c563SMathieu Poirier return -EINVAL; 693a818c563SMathieu Poirier } 694a818c563SMathieu Poirier 695a818c563SMathieu Poirier struct auxtrace_record *cs_etm_record_init(int *err) 696a818c563SMathieu Poirier { 697a818c563SMathieu Poirier struct perf_pmu *cs_etm_pmu; 698a818c563SMathieu Poirier struct cs_etm_recording *ptr; 699a818c563SMathieu Poirier 700a818c563SMathieu Poirier cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME); 701a818c563SMathieu Poirier 702a818c563SMathieu Poirier if (!cs_etm_pmu) { 703a818c563SMathieu Poirier *err = -EINVAL; 704a818c563SMathieu Poirier goto out; 705a818c563SMathieu Poirier } 706a818c563SMathieu Poirier 707a818c563SMathieu Poirier ptr = zalloc(sizeof(struct cs_etm_recording)); 708a818c563SMathieu Poirier if (!ptr) { 709a818c563SMathieu Poirier *err = -ENOMEM; 710a818c563SMathieu Poirier goto out; 711a818c563SMathieu Poirier } 712a818c563SMathieu Poirier 713a818c563SMathieu Poirier ptr->cs_etm_pmu = cs_etm_pmu; 714a818c563SMathieu Poirier ptr->itr.parse_snapshot_options = cs_etm_parse_snapshot_options; 715a818c563SMathieu Poirier ptr->itr.recording_options = cs_etm_recording_options; 716a818c563SMathieu Poirier ptr->itr.info_priv_size = cs_etm_info_priv_size; 717a818c563SMathieu Poirier ptr->itr.info_fill = cs_etm_info_fill; 718a818c563SMathieu Poirier ptr->itr.find_snapshot = cs_etm_find_snapshot; 719a818c563SMathieu Poirier ptr->itr.snapshot_start = cs_etm_snapshot_start; 720a818c563SMathieu Poirier ptr->itr.snapshot_finish = cs_etm_snapshot_finish; 721a818c563SMathieu Poirier ptr->itr.reference = cs_etm_reference; 722a818c563SMathieu Poirier ptr->itr.free = cs_etm_recording_free; 723a818c563SMathieu Poirier ptr->itr.read_finish = cs_etm_read_finish; 724a818c563SMathieu Poirier 725a818c563SMathieu Poirier *err = 0; 726a818c563SMathieu Poirier return &ptr->itr; 727a818c563SMathieu Poirier out: 728a818c563SMathieu Poirier return NULL; 729a818c563SMathieu Poirier } 730