Index: usr.sbin/Makefile.aarch64 =================================================================== --- usr.sbin/Makefile.aarch64 +++ usr.sbin/Makefile.aarch64 @@ -2,3 +2,5 @@ .if ${MK_ACPI} != "no" SUBDIR+= acpi .endif + +SUBDIR+= hwt Index: usr.sbin/Makefile.amd64 =================================================================== --- usr.sbin/Makefile.amd64 +++ usr.sbin/Makefile.amd64 @@ -21,3 +21,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 + +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 +LIBADD+= opencsd +.elif ${MACHINE_CPUARCH} == "amd64" +SRCS+= hwt_pt.c +LIBADD+= ipt +.endif + +.include Index: usr.sbin/hwt/hwt.h =================================================================== --- /dev/null +++ usr.sbin/hwt/hwt.h @@ -0,0 +1,103 @@ +/*- + * Copyright (c) 2023 Ruslan Bukin + * 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 trace_dev_methods { + int (*init)(struct trace_context *tc); + int (*mmap)(struct trace_context *tc); + int (*process)(struct trace_context *tc); + int (*set_config)(struct trace_context *tc); +}; + +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; + + /* 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); +void hwt_procexit(pid_t pid, int status); +void hwt_sleep(int msec); +int hwt_elf_count_libs(const char *elf_path, uint32_t *nlibs0); +int hwt_find_sym(struct trace_context *tc); +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,684 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libpmcstat_stubs.h" +#include +#include + +#include "hwt.h" + +#if defined(__aarch64__) +#include "hwt_coresight.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 }, +#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; + + 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 nrecords; + int error; + + error = hwt_record_fetch(tc, &nrecords); + if (error) + return (error); + + *nrec = nrecords; + + return (0); +} + +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)" + " in bytes.\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_mode_cpu(struct trace_context *tc) +{ + uint32_t nrec; + int error; + + if (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."); + printf("\n"); + return (error); + } + + error = tc->trace_dev->methods->mmap(tc); + if (error) { + printf("cant map memory, error %d\n", error); + return (error); + } + + tc->pp->pp_pid = -1; + + error = hwt_get_records(tc, &nrec); + if (error != 0) + return (error); + + printf("Received %d kernel mappings\n", nrec); + + 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); + + error = tc->trace_dev->methods->process(tc); + if (error) { + printf("cant process data, error %d\n", error); + return (error); + } + + return (0); +} + +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\n", __func__, error, errno); + if (errno == EPERM) + printf("Permission denied."); + printf("\n"); + return (error); + } + + error = tc->trace_dev->methods->mmap(tc); + if (error) { + printf("cant map memory, error %d\n", error); + return (error); + } + + error = tc->trace_dev->methods->set_config(tc); + if (error != 0) + errx(EX_DATAERR, "can't set config"); + + 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 %d records.\n", nlibs); + + tot_rec = 0; + + /* + * Ensure we got expected amount of mmap/interp records so that + * mapping tables constructed before we do symbol lookup. + */ + + do { + error = hwt_get_records(tc, &nrec); + if (error != 0) + return (error); + tot_rec += nrec; + hwt_sleep(10); + } while (tot_rec < nlibs); + + 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_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(optarg); + 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 + * 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,863 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "hwt.h" +#include "hwt_coresight.h" + +#include "libpmcstat_stubs.h" +#include +#include + +#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) +{ + 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 */ + 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; + vm_offset_t curpage_offset; + int curpage; + int error; + + bget.curpage = &curpage; + bget.curpage_offset = &curpage_offset; + + 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.c =================================================================== --- /dev/null +++ usr.sbin/hwt/hwt_elf.c @@ -0,0 +1,136 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "hwt.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); +} 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hwt.h" + +#include "libpmcstat_stubs.h" +#include + +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); + + 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ć + * 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 + +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,428 @@ +/*- + * Copyright (c) 2023 Bojan Novković + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "hwt.h" +#include "hwt_pt.h" +#include "sys/_stdint.h" +#include "sys/systm.h" +#include "sys/types.h" + +#define pt_strerror(errcode) pt_errstr(pt_errcode((errcode))) + +/* + * Decoder per-cpu state. + */ +static struct hwt_pt_cpu { + size_t curoff; + void *tracebuf; + struct pt_packet_decoder *dec; +} *hwt_pt_pcpu; + +static int +hwt_pt_init(struct trace_context *tc) +{ + + hwt_pt_pcpu = calloc(hwt_ncpu(), sizeof(struct hwt_pt_cpu)); + if (!hwt_pt_pcpu) { + printf("%s: failed to allocate decoder\n", __func__); + return (-1); + } + 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); + } + } + + return (0); +} + +static int +hwt_pt_mmap(struct trace_context *tc) +{ + int error; + int cpu_id, tc_fd = -1, _fd; + struct hwt_pt_cpu *cpu; + struct pt_config config; + + if (tc->mode == HWT_MODE_CPU) { + CPU_FOREACH_ISSET (cpu_id, &tc->cpu_map) { + cpu = &hwt_pt_pcpu[cpu_id]; + + error = hwt_map_tracebuf(tc, cpu_id, + tc_fd == -1 ? &tc_fd : &_fd, &cpu->tracebuf); + if (error != 0) { + printf( + "%s: failed to map tracing buffer for cpu %d: %s\n", + __func__, cpu_id, strerror(errno)); + return (-1); + } + + if (!tc->raw) { + memset(&config, 0, sizeof(config)); + config.size = sizeof(config); + config.begin = cpu->tracebuf; + config.end = (uint8_t *)cpu->tracebuf + + tc->bufsize; + + cpu->dec = pt_pkt_alloc_decoder(&config); + if (cpu->dec == NULL) { + printf( + "%s: failed to allocate PT decoder for cpu %d\n", + __func__, cpu_id); + return (-1); + } + } + } + } else { + return (-1); + } + + /* thr_fd is used to issue ioctls which control all cores + * use fd to the first cpu for this (thread is always 0) */ + assert(tc_fd != -1); + tc->thr_fd = tc_fd; + + 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_tnt(const struct pt_packet_tnt *packet) +{ + uint64_t tnt; + uint8_t bits; + + bits = packet->bit_size; + tnt = packet->payload; + + while (--bits) + putc(tnt & (1ull << (bits - 1)) ? '!' : '.', stdout); +} + +static void +hwt_pt_print_mode(const struct pt_packet_mode *pkt) +{ + enum pt_exec_mode mode; + + switch (pkt->leaf) { + case pt_mol_exec: { + printf(".exec: "); + mode = pt_get_exec_mode(&pkt->bits.exec); + switch (mode) { + case ptem_64bit: + printf("64-bit"); + break; + case ptem_32bit: + printf("32-bit"); + break; + case ptem_16bit: + printf("16-bit"); + break; + default: + printf("unknown"); + break; + } + break; + } + case pt_mol_tsx: + printf(".tsx"); + break; + } +} + +static int +hwt_pt_print_packet(const struct pt_packet *pkt) +{ + + switch (pkt->type) { + case ppt_unknown: + printf(""); + break; + case ppt_invalid: + printf(""); + break; + case ppt_tnt_8: + printf("tnt.8\n"); + hwt_pt_print_tnt(&pkt->payload.tnt); + break; + case ppt_tnt_64: + printf("tnt.64\n"); + hwt_pt_print_tnt(&pkt->payload.tnt); + break; + case ppt_mode: + printf("mode"); + hwt_pt_print_mode(&pkt->payload.mode); + break; + case ppt_pip: + printf("pip: cr3 %p", (void *)pkt->payload.pip.cr3); + break; + case ppt_vmcs: + printf("vmcs: %p", (void *)pkt->payload.vmcs.base); + break; + case ppt_cbr: + printf("cbr: ratio %u", pkt->payload.cbr.ratio); + break; + case ppt_tip_pge: + printf("pge: TRACE START @%p", (void *)pkt->payload.ip.ip); + break; + case ppt_tip_pgd: + printf("pgd: TRACE STOP @%p", (void *)pkt->payload.ip.ip); + break; + case ppt_fup: + printf("fup: @%p", (void *)pkt->payload.ip.ip); + break; + case ppt_pad: + case ppt_psb: + case ppt_psbend: + /* Ignore */ + return (0); + default: + printf("%s: unknown packet type encountered: %d\n", __func__, + pkt->type); + return (-1); + } + printf("\n"); + + return (0); +} + +static int +hwt_pt_decode_chunk(struct pt_packet_decoder *dec, uint64_t start, size_t len, + uint64_t *processed) +{ + int error = 0; + uint64_t prevoffs, offs; + int ret; + struct pt_packet packet; + + printf("%s: start %zu, len %zu\n", __func__, start, len); + + offs = prevoffs = start; + /* Set decoder to current offset. */ + pt_pkt_sync_set(dec, start); + + do { + ret = pt_pkt_next(dec, &packet, sizeof(packet)); + if (ret < 0) { + if (ret == -pte_eos) { + /* Restore previous offset */ + pt_pkt_sync_set(dec, prevoffs); + offs = prevoffs; + } else { + error = ret; + printf("%s: error decoding next packet: %s\n", + __func__, pt_strerror(error)); + } + break; + } + error = hwt_pt_print_packet(&packet); + if (error < 0) { + printf("%s: error while processing packet: %s\n", + __func__, pt_strerror(error)); + break; + } + + prevoffs = offs; + offs += ret; + + if (offs > (start + len)) + break; + } while (1); + *processed = offs - start; + + return (error); +} + +/* + * Dumps raw packet bytes into tc->raw_f. + */ +static int +hwt_pt_dump_chunk(struct hwt_pt_cpu *cpu, FILE *raw_f, uint64_t offs, + size_t len, uint64_t *processed) +{ + void *base; + + base = (void *)((uintptr_t)cpu->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 hwt_pt_cpu *cpu, + uint64_t offs, size_t len, uint64_t *processed) +{ + if (tc->raw) { + return hwt_pt_dump_chunk(cpu, tc->raw_f, offs, len, processed); + } else { + return hwt_pt_decode_chunk(cpu->dec, offs, len, processed); + } +} + +static int +hwt_pt_process(struct trace_context *tc) +{ + uint64_t curoff, newoff; + size_t totals; + int error; + int len, cpu_id = 0; // assume cpu_id == 0 for now + uint64_t processed; + struct hwt_pt_cpu *cpu; + + xo_open_container("trace"); + xo_open_list("entries"); + + printf("Decoder started. Press ctrl+c to stop.\n"); + + curoff = 0; + processed = 0; + totals = 0; + len = 0; + + while (1) { + printf("%s: fetching new offset\n", __func__); + error = hwt_get_offs(tc, &newoff); + if (error < 0) + err(EXIT_FAILURE, "hwt_get_offs"); + printf("%s: new offset %zu\n", __func__, newoff); + + cpu = &hwt_pt_pcpu[cpu_id]; + curoff = cpu->curoff; + if (newoff == curoff) { + if (tc->terminate) + break; + } else if (newoff > curoff) { + /* New entries in the trace buffer. */ + len = newoff - curoff; + if (pt_process_chunk(tc, cpu, curoff, len, + &processed)) { + break; + } + curoff += processed; + totals += processed; + + } else if (newoff < curoff) { + /* New entries in the trace buffer. Buffer wrapped. */ + len = tc->bufsize - curoff; + if (pt_process_chunk(tc, cpu, curoff, len, + &processed)) { + break; + } + curoff += processed; + totals += processed; + + curoff = 0; + len = newoff; + if (pt_process_chunk(tc, cpu, curoff, len, + &processed)) { + break; + } + curoff += processed; + totals += processed; + } + /* Save current offset for cpu */ + cpu->curoff = curoff; + } + + 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 pt_methods = { + .init = hwt_pt_init, + .mmap = hwt_pt_mmap, + .process = hwt_pt_process, + .set_config = hwt_pt_set_config, +}; Index: usr.sbin/hwt/hwt_record.c =================================================================== --- /dev/null +++ usr.sbin/hwt/hwt_record.c @@ -0,0 +1,171 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hwt.h" + +#include "libpmcstat_stubs.h" +#include + +#define HWT_RECORD_DEBUG +#undef HWT_RECORD_DEBUG + +#ifdef HWT_RECORD_DEBUG +#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__) +#else +#define dprintf(fmt, ...) +#endif + +int +hwt_record_fetch(struct trace_context *tc, int *nrecords) +{ + struct hwt_record_user_entry *entry; + pmcstat_interned_string path; + struct pmcstat_image *image; + struct pmc_plugins plugins; + struct pmcstat_args args; + unsigned long addr; + struct hwt_record_get record_get; + char imagepath[PATH_MAX]; + struct stat st; + int nentries; + int error; + int j; + + memset(&plugins, 0, sizeof(struct pmc_plugins)); + memset(&args, 0, sizeof(struct pmcstat_args)); + args.pa_fsroot = "/"; + nentries = 256; + + tc->records = malloc(sizeof(struct hwt_record_user_entry) * nentries); + + record_get.records = tc->records; + record_get.nentries = &nentries; + + 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_MUNMAP: + case HWT_RECORD_EXECUTABLE: + case HWT_RECORD_INTERP: + printf(" lib #%d: path %s addr %lx\n", j, + entry->fullpath, + (unsigned long)entry->addr); + + path = pmcstat_string_intern(entry->fullpath); + image = pmcstat_image_from_path(path, 0, &args, + &plugins); + if (image == NULL) + return (-1); + + if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) + pmcstat_image_determine_type(image, &args); + + addr = (unsigned long)entry->addr & ~1; + addr -= (image->pi_start - image->pi_vaddr); + pmcstat_image_link(tc->pp, image, addr); + dprintf("image pi_vaddr %lx pi_start %lx" + " pi_entry %lx\n", + (unsigned long)image->pi_vaddr, + (unsigned long)image->pi_start, + (unsigned long)image->pi_entry); + hwt_mmap_received(tc, entry); + break; + case HWT_RECORD_KERNEL: + snprintf(imagepath, sizeof(imagepath), "%s/%s", + tc->fs_root, entry->fullpath); + error = stat(imagepath, &st); + if (error) + errx(EX_OSERR, "Image \"%s\" not found\n", + imagepath); + printf(" image #%d: path %s addr %lx\n", j, + imagepath, (unsigned long)entry->addr); + path = pmcstat_string_intern(imagepath); + image = pmcstat_image_from_path(path, 1, &args, + &plugins); + if (image == NULL) + return (-1); + if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) + pmcstat_image_determine_type(image, &args); + /* + * Some images have no executable sections - skip them. + */ + if (image->pi_end == 0) { + printf( + " image '%s' has no executable sections, skipping\n", + imagepath); + free(image); + image = NULL; + break; + } + addr = (unsigned long)entry->addr & ~1; + pmcstat_image_link(tc->pp, image, addr); + break; + case HWT_RECORD_THREAD_CREATE: + case HWT_RECORD_THREAD_SET_NAME: + break; + default: + break; + } + } + + *nrecords = nentries; + + return (0); +} Index: usr.sbin/hwt/libpmcstat_stubs.h =================================================================== --- /dev/null +++ usr.sbin/hwt/libpmcstat_stubs.h @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 2023 Ruslan Bukin + * + * 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 +#include + +/* 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 + * + * 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 + +/* 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); +}