Page MenuHomeFreeBSD

D40728.diff
No OneTemporary

D40728.diff

Index: usr.sbin/Makefile.aarch64
===================================================================
--- usr.sbin/Makefile.aarch64
+++ usr.sbin/Makefile.aarch64
@@ -1,6 +1,7 @@
.if ${MK_ACPI} != "no"
SUBDIR+= acpi
.endif
+SUBDIR+= hwt
.if ${MK_BHYVE} != "no"
SUBDIR+= bhyve
SUBDIR+= bhyvectl
Index: usr.sbin/Makefile.amd64
===================================================================
--- usr.sbin/Makefile.amd64
+++ usr.sbin/Makefile.amd64
@@ -20,3 +20,4 @@
SUBDIR+= mptable
SUBDIR+= spkrtest
SUBDIR+= zzz
+SUBDIR+= hwt
Index: usr.sbin/hwt/Makefile
===================================================================
--- /dev/null
+++ usr.sbin/hwt/Makefile
@@ -0,0 +1,24 @@
+# $FreeBSD$
+
+PROG_CXX= hwt
+MAN=
+
+CFLAGS+= -I${SRCTOP}/lib/libpmcstat -I${SRCTOP}/sys
+
+LIBADD= elf pmcstat xo util
+
+SRCS= hwt.c \
+ hwt_elf.c \
+ hwt_process.c \
+ hwt_record.c \
+ libpmcstat_stubs.c
+
+.if ${MACHINE_CPUARCH} == "aarch64"
+SRCS+= hwt_coresight.c hwt_spe.c
+LIBADD+= opencsd
+.elif ${MACHINE_CPUARCH} == "amd64"
+SRCS+= hwt_pt.c
+LIBADD+= ipt
+.endif
+
+.include <bsd.prog.mk>
Index: usr.sbin/hwt/hwt.h
===================================================================
--- /dev/null
+++ usr.sbin/hwt/hwt.h
@@ -0,0 +1,140 @@
+/*-
+ * Copyright (c) 2023 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _HWTVAR_H_
+#define _HWTVAR_H_
+
+#define TC_MAX_ADDR_RANGES 16
+
+struct trace_context;
+struct hwt_exec_img;
+struct pmcstat_image;
+
+struct trace_dev_methods {
+ int (*init)(struct trace_context *tc);
+ void (*shutdown)(struct trace_context *tc);
+
+ /*
+ * Called whenever a tracing buffer needs to mapped into hwt.
+ */
+ int (*mmap)(struct trace_context *tc,
+ struct hwt_record_user_entry *entry);
+ int (*set_config)(struct trace_context *tc);
+
+ /*
+ * Called whenever the traced process's address space layout changes.
+ */
+ int (
+ *image_load_cb)(struct trace_context *tc, struct hwt_exec_img *img);
+
+ /*
+ * Trace data processing methods.
+ *
+ * An hwt backend must specify one of the two processing methods.
+ * If both methods are specified, the 'process_buffer' method will
+ * always take precedence.
+ *
+ * If present, the 'process' method should implement a complete
+ * replacement for hwt's default processing loop. This means that it is
+ * responsible for fetching trace data, fetching records, and processing
+ * the data on its own.
+ *
+ * The 'process_buffer' method gets invoked from hwt's default
+ * processing loop whenever an HWT_BUFFER_RECORD is received and is
+ * tasked with decoding tracing buffer data. This is suitable for
+ * backends whose tracing hardware issues interrupts when the tracing
+ * buffer is ready to be processed.
+ */
+ int (*process)(struct trace_context *tc);
+ int (*process_buffer)(struct trace_context *tc, int buf_id, int curpage,
+ vm_offset_t offset);
+};
+
+struct trace_dev {
+ const char *name;
+ const char *fullname;
+ struct trace_dev_methods *methods;
+};
+
+struct trace_context {
+ struct trace_dev *trace_dev;
+ struct pmcstat_process *pp;
+ struct hwt_record_user_entry *records;
+ void *base;
+ size_t bufsize;
+ int attach;
+ int pid;
+ cpuset_t cpu_map;
+ int fd;
+ int thr_fd;
+ int terminate;
+ int kqueue_fd;
+
+ int thread_id;
+ int ident;
+
+ /* Address range filtering. */
+ int suspend_on_mmap;
+ char *image_name;
+ char *func_name;
+ uintptr_t addr_ranges[TC_MAX_ADDR_RANGES * 2];
+ int nranges;
+
+ /* Backend-specific config. */
+ void *config;
+ int flag_format;
+
+ /* Raw trace. */
+ int raw;
+ FILE *raw_f;
+ uint64_t bytes_written;
+
+ /* Trace file. */
+ char *filename;
+
+ int mode;
+ const char *fs_root;
+};
+
+struct pmcstat_process *hwt_process_alloc(void);
+int hwt_process_create(int *sockpair, char **cmd, char **env, int *pid0);
+int hwt_process_start(int *sockpair);
+int hwt_record_fetch(struct trace_context *tc, int *nrecords, int wait);
+void hwt_procexit(pid_t pid, int status);
+void hwt_sleep(int msec);
+int hwt_find_sym(struct trace_context *tc);
+struct pmcstat_symbol *hwt_sym_lookup(const struct trace_context *tc,
+ uint64_t ip, struct pmcstat_image **img, uint64_t *newpc0);
+int hwt_start_tracing(struct trace_context *tc);
+int hwt_stop_tracing(struct trace_context *tc);
+int hwt_mmap_received(struct trace_context *tc,
+ struct hwt_record_user_entry *entry);
+int hwt_ncpu(void);
+
+#endif /* !_HWTVAR_H_ */
Index: usr.sbin/hwt/hwt.c
===================================================================
--- /dev/null
+++ usr.sbin/hwt/hwt.c
@@ -0,0 +1,789 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/errno.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/cpuset.h>
+#include <sys/hwt.h>
+#include <sys/stat.h>
+#include <sys/user.h>
+
+#include <assert.h>
+#include <err.h>
+#include <sysexits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <libutil.h>
+
+#include "libpmcstat_stubs.h"
+#include <libpmcstat.h>
+#include <libgen.h>
+#include <libxo/xo.h>
+
+#include "hwt.h"
+#include "hwt_elf.h"
+
+#if defined(__aarch64__)
+#include "hwt_coresight.h"
+#include "hwt_spe.h"
+#endif
+
+#if defined(__amd64__)
+#include "hwt_pt.h"
+#endif
+
+
+#define HWT_DEBUG
+#undef HWT_DEBUG
+
+#ifdef HWT_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+#define PARENTSOCKET 0
+#define CHILDSOCKET 1
+#define NSOCKPAIRFD 2
+
+static struct trace_context tcs;
+
+static struct trace_dev trace_devs[] = {
+#if defined(__aarch64__)
+ { "coresight", "ARM Coresight", &cs_methods },
+ { "spe", "ARM Statistical Profiling Extension", &spe_methods },
+#endif
+#if defined(__amd64__)
+ { "pt", "Intel PT", &pt_methods},
+#endif
+ { NULL, NULL, NULL }
+};
+
+void
+hwt_sleep(int msec)
+{
+ struct timespec time_to_sleep;
+
+ time_to_sleep.tv_sec = 0;
+ time_to_sleep.tv_nsec = msec * 1000000;
+
+ nanosleep(&time_to_sleep, &time_to_sleep);
+}
+
+void
+hwt_procexit(pid_t pid, int exit_status __unused)
+{
+ struct trace_context *tc;
+
+ tc = &tcs;
+
+ if (tc->pid == pid)
+ tc->terminate = 1;
+}
+
+static int
+hwt_unsuspend_proc(struct trace_context *tc)
+{
+ struct hwt_wakeup w;
+ int error;
+
+ error = ioctl(tc->thr_fd, HWT_IOC_WAKEUP, &w);
+
+ return (error);
+}
+
+int
+hwt_mmap_received(struct trace_context *tc,
+ struct hwt_record_user_entry *entry __unused)
+{
+ int error;
+
+ assert(tc->mode == HWT_MODE_THREAD);
+
+ if (!tc->suspend_on_mmap)
+ return (0);
+
+ if (tc->func_name == NULL)
+ return (0);
+
+ error = hwt_find_sym(tc);
+ if (error != 0) {
+ hwt_unsuspend_proc(tc);
+ return (-1);
+ }
+
+ tc->suspend_on_mmap = 0;
+
+ error = tc->trace_dev->methods->set_config(tc);
+ if (error)
+ return (-2);
+
+ error = hwt_start_tracing(tc);
+ if (error)
+ return (-2);
+
+ printf("%s: tracing started\n", __func__);
+
+ hwt_unsuspend_proc(tc);
+
+ return (0);
+}
+
+static int
+hwt_ctx_alloc(struct trace_context *tc)
+{
+ struct hwt_alloc al;
+ cpuset_t cpu_map;
+ int error = 0;
+
+ if (tc->trace_dev->methods->init != NULL){
+ error = tc->trace_dev->methods->init(tc);
+ if (error)
+ return (error);
+ }
+
+ CPU_ZERO(&cpu_map);
+
+ memset(&al, 0, sizeof(struct hwt_alloc));
+
+ al.mode = tc->mode;
+ if (tc->mode == HWT_MODE_THREAD)
+ al.pid = tc->pid;
+ else {
+ al.cpu_map = &tc->cpu_map;
+ al.cpusetsize = sizeof(cpuset_t);
+ }
+
+ al.bufsize = tc->bufsize;
+ al.backend_name = tc->trace_dev->name;
+ al.ident = &tc->ident;
+ al.kqueue_fd = tc->kqueue_fd;
+
+ error = ioctl(tc->fd, HWT_IOC_ALLOC, &al);
+
+ return (error);
+}
+
+int
+hwt_ncpu(void)
+{
+ int ncpu;
+
+ ncpu = sysconf(_SC_NPROCESSORS_CONF);
+
+ return (ncpu);
+}
+
+static int
+hwt_get_records(struct trace_context *tc, uint32_t *nrec, int wait)
+{
+ int nrecords;
+ int error;
+
+ error = hwt_record_fetch(tc, &nrecords, wait);
+ if (error)
+ return (error);
+
+ *nrec = nrecords;
+
+ return (0);
+}
+
+struct pmcstat_symbol *
+hwt_sym_lookup(const struct trace_context *tc, uint64_t ip,
+ struct pmcstat_image **img, uint64_t *newpc0)
+{
+ struct pmcstat_image *image;
+ struct pmcstat_symbol *sym;
+ struct pmcstat_pcmap *map;
+ uint64_t newpc;
+
+ map = pmcstat_process_find_map(tc->pp, ip);
+ if (map != NULL) {
+ image = map->ppm_image;
+ newpc = ip -
+ ((unsigned long)map->ppm_lowpc +
+ (image->pi_vaddr - image->pi_start));
+ sym = pmcstat_symbol_search(image, newpc); /* Could be NULL. */
+ newpc += image->pi_vaddr;
+
+ *img = image;
+ *newpc0 = newpc;
+
+ return (sym);
+ } else
+ *img = NULL;
+
+ return (NULL);
+}
+
+int
+hwt_find_sym(struct trace_context *tc)
+{
+ struct pmcstat_symbol *sym;
+ uintptr_t addr_start;
+ uintptr_t addr_end;
+
+ sym = pmcstat_symbol_search_by_name(tc->pp, tc->image_name,
+ tc->func_name, &addr_start, &addr_end);
+ if (sym) {
+ printf("sym found, start end %lx %lx\n", (uint64_t)addr_start,
+ (uint64_t)addr_end);
+ tc->addr_ranges[tc->nranges] = addr_start;
+ tc->addr_ranges[tc->nranges + 1] = addr_end;
+ tc->nranges += 1;
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+int
+hwt_start_tracing(struct trace_context *tc)
+{
+ struct hwt_start s;
+ int error;
+
+ error = ioctl(tc->thr_fd, HWT_IOC_START, &s);
+
+ return (error);
+}
+
+int
+hwt_stop_tracing(struct trace_context *tc)
+{
+ struct hwt_stop s;
+ int error;
+
+ error = ioctl(tc->thr_fd, HWT_IOC_STOP, &s);
+
+ return (error);
+}
+
+static void
+usage(void)
+{
+
+ errx(EX_USAGE,
+ "hwt [-s cpu_id] [-c devname] [-b bufsize] [-t id] [-g]"
+ " [-r] [-w file] [-i name]"
+ " [-f name] [path to executable]\n"
+ "\t -s\tcpu_id\t\tCPU (kernel) mode.\n"
+ "\t -c\tname\t\tName of tracing device, e.g. 'coresight'.\n"
+ "\t -b\tbufsize\t\tSize of trace buffer (per each thread/cpu)\n"
+ "\t\t\t\tin bytes. Must be a multiple of page size\n"
+ "\t\t\t\te.g. 4096.\n"
+ "\t -t\tid\t\tThread index of application passed to decoder.\n"
+ "\t -r\t\t\tRaw flag. Do not decode results.\n"
+ "\t -w\tfilename\tStore results into file.\n"
+#if defined(__aarch64__)
+ "\t -g\t\t\tFormat flag.\n"
+#endif
+ "\t -i\tname\t\tFilter by dynamic library, executable name,\n"
+ "\t\t\t\tkernel module name or 'kernel'.\n"
+ "\t -f\tname\t\tFilter by function name."
+ );
+}
+
+static int
+hwt_process_loop(struct trace_context *tc)
+{
+ int status;
+ int nrec, error;
+
+ xo_open_container("trace");
+ xo_open_list("entries");
+
+ printf("Decoder started. Press ctrl+c to stop.\n");
+
+ while (1) {
+ waitpid(tc->pid, &status, WNOHANG);
+ if (WIFEXITED(status)) {
+ tc->terminate = 1;
+ }
+ if (!tc->terminate) {
+ printf("%s: waiting for new records\n", __func__);
+ error = hwt_record_fetch(tc, &nrec, 1);
+ if (error != 0 || nrec == 0)
+ break;
+ }
+ if (errno == EINTR || tc->terminate) {
+ printf("%s: tracing terminated - exiting\n", __func__);
+ /* Fetch any remaining records */
+ hwt_record_fetch(tc, &nrec, 0);
+ if (tc->trace_dev->methods->shutdown != NULL)
+ tc->trace_dev->methods->shutdown(tc);
+ return (0);
+ }
+ }
+
+ xo_close_list("file");
+ xo_close_container("wc");
+ if (xo_finish() < 0)
+ xo_err(EXIT_FAILURE, "stdout");
+
+ return (0);
+}
+
+static int
+hwt_process(struct trace_context *tc)
+{
+ int error;
+
+ if (tc->trace_dev->methods->process_buffer != NULL) {
+ error = hwt_process_loop(tc);
+ if (error) {
+ printf("Can't process data, error %d.\n", error);
+ return (error);
+ }
+ } else {
+ if (tc->trace_dev->methods->process == NULL)
+ errx(EX_SOFTWARE,
+ "Backend has no data processing methods specified\n");
+ error = tc->trace_dev->methods->process(tc);
+ if (error) {
+ printf("Can't process data, error %d.\n", error);
+ return (error);
+ }
+ }
+
+ return (0);
+}
+
+static int
+hwt_mode_cpu(struct trace_context *tc)
+{
+ uint32_t nrec;
+ int error;
+
+ if (strcmp(tc->trace_dev->name, "coresight") == 0 &&
+ (tc->image_name == NULL || tc->func_name == NULL))
+ errx(EX_USAGE, "IP range filtering must be setup for CPU"
+ " tracing");
+
+ error = hwt_ctx_alloc(tc);
+ if (error) {
+ printf("%s: failed to alloc cpu-mode ctx, error %d errno %d\n",
+ __func__, error, errno);
+ if (errno == EPERM)
+ printf("Permission denied");
+ else if (errno == EINVAL)
+ printf("Invalid argument: buffer size is not a multiple"
+ " of page size, or is too small/large");
+ printf("\n");
+ return (error);
+ }
+
+ error = tc->trace_dev->methods->mmap(tc, NULL);
+ if (error) {
+ printf("%s: cant map memory, error %d\n", __func__, error);
+ return (error);
+ }
+
+ tc->pp->pp_pid = -1;
+
+ error = hwt_get_records(tc, &nrec, 0);
+ if (error != 0)
+ return (error);
+
+ printf("Received %d kernel mappings\n", nrec);
+
+ if(tc->image_name || tc->func_name) {
+ error = hwt_find_sym(tc);
+ if (error)
+ errx(EX_USAGE, "could not find symbol");
+ }
+
+ error = tc->trace_dev->methods->set_config(tc);
+ if (error != 0)
+ errx(EX_DATAERR, "can't set config");
+
+ error = hwt_start_tracing(tc);
+ if (error)
+ errx(EX_SOFTWARE, "failed to start tracing, error %d\n", error);
+
+ return (hwt_process(tc));
+}
+
+static int
+hwt_new_proc(struct trace_context *tc, int *sockpair, char **cmd, char **env,
+ uint32_t *nlibs0)
+{
+ struct stat st;
+ uint32_t nlibs;
+ int error;
+
+ error = stat(*cmd, &st);
+ if (error) {
+ printf("Could not find target executable"
+ " error %d.\n", error);
+ return (error);
+ }
+
+ error = hwt_elf_count_libs(*cmd, &nlibs);
+ if (error != 0) {
+ printf("Could not count libs in the executable provided.\n");
+ return (error);
+ }
+
+ nlibs += 1; /* add binary itself. */
+
+ dprintf("cmd is %s, nlibs %d\n", *cmd, nlibs);
+
+ error = hwt_process_create(sockpair, cmd, env, &tc->pid);
+ if (error != 0)
+ return (error);
+
+ printf("%s: process pid %d created\n", __func__, tc->pid);
+
+ *nlibs0 = nlibs;
+
+ return (0);
+}
+
+static int
+hwt_get_vmmap(struct trace_context *tc)
+{
+ struct kinfo_vmentry *vmmap, *kve;
+ int cnt;
+ int i, j;
+
+ pmcstat_interned_string path;
+ struct pmcstat_image *image;
+ struct pmc_plugins plugins;
+ struct pmcstat_args args;
+ unsigned long addr;
+
+ memset(&plugins, 0, sizeof(struct pmc_plugins));
+ memset(&args, 0, sizeof(struct pmcstat_args));
+ args.pa_fsroot = "/";
+
+ vmmap = kinfo_getvmmap(tc->pid, &cnt);
+ if (vmmap == NULL)
+ return (ENXIO);
+
+ printf("vmmap cnt %d\n", cnt);
+
+ for (i = 0, j = 0; i < cnt; i++) {
+ kve = &vmmap[i];
+ if ((kve->kve_protection & KVME_PROT_EXEC) == 0)
+ continue;
+ if (*kve->kve_path == '\0')
+ continue;
+
+ path = pmcstat_string_intern(kve->kve_path);
+ image = pmcstat_image_from_path(path, 0, &args, &plugins);
+ if (image == NULL)
+ continue;
+
+ if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN)
+ pmcstat_image_determine_type(image, &args);
+
+ addr = (unsigned long)kve->kve_start & ~1;
+ addr -= (image->pi_start - image->pi_vaddr);
+ pmcstat_image_link(tc->pp, image, addr);
+
+ printf(" lib #%d: path %s addr %lx\n", j++,
+ kve->kve_path, (unsigned long)kve->kve_start);
+ }
+
+ return (0);
+}
+
+static int
+hwt_mode_thread(struct trace_context *tc, char **cmd, char **env)
+{
+ uint32_t tot_rec;
+ uint32_t nrec;
+ uint32_t nlibs;
+ int sockpair[NSOCKPAIRFD];
+ int error;
+
+ nlibs = 0;
+
+ if (tc->attach == 0) {
+ error = hwt_new_proc(tc, sockpair, cmd, env, &nlibs);
+ if (error)
+ return (error);
+
+ if (tc->func_name != NULL)
+ tc->suspend_on_mmap = 1;
+ }
+
+ tc->pp->pp_pid = tc->pid;
+
+ error = hwt_ctx_alloc(tc);
+ if (error) {
+ printf("%s: failed to alloc thread-mode ctx "
+ "error %d errno %d %s\n",
+ __func__, error, errno, strerror(errno));
+ if (errno == EPERM)
+ printf("Permission denied");
+ else if (errno == EINVAL)
+ printf("Invalid argument: buffer size is not a multiple"
+ " of page size, or is too small/large");
+ printf("\n");
+ return (error);
+ }
+ error = tc->trace_dev->methods->mmap(tc, NULL);
+ if (error) {
+ printf("%s: failed to map trace buffer for first thread, %d\n",
+ __func__, error);
+ return (error);
+ }
+
+ error = tc->trace_dev->methods->set_config(tc);
+ if (error != 0)
+ errx(EX_DATAERR, "can't set config, errno %d", errno);
+
+ if (tc->attach)
+ hwt_get_vmmap(tc);
+
+ if (tc->attach || tc->suspend_on_mmap == 0) {
+ /* No address range filtering. Start tracing immediately. */
+ error = hwt_start_tracing(tc);
+ if (error)
+ errx(EX_SOFTWARE, "failed to start tracing, error %d\n",
+ error);
+ }
+
+ if (tc->attach == 0) {
+ error = hwt_process_start(sockpair);
+ if (error != 0)
+ return (error);
+ }
+
+ printf("Expect at least %d records.\n", nlibs);
+
+ /*
+ * Ensure we got expected amount of mmap/interp records so that
+ * mapping tables constructed before we do symbol lookup.
+ *
+ * Also receive any records about threads that were created in the
+ * meantime.
+ */
+ tot_rec = 0;
+ do {
+ error = hwt_get_records(tc, &nrec, 0);
+ if (error != 0 || nrec == 0)
+ break;
+ tot_rec += nrec;
+ hwt_sleep(10);
+ } while (1);
+
+ if (tot_rec < nlibs) {
+ errx(EX_DATAERR,
+ "Failed to receive expected amount of HWT records.");
+ }
+
+ return (hwt_process(tc));
+}
+
+static int
+hwt_get_cpumask(const char *arg, cpuset_t *cpumask)
+{
+ const char *start;
+ int cpu_id;
+ char *end;
+
+ CPU_ZERO(cpumask);
+
+ start = arg;
+
+ while (*start) {
+ cpu_id = strtol(start, &end, 0);
+ if (cpu_id < 0)
+ return (-1);
+
+ if (end == start)
+ return (-2);
+
+ CPU_SET(cpu_id, cpumask);
+
+ start = end + strspn(end, ", \t");
+ };
+
+ return (0);
+}
+
+int
+main(int argc, char **argv, char **env)
+{
+ struct trace_context *tc;
+ char *trace_dev_name;
+ int error;
+ int option;
+ int found;
+ int thread_id_specified;
+ int i;
+
+ tc = &tcs;
+
+ memset(tc, 0, sizeof(struct trace_context));
+
+ /* Defaults */
+ tc->bufsize = 128 * 1024 * 1024;
+
+ /* First available is default trace device. */
+ tc->trace_dev = &trace_devs[0];
+ if (tc->trace_dev->name == NULL) {
+ printf("No trace devices available\n");
+ return (1);
+ }
+
+ tc->mode = HWT_MODE_THREAD;
+ tc->fs_root = "/";
+ tc->thread_id = 0;
+ tc->attach = 0;
+ thread_id_specified = 0;
+
+ argc = xo_parse_args(argc, argv);
+ if (argc < 0)
+ exit(EXIT_FAILURE);
+
+ while ((option = getopt(argc, argv, "P:R:gs:hc:b:rw:t:i:f:")) != -1)
+ switch (option) {
+ case 'P':
+ tc->attach = 1;
+ tc->pid = atol(optarg);
+ break;
+ case 's':
+ tc->mode = HWT_MODE_CPU;
+ hwt_get_cpumask(optarg, &tc->cpu_map);
+ break;
+ case 'R':
+ tc->fs_root = optarg;
+ break;
+ case 'c':
+ trace_dev_name = strdup(optarg);
+ found = 0;
+ for (i = 0; trace_devs[i].name != NULL; i++) {
+ if (strcmp(trace_devs[i].name,
+ trace_dev_name) == 0) {
+ tc->trace_dev = &trace_devs[i];
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ printf("Trace device \"%s\" not available.\n",
+ trace_dev_name);
+ return (ENOENT);
+ }
+ break;
+ case 'b':
+ tc->bufsize = atol(optarg);
+ break;
+ case 'r':
+ /* Do not decode trace. */
+ tc->raw = 1;
+ break;
+ case 'w':
+ /* Store trace into a file. */
+ tc->filename = strdup(optarg);
+ break;
+ case 'i':
+ /*
+ * Name of dynamic lib or main executable for IP
+ * address range filtering.
+ */
+ tc->image_name = strdup(basename(optarg));
+ if (*tc->image_name == '.' || *tc->image_name == '/')
+ err(EX_USAGE,
+ "Invalid executable path provided");
+ break;
+ case 'f':
+ /* Name of the func to trace. */
+ tc->func_name = strdup(optarg);
+ break;
+ case 't':
+ tc->thread_id = atoi(optarg);
+ thread_id_specified = 1;
+ break;
+ case 'g':
+ tc->flag_format = 1;
+ break;
+ case 'h':
+ usage();
+ break;
+ default:
+ break;
+ }
+
+ if (tc->mode == HWT_MODE_CPU && thread_id_specified) {
+ printf("Thread ID to decode used in THREAD mode only.\n");
+ exit(1);
+ }
+
+ if (tc->raw != 0 && tc->filename == NULL) {
+ printf("Filename must be specified for the raw data.\n");
+ exit(1);
+ }
+
+ if ((tc->image_name == NULL && tc->func_name != NULL) ||
+ (tc->image_name != NULL && tc->func_name == NULL))
+ errx(EX_USAGE, "For address range tracing specify both image "
+ "and func, or none of them.");
+
+ tc->fd = open("/dev/hwt", O_RDWR);
+ if (tc->fd < 0) {
+ printf("Can't open /dev/hwt\n");
+ return (-1);
+ }
+
+ tc->pp = hwt_process_alloc();
+ tc->pp->pp_isactive = 1;
+
+ argc += optind;
+ argv += optind;
+
+ if (tc->mode == HWT_MODE_THREAD) {
+ if (*argv == NULL && tc->attach == 0)
+ usage();
+ error = hwt_mode_thread(tc, argv, env);
+ } else {
+ if (*argv != NULL)
+ usage();
+ error = hwt_mode_cpu(tc);
+ }
+
+ close(tc->fd);
+
+ return (error);
+}
Index: usr.sbin/hwt/hwt_coresight.h
===================================================================
--- /dev/null
+++ usr.sbin/hwt/hwt_coresight.h
@@ -0,0 +1,284 @@
+/*-
+ * Copyright (c) 2023 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _HWT_CORESIGHT_H_
+#define _HWT_CORESIGHT_H_
+
+#define TRCPRGCTLR 0x004 /* Trace Programming Control Register */
+#define TRCPRGCTLR_EN (1 << 0) /* Trace unit enable bit */
+#define TRCPROCSELR 0x008 /* Trace PE Select Control Register */
+#define TRCSTATR 0x00C /* Trace Trace Status Register */
+#define TRCSTATR_PMSTABLE (1 << 1) /* The programmers' model is stable. */
+#define TRCSTATR_IDLE (1 << 0) /* The trace unit is idle. */
+#define TRCCONFIGR 0x010 /* Trace Trace Configuration Register */
+#define TRCCONFIGR_DV (1 << 17) /* Data value tracing is enabled when INSTP0 is not 0b00 */
+#define TRCCONFIGR_DA (1 << 16) /* Data address tracing is enabled when INSTP0 is not 0b00. */
+#define TRCCONFIGR_VMIDOPT (1 << 15) /* Control bit to configure the Virtual context identifier value */
+#define TRCCONFIGR_QE_S 13 /* Q element enable field */
+#define TRCCONFIGR_QE_M (0x3 << TRCCONFIGR_QE_S)
+#define TRCCONFIGR_RS (1 << 12) /* Return stack enable bit */
+#define TRCCONFIGR_TS (1 << 11) /* Global timestamp tracing is enabled. */
+#define TRCCONFIGR_COND_S 8 /* Conditional instruction tracing bit. */
+#define TRCCONFIGR_COND_M (0x7 << TRCCONFIGR_COND_S)
+#define TRCCONFIGR_COND_DIS 0
+#define TRCCONFIGR_COND_LDR (1 << TRCCONFIGR_COND_S) /* Conditional load instructions are traced. */
+#define TRCCONFIGR_COND_STR (2 << TRCCONFIGR_COND_S) /* Conditional store instructions are traced. */
+#define TRCCONFIGR_COND_LDRSTR (3 << TRCCONFIGR_COND_S) /* Conditional load and store instructions are traced. */
+#define TRCCONFIGR_COND_ALL (7 << TRCCONFIGR_COND_S) /* All conditional instructions are traced. */
+#define TRCCONFIGR_VMID (1 << 7) /* Virtual context identifier tracing is enabled. */
+#define TRCCONFIGR_CID (1 << 6) /* Context ID tracing is enabled. */
+#define TRCCONFIGR_CCI (1 << 4) /* Cycle counting in the instruction trace is enabled. */
+#define TRCCONFIGR_BB (1 << 3) /* Branch broadcast mode is enabled. */
+#define TRCCONFIGR_INSTP0_S 1 /* Instruction P0 field. */
+#define TRCCONFIGR_INSTP0_M (0x3 << TRCCONFIGR_INSTP0_S)
+#define TRCCONFIGR_INSTP0_NONE 0 /* Do not trace load and store instructions as P0 instructions. */
+#define TRCCONFIGR_INSTP0_LDR (1 << TRCCONFIGR_INSTP0_S) /* Trace load instructions as P0 instructions. */
+#define TRCCONFIGR_INSTP0_STR (2 << TRCCONFIGR_INSTP0_S) /* Trace store instructions as P0 instructions. */
+#define TRCCONFIGR_INSTP0_LDRSTR (3 << TRCCONFIGR_INSTP0_S) /* Trace load and store instructions as P0 instr. */
+#define TRCAUXCTLR 0x018 /* Trace Auxiliary Control Register */
+#define TRCEVENTCTL0R 0x020 /* Trace Event Control 0 Register */
+#define TRCEVENTCTL1R 0x024 /* Trace Event Control 1 Register */
+#define TRCSTALLCTLR 0x02C /* Trace Stall Control Register */
+#define TRCTSCTLR 0x030 /* Trace Global Timestamp Control Register */
+#define TRCSYNCPR 0x034 /* Trace Synchronization Period Register */
+#define TRCSYNCPR_PERIOD_S 0
+#define TRCSYNCPR_PERIOD_M 0x1f
+#define TRCSYNCPR_1K (10 << TRCSYNCPR_PERIOD_S)
+#define TRCSYNCPR_2K (11 << TRCSYNCPR_PERIOD_S)
+#define TRCSYNCPR_4K (12 << TRCSYNCPR_PERIOD_S)
+#define TRCCCCTLR 0x038 /* Trace Cycle Count Control Register */
+#define TRCBBCTLR 0x03C /* Trace Branch Broadcast Control Register */
+#define TRCTRACEIDR 0x040 /* Trace Trace ID Register */
+#define TRCQCTLR 0x044 /* Trace Q Element Control Register */
+#define TRCQCTLR_MODE_INC (1 << 8) /* Include mode. */
+#define TRCVICTLR 0x080 /* Trace ViewInst Main Control Register */
+#define TRCVICTLR_SSSTATUS (1 << 9) /* The start/stop logic is in the started state. */
+#define TRCVICTLR_EXLEVEL_NS_S 20
+#define TRCVICTLR_EXLEVEL_NS_M (0xf << TRCVICTLR_EXLEVEL_NS_S)
+#define TRCVICTLR_EXLEVEL_NS(n) (0x1 << ((n) + TRCVICTLR_EXLEVEL_NS_S))
+#define TRCVICTLR_EXLEVEL_S_S 16
+#define TRCVICTLR_EXLEVEL_S_M (0xf << TRCVICTLR_EXLEVEL_S_S)
+#define TRCVICTLR_EXLEVEL_S(n) (0x1 << ((n) + TRCVICTLR_EXLEVEL_S_S))
+#define EVENT_SEL_S 0
+#define EVENT_SEL_M (0x1f << EVENT_SEL_S)
+#define TRCVIIECTLR 0x084 /* Trace ViewInst Include/Exclude Control Register */
+#define TRCVIIECTLR_INCLUDE_S 0
+#define TRCVISSCTLR 0x088 /* Trace ViewInst Start/Stop Control Register */
+#define TRCVIPCSSCTLR 0x08C /* Trace ViewInst Start/Stop PE Comparator Control Register */
+#define TRCVDCTLR 0x0A0 /* Trace ViewData Main Control Register */
+#define TRCVDCTLR_TRCEXDATA (1 << 12) /* Exception and exception return data transfers are traced */
+#define TRCVDCTLR_TBI (1 << 11) /* The trace unit assigns bits[63:56] to have the same value as bits[63:56] of the data address. */
+#define TRCVDCTLR_PCREL (1 << 10) /* The trace unit does not trace the address or value portions of PC-relative transfers. */
+#define TRCVDCTLR_SPREL_S 8
+#define TRCVDCTLR_SPREL_M (0x3 << TRCVDCTLR_SPREL_S)
+#define TRCVDCTLR_EVENT_S 0
+#define TRCVDCTLR_EVENT_M (0xff << TRCVDCTLR_EVENT_S)
+#define TRCVDSACCTLR 0x0A4 /* Trace ViewData Include/Exclude Single Address Comparator Control Register */
+#define TRCVDARCCTLR 0x0A8 /* Trace ViewData Include/Exclude Address Range Comparator Control Register */
+#define TRCSEQEVR(n) (0x100 + (n) * 0x4) /* Trace Sequencer State Transition Control Register [n=0-2] */
+#define TRCSEQRSTEVR 0x118 /* Trace Sequencer Reset Control Register */
+#define TRCSEQSTR 0x11C /* Trace Sequencer State Register */
+#define TRCEXTINSELR 0x120 /* Trace External Input Select Register */
+#define TRCCNTRLDVR(n) (0x140 + (n) * 0x4) /* 32 Trace Counter Reload Value Register [n=0-3] */
+#define TRCCNTCTLR(n) (0x150 + (n) * 0x4) /* 32 Trace Counter Control Register [n=0-3] */
+#define TRCCNTVR(n) (0x160 + (n) * 0x4) /* 32 Trace Counter Value Register [n=0-3] */
+#define TRCIMSPEC(n) (0x1C0 + (n) * 0x4) /* Trace IMPLEMENTATION DEFINED register [n=0-7] */
+
+#define TRCIDR0(n) (0x1E0 + 0x4 * (n))
+#define TRCIDR8(n) (0x180 + 0x4 * (n))
+#define TRCIDR(n) ((n > 7) ? TRCIDR8(n) : TRCIDR0(n))
+#define TRCIDR1_TRCARCHMAJ_S 8
+#define TRCIDR1_TRCARCHMAJ_M (0xf << TRCIDR1_TRCARCHMAJ_S)
+#define TRCIDR1_TRCARCHMIN_S 4
+#define TRCIDR1_TRCARCHMIN_M (0xf << TRCIDR1_TRCARCHMIN_S)
+
+#define TRCRSCTLR(n) (0x200 + (n) * 0x4) /* Trace Resource Selection Control Register [n=2-31] */
+#define TRCSSCCR(n) (0x280 + (n) * 0x4) /* Trace Single-shot Comparator Control Register [n=0-7] */
+#define TRCSSCSR(n) (0x2A0 + (n) * 0x4) /* Trace Single-shot Comparator Status Register [n=0-7] */
+#define TRCSSPCICR(n) (0x2C0 + (n) * 0x4) /* Trace Single-shot PE Comparator Input Control [n=0-7] */
+#define TRCOSLAR 0x300 /* Management OS Lock Access Register */
+#define TRCOSLSR 0x304 /* Management OS Lock Status Register */
+#define TRCPDCR 0x310 /* Management PowerDown Control Register */
+#define TRCPDSR 0x314 /* Management PowerDown Status Register */
+#define TRCACVR(n) (0x400 + (n) * 0x8) /* Trace Address Comparator Value Register [n=0-15] */
+#define TRCACATR(n) (0x480 + (n) * 0x8) /* Trace Address Comparator Access Type Register [n=0-15] */
+#define TRCACATR_DTBM (1 << 21)
+#define TRCACATR_DATARANGE (1 << 20)
+#define TRCACATR_DATASIZE_S 18
+#define TRCACATR_DATASIZE_M (0x3 << TRCACATR_DATASIZE_S)
+#define TRCACATR_DATASIZE_B (0x0 << TRCACATR_DATASIZE_S)
+#define TRCACATR_DATASIZE_HW (0x1 << TRCACATR_DATASIZE_S)
+#define TRCACATR_DATASIZE_W (0x2 << TRCACATR_DATASIZE_S)
+#define TRCACATR_DATASIZE_DW (0x3 << TRCACATR_DATASIZE_S)
+#define TRCACATR_DATAMATCH_S 16
+#define TRCACATR_DATAMATCH_M (0x3 << TRCACATR_DATAMATCH_S)
+#define TRCACATR_EXLEVEL_S_S 8
+#define TRCACATR_EXLEVEL_S_M (0xf << TRCACATR_EXLEVEL_S_S)
+#define TRCACATR_EXLEVEL_S(n) (0x1 << ((n) + TRCACATR_EXLEVEL_S_S))
+#define TRCACATR_EXLEVEL_NS_S 12
+#define TRCACATR_EXLEVEL_NS_M (0xf << TRCACATR_EXLEVEL_NS_S)
+#define TRCACATR_EXLEVEL_NS(n) (0x1 << ((n) + TRCACATR_EXLEVEL_NS_S))
+#define TRCDVCVR(n) (0x500 + (n) * 0x8) /* Trace Data Value Comparator Value Register [n=0-7] */
+#define TRCDVCMR(n) (0x580 + (n) * 0x8) /* Trace Data Value Comparator Mask Register [n=0-7] */
+#define TRCCIDCVR(n) (0x600 + (n) * 0x8) /* Trace Context ID Comparator Value Register [n=0-7] */
+#define TRCVMIDCVR(n) (0x640 + (n) * 0x8) /* Trace Virtual context identifier Comparator Value [n=0-7] */
+#define TRCCIDCCTLR0 0x680 /* Trace Context ID Comparator Control Register 0 */
+#define TRCCIDCCTLR1 0x684 /* Trace Context ID Comparator Control Register 1 */
+#define TRCVMIDCCTLR0 0x688 /* Trace Virtual context identifier Comparator Control Register 0 */
+#define TRCVMIDCCTLR1 0x68C /* Trace Virtual context identifier Comparator Control Register 1 */
+#define TRCITCTRL 0xF00 /* Management Integration Mode Control register */
+#define TRCCLAIMSET 0xFA0 /* Trace Claim Tag Set register */
+#define TRCCLAIMCLR 0xFA4 /* Trace Claim Tag Clear register */
+#define TRCDEVAFF0 0xFA8 /* Management Device Affinity register 0 */
+#define TRCDEVAFF1 0xFAC /* Management Device Affinity register 1 */
+#define TRCLAR 0xFB0 /* Management Software Lock Access Register */
+#define TRCLSR 0xFB4 /* Management Software Lock Status Register */
+#define TRCAUTHSTATUS 0xFB8 /* Management Authentication Status register */
+#define TRCDEVARCH 0xFBC /* Management Device Architecture register */
+#define TRCDEVID 0xFC8 /* Management Device ID register */
+#define TRCDEVTYPE 0xFCC /* Management Device Type register */
+#define TRCPIDR4 0xFD0 /* Management Peripheral ID4 Register */
+#define TRCPIDR(n) (0xFE0 + (n) * 0x4) /* Management Peripheral IDn Register [n=0-3] */
+#define TRCPIDR567(n) (0xFD4 + ((n) - 5) * 0x4) /* Management Peripheral ID5 to Peripheral ID7 Registers */
+#define TRCCIDR(n) (0xFF0 + (n) * 0x4) /* Management Component IDn Register [n=0-4] */
+
+/* ETMv4 resources */
+#define ETM_MAX_NR_PE 8
+#define ETMv4_MAX_CNTR 4
+#define ETM_MAX_SEQ_STATES 4
+#define ETM_MAX_EXT_INP_SEL 4
+#define ETM_MAX_EXT_INP 256
+#define ETM_MAX_EXT_OUT 4
+#define ETM_MAX_SINGLE_ADDR_CMP 16
+#define ETM_MAX_ADDR_RANGE_CMP (ETM_MAX_SINGLE_ADDR_CMP / 2)
+#define ETM_MAX_DATA_VAL_CMP 8
+#define ETMv4_MAX_CTXID_CMP 8
+#define ETM_MAX_VMID_CMP 8
+#define ETM_MAX_PE_CMP 8
+#define ETM_MAX_RES_SEL 32
+#define ETM_MAX_SS_CMP 8
+
+/**
+ * struct etmv4_config - configuration information related to an ETMv4
+ * @mode: Controls various modes supported by this ETM.
+ * @pe_sel: Controls which PE to trace.
+ * @cfg: Controls the tracing options.
+ * @eventctrl0: Controls the tracing of arbitrary events.
+ * @eventctrl1: Controls the behavior of the events that @event_ctrl0 selects.
+ * @stallctl: If functionality that prevents trace unit buffer overflows
+ * is available.
+ * @ts_ctrl: Controls the insertion of global timestamps in the
+ * trace streams.
+ * @syncfreq: Controls how often trace synchronization requests occur.
+ * the TRCCCCTLR register.
+ * @ccctlr: Sets the threshold value for cycle counting.
+ * @vinst_ctrl: Controls instruction trace filtering.
+ * @viiectlr: Set or read, the address range comparators.
+ * @vissctlr: Set, or read, the single address comparators that control the
+ * ViewInst start-stop logic.
+ * @vipcssctlr: Set, or read, which PE comparator inputs can control the
+ * ViewInst start-stop logic.
+ * @seq_idx: Sequencor index selector.
+ * @seq_ctrl: Control for the sequencer state transition control register.
+ * @seq_rst: Moves the sequencer to state 0 when a programmed event occurs.
+ * @seq_state: Set, or read the sequencer state.
+ * @cntr_idx: Counter index seletor.
+ * @cntrldvr: Sets or returns the reload count value for a counter.
+ * @cntr_ctrl: Controls the operation of a counter.
+ * @cntr_val: Sets or returns the value for a counter.
+ * @res_idx: Resource index selector.
+ * @res_ctrl: Controls the selection of the resources in the trace unit.
+ * @ss_idx: Single-shot index selector.
+ * @ss_ctrl: Controls the corresponding single-shot comparator resource.
+ * @ss_status: The status of the corresponding single-shot comparator.
+ * @ss_pe_cmp: Selects the PE comparator inputs for Single-shot control.
+ * @addr_idx: Address comparator index selector.
+ * @addr_val: Value for address comparator.
+ * @addr_acc: Address comparator access type.
+ * @addr_type: Current status of the comparator register.
+ * @ctxid_idx: Context ID index selector.
+ * @ctxid_pid: Value of the context ID comparator.
+ * @ctxid_mask0:Context ID comparator mask for comparator 0-3.
+ * @ctxid_mask1:Context ID comparator mask for comparator 4-7.
+ * @vmid_idx: VM ID index selector.
+ * @vmid_val: Value of the VM ID comparator.
+ * @vmid_mask0: VM ID comparator mask for comparator 0-3.
+ * @vmid_mask1: VM ID comparator mask for comparator 4-7.
+ * @ext_inp: External input selection.
+ * @s_ex_level: Secure ELs where tracing is supported.
+ */
+
+struct etmv4_config {
+ uint32_t mode;
+ uint32_t pe_sel;
+ uint32_t cfg;
+ uint32_t eventctrl0;
+ uint32_t eventctrl1;
+ uint32_t stall_ctrl;
+ uint32_t ts_ctrl;
+ uint32_t syncfreq;
+ uint32_t ccctlr;
+ uint32_t bb_ctrl;
+ uint32_t vinst_ctrl;
+ uint32_t viiectlr;
+ uint32_t vissctlr;
+ uint32_t vipcssctlr;
+ uint8_t seq_idx;
+ uint32_t seq_ctrl[ETM_MAX_SEQ_STATES];
+ uint32_t seq_rst;
+ uint32_t seq_state;
+ uint8_t cntr_idx;
+ uint32_t cntrldvr[ETMv4_MAX_CNTR];
+ uint32_t cntr_ctrl[ETMv4_MAX_CNTR];
+ uint32_t cntr_val[ETMv4_MAX_CNTR];
+ uint8_t res_idx;
+ uint32_t res_ctrl[ETM_MAX_RES_SEL];
+ uint8_t ss_idx;
+ uint32_t ss_ctrl[ETM_MAX_SS_CMP];
+ uint32_t ss_status[ETM_MAX_SS_CMP];
+ uint32_t ss_pe_cmp[ETM_MAX_SS_CMP];
+ uint8_t addr_idx;
+ uint64_t addr_val[ETM_MAX_SINGLE_ADDR_CMP];
+ uint64_t addr_acc[ETM_MAX_SINGLE_ADDR_CMP];
+ uint8_t addr_type[ETM_MAX_SINGLE_ADDR_CMP];
+ uint8_t ctxid_idx;
+ uint64_t ctxid_pid[ETMv4_MAX_CTXID_CMP];
+ uint32_t ctxid_mask0;
+ uint32_t ctxid_mask1;
+ uint8_t vmid_idx;
+ uint64_t vmid_val[ETM_MAX_VMID_CMP];
+ uint32_t vmid_mask0;
+ uint32_t vmid_mask1;
+ uint32_t ext_inp;
+ uint8_t s_ex_level;
+};
+
+extern struct trace_dev_methods cs_methods;
+
+#endif /* !_HWT_CORESIGHT_H_ */
Index: usr.sbin/hwt/hwt_coresight.c
===================================================================
--- /dev/null
+++ usr.sbin/hwt/hwt_coresight.c
@@ -0,0 +1,865 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* ARM CoreSight tracing unit. */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/errno.h>
+#include <sys/cpuset.h>
+#include <sys/hwt.h>
+#include <sys/wait.h>
+#include <sys/sysctl.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include <opencsd/c_api/ocsd_c_api_types.h>
+#include <opencsd/c_api/opencsd_c_api.h>
+
+#include "hwt.h"
+#include "hwt_coresight.h"
+
+#include "libpmcstat_stubs.h"
+#include <libpmcstat.h>
+#include <libxo/xo.h>
+
+#define HWT_CORESIGHT_DEBUG
+#undef HWT_CORESIGHT_DEBUG
+
+#ifdef HWT_CORESIGHT_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+static int cs_flags = 0;
+#define FLAG_FORMAT (1 << 0)
+#define FLAG_FRAME_RAW_UNPACKED (1 << 1)
+#define FLAG_FRAME_RAW_PACKED (1 << 2)
+
+#define PACKET_STR_LEN 1024
+static char packet_str[PACKET_STR_LEN];
+
+struct cs_decoder {
+ dcd_tree_handle_t dcdtree_handle;
+ struct trace_context *tc;
+ int dp_ret;
+ int cpu_id;
+ FILE *out;
+ xo_handle_t *xop;
+};
+
+static int
+hwt_coresight_mmap(struct trace_context *tc,
+ struct hwt_record_user_entry *entry __unused)
+{
+ char filename[32];
+ int tid;
+
+ /* Coresight maps memory only from the first CPU */
+ if (tc->mode == HWT_MODE_CPU)
+ tid = CPU_FFS(&tc->cpu_map) - 1;
+ else /* HWT_MODE_THREAD */
+ tid = 0;
+
+ sprintf(filename, "/dev/hwt_%d_%d", tc->ident, tid);
+
+ tc->thr_fd = open(filename, O_RDONLY);
+ if (tc->thr_fd < 0) {
+ printf("Can't open %s\n", filename);
+ return (-1);
+ }
+
+ tc->base = mmap(NULL, tc->bufsize, PROT_READ, MAP_SHARED, tc->thr_fd,
+ 0);
+ if (tc->base == MAP_FAILED) {
+ printf("mmap failed: err %d\n", errno);
+ return (-1);
+ }
+
+ printf("%s: tc->base %p\n", __func__, tc->base);
+
+ return (0);
+}
+
+static ocsd_err_t
+attach_raw_printers(dcd_tree_handle_t dcd_tree_h)
+{
+ ocsd_err_t err;
+ int flags;
+
+ flags = 0;
+ err = OCSD_OK;
+
+ if (cs_flags & FLAG_FRAME_RAW_UNPACKED)
+ flags |= OCSD_DFRMTR_UNPACKED_RAW_OUT;
+
+ if (cs_flags & FLAG_FRAME_RAW_PACKED)
+ flags |= OCSD_DFRMTR_PACKED_RAW_OUT;
+
+ if (flags)
+ err = ocsd_dt_set_raw_frame_printer(dcd_tree_h, flags);
+
+ return err;
+}
+
+static int
+print_data_array(const uint8_t *p_array, const int array_size,
+ char *p_buffer, int buf_size)
+{
+ int bytes_processed;
+ int chars_printed;
+
+ chars_printed = 0;
+ p_buffer[0] = 0;
+
+ if (buf_size > 9) {
+ strcat(p_buffer, "[ ");
+ chars_printed += 2;
+
+ for (bytes_processed = 0; bytes_processed < array_size;
+ bytes_processed++) {
+ sprintf(p_buffer + chars_printed, "0x%02X ",
+ p_array[bytes_processed]);
+ chars_printed += 5;
+ if ((chars_printed + 5) > buf_size)
+ break;
+ }
+
+ strcat(p_buffer, "];");
+ chars_printed += 2;
+ } else if (buf_size >= 4) {
+ sprintf(p_buffer, "[];");
+ chars_printed += 3;
+ }
+
+ return (chars_printed);
+}
+
+static void
+packet_monitor(void *context __unused,
+ const ocsd_datapath_op_t op,
+ const ocsd_trc_index_t index_sop,
+ const void *p_packet_in,
+ const uint32_t size,
+ const uint8_t *p_data)
+{
+ int offset;
+
+ offset = 0;
+
+ switch (op) {
+ case OCSD_OP_DATA:
+ sprintf(packet_str, "Idx:%" OCSD_TRC_IDX_STR ";", index_sop);
+ offset = strlen(packet_str);
+ offset += print_data_array(p_data, size, packet_str + offset,
+ PACKET_STR_LEN - offset);
+
+ /*
+ * Got a packet -- convert to string and use the libraries'
+ * message output to print to file and stdoout
+ */
+
+ if (ocsd_pkt_str(OCSD_PROTOCOL_ETMV4I, p_packet_in,
+ packet_str + offset, PACKET_STR_LEN - offset) == OCSD_OK) {
+ /* add in <CR> */
+ if (strlen(packet_str) == PACKET_STR_LEN - 1)/*maxlen*/
+ packet_str[PACKET_STR_LEN - 2] = '\n';
+ else
+ strcat(packet_str,"\n");
+
+ /* print it using the library output logger. */
+ ocsd_def_errlog_msgout(packet_str);
+ }
+ break;
+
+ case OCSD_OP_EOT:
+ sprintf(packet_str,"**** END OF TRACE ****\n");
+ ocsd_def_errlog_msgout(packet_str);
+ break;
+ default:
+ printf("%s: unknown op %d\n", __func__, op);
+ break;
+ }
+}
+
+static ocsd_err_t
+create_test_memory_acc(dcd_tree_handle_t handle, struct trace_context *tc)
+{
+ ocsd_vaddr_t address;
+ uint8_t *p_mem_buffer;
+ uint32_t mem_length;
+#ifdef HWT_CORESIGHT_DEBUG
+ uint64_t *t;
+#endif
+ int ret;
+
+ dprintf("%s\n", __func__);
+
+ address = (ocsd_vaddr_t)tc->base;
+
+#ifdef HWT_CORESIGHT_DEBUG
+ t = (uint64_t *)tc->base;
+ printf("%lx %lx %lx %lx\n", t[0], t[1], t[2], t[3]);
+#endif
+
+ p_mem_buffer = (uint8_t *)tc->base;
+ mem_length = tc->bufsize;
+
+ ret = ocsd_dt_add_buffer_mem_acc(handle, address,
+ OCSD_MEM_SPACE_ANY, p_mem_buffer, mem_length);
+ if (ret != OCSD_OK) {
+ printf("%s: can't create memory accessor: ret %d\n",
+ __func__, ret);
+ return (ENXIO);
+ }
+
+ return (ret);
+}
+
+static ocsd_err_t
+create_generic_decoder(dcd_tree_handle_t handle, const char *p_name,
+ const void *p_cfg, const void *p_context __unused,
+ struct trace_context *tc)
+{
+ ocsd_err_t ret;
+ uint8_t CSID;
+
+ CSID = 0;
+
+ dprintf("%s\n", __func__);
+
+ ret = ocsd_dt_create_decoder(handle, p_name,
+ OCSD_CREATE_FLG_FULL_DECODER, p_cfg, &CSID);
+ if (ret != OCSD_OK)
+ return (-1);
+
+ printf("%s: CSID to decode: %d.\n", __func__, CSID);
+
+ if (cs_flags & FLAG_FORMAT) {
+ ret = ocsd_dt_attach_packet_callback(handle, CSID,
+ OCSD_C_API_CB_PKT_MON, packet_monitor, p_context);
+ if (ret != OCSD_OK)
+ return (-1);
+ }
+
+ /* attach a memory accessor */
+ ret = create_test_memory_acc(handle, tc);
+ if (ret != OCSD_OK) {
+ ocsd_dt_remove_decoder(handle, CSID);
+ return (ENXIO);
+ }
+
+ return (ret);
+}
+
+static ocsd_err_t
+create_decoder_etmv4(struct trace_context *tc, dcd_tree_handle_t dcd_tree_h,
+ int thread_id)
+{
+ struct etmv4_config *config;
+ ocsd_etmv4_cfg trace_config;
+ uint32_t id_regs[14];
+ size_t regsize;
+ ocsd_err_t ret;
+ char name[32];
+ int error;
+ int i;
+
+ config = tc->config;
+
+ trace_config.arch_ver = ARCH_V8;
+ trace_config.core_prof = profile_CortexA;
+ trace_config.reg_configr = config->cfg;
+ /* TODO: take thread_id from config ? */
+ trace_config.reg_traceidr = thread_id + 1;
+
+ for (i = 0; i < 14; i++) {
+ snprintf(name, 32, "dev.coresight_etm4x.0.idr%d", i);
+ regsize = sizeof(id_regs[i]);
+ error = sysctlbyname(name, &id_regs[i], &regsize, NULL, 0);
+ if (error) {
+ printf("Error: could not query ETMv4\n");
+ return (error);
+ }
+ }
+
+ trace_config.reg_idr0 = id_regs[0];
+ trace_config.reg_idr1 = id_regs[1];
+ trace_config.reg_idr2 = id_regs[2];
+ trace_config.reg_idr8 = id_regs[8];
+ trace_config.reg_idr9 = id_regs[9];
+ trace_config.reg_idr10 = id_regs[10];
+ trace_config.reg_idr11 = id_regs[11];
+ trace_config.reg_idr12 = id_regs[12];
+ trace_config.reg_idr13 = id_regs[13];
+
+ /* Instruction decoder. */
+ ret = create_generic_decoder(dcd_tree_h, OCSD_BUILTIN_DCD_ETMV4I,
+ (void *)&trace_config, 0, tc);
+
+ return (ret);
+}
+
+static int
+cs_process_chunk_raw(struct trace_context *tc, size_t start, size_t len,
+ uint32_t *consumed)
+{
+ void *base;
+
+ base = (void *)((uintptr_t)tc->base + (uintptr_t)start);
+
+ fwrite(base, len, 1, tc->raw_f);
+ fflush(tc->raw_f);
+
+ *consumed = len;
+
+ return (0);
+}
+
+static int
+cs_process_chunk(struct trace_context *tc, struct cs_decoder *dec,
+ size_t start, size_t len, uint32_t *consumed)
+{
+ void *base;
+ int error;
+
+ /* Coresight data is always on first cpu cdev due to funnelling by HW.*/
+ base = (void *)((uintptr_t)tc->base + (uintptr_t)start);
+
+ dprintf("Processing data for CPU%d\n", dec->cpu_id);
+
+ error = ocsd_dt_process_data(dec->dcdtree_handle,
+ OCSD_OP_DATA, start, len, base, consumed);
+
+ if (*consumed != len) {
+ printf("error");
+ exit(5);
+ }
+
+ return (error);
+}
+
+struct pmcstat_pcmap *
+pmcstat_process_find_map(struct pmcstat_process *p, uintfptr_t pc)
+{
+ struct pmcstat_pcmap *ppm;
+
+ TAILQ_FOREACH(ppm, &p->pp_map, ppm_next) {
+ if (pc >= ppm->ppm_lowpc && pc < ppm->ppm_highpc)
+ return (ppm);
+ if (pc < ppm->ppm_lowpc)
+ return (NULL);
+ }
+
+ return (NULL);
+}
+
+static struct pmcstat_symbol *
+symbol_lookup(const struct trace_context *tc, uint64_t ip,
+ struct pmcstat_image **img, uint64_t *newpc0)
+{
+ struct pmcstat_image *image;
+ struct pmcstat_symbol *sym;
+ struct pmcstat_pcmap *map;
+ uint64_t newpc;
+
+ map = pmcstat_process_find_map(tc->pp, ip);
+ if (map != NULL) {
+ image = map->ppm_image;
+ newpc = ip - ((unsigned long)map->ppm_lowpc +
+ (image->pi_vaddr - image->pi_start));
+ sym = pmcstat_symbol_search(image, newpc); /* Could be NULL. */
+ newpc += image->pi_vaddr;
+
+ *img = image;
+ *newpc0 = newpc;
+
+ return (sym);
+ } else
+ *img = NULL;
+
+ return (NULL);
+}
+
+static void __unused
+print_timestamp(const ocsd_generic_trace_elem *elem)
+{
+ char ts[100];
+
+ if (elem->timestamp != 0)
+ sprintf(ts, "ts %ld", elem->timestamp);
+ else
+ sprintf(ts, " ");
+}
+
+static ocsd_datapath_resp_t
+gen_trace_elem_print_lookup(const void *p_context,
+ const ocsd_trc_index_t index_sop __unused,
+ const uint8_t trc_chan_id __unused,
+ const ocsd_generic_trace_elem *elem)
+{
+ struct trace_context *tc;
+ struct pmcstat_image *image;
+ ocsd_datapath_resp_t resp;
+ struct pmcstat_symbol *sym;
+ unsigned long offset;
+ const struct cs_decoder *dec;
+ const char *piname;
+ const char *psname;
+ uint64_t newpc;
+ uint64_t ip;
+ FILE *out;
+
+ dec = (const struct cs_decoder *)p_context;
+ tc = dec->tc;
+ out = dec->out;
+
+ resp = OCSD_RESP_CONT;
+
+ dprintf("%s: Idx:%d ELEM TYPE %d, st_addr %lx, en_addr %lx\n",
+ __func__, index_sop, elem->elem_type,
+ elem->st_addr, elem->en_addr);
+
+ if (elem->st_addr <= 0)
+ return (resp);
+
+ ip = elem->st_addr;
+
+ sym = symbol_lookup(tc, ip, &image, &newpc);
+
+ static const char *ARMv8Excep[] = {
+ "PE Reset", "Debug Halt", "Call", "Trap",
+ "System Error", "Reserved", "Inst Debug", "Data Debug",
+ "Reserved", "Reserved", "Alignment", "Inst Fault",
+ "Data Fault", "Reserved", "IRQ", "FIQ"
+ };
+
+ switch (elem->elem_type) {
+ case OCSD_GEN_TRC_ELEM_UNKNOWN:
+ fprintf(out, "Unknown packet.\n");
+ return (resp);
+ case OCSD_GEN_TRC_ELEM_NO_SYNC:
+ fprintf(out, "No sync.\n");
+ return (resp);
+ case OCSD_GEN_TRC_ELEM_TRACE_ON:
+ /* fprintf(out, "Trace on.\n"); */
+ return (resp);
+ case OCSD_GEN_TRC_ELEM_EO_TRACE:
+ fprintf(out, "End of Trace.\n");
+ return (resp);
+ case OCSD_GEN_TRC_ELEM_PE_CONTEXT:
+ break;
+ case OCSD_GEN_TRC_ELEM_INSTR_RANGE:
+ case OCSD_GEN_TRC_ELEM_I_RANGE_NOPATH:
+ return (resp);
+ case OCSD_GEN_TRC_ELEM_ADDR_NACC:
+ break;
+ case OCSD_GEN_TRC_ELEM_ADDR_UNKNOWN:
+ return (resp);
+ case OCSD_GEN_TRC_ELEM_EXCEPTION:
+ fprintf(out, "Exception #%d (%s)\n", elem->exception_number,
+ ARMv8Excep[elem->exception_number]);
+ return (resp);
+ case OCSD_GEN_TRC_ELEM_EXCEPTION_RET:
+ fprintf(out, "Exception RET to %lx\n", elem->st_addr);
+ return (resp);
+ case OCSD_GEN_TRC_ELEM_TIMESTAMP:
+ fprintf(out, "Timestamp: %lx\n", elem->timestamp);
+ return (resp);
+ case OCSD_GEN_TRC_ELEM_CYCLE_COUNT:
+ fprintf(out, "Cycle count: %d\n", elem->cycle_count);
+ return (resp);
+ case OCSD_GEN_TRC_ELEM_EVENT:
+ case OCSD_GEN_TRC_ELEM_SWTRACE:
+ case OCSD_GEN_TRC_ELEM_SYNC_MARKER:
+ case OCSD_GEN_TRC_ELEM_MEMTRANS:
+ case OCSD_GEN_TRC_ELEM_INSTRUMENTATION:
+ case OCSD_GEN_TRC_ELEM_CUSTOM:
+ return (resp);
+ };
+
+
+ if (sym || image) {
+ xo_open_instance("entry");
+ xo_emit_h(dec->xop, "{:pc/pc 0x%08lx/%x}", ip);
+ xo_emit_h(dec->xop, " ");
+ }
+
+ if (image) {
+ if (tc->mode == HWT_MODE_THREAD) {
+ xo_emit_h(dec->xop, "{:newpc/(%lx)/%x}", newpc);
+ xo_emit_h(dec->xop, "\t");
+ }
+
+ piname = pmcstat_string_unintern(image->pi_name);
+ xo_emit_h(dec->xop, "{:piname/%12s/%s}", piname);
+ }
+
+ if (sym) {
+ psname = pmcstat_string_unintern(sym->ps_name);
+ offset = newpc - (sym->ps_start + image->pi_vaddr);
+ xo_emit_h(dec->xop, "\t");
+ xo_emit_h(dec->xop, "{:psname/%s/%s}", psname);
+ xo_emit_h(dec->xop, "{:offset/+0x%lx/%ju}", offset);
+ }
+
+ if (sym || image) {
+ xo_emit_h(dec->xop, "\n");
+ xo_close_instance("entry");
+ }
+
+ xo_flush();
+
+ return (resp);
+}
+
+static int
+hwt_coresight_init(struct trace_context *tc, struct cs_decoder *dec,
+ int thread_id)
+{
+ char filename[MAXPATHLEN];
+ int error;
+
+ dec->cpu_id = thread_id;
+ dec->tc = tc;
+ dec->dp_ret = OCSD_RESP_CONT;
+ dec->dcdtree_handle = ocsd_create_dcd_tree(OCSD_TRC_SRC_FRAME_FORMATTED,
+ OCSD_DFRMTR_FRAME_MEM_ALIGN);
+ if (dec->dcdtree_handle == C_API_INVALID_TREE_HANDLE) {
+ printf("can't find dcd tree\n");
+ return (-1);
+ }
+
+ if (tc->filename) {
+ if (tc->mode == HWT_MODE_CPU)
+ snprintf(filename, MAXPATHLEN, "%s%d", tc->filename,
+ dec->cpu_id);
+ else
+ snprintf(filename, MAXPATHLEN, "%s", tc->filename);
+
+ dec->out = fopen(filename, "w");
+ if (dec->out == NULL) {
+ printf("could not open %s\n", filename);
+ return (ENXIO);
+ }
+ dec->xop = xo_create_to_file(dec->out, XO_STYLE_TEXT, XOF_WARN);
+ } else {
+ dec->out = stdout;
+ dec->xop = NULL;
+ }
+
+ if (tc->flag_format)
+ cs_flags |= FLAG_FORMAT;
+
+#if 0
+ cs_flags |= FLAG_FRAME_RAW_UNPACKED;
+ cs_flags |= FLAG_FRAME_RAW_PACKED;
+#endif
+
+ error = create_decoder_etmv4(tc, dec->dcdtree_handle, thread_id);
+ if (error != OCSD_OK) {
+ printf("can't create decoder: tc->base %p\n", tc->base);
+ return (-2);
+ }
+
+#ifdef HWT_CORESIGHT_DEBUG
+ ocsd_tl_log_mapped_mem_ranges(dec->dcdtree_handle);
+#endif
+
+ if (cs_flags & FLAG_FORMAT)
+ ocsd_dt_set_gen_elem_printer(dec->dcdtree_handle);
+ else
+ ocsd_dt_set_gen_elem_outfn(dec->dcdtree_handle,
+ gen_trace_elem_print_lookup, dec);
+
+ attach_raw_printers(dec->dcdtree_handle);
+
+ return (0);
+}
+
+static void
+hwt_coresight_fill_config(struct trace_context *tc, struct etmv4_config *config)
+{
+ int excp_level;
+ uint32_t reg;
+ uint32_t val;
+ int i;
+
+ memset(config, 0, sizeof(struct etmv4_config));
+
+ reg = TRCCONFIGR_RS | TRCCONFIGR_TS;
+ reg |= TRCCONFIGR_CID | TRCCONFIGR_VMID;
+ reg |= TRCCONFIGR_COND_DIS;
+ config->cfg = reg;
+
+ config->ts_ctrl = 0;
+ config->syncfreq = TRCSYNCPR_4K;
+
+ if (tc->mode == HWT_MODE_THREAD)
+ excp_level = 0; /* User mode. */
+ else
+ excp_level = 1; /* CPU mode. */
+
+ reg = TRCVICTLR_SSSTATUS;
+ reg |= (1 << EVENT_SEL_S);
+ reg |= TRCVICTLR_EXLEVEL_NS(1 << excp_level);
+ reg |= TRCVICTLR_EXLEVEL_S(1 << excp_level);
+ config->vinst_ctrl = reg;
+
+ /* Address-range filtering. */
+ val = 0;
+ for (i = 0; i < tc->nranges * 2; i++) {
+ config->addr_val[i] = tc->addr_ranges[i];
+
+ reg = TRCACATR_EXLEVEL_S(1 << excp_level);
+ reg |= TRCACATR_EXLEVEL_NS(1 << excp_level);
+ config->addr_acc[i] = reg;
+
+ /* Include the range ID. */
+ val |= (1 << (TRCVIIECTLR_INCLUDE_S + i / 2));
+ }
+ config->viiectlr = val;
+}
+
+static int
+hwt_coresight_set_config(struct trace_context *tc)
+{
+ struct hwt_set_config sconf;
+ struct etmv4_config *config;
+ int error;
+
+ config = malloc(sizeof(struct etmv4_config));
+ hwt_coresight_fill_config(tc, config);
+
+ tc->config = config;
+
+ sconf.config = config;
+ sconf.config_size = sizeof(struct etmv4_config);
+ sconf.config_version = 1;
+ sconf.pause_on_mmap = tc->suspend_on_mmap ? 1 : 0;
+
+ error = ioctl(tc->thr_fd, HWT_IOC_SET_CONFIG, &sconf);
+
+ return (error);
+}
+
+static int
+cs_process_chunk1(struct trace_context *tc, struct cs_decoder *dec,
+ size_t cursor, size_t len, uint32_t *processed)
+{
+ int cpu_id;
+ int error;
+
+ if (tc->raw) {
+ error = cs_process_chunk_raw(tc, cursor, len, processed);
+ return (error);
+ } else if (tc->mode == HWT_MODE_CPU) {
+ CPU_FOREACH_ISSET(cpu_id, &tc->cpu_map) {
+ error = cs_process_chunk(tc, &dec[cpu_id], cursor, len,
+ processed);
+ if (error)
+ return (error);
+ }
+ } else {
+ error = cs_process_chunk(tc, dec, cursor, len, processed);
+ return (error);
+ }
+
+ return (0);
+}
+
+static int
+hwt_coresight_init1(struct trace_context *tc, struct cs_decoder *dec)
+{
+ int cpu_id;
+ int error;
+
+ if (tc->raw) {
+ /* No decoder needed, just a file for raw data. */
+ tc->raw_f = fopen(tc->filename, "w");
+ if (tc->raw_f == NULL) {
+ printf("could not open file %s\n", tc->filename);
+ return (ENXIO);
+ }
+ } else if (tc->mode == HWT_MODE_CPU) {
+ CPU_FOREACH_ISSET(cpu_id, &tc->cpu_map) {
+ error = hwt_coresight_init(tc, &dec[cpu_id], cpu_id);
+ if (error)
+ return (error);
+ }
+ } else {
+ error = hwt_coresight_init(tc, dec, tc->thread_id);
+ if (error)
+ return (error);
+ }
+
+ return (0);
+}
+
+static void
+catch_int(int sig_num __unused)
+{
+
+ printf("Decoder stopped\n");
+ exit(0);
+}
+
+static int
+cs_get_offs(struct trace_context *tc, size_t *offs)
+{
+ struct hwt_bufptr_get bget = {0};
+ vm_offset_t curpage_offset;
+ int curpage;
+ int error;
+
+ bget.ident = &curpage;
+ bget.offset = &curpage_offset;
+ bget.data = NULL;
+
+ error = ioctl(tc->thr_fd, HWT_IOC_BUFPTR_GET, &bget);
+ if (error)
+ return (error);
+
+ dprintf("curpage %d curpage_offset %ld\n", curpage, curpage_offset);
+
+ *offs = curpage * PAGE_SIZE + curpage_offset;
+
+ return (0);
+}
+
+static int
+hwt_coresight_process(struct trace_context *tc)
+{
+ size_t offs;
+ size_t new_offs;
+ int error;
+ int t;
+ struct cs_decoder *dec;
+ uint32_t processed;
+ size_t cursor;
+ int len;
+ size_t totals;
+ int ncpu;
+
+ xo_open_container("trace");
+ xo_open_list("entries");
+
+ signal(SIGINT, catch_int);
+
+ ocsd_def_errlog_init(OCSD_ERR_SEV_INFO, 1);
+
+ ncpu = hwt_ncpu();
+
+ dec = malloc(sizeof(struct cs_decoder) * ncpu);
+
+ error = hwt_coresight_init1(tc, dec);
+ if (error)
+ return (error);
+
+ error = cs_get_offs(tc, &offs);
+ if (error) {
+ printf("%s: cant get offset\n", __func__);
+ return (-1);
+ }
+
+
+ printf("Decoder started. Press ctrl+c to stop.\n");
+
+ cursor = 0;
+ processed = 0;
+ totals = 0;
+ len = offs;
+
+ cs_process_chunk1(tc, dec, cursor, len, &processed);
+ cursor += processed;
+ totals += processed;
+
+ t = 0;
+
+ while (1) {
+ error = cs_get_offs(tc, &new_offs);
+ if (error)
+ return (-1);
+
+ if (new_offs == cursor) {
+ /* No new entries in trace. */
+ if (tc->terminate && t++ > 2)
+ break;
+ hwt_sleep(10);
+ } else if (new_offs > cursor) {
+ /* New entries in the trace buffer. */
+ len = new_offs - cursor;
+ cs_process_chunk1(tc, dec, cursor, len, &processed);
+ cursor += processed;
+ totals += processed;
+ t = 0;
+
+ } else if (new_offs < cursor) {
+ /* New entries in the trace buffer. Buffer wrapped. */
+ len = tc->bufsize - cursor;
+ cs_process_chunk1(tc, dec, cursor, len, &processed);
+ cursor += processed;
+ totals += processed;
+
+ cursor = 0;
+ len = new_offs;
+ cs_process_chunk1(tc, dec, cursor, len, &processed);
+ cursor += processed;
+ totals += processed;
+ t = 0;
+ }
+ }
+
+ printf("\nBytes processed: %ld\n", totals);
+
+ xo_close_list("file");
+ xo_close_container("wc");
+ if (xo_finish() < 0)
+ xo_err(EXIT_FAILURE, "stdout");
+
+ return (0);
+}
+
+struct trace_dev_methods cs_methods = {
+ .init = NULL,
+ .mmap = hwt_coresight_mmap,
+ .process = hwt_coresight_process,
+ .set_config = hwt_coresight_set_config,
+};
Index: usr.sbin/hwt/hwt_elf.h
===================================================================
--- /dev/null
+++ usr.sbin/hwt/hwt_elf.h
@@ -0,0 +1,45 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Bojan Novković <bnovkov@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef HWT_ELF_H_
+#define HWT_ELF_H_
+
+/*
+ * Metadata for the .text loaded from executable
+ * 'path' into the traced process's address space.
+ */
+struct hwt_exec_img {
+ size_t size;
+ uint64_t offs;
+ uint64_t addr;
+ const char *path;
+};
+
+int hwt_elf_count_libs(const char *elf_path, uint32_t *nlibs0);
+int hwt_elf_get_text_offs(const char *elf_path, uint64_t *offs);
+
+#endif /* HWT_ELF_H_ */
Index: usr.sbin/hwt/hwt_elf.c
===================================================================
--- /dev/null
+++ usr.sbin/hwt/hwt_elf.c
@@ -0,0 +1,196 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/cpuset.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/errno.h>
+#include <sys/hwt.h>
+#include <sys/hwt_record.h>
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <err.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "hwt.h"
+#include "hwt_elf.h"
+#include "sys/hwt_record.h"
+
+#include "libpmcstat_stubs.h"
+#include <libpmcstat.h>
+
+int
+hwt_elf_count_libs(const char *elf_path, uint32_t *nlibs0)
+{
+ GElf_Shdr shdr;
+ GElf_Phdr ph;
+ GElf_Ehdr eh;
+ Elf_Scn *scn;
+ Elf *elf;
+ size_t sh_entsize;
+ Elf_Data *data;
+ GElf_Dyn dyn;
+ int is_dynamic;
+ uint32_t nlibs;
+ int fd;
+ size_t i;
+
+ nlibs = 0;
+
+ assert(elf_version(EV_CURRENT) != EV_NONE);
+
+ fd = open(elf_path, O_RDONLY, 0);
+
+ assert(fd >= 0);
+
+ elf = elf_begin(fd, ELF_C_READ, NULL);
+
+ assert(elf != NULL);
+ assert(elf_kind(elf) == ELF_K_ELF);
+
+ if (gelf_getehdr(elf, &eh) != &eh) {
+ printf("could not find elf header\n");
+ return (-1);
+ }
+
+ if (eh.e_type != ET_EXEC && eh.e_type != ET_DYN) {
+ printf("unsupported image\n");
+ return (-2);
+ }
+
+ if (eh.e_ident[EI_CLASS] != ELFCLASS32 &&
+ eh.e_ident[EI_CLASS] != ELFCLASS64)
+ return (-3);
+
+ is_dynamic = 0;
+
+ for (i = 0; i < eh.e_phnum; i++) {
+ if (gelf_getphdr(elf, i, &ph) != &ph) {
+ printf("could not get program header %zu\n", i);
+ return (-4);
+ }
+ switch (ph.p_type) {
+ case PT_DYNAMIC:
+ is_dynamic = 1;
+ break;
+ case PT_INTERP:
+ nlibs++;
+ break;
+ }
+ }
+
+ if (!is_dynamic)
+ goto done;
+
+ scn = NULL;
+ data = NULL;
+
+ while ((scn = elf_nextscn(elf, scn)) != NULL) {
+ assert(gelf_getshdr(scn, &shdr) == &shdr);
+
+ if (shdr.sh_type == SHT_DYNAMIC) {
+ data = elf_getdata(scn, data);
+ assert(data != NULL);
+
+ sh_entsize = gelf_fsize(elf, ELF_T_DYN, 1, EV_CURRENT);
+
+ for (i = 0; i < shdr.sh_size / sh_entsize; i++) {
+ assert(gelf_getdyn(data, i, &dyn) == &dyn);
+ if (dyn.d_tag == DT_NEEDED)
+ nlibs++;
+ }
+ }
+ }
+
+done:
+ assert(elf_end(elf) == 0);
+ assert(close(fd) == 0);
+
+ *nlibs0 = nlibs;
+
+ return (0);
+}
+
+int
+hwt_elf_get_text_offs(const char *elf_path, uint64_t *offs)
+{
+ const char *scnname;
+ GElf_Shdr shdr;
+ int fd, error;
+ Elf_Scn *scn;
+ size_t ndx;
+ Elf *elf;
+
+ elf = NULL;
+ scn = NULL;
+ fd = open(elf_path, O_RDONLY, 0);
+ if (fd < 0) {
+ warnx("%s: cannot open \"%s\": %s", __func__, elf_path,
+ elf_errmsg(elf_errno()));
+ return (-1);
+ }
+ if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
+ warnx("%s: cannot read \"%s\": %s", __func__, elf_path,
+ elf_errmsg(elf_errno()));
+ error = -1;
+ goto out;
+ }
+
+ error = -1;
+ while ((scn = elf_nextscn(elf, scn)) != NULL) {
+ if (elf_getshdrstrndx(elf, &ndx) != 0) {
+ warnx("%s: elf_getshdrstrndx failed: %s", __func__,
+ elf_errmsg(elf_errno()));
+ goto out;
+ }
+ gelf_getshdr(scn, &shdr);
+ if ((scnname = elf_strptr(elf, ndx, shdr.sh_name)) == NULL)
+ continue;
+
+ if (strncmp(scnname, ".text", strlen(".text")) == 0) {
+ error = 0;
+ *offs = shdr.sh_offset;
+ break;
+ }
+ }
+
+out:
+ close(fd);
+
+ return (error);
+}
Index: usr.sbin/hwt/hwt_process.c
===================================================================
--- /dev/null
+++ usr.sbin/hwt/hwt_process.c
@@ -0,0 +1,141 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/errno.h>
+#include <sys/wait.h>
+#include <sys/cpuset.h>
+#include <sys/hwt.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sysexits.h>
+#include <string.h>
+
+#include "hwt.h"
+
+#include "libpmcstat_stubs.h"
+#include <libpmcstat.h>
+
+struct pmcstat_image_hash_list pmcstat_image_hash[PMCSTAT_NHASH];
+
+int
+hwt_process_start(int *sockpair)
+{
+ int error;
+
+ error = write(sockpair[PARENTSOCKET], "!", 1);
+ if (error != 1)
+ return (-1);
+
+ return (0);
+}
+
+static void
+hwt_process_onsig(int signo)
+{
+ pid_t pid;
+ int status;
+
+ assert(signo == SIGCHLD);
+
+ while ((pid = wait3(&status, WNOHANG, NULL)) > 0)
+ if (WIFEXITED(status))
+ hwt_procexit(pid, WEXITSTATUS(status));
+}
+
+int
+hwt_process_create(int *sockpair, char **cmd, char **env __unused, int *pid0)
+{
+ char token;
+ pid_t pid;
+ int error;
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockpair) < 0)
+ return (-1);
+
+ signal(SIGCHLD, hwt_process_onsig);
+ siginterrupt(SIGCHLD, 1);
+ pid = fork();
+
+ switch (pid) {
+ case -1:
+ return (-1);
+ case 0:
+ /* Child */
+ close(sockpair[PARENTSOCKET]);
+
+ error = write(sockpair[CHILDSOCKET], "+", 1);
+ if (error != 1)
+ return (-1);
+
+ error = read(sockpair[CHILDSOCKET], &token, 1);
+ if (error < 0)
+ return (-2);
+
+ if (token != '!')
+ return (-3);
+
+ close(sockpair[CHILDSOCKET]);
+
+ execvp(cmd[0], cmd);
+
+ kill(getppid(), SIGCHLD);
+
+ exit(-3);
+ default:
+ /* Parent */
+ close(sockpair[CHILDSOCKET]);
+ break;
+ }
+
+ *pid0 = pid;
+
+ return (0);
+}
+
+struct pmcstat_process *
+hwt_process_alloc(void)
+{
+ struct pmcstat_process *pp;
+
+ pp = malloc(sizeof(struct pmcstat_process));
+ if (pp)
+ TAILQ_INIT(&pp->pp_map);
+
+ return (pp);
+}
Index: usr.sbin/hwt/hwt_pt.h
===================================================================
--- /dev/null
+++ usr.sbin/hwt/hwt_pt.h
@@ -0,0 +1,33 @@
+/*-
+ * Copyright (c) 2023 Bojan Novković <bnovkov@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _HWT_PT_H_
+#define _HWT_PT_H_
+
+#include <amd64/pt/pt.h>
+
+extern struct trace_dev_methods pt_methods;
+#endif /* !_HWT_PT_H_ */
Index: usr.sbin/hwt/hwt_pt.c
===================================================================
--- /dev/null
+++ usr.sbin/hwt/hwt_pt.c
@@ -0,0 +1,687 @@
+/*-
+ * Copyright (c) 2023 Bojan Novković <bnovkov@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/cpuset.h>
+#include <sys/errno.h>
+#include <sys/event.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+#include <sys/tree.h>
+#include <sys/hwt.h>
+#include <sys/hwt_record.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <libxo/xo.h>
+#include <amd64/pt/pt.h>
+#include <libipt/intel-pt.h>
+
+#include "libpmcstat_stubs.h"
+#include <libpmcstat.h>
+
+#include "hwt.h"
+#include "hwt_elf.h"
+#include "hwt_pt.h"
+
+#define pt_strerror(errcode) pt_errstr(pt_errcode((errcode)))
+
+/*
+ * Trace decoder state.
+ */
+struct pt_dec_ctx {
+ size_t curoff;
+ uint64_t ts;
+ uint64_t curip;
+ void *tracebuf;
+ struct pt_block_decoder *dec;
+
+ int id;
+ RB_ENTRY(pt_dec_ctx) entry;
+
+ xo_handle_t *xop;
+ int dev_fd;
+};
+
+typedef void (
+ *pt_ctx_iter_cb)(struct trace_context *, struct pt_dec_ctx *, void *);
+static int pt_ctx_compare(const void *n1, const void *n2);
+
+/*
+ * Active decoder states.
+ */
+static struct pt_image_section_cache *pt_iscache;
+static struct pt_dec_ctx *cpus;
+static RB_HEAD(threads, pt_dec_ctx) threads;
+RB_GENERATE_STATIC(threads, pt_dec_ctx, entry, pt_ctx_compare);
+
+static int
+pt_ctx_compare(const void *n1, const void *n2)
+{
+ const struct pt_dec_ctx *c1 = n1;
+ const struct pt_dec_ctx *c2 = n2;
+
+ return (c1->id < c2->id ? -1 : c1->id > c2->id ? 1 : 0);
+}
+
+/*
+ * Iterate over all active decoders and invoked provided callback.
+ */
+static void
+pt_foreach_ctx(struct trace_context *tc, pt_ctx_iter_cb callback, void *arg)
+{
+ int cpu_id;
+ struct pt_dec_ctx *dctx;
+
+ switch (tc->mode) {
+ case HWT_MODE_CPU:
+ CPU_FOREACH_ISSET(cpu_id, &tc->cpu_map) {
+ callback(tc, &cpus[cpu_id], arg);
+ }
+ break;
+ case HWT_MODE_THREAD:
+ RB_FOREACH(dctx, threads, &threads) {
+ callback(tc, dctx, arg);
+ }
+ break;
+ default:
+ errx(EXIT_FAILURE, "%s: unknown mode %d\n", __func__, tc->mode);
+ break;
+ }
+}
+
+/*
+ * Initialize a trace decoder.
+ * Invoked as a callback from 'pt_foreach_ctx'.
+ */
+static void
+pt_cpu_ctx_init_cb(struct trace_context *tc, struct pt_dec_ctx *dctx,
+ void *arg __unused)
+{
+ int cpu_id, fd;
+ char filename[32];
+ struct pt_config config;
+
+ cpu_id = dctx - cpus;
+ sprintf(filename, "/dev/hwt_%d_%d", tc->ident, cpu_id);
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ errx(EXIT_FAILURE, "Can't open %s\n", filename);
+ }
+
+ /*
+ * thr_fd is used to issue ioctls which control all
+ * cores use fd to the first cpu for this (thread is
+ * always 0).
+ */
+ if (tc->thr_fd == 0) {
+ tc->thr_fd = fd;
+ }
+ dctx->tracebuf = mmap(NULL, tc->bufsize, PROT_READ, MAP_SHARED, fd, 0);
+ if (dctx->tracebuf == MAP_FAILED) {
+ errx(EXIT_FAILURE,
+ "%s: failed to map tracing buffer for cpu %d: %s\n",
+ __func__, cpu_id, strerror(errno));
+ }
+ dctx->id = cpu_id;
+
+ if (!tc->raw) {
+ memset(&config, 0, sizeof(config));
+ config.size = sizeof(config);
+ config.begin = dctx->tracebuf;
+ config.end = (uint8_t *)dctx->tracebuf + tc->bufsize;
+
+ dctx->dec = pt_blk_alloc_decoder(&config);
+ if (dctx->dec == NULL) {
+ printf("%s: failed to allocate PT decoder for CPU %d\n",
+ __func__, cpu_id);
+ free(dctx);
+ return;
+ }
+ }
+}
+
+/*
+ * Add a new ELF section to the trace decoder.
+ * Invoked as a callback from 'pt_foreach_ctx'.
+ */
+static void
+pt_update_image_cb(struct trace_context *tc __unused, struct pt_dec_ctx *dctx,
+ void *arg)
+{
+ int isid;
+ int error;
+ struct pt_image *image;
+
+ isid = *(int *)arg;
+ image = pt_blk_get_image(dctx->dec);
+ error = pt_image_add_cached(image, pt_iscache, isid, NULL);
+ if (error)
+ errx(EXIT_FAILURE,
+ "%s: failed to add cached section to decoder image: %s",
+ __func__, pt_strerror(error));
+}
+
+/*
+ * Add all ELF sections to the trace decoder.
+ * Invoked as a callback from 'pt_foreach_ctx'.
+ */
+static int
+hwt_pt_image_load_cb(struct trace_context *tc, struct hwt_exec_img *img)
+{
+ int isid;
+
+ if (tc->raw)
+ return (0);
+ isid = pt_iscache_add_file(pt_iscache, img->path, img->offs, img->size,
+ img->addr);
+ if (isid < 0) {
+ printf("%s: error adding file '%s' to the section cache: %s\n",
+ __func__, img->path, pt_strerror(isid));
+ return (-1);
+ }
+
+ pt_foreach_ctx(tc, pt_update_image_cb, &isid);
+
+ return (0);
+}
+
+static int
+hwt_pt_init(struct trace_context *tc)
+{
+
+ /* Buffer size must be power of two. */
+ assert((tc->bufsize & (tc->bufsize - 1)) == 0);
+
+ if (tc->raw) {
+ /* No decoder needed, just a file for raw data. */
+ tc->raw_f = fopen(tc->filename, "w");
+ if (tc->raw_f == NULL) {
+ printf("%s: could not open file %s\n", __func__,
+ tc->filename);
+ return (ENXIO);
+ }
+ }
+
+ pt_iscache = pt_iscache_alloc(tc->image_name);
+ if (pt_iscache == NULL)
+ errx(EXIT_FAILURE, "%s: failed to allocate section cache",
+ __func__);
+
+ switch (tc->mode) {
+ case HWT_MODE_CPU:
+ cpus = calloc(hwt_ncpu(), sizeof(struct pt_dec_ctx));
+ if (!cpus) {
+ printf("%s: failed to allocate decoders\n", __func__);
+ return (ENOMEM);
+ }
+ break;
+ case HWT_MODE_THREAD:
+ RB_INIT(&threads);
+ break;
+ default:
+ printf("%s: invalid tracing mode %d\n", __func__, tc->mode);
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+/*
+ * Map and initialize the tracing buffer.
+ * Called whenever a new traced thread gets created or
+ * when HWT_MODE_CPU tracing is started.
+ */
+static int
+hwt_pt_mmap(struct trace_context *tc, struct hwt_record_user_entry *rec)
+{
+ int tid, fd;
+ char filename[32];
+ struct pt_config config;
+ struct pt_image *srcimg, *dstimg;
+ struct pt_dec_ctx *dctx, *srcctx;
+
+ switch (tc->mode) {
+ case HWT_MODE_CPU:
+ pt_foreach_ctx(tc, pt_cpu_ctx_init_cb, NULL);
+ break;
+ case HWT_MODE_THREAD:
+ if (rec == NULL) {
+ /* Have we already mapped the first thread? */
+ if (tc->thr_fd != 0)
+ return (EINVAL);
+ tid = 0;
+ } else {
+ tid = rec->thread_id;
+ }
+ sprintf(filename, "/dev/hwt_%d_%d", tc->ident, tid);
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ printf("Can't open %s\n", filename);
+ return (-1);
+ }
+ if (tc->thr_fd == 0) {
+ tc->thr_fd = fd;
+ }
+ dctx = calloc(1, sizeof(*dctx));
+ if (dctx == NULL)
+ return (ENOMEM);
+ dctx->dev_fd = fd;
+ dctx->tracebuf = mmap(NULL, tc->bufsize, PROT_READ, MAP_SHARED,
+ fd, 0);
+ if (dctx->tracebuf == MAP_FAILED) {
+ printf(
+ "%s: failed to map tracing buffer for thread %d: %s\n",
+ __func__, tid, strerror(errno));
+ free(dctx);
+ return (ENOMEM);
+ }
+ dctx->id = tid;
+ if (!tc->raw) {
+ /*
+ * Grab another context, if any, and copy its decoder
+ * image.
+ */
+ if (!RB_EMPTY(&threads)) {
+ srcctx = RB_ROOT(&threads);
+ srcimg = pt_blk_get_image(srcctx->dec);
+ dstimg = pt_blk_get_image(dctx->dec);
+ pt_image_copy(dstimg, srcimg);
+ }
+ memset(&config, 0, sizeof(config));
+ config.size = sizeof(config);
+ config.begin = dctx->tracebuf;
+ config.end = (uint8_t *)dctx->tracebuf + tc->bufsize;
+
+ dctx->dec = pt_blk_alloc_decoder(&config);
+ if (dctx->dec == NULL) {
+ printf(
+ "%s: failed to allocate PT decoder for thread\n",
+ __func__);
+ free(dctx);
+ return (ENOMEM);
+ }
+ }
+ RB_INSERT(threads, &threads, dctx);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+static int
+hwt_pt_set_config(struct trace_context *tc)
+{
+ struct hwt_set_config sconf;
+ struct pt_cpu_config *config;
+ int i, error;
+ uint64_t rtit_ctl = 0;
+
+ config = calloc(1, sizeof(struct pt_cpu_config));
+ /* Fill config */
+ if (tc->mode == HWT_MODE_THREAD)
+ rtit_ctl |= RTIT_CTL_USER;
+ else
+ rtit_ctl |= RTIT_CTL_OS;
+
+ if (tc->nranges) {
+ /* IP range filtering. */
+ config->nranges = tc->nranges;
+ for (i = 0; i < tc->nranges; i++) {
+ config->ip_ranges[i].start = tc->addr_ranges[i * 2];
+ config->ip_ranges[i].end = tc->addr_ranges[i * 2 + 1];
+ }
+ }
+
+ rtit_ctl |= RTIT_CTL_BRANCHEN;
+
+ config->rtit_ctl = rtit_ctl;
+ tc->config = config;
+
+ sconf.config = config;
+ sconf.config_size = sizeof(struct pt_config);
+ sconf.config_version = 1;
+ sconf.pause_on_mmap = tc->suspend_on_mmap ? 1 : 0;
+
+ error = ioctl(tc->thr_fd, HWT_IOC_SET_CONFIG, &sconf);
+
+ return (error);
+}
+
+static void
+hwt_pt_print(struct trace_context *tc, struct pt_dec_ctx *dctx, uint64_t ip)
+{
+ uint64_t newpc;
+ unsigned long offset;
+ struct pmcstat_symbol *sym;
+ struct pmcstat_image *image;
+ const char *piname;
+ const char *psname;
+
+ sym = hwt_sym_lookup(tc, ip, &image, &newpc);
+ if (sym || image) {
+ xo_emit_h(dctx->xop, "{:type/%s} {:id/%d}\t",
+ tc->mode == HWT_MODE_CPU ? "CPU" : "thr", dctx->id);
+ xo_emit_h(dctx->xop, "{:pc/pc 0x%08lx/%x}", ip);
+ xo_emit_h(dctx->xop, " ");
+ }
+
+ if (image) {
+ if (tc->mode == HWT_MODE_THREAD) {
+ xo_emit_h(dctx->xop, "{:newpc/(%lx)/%x}", newpc);
+ xo_emit_h(dctx->xop, "\t");
+ }
+
+ piname = pmcstat_string_unintern(image->pi_name);
+ xo_emit_h(dctx->xop, "{:piname/%12s/%s}", piname);
+ }
+
+ if (sym) {
+ psname = pmcstat_string_unintern(sym->ps_name);
+ offset = newpc -
+ (sym->ps_start + (image != NULL ? image->pi_vaddr : 0));
+ xo_emit_h(dctx->xop, "\t");
+ xo_emit_h(dctx->xop, "{:psname/%s/%s}", psname);
+ xo_emit_h(dctx->xop, "{:offset/+0x%lx/%ju}", offset);
+ }
+
+ if (sym || image) {
+ xo_emit_h(dctx->xop, "\n");
+ xo_close_instance("entry");
+ }
+}
+
+static int
+hwt_pt_decode_chunk(struct trace_context *tc, struct pt_dec_ctx *dctx,
+ uint64_t start, size_t len, uint64_t *processed)
+{
+ int ret;
+ int error = 0;
+ struct pt_block blk;
+ struct pt_event event;
+ uint64_t oldoffs, offs;
+ struct pt_block_decoder *dec;
+
+ dec = dctx->dec;
+ offs = start;
+ /* Set decoder to current offset. */
+ ret = pt_blk_sync_set(dec, start);
+ blk.ip = 0;
+ do {
+ /* Process any pending events. */
+ while (ret & pts_event_pending) {
+ ret = pt_blk_event(dec, &event, sizeof(event));
+ pt_blk_get_offset(dec, &offs);
+ if (offs >= (start + len))
+ break;
+ }
+ ret = pt_blk_next(dec, &blk, sizeof(blk));
+ if (ret < 0) {
+ if (ret == -pte_eos) {
+ /* Restore to last valid offset. */
+ pt_blk_sync_backward(dec);
+ error = 0;
+ break;
+ }
+
+ ret = pt_blk_sync_forward(dec);
+ oldoffs = offs;
+ pt_blk_get_offset(dec, &offs);
+ if (ret < 0 || offs >= (start + len) ||
+ offs <= oldoffs) {
+ if (ret < 0 && ret != -pte_eos) {
+ error = ret;
+ printf("ip: %p\n", (void *)blk.ip);
+ printf(
+ "%s: error decoding next instruction: %s\n",
+ __func__, pt_strerror(error));
+ }
+ break;
+ }
+ }
+ pt_blk_get_offset(dec, &offs);
+ /* Print new symbol offset. */
+ hwt_pt_print(tc, dctx, blk.ip);
+ } while (offs < (start + len));
+ pt_blk_get_offset(dec, &offs);
+ printf("len %p, offs %p\n", (void *)len, (void *)offs);
+ *processed = offs - start;
+
+ return (error);
+}
+
+/*
+ * Dumps raw packet bytes into tc->raw_f.
+ */
+static int
+hwt_pt_dump_chunk(struct pt_dec_ctx *dctx, FILE *raw_f, uint64_t offs,
+ size_t len, uint64_t *processed)
+{
+ void *base;
+
+ base = (void *)((uintptr_t)dctx->tracebuf + (uintptr_t)offs);
+ fwrite(base, len, 1, raw_f);
+ fflush(raw_f);
+
+ *processed = len;
+
+ return (0);
+}
+
+static int
+pt_process_chunk(struct trace_context *tc, struct pt_dec_ctx *dctx,
+ uint64_t offs, size_t len, uint64_t *processed)
+{
+ if (tc->raw) {
+ return (
+ hwt_pt_dump_chunk(dctx, tc->raw_f, offs, len, processed));
+ } else {
+ return (hwt_pt_decode_chunk(tc, dctx, offs, len, processed));
+ }
+}
+
+static struct pt_dec_ctx *
+pt_get_decoder_ctx(struct trace_context *tc, int ctxid)
+{
+ switch (tc->mode) {
+ case HWT_MODE_CPU:
+ assert(ctxid < hwt_ncpu());
+ return (&cpus[ctxid]);
+ case HWT_MODE_THREAD: {
+ struct pt_dec_ctx srch;
+ srch.id = ctxid;
+ return (RB_FIND(threads, &threads, &srch));
+ }
+ default:
+ break;
+ }
+
+ return (NULL);
+}
+
+/*
+ * Re-sync buffer when wrap is detected.
+ */
+static void
+pt_ctx_sync_ts(struct trace_context *tc, struct pt_dec_ctx *dctx,
+ uint64_t newts)
+{
+ size_t newoff;
+
+ newoff = (newts - dctx->ts) & (tc->bufsize - 1);
+
+ dctx->ts = newts;
+ dctx->curoff = newoff;
+}
+
+static int
+pt_process_data(struct trace_context *tc, struct pt_dec_ctx *dctx, uint64_t ts)
+{
+ int error;
+ uint64_t processed;
+ size_t newoff, curoff, len;
+
+ /*
+ * Check if the buffer wrapped since
+ * the last time we processed it
+ * and try to resync.
+ */
+ if ((ts - dctx->ts) > tc->bufsize) {
+ printf(
+ "%s: WARNING: buffer wrapped - re-syncing to last known offset\n",
+ __func__);
+ pt_ctx_sync_ts(tc, dctx, ts);
+ return (0);
+ }
+
+ len = ts - dctx->ts;
+ curoff = dctx->curoff;
+ newoff = (curoff + len) % tc->bufsize;
+
+ if (newoff > curoff) {
+ /* New entries in the trace buffer. */
+ len = newoff - curoff;
+ error = pt_process_chunk(tc, dctx, curoff, len, &processed);
+ if (error != 0) {
+ return (error);
+ }
+ dctx->curoff += processed;
+ dctx->ts += processed;
+
+ } else if (newoff < curoff) {
+ /* New entries in the trace buffer. Buffer wrapped. */
+ len = tc->bufsize - curoff;
+ error = pt_process_chunk(tc, dctx, curoff, len, &processed);
+ if (error != 0) {
+ return (error);
+ }
+
+ dctx->curoff += processed;
+ dctx->ts += processed;
+
+ curoff = 0;
+ len = newoff;
+ error = pt_process_chunk(tc, dctx, curoff, len, &processed);
+ if (error != 0) {
+ return (error);
+ }
+
+ dctx->curoff = processed;
+ dctx->ts += processed;
+ }
+
+ return (0);
+}
+
+/*
+ * Decode part of the tracing buffer.
+ */
+static int
+hwt_pt_process_buffer(struct trace_context *tc, int id, int curpage,
+ vm_offset_t offset)
+{
+ int error;
+ uint64_t ts;
+ struct pt_dec_ctx *dctx;
+
+ dctx = pt_get_decoder_ctx(tc, id);
+ if (dctx == NULL) {
+ printf("%s: unable to find decorder context for ID %d\n",
+ __func__, id);
+
+ return (-1);
+ }
+
+ ts = (curpage * PAGE_SIZE) + offset;
+ error = pt_process_data(tc, dctx, ts);
+ if (error) {
+ return (error);
+ }
+
+ return (0);
+}
+
+/*
+ * Fetch last valid trace offset for 'dev_fd'.
+ */
+static int
+pt_get_offs(int dev_fd, uint64_t *offs)
+{
+ struct hwt_bufptr_get bget;
+ vm_offset_t offset;
+ int curpage;
+ int error;
+
+ bget.ident = &curpage;
+ bget.offset = &offset;
+ error = ioctl(dev_fd, HWT_IOC_BUFPTR_GET, &bget);
+ if (error)
+ return (error);
+ *offs = curpage * PAGE_SIZE + offset;
+
+ return (0);
+}
+
+static void
+pt_ctx_shutdown_cb(struct trace_context *tc, struct pt_dec_ctx *dctx,
+ void *arg __unused)
+{
+ int error;
+ uint64_t ts;
+
+ error = pt_get_offs(dctx->dev_fd, &ts);
+ if (error)
+ errx(EXIT_FAILURE, "pt_get_offs");
+ error = pt_process_data(tc, dctx, ts);
+ if (error)
+ errx(EXIT_FAILURE, "pt_process_data");
+}
+
+static void
+hwt_pt_shutdown(struct trace_context *tc)
+{
+ pt_foreach_ctx(tc, pt_ctx_shutdown_cb, NULL);
+}
+
+struct trace_dev_methods pt_methods = {
+ .init = hwt_pt_init,
+ .shutdown = hwt_pt_shutdown,
+ .mmap = hwt_pt_mmap,
+ .process_buffer = hwt_pt_process_buffer,
+ .set_config = hwt_pt_set_config,
+ .image_load_cb = hwt_pt_image_load_cb
+};
Index: usr.sbin/hwt/hwt_record.c
===================================================================
--- /dev/null
+++ usr.sbin/hwt/hwt_record.c
@@ -0,0 +1,195 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/errno.h>
+#include <sys/cpuset.h>
+#include <sys/hwt.h>
+#include <sys/hwt_record.h>
+#include <sys/stat.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <err.h>
+#include <string.h>
+#include <assert.h>
+
+#include "hwt.h"
+#include "hwt_elf.h"
+
+#include "libpmcstat_stubs.h"
+#include <libpmcstat.h>
+
+#define HWT_RECORD_DEBUG
+#undef HWT_RECORD_DEBUG
+
+#ifdef HWT_RECORD_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+static int
+hwt_record_to_elf_img(struct trace_context *tc,
+ struct hwt_record_user_entry *entry, struct hwt_exec_img *img)
+{
+ pmcstat_interned_string path;
+ struct pmcstat_image *image;
+ struct pmc_plugins plugins;
+ struct pmcstat_args args;
+ unsigned long addr;
+ char imagepath[PATH_MAX];
+
+ dprintf(" path %s addr %lx\n", entry->fullpath,
+ (unsigned long)entry->addr);
+
+ memset(&plugins, 0, sizeof(struct pmc_plugins));
+ memset(&args, 0, sizeof(struct pmcstat_args));
+ args.pa_fsroot = "/";
+ snprintf(imagepath, sizeof(imagepath), "%s/%s", tc->fs_root,
+ entry->fullpath);
+ path = pmcstat_string_intern(imagepath);
+
+ image = pmcstat_image_from_path(path,
+ !!(entry->record_type == HWT_RECORD_KERNEL), &args, &plugins);
+ if (image == NULL)
+ return (-1);
+
+ if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN)
+ pmcstat_image_determine_type(image, &args);
+
+ if (image->pi_end == 0) {
+ printf(" image '%s' has no executable sections, skipping\n",
+ imagepath);
+ free(image);
+ image = NULL;
+ img->size = 0;
+ return (0);
+ }
+ addr = (unsigned long)entry->addr & ~1;
+ if (entry->record_type != HWT_RECORD_KERNEL)
+ addr -= (image->pi_start - image->pi_vaddr);
+
+ pmcstat_image_link(tc->pp, image, addr);
+ dprintf("image pi_vaddr %lx pi_start %lx"
+ " pi_end %lx pi_entry %lx\n",
+ (unsigned long)image->pi_vaddr, (unsigned long)image->pi_start,
+ (unsigned long)image->pi_end, (unsigned long)image->pi_entry);
+
+ if (hwt_elf_get_text_offs(imagepath, &img->offs))
+ return (-1);
+ img->size = image->pi_end - image->pi_start;
+ img->addr = addr;
+ img->path = entry->fullpath;
+
+ return (0);
+}
+
+int
+hwt_record_fetch(struct trace_context *tc, int *nrecords, int wait)
+{
+ struct hwt_record_user_entry *entry;
+ struct hwt_record_get record_get;
+ struct hwt_exec_img img;
+ struct hwt_wakeup w;
+ int nentries;
+ int error;
+ int j;
+
+ nentries = 256;
+ tc->records = malloc(sizeof(struct hwt_record_user_entry) * nentries);
+
+ record_get.records = tc->records;
+ record_get.nentries = &nentries;
+ record_get.wait = wait;
+
+ error = ioctl(tc->thr_fd, HWT_IOC_RECORD_GET, &record_get);
+ if (error != 0) {
+ printf("RECORD_GET error %d entires %d\n",
+ error, nentries);
+ return (error);
+ }
+
+ dprintf("%s: error %d: nent %d\n", __func__, error, nentries);
+
+ for (j = 0; j < nentries; j++) {
+ entry = &tc->records[j];
+
+ switch (entry->record_type) {
+ case HWT_RECORD_MMAP:
+ case HWT_RECORD_EXECUTABLE:
+ case HWT_RECORD_INTERP:
+ case HWT_RECORD_KERNEL:
+ hwt_record_to_elf_img(tc, entry, &img);
+ /* Invoke backend callback, if any. */
+ if (tc->trace_dev->methods->image_load_cb != NULL &&
+ (error = tc->trace_dev->methods->image_load_cb(tc,
+ &img))) {
+ ioctl(tc->thr_fd, HWT_IOC_WAKEUP, &w);
+ return (error);
+ }
+
+ if (entry->record_type != HWT_RECORD_KERNEL)
+ hwt_mmap_received(tc, entry);
+ break;
+ case HWT_RECORD_THREAD_CREATE:
+ /* Let the backend register the newly created thread. */
+ if ((error = tc->trace_dev->methods->mmap(tc, entry)) !=
+ 0)
+ return (error);
+ break;
+ case HWT_RECORD_THREAD_SET_NAME:
+ case HWT_RECORD_MUNMAP:
+ break;
+ case HWT_RECORD_BUFFER:
+ /* Invoke backend process method. */
+ assert(tc->trace_dev->methods->process_buffer != NULL);
+ if ((error = tc->trace_dev->methods->process_buffer(tc,
+ entry->buf_id, entry->curpage,
+ entry->offset)) != 0)
+ return (error);
+ default:
+ break;
+ }
+ }
+ free(tc->records);
+ tc->records = NULL;
+
+ *nrecords = nentries;
+
+ return (error);
+}
Index: usr.sbin/hwt/hwt_spe.h
===================================================================
--- /dev/null
+++ usr.sbin/hwt/hwt_spe.h
@@ -0,0 +1,43 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Arm Ltd
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _HWT_SPE_H_
+#define _HWT_SPE_H_
+
+extern struct trace_dev_methods spe_methods;
+
+struct arm_spe_mmap {
+ int n;
+ /* fd[n], *buf[n] */
+ int *fds;
+ void **bufs;
+ int m; /* CPU_FLS (highest cpu_id in cpuset + 1) */
+ /* idx[m] */
+ uint16_t *idx; /* mapping between cpu_id + bufs[idx] */
+};
+
+#endif /* !_HWT_SPE_H_ */
Index: usr.sbin/hwt/hwt_spe.c
===================================================================
--- /dev/null
+++ usr.sbin/hwt/hwt_spe.c
@@ -0,0 +1,444 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Arm Ltd
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Arm Statistical Profiling Extension (SPE)
+ *
+ * Note: this userspace tool currently has basic functionality in terms of
+ * configuration + filtering. It can only dump raw SPE records via the `-w
+ * filename` command line option. See arm_spe_config for currently available
+ * config however these are not yet available via command line options.
+ *
+ * There is currently no inbuilt decoder but the raw SPE records can be decoded
+ * with the following tool:
+ *
+ * https://gitlab.arm.com/telemetry-solution/telemetry-solution/-/tree/main/tools/spe_parser
+ *
+ */
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/errno.h>
+#include <sys/cpuset.h>
+#include <sys/hwt.h>
+#include <sys/wait.h>
+#include <sys/sysctl.h>
+#include <sys/event.h>
+
+#include <err.h>
+#include <sysexits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "hwt.h"
+#include "hwt_spe.h"
+#include "arm64/spe/arm_spe.h"
+
+#define HWT_SPE_DEBUG
+#undef HWT_SPE_DEBUG
+
+#ifdef HWT_SPE_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+static struct arm_spe_mmap *spe_mmap;
+static int kq_fd;
+
+#ifdef HWT_SPE_DEBUG
+static void hex_dump(uint8_t *buf, size_t len)
+{
+ size_t i;
+
+ printf("--------------------------------------------------------------\n");
+ for (i = 0; i < len; ++i) {
+ if (i % 8 == 0) {
+ printf(" ");
+ }
+ if (i % 16 == 0) {
+ if (i != 0) {
+ printf("\r\n");
+ }
+ printf("\t");
+ }
+ printf("%02X ", buf[i]);
+ }
+ printf("\r\n");
+}
+#else
+static void hex_dump(uint8_t *buf __unused, size_t len __unused) {}
+#endif
+
+static int
+hwt_map_memory(struct trace_context *tc, int id, int *fd, void **addr)
+{
+ char filename[32];
+
+ sprintf(filename, "/dev/hwt_%d_%d", tc->ident, id);
+
+ *fd = open(filename, O_RDONLY);
+ if (*fd < 0) {
+ printf("Can't open %s\n", filename);
+ return (-1);
+ }
+
+ *addr = mmap(NULL, tc->bufsize, PROT_READ, MAP_SHARED, *fd, 0);
+ if (*addr == MAP_FAILED) {
+ printf("mmap failed: err %d\n", errno);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+hwt_spe_mmap(struct trace_context *tc,
+ struct hwt_record_user_entry *entry __unused)
+{
+ int error, cpu_id, i=0;
+
+ spe_mmap = malloc(sizeof(struct arm_spe_mmap));
+ if (spe_mmap == NULL)
+ return (ENOMEM);
+
+ if (tc->mode == HWT_MODE_CPU)
+ spe_mmap->n = CPU_COUNT(&tc->cpu_map);
+ else /* HWT_MODE_THREAD */
+ spe_mmap->n = 1;
+
+ spe_mmap->m = CPU_FLS(&tc->cpu_map); /* Highest id in cpu_map + 1 */
+ /* Maintain a mapping between cpu_id + idx in bufs[idx] */
+ spe_mmap->idx = malloc(sizeof(uint16_t) * spe_mmap->m);
+ spe_mmap->bufs = malloc(sizeof(void *) * spe_mmap->n);
+ spe_mmap->fds = malloc(sizeof(int) * spe_mmap->n);
+ if (spe_mmap->bufs == NULL || spe_mmap->fds == NULL)
+ return (ENOMEM);
+ dprintf("spe_mmap->bufs:%p, spe_mmap->n:%d\n", spe_mmap->bufs, spe_mmap->n);
+
+ if (tc->mode == HWT_MODE_CPU) {
+ CPU_FOREACH_ISSET(cpu_id, &tc->cpu_map) {
+ error = hwt_map_memory(tc, cpu_id,
+ &spe_mmap->fds[i], &spe_mmap->bufs[i]);
+ dprintf("cpu_id:%d &spe_mmap->bufs[%d]:%p value:%p\n", cpu_id, i, &spe_mmap->bufs[i], spe_mmap->bufs[i]);
+ if (error)
+ return error;
+ spe_mmap->idx[cpu_id] = i;
+ i++;
+ }
+ } else { /* HWT_MODE_THREAD */
+ error = hwt_map_memory(tc, 0,
+ &spe_mmap->fds[0], &spe_mmap->bufs[0]);
+ if (error)
+ return error;
+ }
+
+ /*
+ * thr_fd is used to issue ioctls which control all cores
+ * use fd to the first cpu for this (thread is always 0)
+ */
+ tc->thr_fd = spe_mmap->fds[0];
+
+ return (0);
+}
+
+static int
+hwt_spe_init(struct trace_context *tc)
+{
+ struct kevent event[3];
+ int ret;
+
+ if (tc->raw) {
+ /* No decoder needed, just a file for raw data. */
+ tc->raw_f = fopen(tc->filename, "wb");
+ if (tc->raw_f == NULL) {
+ printf("%s: could not open file %s\n", __func__,
+ tc->filename);
+ return (ENXIO);
+ }
+ } else {
+ printf("error: SPE can currently only dump raw data: '-r' "
+ "flag is required with '-w filename'\n");
+ return (ENOTSUP);
+ }
+
+ /*
+ * Setup kevent queue to process ARM_SPE_KQ_{...} events from:
+ * 1. kernel (e.g. buffer full event)
+ * 2. userspace signal handler
+ */
+ tc->kqueue_fd = kqueue();
+ if (tc->kqueue_fd == -1)
+ err(EXIT_FAILURE, "kqueue() failed");
+
+ kq_fd = tc->kqueue_fd; /* sig handler needs access to kq via global */
+
+ EV_SET(&event[0], ARM_SPE_KQ_BUF, EVFILT_USER, EV_ADD | EV_CLEAR,
+ NOTE_FFCOPY, 0, NULL);
+ EV_SET(&event[1], ARM_SPE_KQ_SHUTDOWN, EVFILT_USER, EV_ADD | EV_CLEAR,
+ NOTE_FFCOPY, 0, NULL);
+ EV_SET(&event[2], ARM_SPE_KQ_SIGNAL, EVFILT_USER, EV_ADD | EV_CLEAR,
+ NOTE_FFCOPY, 0, NULL);
+
+ ret = kevent(tc->kqueue_fd, event, 3, NULL, 0, NULL);
+ if (ret == -1)
+ err(EXIT_FAILURE, "kevent register");
+ for (int i = 0; i < 3; i++) {
+ if (event[i].flags & EV_ERROR)
+ errx(EXIT_FAILURE, "Event error: %s",
+ strerror(event[i].data));
+ }
+
+ return ret;
+}
+
+static void
+sighandler(const int sig_num)
+{
+ struct kevent kev;
+ int ret;
+
+ // TODO - handle different signals differently?
+ switch (sig_num) {
+ case SIGINT:
+ printf("SIGINT\n"); /* ctrl+c */
+ break;
+ case SIGTERM:
+ printf("SIGTERM\n");
+ break;
+ default:
+ break;
+ }
+
+ /* Wake up event loop in hwt_spe_process */
+ EV_SET(&kev, ARM_SPE_KQ_SIGNAL, EVFILT_USER, 0,
+ NOTE_TRIGGER | NOTE_FFCOPY, 0, NULL);
+ ret = kevent(kq_fd, &kev, 1, NULL, 0, NULL);
+ if (ret == -1)
+ err(EXIT_FAILURE, "kevent register");
+ if (kev.flags & EV_ERROR)
+ errx(EXIT_FAILURE, "event error: %s", strerror(kev.data));
+}
+
+static int
+hwt_spe_stop_tracing(struct trace_context *tc)
+{
+ int err;
+
+ err = hwt_stop_tracing(tc);
+ if (err)
+ errx(EX_SOFTWARE, "failed to stop tracing, error %d\n", err);
+
+ return err;
+}
+
+static void
+hwt_spe_free(void)
+{
+ free(spe_mmap->idx);
+ free(spe_mmap->bufs);
+ free(spe_mmap->fds);
+ free(spe_mmap);
+}
+
+static int
+hwt_spe_get_data(struct trace_context *tc)
+{
+
+ struct hwt_bufptr_get bget;
+ struct hwt_svc_buf svc_buf;
+ struct arm_spe_svc_buf binfo;
+ vm_offset_t buf_start = 0;
+ size_t size, ret;
+ uint64_t data;
+ void *buf;
+ int ident, idx, error;
+ bool not_final;
+
+ bget.ident = &ident;
+ bget.offset = &size;
+ bget.data = &data;
+
+ /*
+ * Returns the oldest buffer that needs servicing or errno ENOENT if
+ * there are no buffers pending
+ */
+ error = ioctl(tc->thr_fd, HWT_IOC_BUFPTR_GET, &bget);
+ if (error) {
+ if (errno == ENOENT) /* no items on queue */
+ return (0);
+
+ errx(EX_SOFTWARE, "Failed HWT_IOC_BUFPTR_GET err:%d %s\n",
+ errno, strerror(errno));
+ }
+
+ if (ident > (spe_mmap->m - 1))
+ return ENXIO;
+ idx = spe_mmap->idx[ident];
+
+
+ /* Copy from the correct half of the ping pong buffer */
+ if ((data & KQ_BUF_POS) == 0)
+ buf_start = 0;
+ else
+ buf_start = tc->bufsize/2;
+
+ /* TODO: once decoder is implemented, if KQ_PARTREC, discard last (partial) record */
+ buf = (uint8_t *)spe_mmap->bufs[idx]+buf_start;
+ hex_dump(buf, 64);
+ ret = fwrite(buf, 1, size, tc->raw_f);
+ if (ret < size)
+ errx(EX_SOFTWARE, "failed to write to file, wrote %zu/%zu bytes\n",
+ ret, size);
+ else
+ tc->bytes_written += ret;
+
+ /* Clear service buffer flag */
+ binfo.ident = ident;
+ binfo.buf_idx = (data & KQ_BUF_POS);
+ svc_buf.data = &binfo;
+ svc_buf.data_size = sizeof(binfo);
+ svc_buf.data_version = 1;
+
+ /* KQ_BUF_FINAL indicates shutdown so no need to clear service flag */
+ not_final = !(data & KQ_FINAL_BUF);
+ if (not_final && ioctl(tc->thr_fd, HWT_IOC_SVC_BUF, &svc_buf)) {
+ errx(EX_SOFTWARE, "Failed HWT_IOC_SVC_BUF err:%d %s\n",
+ errno, strerror(errno));
+ }
+
+ return error;
+}
+
+static int
+hwt_spe_process(struct trace_context *tc)
+{
+ struct kevent kev;
+ int ret;
+ struct sigaction sigact = {0};
+ int nevents;
+ u_int npending;
+
+ sigact.sa_handler = sighandler;
+ sigact.sa_flags = SA_RESTART;
+ /* TODO: register/handle other signals */
+ sigaction(SIGINT, &sigact, 0);
+ sigaction(SIGHUP, &sigact, 0);
+ sigaction(SIGTERM, &sigact, 0);
+ sigaction(SIGQUIT, &sigact, 0);
+
+ dprintf("%s tc->image_name:%s tc->func_name:%s tc->ident:%d\n", __func__, tc->image_name, tc->func_name, tc->ident);
+ printf("Press ctrl+c to stop profiling\n");
+
+ for (;;) {
+ /* Sleep until something happens. */
+ nevents = kevent(tc->kqueue_fd, NULL, 0, &kev, 1, NULL);
+ if (nevents == -1 && errno != EINTR)
+ err(EXIT_FAILURE, "kevent wait");
+ else if (nevents == -1 && errno == EINTR)
+ continue;
+
+ dprintf("%s nevents:%d kev.fflags:%x data:%#lx\n", __func__,
+ nevents, kev.fflags, kev.data);
+
+ if (kev.ident == ARM_SPE_KQ_SIGNAL) {
+ dprintf("%s ARM_SPE_KQ_SIGNAL\n", __func__);
+ ret = hwt_spe_stop_tracing(tc);
+ if (ret)
+ goto free;
+ }
+ if (kev.ident == ARM_SPE_KQ_BUF) {
+ dprintf("%s ARM_SPE_KQ_BUF npending:%lu\n", __func__, kev.data);
+ npending = kev.data;
+ while (npending) {
+ ret = hwt_spe_get_data(tc);
+ if (ret) {
+ printf("hwt_spe_get_data error:%d %s\n",
+ ret, strerror(errno));
+ goto free;
+ }
+ npending--;
+ }
+ }
+ if (kev.ident == ARM_SPE_KQ_SHUTDOWN) {
+ dprintf("%s ARM_SPE_KQ_SHUTDOWN\n", __func__);
+ goto free;
+ }
+ }
+
+free:
+ hwt_spe_free();
+
+ if (tc->raw)
+ printf("%s %lu bytes written to file %s\n", __func__,
+ tc->bytes_written, tc->filename);
+
+ if (errno == EINTR)
+ return 0;
+
+ return ret;
+}
+
+static int
+hwt_spe_set_config(struct trace_context *tc)
+{
+ struct hwt_set_config sconf;
+ struct arm_spe_config *cfg;
+ int ret;
+
+ cfg = calloc(1, sizeof(struct arm_spe_config));
+ if (cfg == NULL)
+ return (ENOMEM);
+
+ sconf.config = cfg;
+ sconf.config_size = sizeof(struct arm_spe_config);
+ sconf.config_version = 1;
+ sconf.pause_on_mmap = tc->suspend_on_mmap ? 1 : 0;
+
+ cfg->interval = 4096;
+ cfg->level = ARM_SPE_KERNEL_AND_USER;
+ cfg->ctx_field = ARM_SPE_CTX_PID;
+
+ tc->config = cfg;
+
+ ret = ioctl(tc->thr_fd, HWT_IOC_SET_CONFIG, &sconf);
+
+ return ret;
+}
+
+struct trace_dev_methods spe_methods = {
+ .init = hwt_spe_init,
+ .mmap = hwt_spe_mmap,
+ .process = hwt_spe_process,
+ .set_config = hwt_spe_set_config,
+};
Index: usr.sbin/hwt/libpmcstat_stubs.h
===================================================================
--- /dev/null
+++ usr.sbin/hwt/libpmcstat_stubs.h
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 2023 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <machine/profile.h>
+
+/* These are stubs, needed for compilation only. */
+
+#ifndef _HWT_LIBPMCSTAT_STUBS_H_
+#define _HWT_LIBPMCSTAT_STUBS_H_
+
+enum pmc_mode {
+ PMC_MODE_INVALID,
+};
+
+typedef int pmc_id_t;
+typedef int pmc_value_t;
+
+struct pmclog_ev;
+
+int pmclog_read(void *cookie __unused, struct pmclog_ev *ev __unused);
+int pmc_close_logfile(void);
+int pmc_attach(pmc_id_t pmc __unused, pid_t pid __unused);
+
+#endif /* !_HWT_LIBPMCSTAT_STUBS_H_ */
Index: usr.sbin/hwt/libpmcstat_stubs.c
===================================================================
--- /dev/null
+++ usr.sbin/hwt/libpmcstat_stubs.c
@@ -0,0 +1,58 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "libpmcstat_stubs.h"
+#include <libpmcstat.h>
+
+/* This is a stub file, needed for compilation only. */
+
+struct pmcstat_pmcs pmcstat_pmcs;
+struct pmcstat_process_hash_list pmcstat_process_hash[PMCSTAT_NHASH];
+
+int
+pmclog_read(void *cookie __unused, struct pmclog_ev *ev __unused)
+{
+
+ return (-1);
+}
+
+int
+pmc_close_logfile(void)
+{
+
+ return (-1);
+}
+
+int
+pmc_attach(pmc_id_t pmc __unused, pid_t pid __unused)
+{
+
+ return (-1);
+}

File Metadata

Mime Type
text/plain
Expires
Thu, Nov 7, 3:22 AM (21 h, 29 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14503836
Default Alt Text
D40728.diff (111 KB)

Event Timeline