Index: usr.sbin/Makefile.aarch64 =================================================================== --- usr.sbin/Makefile.aarch64 +++ usr.sbin/Makefile.aarch64 @@ -4,3 +4,4 @@ SUBDIR+= acpi .endif SUBDIR+= ofwdump +SUBDIR+= hwt Index: usr.sbin/hwt/Makefile =================================================================== --- /dev/null +++ usr.sbin/hwt/Makefile @@ -0,0 +1,21 @@ +# $FreeBSD$ + +PROG_CXX= hwt +MAN= + +CFLAGS+= -I${SRCTOP}/lib/libpmcstat + +LIBADD= elf pmcstat + +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 +.endif + +.include Index: usr.sbin/hwt/hwt.c =================================================================== --- /dev/null +++ usr.sbin/hwt/hwt.c @@ -0,0 +1,500 @@ +/*- + * 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 "libpmcstat_stubs.h" +#include + +#include "hwtvar.h" + +#if defined(__aarch64__) +#include "hwt_coresight.h" +#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 + { NULL, NULL, NULL } +}; + +void +hwt_sleep(void) +{ + struct timespec time_to_sleep; + + time_to_sleep.tv_sec = 0; + time_to_sleep.tv_nsec = 10000000; /* 10 ms */ + + 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, lwpid_t tid) +{ + struct hwt_wakeup w; + int error; + + w.pid = tc->pid; + w.tid = tid; + + error = ioctl(tc->fd, HWT_IOC_WAKEUP, &w); + + return (error); +} + +int +hwt_mmap_received(struct trace_context *tc, + struct hwt_record_user_entry *entry) +{ + int error; + + 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, entry->tid); + return (-1); + } + + tc->suspend_on_mmap = 0; + + /* Start tracing. */ + 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, entry->tid); + + return (0); +} + +static int +hwt_ctx_alloc(struct trace_context *tc) +{ + struct hwt_alloc al; + int error; + + al.mode = HWT_MODE_THREAD; + al.pid = tc->pid; + al.bufsize = tc->bufsize; + al.backend_name = tc->trace_dev->name; + + error = ioctl(tc->fd, HWT_IOC_ALLOC, &al); + + return (error); +} + +static int +hwt_map_memory(struct trace_context *tc, int tid) +{ + char filename[32]; + + sprintf(filename, "/dev/hwt_%d_%d", tc->pid, 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 int __unused +hwt_ncpu(void) +{ + int ncpu; + + ncpu = sysconf(_SC_NPROCESSORS_CONF); + + return (ncpu); +} + +size_t +hwt_get_offs(struct trace_context *tc, size_t *offs) +{ + struct hwt_bufptr_get bget; + vm_offset_t curpage_offset; + int curpage; + int error; + int ptr; + + bget.pid = tc->pid; + bget.ptr = &ptr; + bget.curpage = &curpage; + bget.curpage_offset = &curpage_offset; + error = ioctl(tc->thr_fd, HWT_IOC_BUFPTR_GET, &bget); + if (error) + return (error); + +#if 0 + printf("curpage %ld curpage_offset %ld\n", curpage, curpage_offset); +#endif + + *offs = curpage * PAGE_SIZE + curpage_offset; + + return (0); +} + +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; + + s.pid = tc->pid; + error = ioctl(tc->fd, HWT_IOC_START, &s); + + return (error); +} + +static void +usage(void) +{ + + errx(EX_USAGE, + "hwt [-c devname] [-b bufsize] [-t id] [-r] [-w file] [-i name]" + " [-f name] path\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" + "\t -i\tname\t\tfilter by dynamic library / executable name\n" + "\t -f\tname\t\tfilter by function name\n" + +#if defined(__aarch64__) +#endif + ); +} + +int +main(int argc, char **argv, char **env) +{ + struct hwt_record_user_entry *entry; + struct pmcstat_process *pp; + struct trace_context *tc; + struct stat st; + uint32_t tot_rec; + uint32_t nrec; + uint32_t nlibs; + char **cmd; + char *trace_dev_name; + int error; + int sockpair[NSOCKPAIRFD]; + int pid; + int option; + int i; + int found; + + 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); + } + + while ((option = getopt(argc, argv, "hc:b:rw:t:i:f:")) != -1) + switch (option) { + 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); + break; + case 'h': + usage(); + break; + default: + break; + } + + if (tc->raw != 0 && tc->filename == NULL) { + printf("Filename must be specified for the raw data.\n"); + exit(1); + } + + if (tc->filename != NULL) { + tc->f = fopen(tc->filename, "w"); + if (tc->f == NULL) { + printf("could not open file %s\n", tc->filename); + return (ENXIO); + } + } + + 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."); + + argv += optind; + argc += optind; + + cmd = argv; + + if (*cmd == NULL) + usage(); + + 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\n"); + return (error); + } + + nlibs += 1; /* add binary itself. */ + + printf("cmd is %s, nlibs %d\n", *cmd, nlibs); + + tc->fd = open("/dev/hwt", O_RDWR); + if (tc->fd < 0) { + printf("Can't open /dev/hwt\n"); + return (-1); + } + + error = hwt_process_create(sockpair, cmd, env, &pid); + if (error != 0) + return (error); + + printf("%s: process pid %d created\n", __func__, pid); + + pp = hwt_process_alloc(); + pp->pp_pid = pid; + pp->pp_isactive = 1; + + tc->pp = pp; + tc->pid = pid; + + error = hwt_ctx_alloc(tc); + if (error) { + printf("%s: failed to alloc ctx, pid %d error %d\n", __func__, + tc->pid, error); + return (error); + } + + error = hwt_get_records(tc, &nrec); + if (error != 0) + return (error); + + if (nrec != 1) + return (error); + + entry = &tc->records[0]; + + error = hwt_map_memory(tc, entry->tid); + if (error != 0) { + printf("can't map memory"); + return (error); + } + + if (tc->func_name != NULL) + tc->suspend_on_mmap = 1; + + error = tc->trace_dev->methods->set_config(tc); + if (error != 0) + errx(EX_DATAERR, "can't set config"); + + if (tc->func_name == NULL) { + /* 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); + } + + error = hwt_process_start(sockpair); + if (error != 0) + return (error); + + printf("nlibs %d\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(); + } while (tot_rec < nlibs); + + tc->trace_dev->methods->process(tc); + + close(tc->fd); + + if (tc->filename) + fclose(tc->f); + + return (0); +} 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,688 @@ +/*- + * 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 "hwtvar.h" +#include "hwt_coresight.h" + +#include "libpmcstat_stubs.h" +#include + +#define PMCTRACE_CS_DEBUG +#undef PMCTRACE_CS_DEBUG + +#ifdef PMCTRACE_CS_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 FLAG_CALLBACK_MEM_ACC (1 << 3) + +#define PACKET_STR_LEN 1024 +static char packet_str[PACKET_STR_LEN]; + +struct cs_decoder { + dcd_tree_handle_t dcdtree_handle; + int dp_ret; +}; + +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; + } +} + +#if 0 +static uint32_t +cs_decoder__mem_access(const void *context __unused, + const ocsd_vaddr_t address __unused, + const ocsd_mem_space_acc_t mem_space __unused, + const uint32_t req_size __unused, uint8_t *buffer __unused) +{ + + /* TODO */ + + return (0); +} +#endif + +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; + int ret; + + dprintf("%s\n", __func__); + +#if 0 + if (cs_flags & FLAG_CALLBACK_MEM_ACC) + ret = ocsd_dt_add_callback_mem_acc(handle, base + start, + base + end - 1, OCSD_MEM_SPACE_ANY, + cs_decoder__mem_access, NULL); + else +#endif + { + address = (ocsd_vaddr_t)tc->base; + + uint64_t *t; + t = (uint64_t *)tc->base; + printf("%lx %lx %lx %lx\n", t[0], t[1], t[2], t[3]); + + 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 (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 %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 (ret); +} + +static ocsd_err_t +create_decoder_etmv4(struct trace_context *tc, dcd_tree_handle_t dcd_tree_h) +{ + ocsd_etmv4_cfg trace_config; + ocsd_err_t ret; + + trace_config.arch_ver = ARCH_V8; + trace_config.core_prof = profile_CortexA; + + trace_config.reg_configr = 0x00001fc6; + trace_config.reg_configr = 0x000000C1; + trace_config.reg_traceidr = tc->thread_id + 1; + + trace_config.reg_idr0 = 0x28000ea1; + trace_config.reg_idr1 = 0x4100f424; + trace_config.reg_idr2 = 0x20001088; + trace_config.reg_idr8 = 0x0; + trace_config.reg_idr9 = 0x0; + trace_config.reg_idr10 = 0x0; + trace_config.reg_idr11 = 0x0; + trace_config.reg_idr12 = 0x0; + trace_config.reg_idr13 = 0x0; + + /* 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->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; + + if (tc->raw) { + error = cs_process_chunk_raw(tc, start, len, consumed); + return (error); + } + + base = (void *)((uintptr_t)tc->base + (uintptr_t)start); + + 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 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) +{ + const struct trace_context *tc; + struct pmcstat_image *image; + ocsd_datapath_resp_t resp; + struct pmcstat_symbol *sym; + unsigned long offset; + uint64_t newpc; + uint64_t ip; + FILE *out; + + tc = (const struct trace_context *)p_context; + if (tc->filename) + out = tc->f; + else + out = stdout; + + resp = OCSD_RESP_CONT; + +#if 0 + 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); +#endif + +#if 0 + if (elem->st_addr == -1) + return (resp); +#endif + + 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 0 + char ts[100]; + + if (elem->timestamp != 0) + sprintf(ts, "ts %ld", elem->timestamp); + else + sprintf(ts, " "); +#endif + + if (sym) { + offset = newpc - (sym->ps_start + image->pi_vaddr); + + fprintf(out, "pc 0x%08lx (%lx)\t%12s\t%s+0x%lx\n", + //elem->elem_type, + ip, newpc, + pmcstat_string_unintern(image->pi_name), + pmcstat_string_unintern(sym->ps_name), offset); + } else + if (image) + fprintf(out, "pc 0x%08lx (%lx)\t%12s\n", + //elem->elem_type, + ip, newpc, + pmcstat_string_unintern(image->pi_name)); + else { + /* image not found. */ + } + + return (resp); +} + +static int +hwt_coresight_init(struct trace_context *tc, struct cs_decoder *dec) +{ + int error; + + ocsd_def_errlog_init(OCSD_ERR_SEV_INFO, 1); + + 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); + } + + //cs_flags |= FLAG_FORMAT; + //cs_flags |= FLAG_FRAME_RAW_UNPACKED; + //cs_flags |= FLAG_FRAME_RAW_PACKED; + + error = create_decoder_etmv4(tc, dec->dcdtree_handle); + if (error != OCSD_OK) { + printf("can't create decoder: tc->base %#p\n", tc->base); + return (-2); + } + +#ifdef PMCTRACE_CS_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, tc); + + 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_INSTP0_LDRSTR; + reg |= TRCCONFIGR_COND_ALL; + config->cfg = reg; + + config->ts_ctrl = 0; + config->syncfreq = TRCSYNCPR_4K; + + excp_level = 1; /* User mode. */ + + reg = TRCVICTLR_SSSTATUS; + reg |= (1 << EVENT_SEL_S); + reg |= TRCVICTLR_EXLEVEL_NS(excp_level); + reg |= TRCVICTLR_EXLEVEL_S(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(excp_level); + reg |= TRCACATR_EXLEVEL_NS(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.pid = tc->pid; + 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->fd, HWT_IOC_SET_CONFIG, &sconf); + + return (error); +} + +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; + + /* Coresight data is always on CPU0 due to funnelling by HW. */ + + dec = malloc(sizeof(struct cs_decoder)); + + hwt_coresight_init(tc, dec); + + error = hwt_get_offs(tc, &offs); + if (error) + return (-1); + +#if 0 + printf("data to process %ld\n", offs); +#endif + + cursor = 0; + processed = 0; + totals = 0; + len = offs; + + cs_process_chunk(tc, dec, cursor, len, &processed); + cursor += processed; + totals += processed; + + t = 0; + + while (1) { + error = hwt_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(); + } else if (new_offs > cursor) { + /* New entries in the trace buffer. */ + len = new_offs - cursor; + cs_process_chunk(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_chunk(tc, dec, cursor, len, &processed); + cursor += processed; + totals += processed; + + cursor = 0; + len = new_offs; + cs_process_chunk(tc, dec, cursor, len, &processed); + cursor += processed; + totals += processed; + t = 0; + } + } + + printf("\nBytes processed: %ld\n", totals); + + return (0); +} + +struct trace_dev_methods cs_methods = { + .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,135 @@ +/*- + * 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 "hwtvar.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,140 @@ +/*- + * 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 "hwtvar.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_record.c =================================================================== --- /dev/null +++ usr.sbin/hwt/hwt_record.c @@ -0,0 +1,127 @@ +/*- + * 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 "hwtvar.h" + +#include "libpmcstat_stubs.h" +#include + +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; + 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.pid = tc->pid; + record_get.records = tc->records; + record_get.nentries = &nentries; + + error = ioctl(tc->fd, HWT_IOC_RECORD_GET, &record_get); + if (error != 0) { + printf("RECORD_GET error %d entires %d\n", + error, nentries); + return (error); + } + +printf("%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 size %lx\n", j, + entry->fullpath, + (unsigned long)entry->addr, + entry->size); + + path = pmcstat_string_intern(entry->fullpath); + if ((image = pmcstat_image_from_path(path, 0, + &args, &plugins)) == 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); +#if 0 + printf("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); +#endif + hwt_mmap_received(tc, entry); + break; + case HWT_RECORD_THREAD_CREATE: + case HWT_RECORD_THREAD_SET_NAME: + default: + break; + } + } + + *nrecords = nentries; + + return (0); +} Index: usr.sbin/hwt/hwtvar.h =================================================================== --- /dev/null +++ usr.sbin/hwt/hwtvar.h @@ -0,0 +1,91 @@ +/*- + * 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 (*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 pid; + int fd; + int thr_fd; + int terminate; + int thread_id; + + /* 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; + + /* Raw trace. */ + int raw; + + /* Trace file. */ + char *filename; + FILE *f; +}; + +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); +size_t hwt_get_offs(struct trace_context *tc, size_t *offs); +void hwt_sleep(void); +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_mmap_received(struct trace_context *tc, + struct hwt_record_user_entry *entry); + +#endif /* !_HWTVAR_H_ */ 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); +}