Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F101981188
D40728.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
111 KB
Referenced Files
None
Subscribers
None
D40728.diff
View Options
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], ®size, 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
Details
Attached
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)
Attached To
Mode
D40728: hwt(8) utility added
Attached
Detach File
Event Timeline
Log In to Comment