Page MenuHomeFreeBSD

D54826.id170233.diff
No OneTemporary

D54826.id170233.diff

Index: sys/conf/files
===================================================================
--- sys/conf/files
+++ sys/conf/files
@@ -1794,6 +1794,14 @@
dev/hid/u2f.c optional u2f
dev/hid/xb360gp.c optional xb360gp
dev/hptiop/hptiop.c optional hptiop scbus
+dev/hwc/hwc.c optional hwc
+dev/hwc/hwc_backend.c optional hwc
+dev/hwc/hwc_context.c optional hwc
+dev/hwc/hwc_contexthash.c optional hwc
+dev/hwc/hwc_ioctl.c optional hwc
+dev/hwc/hwc_owner.c optional hwc
+dev/hwc/hwc_ownerhash.c optional hwc
+dev/hwc/hwc_vm.c optional hwc
dev/hwpmc/hwpmc_logging.c optional hwpmc
dev/hwpmc/hwpmc_mod.c optional hwpmc
dev/hwpmc/hwpmc_soft.c optional hwpmc
Index: sys/conf/files.riscv
===================================================================
--- sys/conf/files.riscv
+++ sys/conf/files.riscv
@@ -40,6 +40,7 @@
libkern/strcmp.c standard
libkern/strlen.c standard
libkern/strncmp.c standard
+riscv/pmu/pmu.c optional hwc
riscv/riscv/aplic.c standard
riscv/riscv/autoconf.c standard
riscv/riscv/bus_machdep.c standard
Index: sys/dev/hwc/hwc.c
===================================================================
--- /dev/null
+++ sys/dev/hwc/hwc.c
@@ -0,0 +1,145 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025-2026 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Hardware Counting Framework */
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <dev/hwc/hwc_context.h>
+#include <dev/hwc/hwc_contexthash.h>
+#include <dev/hwc/hwc_owner.h>
+#include <dev/hwc/hwc_ownerhash.h>
+#include <dev/hwc/hwc_backend.h>
+#include <dev/hwc/hwc_ioctl.h>
+
+#define HWC_DEBUG
+#undef HWC_DEBUG
+
+#ifdef HWC_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+static eventhandler_tag hwc_exit_tag;
+static struct cdev *hwc_cdev;
+static struct cdevsw hwc_cdevsw = {
+ .d_version = D_VERSION,
+ .d_name = "hwc",
+ .d_mmap_single = NULL,
+ .d_ioctl = hwc_ioctl
+};
+
+static void
+hwc_process_exit(void *arg __unused, struct proc *p)
+{
+ struct hwc_owner *ho;
+
+ /* Stop HWCs associated with exiting owner, if any. */
+ ho = hwc_ownerhash_lookup(p);
+ if (ho)
+ hwc_owner_shutdown(ho);
+}
+
+static int
+hwc_load(void)
+{
+ struct make_dev_args args;
+ int error;
+
+ make_dev_args_init(&args);
+ args.mda_devsw = &hwc_cdevsw;
+ args.mda_flags = MAKEDEV_CHECKNAME | MAKEDEV_WAITOK;
+ args.mda_uid = UID_ROOT;
+ args.mda_gid = GID_WHEEL;
+ args.mda_mode = 0660;
+ args.mda_si_drv1 = NULL;
+
+ hwc_backend_load();
+ hwc_ctx_load();
+ hwc_contexthash_load();
+ hwc_ownerhash_load();
+
+ error = make_dev_s(&args, &hwc_cdev, "hwc");
+ if (error != 0)
+ return (error);
+
+ hwc_exit_tag = EVENTHANDLER_REGISTER(process_exit, hwc_process_exit,
+ NULL, EVENTHANDLER_PRI_ANY);
+
+ return (0);
+}
+
+static int
+hwc_unload(void)
+{
+
+ EVENTHANDLER_DEREGISTER(process_exit, hwc_exit_tag);
+ destroy_dev(hwc_cdev);
+ hwc_ownerhash_unload();
+ hwc_contexthash_unload();
+ hwc_ctx_unload();
+ hwc_backend_unload();
+
+ return (0);
+}
+
+static int
+hwc_modevent(module_t mod, int type, void *data)
+{
+ int error;
+
+ switch (type) {
+ case MOD_LOAD:
+ error = hwc_load();
+ break;
+ case MOD_UNLOAD:
+ error = hwc_unload();
+ break;
+ default:
+ error = 0;
+ break;
+ }
+
+ return (error);
+}
+
+static moduledata_t hwc_mod = {
+ "hwc",
+ hwc_modevent,
+ NULL
+};
+
+DECLARE_MODULE(hwc, hwc_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(hwc, 1);
Index: sys/dev/hwc/hwc_backend.h
===================================================================
--- /dev/null
+++ sys/dev/hwc/hwc_backend.h
@@ -0,0 +1,89 @@
+/*-
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _DEV_HWT_HWT_BACKEND_H_
+#define _DEV_HWT_HWT_BACKEND_H_
+
+struct hwc_vm;
+struct hwc_configure;
+struct hwc_start;
+struct hwc_stop;
+
+struct hwc_backend_ops {
+ int (*hwc_backend_init)(struct hwc_context *);
+ int (*hwc_backend_deinit)(struct hwc_context *);
+ int (*hwc_backend_configure)(struct hwc_context *,
+ struct hwc_configure *hc);
+ int (*hwc_backend_svc_buf)(struct hwc_context *, void *data,
+ size_t data_size, int data_version);
+ void (*hwc_backend_enable)(struct hwc_context *, int cpu_id);
+ void (*hwc_backend_disable)(struct hwc_context *, int cpu_id);
+ int (*hwc_backend_read)(struct hwc_vm *, int *ident,
+ vm_offset_t *offset, uint64_t *data);
+ int (*hwc_backend_stop)(struct hwc_context *, struct hwc_stop *);
+ int (*hwc_backend_start)(struct hwc_context *, struct hwc_start *);
+ /* For backends that are tied to local CPU registers */
+ int (*hwc_backend_enable_smp)(struct hwc_context *);
+ int (*hwc_backend_disable_smp)(struct hwc_context *);
+ /* Debugging only. */
+ void (*hwc_backend_dump)(int cpu_id);
+};
+
+struct hwc_backend {
+ const char *name;
+ struct hwc_backend_ops *ops;
+ /* buffers require kernel virtual addresses */
+ bool kva_req;
+};
+
+int hwc_backend_init(struct hwc_context *ctx);
+void hwc_backend_deinit(struct hwc_context *ctx);
+int hwc_backend_configure(struct hwc_context *ctx, struct hwc_configure *hc);
+void hwc_backend_enable(struct hwc_context *ctx, int cpu_id);
+void hwc_backend_disable(struct hwc_context *ctx, int cpu_id);
+void hwc_backend_enable_smp(struct hwc_context *ctx);
+void hwc_backend_disable_smp(struct hwc_context *ctx);
+void hwc_backend_dump(struct hwc_context *ctx, int cpu_id);
+int hwc_backend_read(struct hwc_context *ctx, struct hwc_vm *vm, int *ident,
+ vm_offset_t *offset, uint64_t *data);
+int hwc_backend_register(struct hwc_backend *);
+int hwc_backend_unregister(struct hwc_backend *);
+int hwc_backend_stop(struct hwc_context *, struct hwc_stop *);
+int hwc_backend_start(struct hwc_context *, struct hwc_start *);
+int hwc_backend_svc_buf(struct hwc_context *ctx, void *data, size_t data_size,
+ int data_version);
+struct hwc_backend * hwc_backend_lookup(const char *name);
+
+void hwc_backend_load(void);
+void hwc_backend_unload(void);
+
+#define HWT_BACKEND_LOCK() mtx_lock(&hwc_backend_mtx)
+#define HWT_BACKEND_UNLOCK() mtx_unlock(&hwc_backend_mtx)
+
+#endif /* !_DEV_HWT_HWT_BACKEND_H_ */
+
Index: sys/dev/hwc/hwc_backend.c
===================================================================
--- /dev/null
+++ sys/dev/hwc/hwc_backend.c
@@ -0,0 +1,257 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023-2026 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Hardware Trace (HWT) framework. */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/hwc.h>
+
+#include <dev/hwc/hwc_context.h>
+#include <dev/hwc/hwc_backend.h>
+
+#define HWT_BACKEND_DEBUG
+#undef HWT_BACKEND_DEBUG
+
+#ifdef HWT_BACKEND_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+static struct mtx hwc_backend_mtx;
+
+struct hwc_backend_entry {
+ struct hwc_backend *backend;
+ LIST_ENTRY(hwc_backend_entry) next;
+};
+
+static LIST_HEAD(, hwc_backend_entry) hwc_backends;
+
+static MALLOC_DEFINE(M_HWT_BACKEND, "hwc_backend", "HWT backend");
+
+int
+hwc_backend_init(struct hwc_context *ctx)
+{
+ int error;
+
+ dprintf("%s\n", __func__);
+
+ error = ctx->hwc_backend->ops->hwc_backend_init(ctx);
+
+ return (error);
+}
+
+void
+hwc_backend_deinit(struct hwc_context *ctx)
+{
+
+ dprintf("%s\n", __func__);
+
+ ctx->hwc_backend->ops->hwc_backend_deinit(ctx);
+}
+
+int
+hwc_backend_configure(struct hwc_context *ctx, struct hwc_configure *hc)
+{
+ int error;
+
+ dprintf("%s\n", __func__);
+
+ error = ctx->hwc_backend->ops->hwc_backend_configure(ctx, hc);
+
+ return (error);
+}
+
+void
+hwc_backend_enable(struct hwc_context *ctx, int cpu_id)
+{
+
+ dprintf("%s\n", __func__);
+
+ ctx->hwc_backend->ops->hwc_backend_enable(ctx, cpu_id);
+}
+
+void
+hwc_backend_disable(struct hwc_context *ctx, int cpu_id)
+{
+
+ dprintf("%s\n", __func__);
+
+ ctx->hwc_backend->ops->hwc_backend_disable(ctx, cpu_id);
+}
+
+void
+hwc_backend_enable_smp(struct hwc_context *ctx)
+{
+
+ dprintf("%s\n", __func__);
+
+ ctx->hwc_backend->ops->hwc_backend_enable_smp(ctx);
+}
+
+void
+hwc_backend_disable_smp(struct hwc_context *ctx)
+{
+
+ dprintf("%s\n", __func__);
+
+ ctx->hwc_backend->ops->hwc_backend_disable_smp(ctx);
+}
+
+void __unused
+hwc_backend_dump(struct hwc_context *ctx, int cpu_id)
+{
+
+ dprintf("%s\n", __func__);
+
+ ctx->hwc_backend->ops->hwc_backend_dump(cpu_id);
+}
+
+int
+hwc_backend_read(struct hwc_context *ctx, struct hwc_vm *vm, int *ident,
+ vm_offset_t *offset, uint64_t *data)
+{
+ int error;
+
+ dprintf("%s\n", __func__);
+
+ error = ctx->hwc_backend->ops->hwc_backend_read(vm, ident,
+ offset, data);
+
+ return (error);
+}
+
+struct hwc_backend *
+hwc_backend_lookup(const char *name)
+{
+ struct hwc_backend_entry *entry;
+ struct hwc_backend *backend;
+
+ HWT_BACKEND_LOCK();
+ LIST_FOREACH(entry, &hwc_backends, next) {
+ backend = entry->backend;
+ if (strcmp(backend->name, name) == 0) {
+ HWT_BACKEND_UNLOCK();
+ return (backend);
+ }
+ }
+ HWT_BACKEND_UNLOCK();
+
+ return (NULL);
+}
+
+int
+hwc_backend_register(struct hwc_backend *backend)
+{
+ struct hwc_backend_entry *entry;
+
+ if (backend == NULL ||
+ backend->name == NULL ||
+ backend->ops == NULL)
+ return (EINVAL);
+
+ entry = malloc(sizeof(struct hwc_backend_entry), M_HWT_BACKEND,
+ M_WAITOK | M_ZERO);
+ entry->backend = backend;
+
+ HWT_BACKEND_LOCK();
+ LIST_INSERT_HEAD(&hwc_backends, entry, next);
+ HWT_BACKEND_UNLOCK();
+
+ return (0);
+}
+
+int
+hwc_backend_unregister(struct hwc_backend *backend)
+{
+ struct hwc_backend_entry *entry, *tmp;
+
+ if (backend == NULL)
+ return (EINVAL);
+
+ /* TODO: check if not in use */
+
+ HWT_BACKEND_LOCK();
+ LIST_FOREACH_SAFE(entry, &hwc_backends, next, tmp) {
+ if (entry->backend == backend) {
+ LIST_REMOVE(entry, next);
+ HWT_BACKEND_UNLOCK();
+ free(entry, M_HWT_BACKEND);
+ return (0);
+ }
+ }
+ HWT_BACKEND_UNLOCK();
+
+ return (ENOENT);
+}
+
+void
+hwc_backend_load(void)
+{
+
+ mtx_init(&hwc_backend_mtx, "hwc backend", NULL, MTX_DEF);
+ LIST_INIT(&hwc_backends);
+}
+
+void
+hwc_backend_unload(void)
+{
+
+ /* TODO: ensure all unregistered */
+
+ mtx_destroy(&hwc_backend_mtx);
+}
+
+int
+hwc_backend_stop(struct hwc_context *ctx, struct hwc_stop *hs)
+{
+ int error;
+
+ dprintf("%s\n", __func__);
+
+ error = ctx->hwc_backend->ops->hwc_backend_stop(ctx, hs);
+
+ return (error);
+}
+
+int
+hwc_backend_start(struct hwc_context *ctx, struct hwc_start *hs)
+{
+ int error;
+
+ dprintf("%s\n", __func__);
+
+ error = ctx->hwc_backend->ops->hwc_backend_start(ctx, hs);
+
+ return (error);
+}
Index: sys/dev/hwc/hwc_context.h
===================================================================
--- /dev/null
+++ sys/dev/hwc/hwc_context.h
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _DEV_HWT_HWT_CONTEXT_H_
+#define _DEV_HWT_HWT_CONTEXT_H_
+
+enum hwc_ctx_state {
+ CTX_STATE_STOPPED,
+ CTX_STATE_RUNNING,
+};
+
+struct hwc_context {
+ LIST_ENTRY(hwc_context) next_hch; /* Entry in contexthash. */
+ LIST_ENTRY(hwc_context) next_hwcs; /* Entry in ho->hwcs. */
+
+ int mode;
+ int ident;
+
+ int kqueue_fd;
+ struct thread *hwc_td;
+
+ /* CPU mode. */
+ cpuset_t cpu_map;
+ TAILQ_HEAD(, hwc_cpu) cpus;
+
+ /* Thread mode. */
+ struct proc *proc; /* Target proc. */
+ pid_t pid; /* Target pid. */
+
+ size_t bufsize; /* Trace bufsize for each vm.*/
+
+ void *config;
+ size_t config_size;
+ int config_version;
+
+ struct hwc_owner *hwc_owner;
+ struct hwc_backend *hwc_backend;
+
+ struct mtx mtx;
+ struct mtx rec_mtx;
+ enum hwc_ctx_state state;
+ int refcnt;
+
+ struct hwc_vm *vm;
+};
+
+#define HWT_CTX_LOCK(ctx) mtx_lock_spin(&(ctx)->mtx)
+#define HWT_CTX_UNLOCK(ctx) mtx_unlock_spin(&(ctx)->mtx)
+#define HWT_CTX_ASSERT_LOCKED(ctx) mtx_assert(&(ctx)->mtx, MA_OWNED)
+
+int hwc_ctx_alloc(struct hwc_context **ctx0);
+void hwc_ctx_free(struct hwc_context *ctx);
+void hwc_ctx_put(struct hwc_context *ctx);
+
+void hwc_ctx_load(void);
+void hwc_ctx_unload(void);
+
+#endif /* !_DEV_HWT_HWT_CONTEXT_H_ */
Index: sys/dev/hwc/hwc_context.c
===================================================================
--- /dev/null
+++ sys/dev/hwc/hwc_context.c
@@ -0,0 +1,142 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/bitstring.h>
+#include <sys/conf.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mman.h>
+#include <sys/mutex.h>
+#include <sys/refcount.h>
+#include <sys/rwlock.h>
+#include <sys/hwc.h>
+
+#include <dev/hwc/hwc_context.h>
+#include <dev/hwc/hwc_owner.h>
+#include <dev/hwc/hwc_vm.h>
+
+#define HWT_DEBUG
+#undef HWT_DEBUG
+
+#ifdef HWT_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+static MALLOC_DEFINE(M_HWT_CTX, "hwc_ctx", "Hardware Trace");
+
+static bitstr_t *ident_set;
+static int ident_set_size;
+static struct mtx ident_set_mutex;
+
+static int
+hwc_ctx_ident_alloc(int *new_ident)
+{
+
+ mtx_lock(&ident_set_mutex);
+ bit_ffc(ident_set, ident_set_size, new_ident);
+ if (*new_ident == -1) {
+ mtx_unlock(&ident_set_mutex);
+ return (ENOMEM);
+ }
+ bit_set(ident_set, *new_ident);
+ mtx_unlock(&ident_set_mutex);
+
+ return (0);
+}
+
+static void
+hwc_ctx_ident_free(int ident)
+{
+
+ mtx_lock(&ident_set_mutex);
+ bit_clear(ident_set, ident);
+ mtx_unlock(&ident_set_mutex);
+}
+
+int
+hwc_ctx_alloc(struct hwc_context **ctx0)
+{
+ struct hwc_context *ctx;
+ int error;
+
+ ctx = malloc(sizeof(struct hwc_context), M_HWT_CTX, M_WAITOK | M_ZERO);
+
+ TAILQ_INIT(&ctx->cpus);
+ mtx_init(&ctx->mtx, "ctx", NULL, MTX_SPIN);
+ mtx_init(&ctx->rec_mtx, "ctx_rec", NULL, MTX_DEF);
+ refcount_init(&ctx->refcnt, 0);
+
+ error = hwc_ctx_ident_alloc(&ctx->ident);
+ if (error) {
+ printf("could not allocate ident bit str\n");
+ return (error);
+ }
+
+ *ctx0 = ctx;
+
+ return (0);
+}
+
+void
+hwc_ctx_free(struct hwc_context *ctx)
+{
+
+ hwc_vm_free(ctx->vm);
+ hwc_ctx_ident_free(ctx->ident);
+ free(ctx, M_HWT_CTX);
+}
+
+void
+hwc_ctx_put(struct hwc_context *ctx)
+{
+
+ refcount_release(&ctx->refcnt);
+}
+
+void
+hwc_ctx_load(void)
+{
+
+ ident_set_size = (1 << 8);
+ ident_set = bit_alloc(ident_set_size, M_HWT_CTX, M_WAITOK);
+ mtx_init(&ident_set_mutex, "ident set", NULL, MTX_DEF);
+}
+
+void
+hwc_ctx_unload(void)
+{
+
+ mtx_destroy(&ident_set_mutex);
+ free(ident_set, M_HWT_CTX);
+}
Index: sys/dev/hwc/hwc_contexthash.h
===================================================================
--- /dev/null
+++ sys/dev/hwc/hwc_contexthash.h
@@ -0,0 +1,42 @@
+/*-
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _DEV_HWT_HWT_CONTEXTHASH_H_
+#define _DEV_HWT_HWT_CONTEXTHASH_H_
+
+struct hwc_context * hwc_contexthash_lookup(struct proc *p);
+void hwc_contexthash_insert(struct hwc_context *ctx);
+void hwc_contexthash_remove(struct hwc_context *ctx);
+
+void hwc_contexthash_load(void);
+void hwc_contexthash_unload(void);
+
+#define HWT_CTXHASH_LOCK() mtx_lock_spin(&hwc_contexthash_mtx)
+#define HWT_CTXHASH_UNLOCK() mtx_unlock_spin(&hwc_contexthash_mtx)
+
+#endif /* !_DEV_HWT_HWT_CONTEXTHASH_H_ */
Index: sys/dev/hwc/hwc_contexthash.c
===================================================================
--- /dev/null
+++ sys/dev/hwc/hwc_contexthash.c
@@ -0,0 +1,133 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/refcount.h>
+#include <sys/hwc.h>
+
+#include <dev/hwc/hwc_context.h>
+#include <dev/hwc/hwc_contexthash.h>
+
+#define HWT_DEBUG
+#undef HWT_DEBUG
+
+#ifdef HWT_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+#define HWT_CONTEXTHASH_SIZE 1024
+
+static MALLOC_DEFINE(M_HWT_CONTEXTHASH, "hwc_chash", "Hardware Trace");
+
+/*
+ * Hash function. Discard the lower 2 bits of the pointer since
+ * these are always zero for our uses. The hash multiplier is
+ * round((2^LONG_BIT) * ((sqrt(5)-1)/2)).
+ */
+
+#define _HWT_HM 11400714819323198486u /* hash multiplier */
+#define HWT_HASH_PTR(P, M) ((((unsigned long) (P) >> 2) * _HWT_HM) & (M))
+
+static struct mtx hwc_contexthash_mtx;
+static u_long hwc_contexthashmask;
+static LIST_HEAD(hwc_contexthash, hwc_context) *hwc_contexthash;
+
+/*
+ * To use by hwc_switch_in/out() only.
+ * This function returns with refcnt acquired.
+ */
+struct hwc_context *
+hwc_contexthash_lookup(struct proc *p)
+{
+ struct hwc_contexthash *hch;
+ struct hwc_context *ctx;
+ int hindex;
+
+ hindex = HWT_HASH_PTR(p, hwc_contexthashmask);
+ hch = &hwc_contexthash[hindex];
+
+ HWT_CTXHASH_LOCK();
+ LIST_FOREACH(ctx, hch, next_hch) {
+ if (ctx->proc == p) {
+ refcount_acquire(&ctx->refcnt);
+ HWT_CTXHASH_UNLOCK();
+ return (ctx);
+ }
+ }
+ HWT_CTXHASH_UNLOCK();
+
+ return (NULL);
+}
+
+void
+hwc_contexthash_insert(struct hwc_context *ctx)
+{
+ struct hwc_contexthash *hch;
+ int hindex;
+
+ hindex = HWT_HASH_PTR(ctx->proc, hwc_contexthashmask);
+ hch = &hwc_contexthash[hindex];
+
+ HWT_CTXHASH_LOCK();
+ LIST_INSERT_HEAD(hch, ctx, next_hch);
+ HWT_CTXHASH_UNLOCK();
+}
+
+void
+hwc_contexthash_remove(struct hwc_context *ctx)
+{
+
+ HWT_CTXHASH_LOCK();
+ LIST_REMOVE(ctx, next_hch);
+ HWT_CTXHASH_UNLOCK();
+}
+
+void
+hwc_contexthash_load(void)
+{
+
+ hwc_contexthash = hashinit(HWT_CONTEXTHASH_SIZE, M_HWT_CONTEXTHASH,
+ &hwc_contexthashmask);
+ mtx_init(&hwc_contexthash_mtx, "hwc ctx hash", "hwc ctx", MTX_SPIN);
+}
+
+void
+hwc_contexthash_unload(void)
+{
+
+ mtx_destroy(&hwc_contexthash_mtx);
+ hashdestroy(hwc_contexthash, M_HWT_CONTEXTHASH, hwc_contexthashmask);
+}
Index: sys/dev/hwc/hwc_ioctl.h
===================================================================
--- /dev/null
+++ sys/dev/hwc/hwc_ioctl.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _DEV_HWC_HWC_IOCTL_H
+#define _DEV_HWC_HWC_IOCTL_H
+
+int hwc_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
+ struct thread *td);
+
+#endif /* !_DEV_HWC_HWC_IOCTL_H */
Index: sys/dev/hwc/hwc_ioctl.c
===================================================================
--- /dev/null
+++ sys/dev/hwc/hwc_ioctl.c
@@ -0,0 +1,278 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Hardware Trace (HWC) framework. */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/ioccom.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mman.h>
+#include <sys/mutex.h>
+#include <sys/refcount.h>
+#include <sys/rwlock.h>
+#include <sys/smp.h>
+#include <sys/hwc.h>
+
+#include <dev/hwc/hwc_context.h>
+#include <dev/hwc/hwc_contexthash.h>
+#include <dev/hwc/hwc_owner.h>
+#include <dev/hwc/hwc_ownerhash.h>
+#include <dev/hwc/hwc_backend.h>
+#include <dev/hwc/hwc_ioctl.h>
+#include <dev/hwc/hwc_vm.h>
+
+#define HWC_IOCTL_DEBUG
+#undef HWC_IOCTL_DEBUG
+
+#ifdef HWC_IOCTL_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+/* No real reason for these limitations just sanity checks. */
+#define HWC_MAXBUFSIZE (32UL * 1024 * 1024 * 1024) /* 32 GB */
+
+static MALLOC_DEFINE(M_HWC_IOCTL, "hwc_ioctl", "Hardware Counting");
+
+/*
+ * Check if owner process *o can trace target process *t.
+ */
+
+static int
+hwc_priv_check(struct proc *o, struct proc *t)
+{
+ struct ucred *oc, *tc;
+ int error;
+ int i;
+
+ PROC_LOCK(o);
+ oc = o->p_ucred;
+ crhold(oc);
+ PROC_UNLOCK(o);
+
+ PROC_LOCK_ASSERT(t, MA_OWNED);
+ tc = t->p_ucred;
+ crhold(tc);
+
+ error = 0;
+
+ /*
+ * The effective uid of the HWC owner should match at least one
+ * of the effective / real / saved uids of the target process.
+ */
+
+ if (oc->cr_uid != tc->cr_uid &&
+ oc->cr_uid != tc->cr_svuid &&
+ oc->cr_uid != tc->cr_ruid) {
+ error = EPERM;
+ goto done;
+ }
+
+ /*
+ * Everyone of the target's group ids must be in the owner's
+ * group list.
+ */
+ for (i = 0; i < tc->cr_ngroups; i++)
+ if (!groupmember(tc->cr_groups[i], oc)) {
+ error = EPERM;
+ goto done;
+ }
+
+ /* Check the read and saved GIDs too. */
+ if (!groupmember(tc->cr_rgid, oc) ||
+ !groupmember(tc->cr_svgid, oc)) {
+ error = EPERM;
+ goto done;
+ }
+
+done:
+ crfree(tc);
+ crfree(oc);
+
+ return (error);
+}
+
+static int
+hwc_ioctl_alloc_mode_thread(struct thread *td, struct hwc_owner *ho,
+ struct hwc_backend *backend, struct hwc_alloc *halloc)
+{
+ struct hwc_context *ctx, *ctx1;
+ char path[MAXPATHLEN];
+ struct proc *p;
+ int error;
+ struct hwc_vm *vm;
+
+ /* Check if the owner have this pid configured already. */
+ ctx = hwc_owner_lookup_ctx(ho, halloc->pid);
+ if (ctx)
+ return (EEXIST);
+
+ /* Allocate a new HWC context. */
+ error = hwc_ctx_alloc(&ctx);
+ if (error)
+ return (error);
+ ctx->pid = halloc->pid;
+ ctx->hwc_backend = backend;
+ ctx->hwc_owner = ho;
+ ctx->mode = HWC_MODE_THREAD;
+ ctx->hwc_td = td;
+
+ error = copyout(&ctx->ident, halloc->ident, sizeof(int));
+ if (error) {
+ hwc_ctx_free(ctx);
+ return (error);
+ }
+
+ /* Now get the victim proc. */
+ p = pfind(halloc->pid);
+ if (p == NULL) {
+ hwc_ctx_free(ctx);
+ return (ENXIO);
+ }
+
+ /* Ensure we can trace it. */
+ error = hwc_priv_check(td->td_proc, p);
+ if (error) {
+ PROC_UNLOCK(p);
+ hwc_ctx_free(ctx);
+ return (error);
+ }
+
+ /* Ensure it is not being traced already. */
+ ctx1 = hwc_contexthash_lookup(p);
+ if (ctx1) {
+ refcount_release(&ctx1->refcnt);
+ PROC_UNLOCK(p);
+ hwc_ctx_free(ctx);
+ return (EEXIST);
+ }
+
+ ctx->proc = p;
+ PROC_UNLOCK(p);
+
+ sprintf(path, "hwc_%d", ctx->ident);
+
+ error = hwc_vm_alloc(0, 0, path, &vm);
+ if (error) {
+ hwc_ctx_free(ctx);
+ return (error);
+ }
+
+ ctx->vm = vm;
+ vm->ctx = ctx;
+
+ error = hwc_backend_init(ctx);
+ if (error) {
+ hwc_ctx_free(ctx);
+ return (error);
+ }
+
+ /* hwc_owner_insert_ctx? */
+ mtx_lock(&ho->mtx);
+ LIST_INSERT_HEAD(&ho->hwcs, ctx, next_hwcs);
+ mtx_unlock(&ho->mtx);
+
+ /*
+ * Hooks are now in action after this, but the ctx is not in RUNNING
+ * state.
+ */
+ hwc_contexthash_insert(ctx);
+
+ p = pfind(halloc->pid);
+ if (p) {
+ p->p_flag2 |= P2_HWC;
+ PROC_UNLOCK(p);
+ }
+
+ return (0);
+}
+
+static int
+hwc_ioctl_alloc(struct thread *td, struct hwc_alloc *halloc)
+{
+ char backend_name[HWC_BACKEND_MAXNAMELEN];
+ struct hwc_backend *backend;
+ struct hwc_owner *ho;
+ int error;
+
+ dprintf("%s\n", __func__);
+
+ if (halloc->backend_name == NULL)
+ return (EINVAL);
+
+ error = copyinstr(halloc->backend_name, (void *)backend_name,
+ HWC_BACKEND_MAXNAMELEN, NULL);
+ if (error)
+ return (error);
+
+ backend = hwc_backend_lookup(backend_name);
+ if (backend == NULL)
+ return (ENODEV);
+
+ /* First get the owner. */
+ ho = hwc_ownerhash_lookup(td->td_proc);
+ if (ho == NULL) {
+ /* Create a new owner. */
+ ho = hwc_owner_alloc(td->td_proc);
+ if (ho == NULL)
+ return (ENOMEM);
+ hwc_ownerhash_insert(ho);
+ }
+
+ switch (halloc->mode) {
+ case HWC_MODE_THREAD:
+ error = hwc_ioctl_alloc_mode_thread(td, ho, backend, halloc);
+ break;
+ default:
+ error = ENXIO;
+ };
+
+ return (error);
+}
+
+int
+hwc_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
+ struct thread *td)
+{
+ int error;
+
+ /* Allocate HWC context. */
+
+ switch (cmd) {
+ case HWC_IOC_ALLOC:
+ error = hwc_ioctl_alloc(td, (struct hwc_alloc *)addr);
+ return (error);
+ default:
+ return (ENXIO);
+ };
+}
Index: sys/dev/hwc/hwc_owner.h
===================================================================
--- /dev/null
+++ sys/dev/hwc/hwc_owner.h
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _DEV_HWT_HWT_OWNER_H_
+#define _DEV_HWT_HWT_OWNER_H_
+
+struct hwc_owner {
+ struct proc *p;
+ struct mtx mtx; /* Protects hwcs. */
+ LIST_HEAD(, hwc_context) hwcs; /* Owned HWTs. */
+ LIST_ENTRY(hwc_owner) next; /* Entry in hwc owner hash. */
+};
+
+struct hwc_context * hwc_owner_lookup_ctx(struct hwc_owner *ho, pid_t pid);
+struct hwc_owner * hwc_owner_alloc(struct proc *p);
+void hwc_owner_shutdown(struct hwc_owner *ho);
+struct hwc_context * hwc_owner_lookup_ctx_by_cpu(struct hwc_owner *ho, int cpu);
+
+#endif /* !_DEV_HWT_HWT_OWNER_H_ */
Index: sys/dev/hwc/hwc_owner.c
===================================================================
--- /dev/null
+++ sys/dev/hwc/hwc_owner.c
@@ -0,0 +1,132 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mman.h>
+#include <sys/mutex.h>
+#include <sys/refcount.h>
+#include <sys/rwlock.h>
+#include <sys/hwc.h>
+
+#include <dev/hwc/hwc_context.h>
+#include <dev/hwc/hwc_contexthash.h>
+#include <dev/hwc/hwc_owner.h>
+#include <dev/hwc/hwc_ownerhash.h>
+#include <dev/hwc/hwc_backend.h>
+#include <dev/hwc/hwc_vm.h>
+
+#define HWT_DEBUG
+#undef HWT_DEBUG
+
+#ifdef HWT_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+static MALLOC_DEFINE(M_HWT_OWNER, "hwc_owner", "Hardware Trace");
+
+struct hwc_context *
+hwc_owner_lookup_ctx(struct hwc_owner *ho, pid_t pid)
+{
+ struct hwc_context *ctx;
+
+ mtx_lock(&ho->mtx);
+ LIST_FOREACH(ctx, &ho->hwcs, next_hwcs) {
+ if (ctx->pid == pid) {
+ mtx_unlock(&ho->mtx);
+ return (ctx);
+ }
+ }
+ mtx_unlock(&ho->mtx);
+
+ return (NULL);
+}
+
+struct hwc_owner *
+hwc_owner_alloc(struct proc *p)
+{
+ struct hwc_owner *ho;
+
+ ho = malloc(sizeof(struct hwc_owner), M_HWT_OWNER,
+ M_WAITOK | M_ZERO);
+ ho->p = p;
+
+ LIST_INIT(&ho->hwcs);
+ mtx_init(&ho->mtx, "hwcs", NULL, MTX_DEF);
+
+ return (ho);
+}
+
+void
+hwc_owner_shutdown(struct hwc_owner *ho)
+{
+ struct hwc_context *ctx;
+
+ dprintf("%s: stopping hwc owner\n", __func__);
+
+ while (1) {
+ mtx_lock(&ho->mtx);
+ ctx = LIST_FIRST(&ho->hwcs);
+ if (ctx)
+ LIST_REMOVE(ctx, next_hwcs);
+ mtx_unlock(&ho->mtx);
+
+ if (ctx == NULL)
+ break;
+
+ if (ctx->mode == HWC_MODE_THREAD)
+ hwc_contexthash_remove(ctx);
+
+ /*
+ * A hook could be still dealing with this ctx right here.
+ */
+
+ HWT_CTX_LOCK(ctx);
+ ctx->state = 0;
+ HWT_CTX_UNLOCK(ctx);
+
+ /* Ensure hooks invocation is now completed. */
+ while (refcount_load(&ctx->refcnt) > 0)
+ continue;
+
+ /*
+ * Note that a thread could be still sleeping on msleep(9).
+ */
+
+ hwc_backend_deinit(ctx);
+ hwc_ctx_free(ctx);
+ }
+
+ hwc_ownerhash_remove(ho);
+ free(ho, M_HWT_OWNER);
+}
Index: sys/dev/hwc/hwc_ownerhash.h
===================================================================
--- /dev/null
+++ sys/dev/hwc/hwc_ownerhash.h
@@ -0,0 +1,42 @@
+/*-
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _DEV_HWT_HWT_OWNERHASH_H_
+#define _DEV_HWT_HWT_OWNERHASH_H_
+
+struct hwc_owner * hwc_ownerhash_lookup(struct proc *p);
+void hwc_ownerhash_insert(struct hwc_owner *ho);
+void hwc_ownerhash_remove(struct hwc_owner *ho);
+
+void hwc_ownerhash_load(void);
+void hwc_ownerhash_unload(void);
+
+#define HWT_OWNERHASH_LOCK() mtx_lock(&hwc_ownerhash_mtx)
+#define HWT_OWNERHASH_UNLOCK() mtx_unlock(&hwc_ownerhash_mtx)
+
+#endif /* !_DEV_HWT_HWT_OWNERHASH_H_ */
Index: sys/dev/hwc/hwc_ownerhash.c
===================================================================
--- /dev/null
+++ sys/dev/hwc/hwc_ownerhash.c
@@ -0,0 +1,141 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mman.h>
+#include <sys/mutex.h>
+#include <sys/rwlock.h>
+#include <sys/hwc.h>
+
+#include <dev/hwc/hwc_owner.h>
+#include <dev/hwc/hwc_ownerhash.h>
+
+#define HWT_DEBUG
+#undef HWT_DEBUG
+
+#ifdef HWT_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+#define HWT_OWNERHASH_SIZE 1024
+
+static MALLOC_DEFINE(M_HWT_OWNERHASH, "hwc_ohash", "Hardware Trace");
+
+/*
+ * Hash function. Discard the lower 2 bits of the pointer since
+ * these are always zero for our uses. The hash multiplier is
+ * round((2^LONG_BIT) * ((sqrt(5)-1)/2)).
+ */
+
+#define _HWT_HM 11400714819323198486u /* hash multiplier */
+#define HWT_HASH_PTR(P, M) ((((unsigned long) (P) >> 2) * _HWT_HM) & (M))
+
+static struct mtx hwc_ownerhash_mtx;
+static u_long hwc_ownerhashmask;
+static LIST_HEAD(hwc_ownerhash, hwc_owner) *hwc_ownerhash;
+
+struct hwc_owner *
+hwc_ownerhash_lookup(struct proc *p)
+{
+ struct hwc_ownerhash *hoh;
+ struct hwc_owner *ho;
+ int hindex;
+
+ hindex = HWT_HASH_PTR(p, hwc_ownerhashmask);
+ hoh = &hwc_ownerhash[hindex];
+
+ HWT_OWNERHASH_LOCK();
+ LIST_FOREACH(ho, hoh, next) {
+ if (ho->p == p) {
+ HWT_OWNERHASH_UNLOCK();
+ return (ho);
+ }
+ }
+ HWT_OWNERHASH_UNLOCK();
+
+ return (NULL);
+}
+
+void
+hwc_ownerhash_insert(struct hwc_owner *ho)
+{
+ struct hwc_ownerhash *hoh;
+ int hindex;
+
+ hindex = HWT_HASH_PTR(ho->p, hwc_ownerhashmask);
+ hoh = &hwc_ownerhash[hindex];
+
+ HWT_OWNERHASH_LOCK();
+ LIST_INSERT_HEAD(hoh, ho, next);
+ HWT_OWNERHASH_UNLOCK();
+}
+
+void
+hwc_ownerhash_remove(struct hwc_owner *ho)
+{
+
+ /* Destroy hwc owner. */
+ HWT_OWNERHASH_LOCK();
+ LIST_REMOVE(ho, next);
+ HWT_OWNERHASH_UNLOCK();
+}
+
+void
+hwc_ownerhash_load(void)
+{
+
+ hwc_ownerhash = hashinit(HWT_OWNERHASH_SIZE, M_HWT_OWNERHASH,
+ &hwc_ownerhashmask);
+ mtx_init(&hwc_ownerhash_mtx, "hwc-owner-hash", "hwc-owner", MTX_DEF);
+}
+
+void
+hwc_ownerhash_unload(void)
+{
+ struct hwc_ownerhash *hoh;
+ struct hwc_owner *ho, *tmp;
+
+ HWT_OWNERHASH_LOCK();
+ for (hoh = hwc_ownerhash;
+ hoh <= &hwc_ownerhash[hwc_ownerhashmask];
+ hoh++) {
+ LIST_FOREACH_SAFE(ho, hoh, next, tmp) {
+ /* TODO: module is in use ? */
+ }
+ }
+ HWT_OWNERHASH_UNLOCK();
+
+ mtx_destroy(&hwc_ownerhash_mtx);
+ hashdestroy(hwc_ownerhash, M_HWT_OWNERHASH, hwc_ownerhashmask);
+}
Index: sys/dev/hwc/hwc_vm.h
===================================================================
--- /dev/null
+++ sys/dev/hwc/hwc_vm.h
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _DEV_HWT_HWT_VM_H_
+#define _DEV_HWT_HWT_VM_H_
+
+struct hwc_vm {
+ vm_object_t obj;
+ struct cdev *cdev;
+ struct hwc_context *ctx;
+};
+
+int hwc_vm_alloc(size_t bufsize, int kva_req, char *path, struct hwc_vm **vm0);
+void hwc_vm_free(struct hwc_vm *vm);
+
+#endif /* !_DEV_HWT_HWT_VM_H_ */
Index: sys/dev/hwc/hwc_vm.c
===================================================================
--- /dev/null
+++ sys/dev/hwc/hwc_vm.c
@@ -0,0 +1,201 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioccom.h>
+#include <sys/conf.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mman.h>
+#include <sys/refcount.h>
+#include <sys/rwlock.h>
+#include <sys/hwc.h>
+#include <sys/smp.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_param.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+#include <vm/vm_object.h>
+#include <vm/vm_pager.h>
+#include <vm/vm_pageout.h>
+#include <vm/vm_phys.h>
+
+#include <dev/hwc/hwc_context.h>
+#include <dev/hwc/hwc_contexthash.h>
+#include <dev/hwc/hwc_owner.h>
+#include <dev/hwc/hwc_ownerhash.h>
+#include <dev/hwc/hwc_backend.h>
+#include <dev/hwc/hwc_vm.h>
+
+#define HWT_THREAD_DEBUG
+#undef HWT_THREAD_DEBUG
+
+#ifdef HWT_THREAD_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+static MALLOC_DEFINE(M_HWT_VM, "hwc_vm", "Hardware Counters");
+
+static int
+hwc_vm_open(struct cdev *cdev, int oflags, int devtype, struct thread *td)
+{
+
+ dprintf("%s\n", __func__);
+
+ return (0);
+}
+
+static int
+hwc_vm_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
+ struct thread *td)
+{
+ struct hwc_configure *hc;
+ struct hwc_start *hstart;
+ struct hwc_stop *hstop;
+ struct hwc_context *ctx;
+ struct hwc_vm *vm;
+ struct hwc_owner *ho;
+ int error;
+
+ vm = dev->si_drv1;
+ KASSERT(vm != NULL, ("si_drv1 is NULL"));
+
+ ctx = vm->ctx;
+
+ /* Ensure process is registered owner of this HWT. */
+ ho = hwc_ownerhash_lookup(td->td_proc);
+ if (ho == NULL)
+ return (ENXIO);
+
+ if (ctx->hwc_owner != ho)
+ return (EPERM);
+
+ switch (cmd) {
+ case HWC_IOC_START:
+ dprintf("%s: start tracing\n", __func__);
+
+ HWT_CTX_LOCK(ctx);
+ if (ctx->state == CTX_STATE_RUNNING) {
+ /* Already running ? */
+ HWT_CTX_UNLOCK(ctx);
+ return (ENXIO);
+ }
+ ctx->state = CTX_STATE_RUNNING;
+ HWT_CTX_UNLOCK(ctx);
+
+ hstart = (struct hwc_start *)addr;
+ error = hwc_backend_start(ctx, hstart);
+ if (error)
+ return (error);
+ break;
+ case HWC_IOC_STOP:
+ hstop = (struct hwc_stop *)addr;
+ error = hwc_backend_stop(ctx, hstop);
+ if (error)
+ return (error);
+ ctx->state = CTX_STATE_STOPPED;
+ break;
+ case HWC_IOC_CONFIGURE:
+ hc = (struct hwc_configure *)addr;
+ hwc_backend_configure(ctx, hc);
+ break;
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static struct cdevsw hwc_vm_cdevsw = {
+ .d_version = D_VERSION,
+ .d_name = "hwc",
+ .d_open = hwc_vm_open,
+ .d_mmap_single = NULL,
+ .d_ioctl = hwc_vm_ioctl,
+};
+
+static int
+hwc_vm_create_cdev(struct hwc_vm *vm, char *path)
+{
+ struct make_dev_args args;
+ int error;
+
+ dprintf("%s: path %s\n", __func__, path);
+
+ make_dev_args_init(&args);
+ args.mda_devsw = &hwc_vm_cdevsw;
+ args.mda_flags = MAKEDEV_CHECKNAME | MAKEDEV_WAITOK;
+ args.mda_uid = UID_ROOT;
+ args.mda_gid = GID_WHEEL;
+ args.mda_mode = 0660;
+ args.mda_si_drv1 = vm;
+
+ error = make_dev_s(&args, &vm->cdev, "%s", path);
+ if (error != 0)
+ return (error);
+
+ return (0);
+}
+
+void
+hwc_vm_free(struct hwc_vm *vm)
+{
+
+ dprintf("%s\n", __func__);
+
+ if (vm->cdev)
+ destroy_dev_sched(vm->cdev);
+ free(vm, M_HWT_VM);
+}
+
+int
+hwc_vm_alloc(size_t bufsize, int kva_req, char *path, struct hwc_vm **vm0)
+{
+ struct hwc_vm *vm;
+ int error;
+
+ vm = malloc(sizeof(struct hwc_vm), M_HWT_VM, M_WAITOK | M_ZERO);
+
+ error = hwc_vm_create_cdev(vm, path);
+ if (error) {
+ hwc_vm_free(vm);
+ return (error);
+ }
+
+ *vm0 = vm;
+
+ return (0);
+}
Index: sys/riscv/include/sbi.h
===================================================================
--- sys/riscv/include/sbi.h
+++ sys/riscv/include/sbi.h
@@ -103,6 +103,35 @@
#define SBI_HSM_STATUS_START_PENDING 2
#define SBI_HSM_STATUS_STOP_PENDING 3
+#define SBI_EXT_ID_PMU 0x504D55
+#define SBI_PMU_NUM_COUNTERS 0
+#define SBI_PMU_COUNTER_GET_INFO 1
+#define SBI_PMU_COUNTER_CONFIG_MATCHING 2
+#define SBI_PMU_COUNTER_START 3
+#define SBI_PMU_COUNTER_STOP 4
+#define SBI_PMU_COUNTER_FW_READ 5
+#define SBI_PMU_COUNTER_FW_READ_HI 6
+#define SBI_PMU_SNAPSHOT_SET_SHMEM 7
+#define SBI_PMU_EVENT_GET_INFO 8
+
+/* SBI PMU flags */
+#define SBI_PMU_CFG_FLAG_SKIP_MATCH (1 << 0)
+#define SBI_PMU_CFG_FLAG_CLEAR_VALUE (1 << 1)
+#define SBI_PMU_CFG_FLAG_AUTO_START (1 << 2)
+#define SBI_PMU_CFG_FLAG_SET_VUINH (1 << 3)
+#define SBI_PMU_CFG_FLAG_SET_VSINH (1 << 4)
+#define SBI_PMU_CFG_FLAG_SET_UINH (1 << 5)
+#define SBI_PMU_CFG_FLAG_SET_SINH (1 << 6)
+#define SBI_PMU_CFG_FLAG_SET_MINH (1 << 7)
+
+#define SBI_PMU_EVENT_RAW_IDX 0x20000
+
+#define SBI_PMU_START_FLAG_SET_INIT_VALUE (1 << 0)
+#define SBI_PMU_START_FLAG_INIT_SNAPSHOT (1 << 1)
+
+#define SBI_PMU_STOP_FLAG_RESET (1 << 0)
+#define SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT (1 << 1)
+
/* System Reset (SRST) Extension */
#define SBI_EXT_ID_SRST 0x53525354
#define SBI_SRST_SYSTEM_RESET 0
Index: sys/riscv/pmu/pmu.c
===================================================================
--- /dev/null
+++ sys/riscv/pmu/pmu.c
@@ -0,0 +1,185 @@
+/*-
+ * Copyright (c) 2025-2026 Ruslan Bukin <br@bsdpad.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <machine/bus.h>
+#include <machine/sbi.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <sys/hwc.h>
+#include <dev/hwc/hwc_context.h>
+#include <dev/hwc/hwc_backend.h>
+
+#define dprintf(fmt, ...)
+
+static struct ofw_compat_data compat_data[] = {
+ { "riscv,pmu", 1 },
+ { NULL, 0 }
+};
+
+struct pmu_softc {
+ device_t dev;
+};
+
+static int
+pmu_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "RISC-V Performance Monitoring Unit (PMU)");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+pmu_backend_init(struct hwc_context *ctx)
+{
+
+ dprintf("%s\n", __func__);
+
+ return (0);
+}
+
+static int
+pmu_backend_deinit(struct hwc_context *ctx)
+{
+
+ dprintf("%s\n", __func__);
+
+ return (0);
+}
+
+static int
+pmu_backend_configure(struct hwc_context *ctx, struct hwc_configure *hc)
+{
+ struct sbi_ret ret;
+ uint32_t reg;
+
+ dprintf("%s: event_id %d counter_id %d\n", __func__, hc->event_id,
+ hc->counter_id);
+
+ /* Raw counters. */
+ ret = SBI_CALL5(SBI_EXT_ID_PMU, SBI_PMU_COUNTER_CONFIG_MATCHING, 0,
+ (1 << hc->counter_id), hc->flags, SBI_PMU_EVENT_RAW_IDX,
+ hc->event_id);
+
+ dprintf("%s: config match ev_id %d counter_id %d, err %ld val %ld\n",
+ __func__, hc->event_id, hc->counter_id, ret.error, ret.value);
+
+ /* Enable user access. */
+ if (ret.error == 0) {
+ reg = csr_read(scounteren);
+ reg |= (1 << hc->counter_id);
+ csr_write(scounteren, reg);
+ }
+
+ return (ret.error);
+}
+
+static int
+pmu_backend_start(struct hwc_context *ctx, struct hwc_start *hs)
+{
+ struct sbi_ret ret;
+
+ ret = SBI_CALL4(SBI_EXT_ID_PMU, SBI_PMU_COUNTER_START, 0,
+ hs->counter_mask, hs->flags, hs->data);
+
+ dprintf("start counters err %ld num %ld\n", ret.error, ret.value);
+
+ return (ret.error);
+}
+
+static int
+pmu_backend_stop(struct hwc_context *ctx, struct hwc_stop *hs)
+{
+ struct sbi_ret ret;
+
+ ret = SBI_CALL3(SBI_EXT_ID_PMU, SBI_PMU_COUNTER_STOP, 0,
+ hs->counter_mask, hs->flags);
+
+ dprintf("stop counters err %ld num %ld\n", ret.error, ret.value);
+
+ return (ret.error);
+}
+
+static struct hwc_backend_ops pmu_ops = {
+ .hwc_backend_init = pmu_backend_init,
+ .hwc_backend_deinit = pmu_backend_deinit,
+ .hwc_backend_configure = pmu_backend_configure,
+ .hwc_backend_start = pmu_backend_start,
+ .hwc_backend_stop = pmu_backend_stop,
+};
+
+static struct hwc_backend pmu_backend = {
+ .ops = &pmu_ops,
+ .name = "pmu",
+};
+
+static int
+pmu_attach(device_t dev)
+{
+ struct pmu_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ error = hwc_backend_register(&pmu_backend);
+ if (error) {
+ device_printf(dev, "Could not register in HWC\n");
+ return (error);
+ }
+
+ return (0);
+}
+
+static device_method_t pmu_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, pmu_probe),
+ DEVMETHOD(device_attach, pmu_attach),
+ DEVMETHOD_END
+};
+
+static driver_t pmu_driver = {
+ "pmu",
+ pmu_methods,
+ sizeof(struct pmu_softc)
+};
+
+DRIVER_MODULE(pmu, simplebus, pmu_driver, 0, 0);
+MODULE_DEPEND(pmu, hwc, 1, 1, 1);
+MODULE_VERSION(pmu, 1);
Index: sys/sys/hwc.h
===================================================================
--- /dev/null
+++ sys/sys/hwc.h
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 2026 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* User-visible header. */
+
+#include <sys/param.h>
+#include <sys/cpuset.h>
+#include <sys/types.h>
+
+#ifndef _SYS_HWC_H_
+#define _SYS_HWC_H_
+
+#define HWC_MAGIC 0x42
+#define HWC_IOC_ALLOC _IOW(HWC_MAGIC, 0x00, struct hwc_alloc)
+#define HWC_IOC_CONFIGURE _IOW(HWC_MAGIC, 0x01, struct hwc_configure)
+#define HWC_IOC_START _IOW(HWC_MAGIC, 0x02, struct hwc_start)
+#define HWC_IOC_STOP _IOW(HWC_MAGIC, 0x03, struct hwc_stop)
+
+#define HWC_BACKEND_MAXNAMELEN 256
+
+#define HWC_MODE_THREAD 1
+
+struct hwc_alloc {
+ int mode;
+ pid_t pid; /* thread mode */
+ const char *backend_name;
+ int *ident;
+} __aligned(16);
+
+struct hwc_configure {
+ int event_id;
+ int counter_id;
+ int flags;
+} __aligned(16);
+
+struct hwc_start {
+ int counter_mask;
+ int flags;
+ int data;
+} __aligned(16);
+
+struct hwc_stop {
+ int counter_mask;
+ int flags;
+} __aligned(16);
+
+#endif /* !_SYS_HWC_H_ */
Index: sys/sys/proc.h
===================================================================
--- sys/sys/proc.h
+++ sys/sys/proc.h
@@ -893,6 +893,7 @@
#define P2_LOGSIGEXIT_CTL 0x01000000 /* Override kern.logsigexit */
#define P2_HWT 0x02000000 /* Process is using HWT. */
+#define P2_HWC 0x04000000 /* Process is using HWC. */
/* Flags protected by proctree_lock, kept in p_treeflags. */
#define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */
Index: usr.sbin/Makefile.riscv
===================================================================
--- usr.sbin/Makefile.riscv
+++ usr.sbin/Makefile.riscv
@@ -2,3 +2,4 @@
SUBDIR+= bhyve
SUBDIR+= bhyvectl
.endif
+SUBDIR+= hwc
Index: usr.sbin/hwc/Makefile
===================================================================
--- /dev/null
+++ usr.sbin/hwc/Makefile
@@ -0,0 +1,17 @@
+PROG_CXX= hwc
+MAN=
+
+CFLAGS+= -I${SRCTOP}/contrib/libucl/include
+CFLAGS+= -I${SRCTOP}/sys
+CFLAGS+= -I${SRCTOP}/usr.sbin/hwc
+
+LIBADD= xo ucl
+
+SRCS= hwc.c \
+ hwc_process.c
+
+.if ${MACHINE_CPUARCH} == "riscv"
+SRCS+= riscv/hwc_pmu.c
+.endif
+
+.include <bsd.prog.mk>
Index: usr.sbin/hwc/hwc.h
===================================================================
--- /dev/null
+++ usr.sbin/hwc/hwc.h
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _HWC_H_
+#define _HWC_H_
+
+struct hwc_context;
+
+struct hwc_methods {
+ int (*init)(struct hwc_context *tc);
+ int (*configure)(struct hwc_context *tc);
+ int (*shutdown)(struct hwc_context *tc);
+ void (*run_once)(struct hwc_context *tc);
+};
+
+struct hwc_backend {
+ const char *name;
+ const char *fullname;
+ struct hwc_methods *methods;
+};
+
+struct hwc_context {
+ struct hwc_backend *backend;
+ int terminate;
+ int pid;
+ cpuset_t cpu_map;
+ int fd;
+ int attach;
+ int ident;
+ char *config_file;
+ char *output_file;
+ int mode;
+ int ctx_fd;
+};
+
+void hwc_procexit(pid_t pid, int status);
+
+void hwc_sleep(int msec);
+int hwc_start_tracing(struct hwc_context *tc);
+int hwc_stop_tracing(struct hwc_context *tc);
+int hwc_ncpu(void);
+
+#endif /* !_HWC_H_ */
Index: usr.sbin/hwc/hwc.c
===================================================================
--- /dev/null
+++ usr.sbin/hwc/hwc.c
@@ -0,0 +1,354 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/errno.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/cpuset.h>
+#include <sys/hwc.h>
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <err.h>
+#include <sysexits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <libutil.h>
+
+#include <libgen.h>
+#include <libxo/xo.h>
+
+#include "hwc.h"
+#include "hwc_process.h"
+
+#if defined(__riscv)
+#include "riscv/hwc_pmu.h"
+#endif
+
+#define HWC_DEBUG
+#undef HWC_DEBUG
+
+#ifdef HWC_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+static struct hwc_context tcs;
+
+static struct hwc_backend backends[] = {
+#if defined(__riscv)
+ { "pmu", "RISC-V Performace Monitoring Unit", &pmu_methods },
+#endif
+ { NULL, NULL, NULL }
+};
+
+void
+hwc_procexit(pid_t pid, int exit_status __unused)
+{
+ struct hwc_context *tc;
+
+ tc = &tcs;
+
+ if (tc->pid == pid)
+ tc->terminate = 1;
+}
+
+static int
+hwc_ctx_alloc(struct hwc_context *tc)
+{
+ struct hwc_alloc al;
+ char filename[32];
+ int error = 0;
+
+ if (tc->backend->methods->init != NULL){
+ error = tc->backend->methods->init(tc);
+ if (error)
+ return (error);
+ }
+
+ memset(&al, 0, sizeof(struct hwc_alloc));
+ al.mode = tc->mode;
+ if (tc->mode == HWC_MODE_THREAD)
+ al.pid = tc->pid;
+ else
+ return (-1);
+
+ al.pid = tc->pid;
+ al.backend_name = tc->backend->name;
+ al.ident = &tc->ident;
+
+ error = ioctl(tc->fd, HWC_IOC_ALLOC, &al);
+ if (error) {
+ printf("%s: could not allocate ctx, error %d\n", __func__,
+ error);
+ return (error);
+ }
+
+ sprintf(filename, "/dev/hwc_%d", tc->ident);
+
+ tc->ctx_fd = open(filename, O_RDWR);
+ if (tc->ctx_fd < 0) {
+ printf("Can't open %s\n", filename);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+hwc_start_tracing(struct hwc_context *tc)
+{
+ struct hwc_start s;
+ int error;
+
+ error = ioctl(tc->fd, HWC_IOC_START, &s);
+
+ return (error);
+}
+
+int
+hwc_stop_tracing(struct hwc_context *tc)
+{
+ struct hwc_stop s;
+ int error;
+
+ error = ioctl(tc->fd, HWC_IOC_STOP, &s);
+
+ return (error);
+}
+
+static void
+usage(void)
+{
+
+ errx(EX_USAGE,
+ "hwc [-c devname] [path to executable]\n"
+ "\t -c\tname\t\tName of tracing device, e.g. 'coresight'.\n"
+ "\t -f\tconfig-file\tHW counters configuration file.\n"
+ "\t -h\tHelp."
+ );
+}
+
+static int
+hwc_process_loop(struct hwc_context *tc)
+{
+ int status;
+ int error;
+
+ xo_open_container("trace");
+ xo_open_list("entries");
+
+ dprintf("Decoder started. Press ctrl+c to stop.\n");
+
+ while (1) {
+ error = waitpid(tc->pid, &status, WNOHANG);
+ if (error != 0 && WIFEXITED(status))
+ tc->terminate = 1;
+
+ if (!tc->terminate)
+ tc->backend->methods->run_once(tc);
+
+ if (errno == EINTR || tc->terminate) {
+ dprintf("%s: tracing terminated - exiting\n", __func__);
+ /* Fetch any remaining records */
+ if (tc->backend->methods->shutdown != NULL)
+ tc->backend->methods->shutdown(tc);
+ return (0);
+ }
+ }
+
+ xo_close_list("file");
+ xo_close_container("wc");
+ if (xo_finish() < 0)
+ xo_err(EXIT_FAILURE, "stdout");
+
+ return (0);
+}
+
+static int
+hwc_new_proc(struct hwc_context *tc, int *sockpair, char **cmd, char **env)
+{
+ struct stat st;
+ int error;
+
+ error = stat(*cmd, &st);
+ if (error) {
+ printf("Could not find target executable"
+ " error %d.\n", error);
+ return (error);
+ }
+
+ dprintf("cmd is %s\n", *cmd);
+
+ error = hwc_process_create(sockpair, cmd, env, &tc->pid);
+ if (error != 0)
+ return (error);
+
+ dprintf("%s: process pid %d created\n", __func__, tc->pid);
+
+ return (0);
+}
+
+static int
+hwc_mode_thread(struct hwc_context *tc, char **cmd, char **env)
+{
+ int sockpair[NSOCKPAIRFD];
+ int error;
+
+ if (tc->attach == 0) {
+ error = hwc_new_proc(tc, sockpair, cmd, env);
+ if (error)
+ return (error);
+ }
+
+ error = hwc_ctx_alloc(tc);
+ if (error) {
+ printf("%s: failed to alloc thread-mode ctx "
+ "error %d errno %d %s\n",
+ __func__, error, errno, strerror(errno));
+ if (errno == EPERM)
+ printf("Permission denied");
+ else if (errno == EINVAL)
+ printf("Invalid argument: buffer size is not a multiple"
+ " of page size, or is too small/large");
+ printf("\n");
+ return (error);
+ }
+
+ if (tc->backend->methods->configure == NULL) {
+ printf("configure method is missing\n");
+ return (ENXIO);
+ }
+
+ error = tc->backend->methods->configure(tc);
+ if (error) {
+ printf("could not configure backend, error %d\n", error);
+ return (error);
+ }
+
+ if (tc->attach == 0) {
+ error = hwc_process_start(sockpair);
+ if (error != 0)
+ return (error);
+ }
+
+ return (hwc_process_loop(tc));
+}
+
+int
+main(int argc, char **argv, char **env)
+{
+ struct hwc_context *tc;
+ char *backend_name;
+ int error;
+ int option;
+ int found;
+ int i;
+
+ tc = &tcs;
+
+ memset(tc, 0, sizeof(struct hwc_context));
+
+ /* First available is default trace device. */
+ tc->backend = &backends[0];
+ if (tc->backend->name == NULL) {
+ printf("No trace devices available\n");
+ return (1);
+ }
+
+ argc = xo_parse_args(argc, argv);
+ if (argc < 0)
+ exit(EXIT_FAILURE);
+
+ while ((option = getopt(argc, argv, "P:R:hc:f:o:")) != -1)
+ switch (option) {
+ case 'P':
+ tc->attach = 1;
+ tc->pid = atol(optarg);
+ break;
+ case 'f':
+ tc->config_file = strdup(optarg);
+ break;
+ case 'o':
+ tc->output_file = strdup(optarg);
+ break;
+ case 'c':
+ backend_name = strdup(optarg);
+ found = 0;
+ for (i = 0; backends[i].name != NULL; i++) {
+ if (strcmp(backends[i].name, backend_name) ==
+ 0) {
+ tc->backend = &backends[i];
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ printf("Backend with name \"%s\" not found.\n",
+ backend_name);
+ return (ENOENT);
+ }
+ break;
+ case 'h':
+ usage();
+ break;
+ default:
+ break;
+ }
+
+ tc->fd = open("/dev/hwc", O_RDWR);
+ if (tc->fd < 0) {
+ printf("Can't open /dev/hwc\n");
+ return (-1);
+ }
+
+ argc += optind;
+ argv += optind;
+
+ if (*argv == NULL && tc->attach == 0)
+ usage();
+
+ tc->mode = HWC_MODE_THREAD;
+
+ error = hwc_mode_thread(tc, argv, env);
+
+ close(tc->fd);
+
+ return (error);
+}
Index: usr.sbin/hwc/hwc_process.h
===================================================================
--- /dev/null
+++ usr.sbin/hwc/hwc_process.h
@@ -0,0 +1,40 @@
+/*-
+ * Copyright (c) 2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _HWC_PROCESS_H_
+#define _HWC_PROCESS_H_
+
+#define PARENTSOCKET 0
+#define CHILDSOCKET 1
+#define NSOCKPAIRFD 2
+
+int hwc_process_start(int *sockpair);
+int hwc_process_create(int *sockpair, char **cmd, char **env __unused,
+ int *pid0);
+
+#endif /* !_HWC_PROCESS_H_ */
Index: usr.sbin/hwc/hwc_process.c
===================================================================
--- /dev/null
+++ usr.sbin/hwc/hwc_process.c
@@ -0,0 +1,125 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/errno.h>
+#include <sys/wait.h>
+#include <sys/cpuset.h>
+#include <sys/hwc.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sysexits.h>
+#include <string.h>
+
+#include "hwc.h"
+#include "hwc_process.h"
+
+int
+hwc_process_start(int *sockpair)
+{
+ int error;
+
+ error = write(sockpair[PARENTSOCKET], "!", 1);
+ if (error != 1)
+ return (-1);
+
+ return (0);
+}
+
+static void
+hwc_process_onsig(int signo)
+{
+ pid_t pid;
+ int status;
+
+ assert(signo == SIGCHLD);
+
+ while ((pid = wait3(&status, WNOHANG, NULL)) > 0)
+ if (WIFEXITED(status))
+ hwc_procexit(pid, WEXITSTATUS(status));
+}
+
+int
+hwc_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, hwc_process_onsig);
+ siginterrupt(SIGCHLD, 1);
+ pid = fork();
+
+ switch (pid) {
+ case -1:
+ return (-1);
+ case 0:
+ /* Child */
+ close(sockpair[PARENTSOCKET]);
+
+ error = write(sockpair[CHILDSOCKET], "+", 1);
+ if (error != 1)
+ return (-1);
+
+ error = read(sockpair[CHILDSOCKET], &token, 1);
+ if (error < 0)
+ return (-2);
+
+ if (token != '!')
+ return (-3);
+
+ close(sockpair[CHILDSOCKET]);
+
+ execvp(cmd[0], cmd);
+
+ kill(getppid(), SIGCHLD);
+
+ exit(-3);
+ default:
+ /* Parent */
+ close(sockpair[CHILDSOCKET]);
+ break;
+ }
+
+ *pid0 = pid;
+
+ return (0);
+}
Index: usr.sbin/hwc/riscv/examples/cva6
===================================================================
--- /dev/null
+++ usr.sbin/hwc/riscv/examples/cva6
@@ -0,0 +1,137 @@
+#
+# CVA6 event id table.
+#
+mhpmcounters {
+ mhpmcounter = {
+ id = 3;
+ name = 'L1 I-Cache misses';
+ event_id = 0x01;
+ enabled = true;
+ }
+ mhpmcounter = {
+ id = 4;
+ name = 'L1 D-Cache misses';
+ event_id = 0x02;
+ enabled = true;
+ }
+ mhpmcounter = {
+ id = 5;
+ name = 'ITLB misses';
+ event_id = 0x03;
+ enabled = true;
+ }
+ mhpmcounter = {
+ id = 6;
+ name = 'DTLB misses';
+ event_id = 0x04;
+ enabled = true;
+ }
+ mhpmcounter = {
+ id = 7;
+ name = 'Load accesses';
+ event_id = 0x05;
+ enabled = true;
+ }
+ mhpmcounter = {
+ id = 8;
+ name = 'Store accesses';
+ event_id = 0x06;
+ enabled = true;
+ }
+ mhpmcounter = {
+ id = 9;
+ name = 'Exceptions';
+ event_id = 0x07;
+ enabled = true;
+ }
+ mhpmcounter = {
+ id = 10;
+ name = 'Exception handler returns';
+ event_id = 0x08;
+ enabled = true;
+ }
+ mhpmcounter = {
+ id = 11;
+ name = 'Branch instructions';
+ event_id = 0x09;
+ enabled = true;
+ }
+ mhpmcounter = {
+ id = 12;
+ name = 'Branch mispredicts';
+ event_id = 0x0a;
+ enabled = true;
+ }
+ mhpmcounter = {
+ id = 13;
+ name = 'Branch exceptions';
+ event_id = 0x0b;
+ enabled = true;
+ }
+ mhpmcounter = {
+ id = 14;
+ name = 'Call';
+ event_id = 0x0c;
+ enabled = true;
+ }
+ mhpmcounter = {
+ id = 15;
+ name = 'Return';
+ event_id = 0x0d;
+ enabled = true;
+ }
+ mhpmcounter = {
+ id = 16;
+ name = 'MSB Full';
+ event_id = 0x0e;
+ enabled = true;
+ }
+ mhpmcounter = {
+ id = 17;
+ name = 'Instruction fetch Empty';
+ event_id = 0x0f;
+ enabled = true;
+ }
+ mhpmcounter = {
+ id = 18;
+ name = 'L1 I-Cache accesses';
+ event_id = 0x10;
+ enabled = true;
+ }
+ mhpmcounter = {
+ id = 19;
+ name = 'L1 D-Cache accesses';
+ event_id = 0x11;
+ enabled = true;
+ }
+ mhpmcounter = {
+ id = 20;
+ name = 'Eviction';
+ event_id = 0x12;
+ enabled = true;
+ }
+ mhpmcounter = {
+ id = 21;
+ name = 'I-TLB flush';
+ event_id = 0x13;
+ enabled = true;
+ }
+ mhpmcounter = {
+ id = 22;
+ name = 'Integer instructions';
+ event_id = 0x14;
+ enabled = true;
+ }
+ mhpmcounter = {
+ id = 23;
+ name = 'Floating Point Instruction';
+ event_id = 0x15;
+ enabled = true;
+ }
+ mhpmcounter = {
+ id = 24;
+ name = 'Pipeline bubbles';
+ event_id = 0x16;
+ enabled = true;
+ }
+}
Index: usr.sbin/hwc/riscv/examples/qemu
===================================================================
--- /dev/null
+++ usr.sbin/hwc/riscv/examples/qemu
@@ -0,0 +1,20 @@
+mhpmcounters {
+ mhpmcounter = {
+ id = 3;
+ name = 'dtlb read miss';
+ event_id = 0x10019;
+ enabled = true;
+ }
+ mhpmcounter = {
+ id = 4;
+ name = 'dtlb write miss';
+ event_id = 0x1001B;
+ enabled = true;
+ }
+ mhpmcounter = {
+ id = 5;
+ name = 'itlb prefetch miss';
+ event_id = 0x10021;
+ enabled = true;
+ }
+}
Index: usr.sbin/hwc/riscv/examples/x730
===================================================================
--- /dev/null
+++ usr.sbin/hwc/riscv/examples/x730
@@ -0,0 +1,612 @@
+#
+# Codasip X730 event id table.
+# Counter IDs available: 3 to 6.
+#
+mhpmcounters {
+ mhpmcounter = {
+ id = 3;
+ name = 'RISC-V Exception taken';
+ event_id = 0x001;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'CHERI Exception taken (distinguished by mcause=0x1c)';
+ event_id = 0x002;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Interrupt taken';
+ event_id = 0x003;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Integer/FP Load committed';
+ event_id = 0x004;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Integer/FP Store committed';
+ event_id = 0x005;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Integer/FP Atomic committed';
+ event_id = 0x006;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Capability (XLEN*2-1 width) Load committed';
+ event_id = 0x007;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Capability (XLEN*2-1 width) Store committed';
+ event_id = 0x008;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Capability (XLEN*2-1 width) Atomic committed';
+ event_id = 0x009;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Any instruction committed in Integer Pointer Mode';
+ event_id = 0x00a;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Any instruction committed in Capability Pointer Mode';
+ event_id = 0x00b;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'CCU_I0_ALU_I1 class instruction committed in Integer Pointer Mode';
+ event_id = 0x100;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'CSR_I0_ALU_I1 class instruction committed in Integer Pointer Mode';
+ event_id = 0x101;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'EARLY_ALU_DUAL class instruction committed in Integer Pointer Mode';
+ event_id = 0x102;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'EARLY_ALU_DUAL_SINGLE class instruction committed in Integer Pointer Mode';
+ event_id = 0x103;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'EARLY_LATE_ALU_DUAL class instruction committed in Integer Pointer Mode';
+ event_id = 0x104;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'FPU_I0_ALU_I1 class instruction committed in Integer Pointer Mode';
+ event_id = 0x105;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'LSU_ALU_DUAL class instruction committed in Integer Pointer Mode';
+ event_id = 0x106;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'MUL_ALU_DUAL class instruction committed in Integer Pointer Mode';
+ event_id = 0x107;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'SINGLE class instruction committed in Integer Pointer Mode';
+ event_id = 0x108;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'CCU_I0_ALU_I1 class instruction committed in CHERI mode';
+ event_id = 0x200;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'CSR_I0_ALU_I1 class instruction committed in CHERI mode';
+ event_id = 0x201;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'EARLY_ALU_DUAL class instruction committed in CHERI mode';
+ event_id = 0x202;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'EARLY_ALU_DUAL_SINGLE class instruction committed in CHERI mode';
+ event_id = 0x203;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'EARLY_LATE_ALU_DUAL class instruction committed in CHERI mode';
+ event_id = 0x204;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'FPU_I0_ALU_I1 class instruction committed in CHERI mode';
+ event_id = 0x205;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'LSU_ALU_DUAL class instruction committed in CHERI mode';
+ event_id = 0x206;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'MUL_ALU_DUAL class instruction committed in CHERI mode';
+ event_id = 0x207;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'SINGLE class instruction committed in CHERI mode';
+ event_id = 0x208;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed not taken conditional branch on i0';
+ event_id = 0x300;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed taken conditional branch on i0';
+ event_id = 0x301;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed inferable call on i0';
+ event_id = 0x302;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed uninferable call on i0';
+ event_id = 0x303;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed uninferable tail-call on i0';
+ event_id = 0x304;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed inferable tail-call on i0';
+ event_id = 0x305;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed co-routine swap on i0';
+ event_id = 0x306;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed return on i0';
+ event_id = 0x307;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed other uninferable jump on i0';
+ event_id = 0x308;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed other inferable jump on i0';
+ event_id = 0x309;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed not taken conditional branch on i1';
+ event_id = 0x310;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed taken conditional branch on i1';
+ event_id = 0x311;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed inferable call on i1';
+ event_id = 0x312;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed uninferable call on i1';
+ event_id = 0x313;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed uninferable tail-call on i1';
+ event_id = 0x314;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed inferable tail-call on i1';
+ event_id = 0x315;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed co-routine swap on i1';
+ event_id = 0x316;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed return on i1';
+ event_id = 0x317;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed other uninferable jump on i1';
+ event_id = 0x318;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed other inferable jump on i1';
+ event_id = 0x319;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed and mispredicted not taken conditional branch on i0';
+ event_id = 0x330;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed and mispredicted taken conditional branch on i0';
+ event_id = 0x331;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed and mispredicted uninferable call on i0';
+ event_id = 0x332;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed and mispredicted uninferable tail-call on i0';
+ event_id = 0x333;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed and mispredicted co-routine swap on i0';
+ event_id = 0x334;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed and mispredicted return on i0';
+ event_id = 0x335;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed and mispredicted other uninferable jump on i0';
+ event_id = 0x336;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed and mispredicted not taken conditional branch on i1';
+ event_id = 0x340;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed and mispredicted taken conditional branch on i1';
+ event_id = 0x341;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed and mispredicted uninferable call on i1';
+ event_id = 0x342;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed and mispredicted uninferable tail-call on i1';
+ event_id = 0x343;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed and mispredicted co-routine swap on i1';
+ event_id = 0x344;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed and mispredicted return on i1';
+ event_id = 0x345;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Committed and mispredicted other uninferable jump on i1';
+ event_id = 0x346;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Cycles with zero instructions available from the Instruction Queue';
+ event_id = 0x800;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Cycles with one instruction available from the Instruction Queue';
+ event_id = 0x801;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Cycles with two instructions available from the Instruction Queue';
+ event_id = 0x802;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Cycles with zero instructions issued';
+ event_id = 0x803;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Cycles with one instruction issued';
+ event_id = 0x804;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Cycles with two instructions issued';
+ event_id = 0x805;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'i0 Issue stall due to RAW hazard';
+ event_id = 0x806;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'i1 Issue stall due to RAW hazard';
+ event_id = 0x807;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'i0 Issue stall due to AGU hazard (subset of RAW hazard)';
+ event_id = 0x808;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'i1 Issue stall due to AGU hazard (subset of RAW hazard)';
+ event_id = 0x809;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'i1 not issued due to instruction mapping to i0 only';
+ event_id = 0x80a;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'LSU stall cycle';
+ event_id = 0x905;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'SINGLE class pipelined stalls';
+ event_id = 0x906;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Branch to next PC due to CHERI tag clear';
+ event_id = 0x908;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'I$ miss when I$ is enabled';
+ event_id = 0xa00;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'ITLB miss';
+ event_id = 0xa01;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'A PTW triggered by an ITLB miss that found a 1GB leaf page';
+ event_id = 0xa03;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'A PTW triggered by an ITLB miss that found a 2MB leaf page';
+ event_id = 0xa04;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'A PTW triggered by an ITLB miss that found a 4KB leaf page';
+ event_id = 0xa05;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'IQ underflow';
+ event_id = 0xa07;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'IQ full';
+ event_id = 0xa08;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Fetch stall due to BPU RAM contention';
+ event_id = 0xa10;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Speculative prediction originating from the Static Branch Predictor 0';
+ event_id = 0xa11;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Speculative prediction originating from the Static Branch Predictor 1';
+ event_id = 0xa12;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Speculative prediction originating from the Static Branch Predictor 2';
+ event_id = 0xa13;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Speculative prediction originating from the Static Branch Predictor 3';
+ event_id = 0xa14;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Speculative prediction originating from the RAS';
+ event_id = 0xa15;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Speculative prediction originating from the ITB';
+ event_id = 0xa16;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Speculative prediction originating from the BTB';
+ event_id = 0xa17;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Speculative prediction originating from the uPredictor';
+ event_id = 0xa18;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'D$ miss when D$ is enabled';
+ event_id = 0xb00;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'DTLB miss';
+ event_id = 0xb01;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'Main TLB miss';
+ event_id = 0xb02;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'A PTW triggered by a DTLB miss that found a 1GB leaf page';
+ event_id = 0xb03;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'A PTW triggered by a DTLB miss that found a 2MB leaf page';
+ event_id = 0xb04;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'A PTW triggered by a DTLB miss that found a 4KB leaf page';
+ event_id = 0xb05;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'D$ physical index predictor hit';
+ event_id = 0xb10;
+ enabled = false;
+ }
+ mhpmcounter = {
+ id = 3;
+ name = 'D$ physical index predictor miss';
+ event_id = 0xb11;
+ enabled = false;
+ }
+}
Index: usr.sbin/hwc/riscv/hwc_pmu.h
===================================================================
--- /dev/null
+++ usr.sbin/hwc/riscv/hwc_pmu.h
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _HWC_PMU_H_
+#define _HWC_PMU_H_
+
+extern struct hwc_methods pmu_methods;
+
+#endif /* !_HWC_PMU_H_ */
Index: usr.sbin/hwc/riscv/hwc_pmu.c
===================================================================
--- /dev/null
+++ usr.sbin/hwc/riscv/hwc_pmu.c
@@ -0,0 +1,485 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* RISC-V PMU. */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/errno.h>
+#include <sys/cpuset.h>
+#include <sys/hwc.h>
+#include <sys/wait.h>
+#include <sys/sysctl.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+
+#include <ucl.h>
+
+#include <machine/riscvreg.h>
+#include <machine/encoding.h>
+#include <machine/sbi.h>
+
+#include "hwc.h"
+#include "hwc_pmu.h"
+
+#include <libxo/xo.h>
+
+#define HWC_DEBUG
+#undef HWC_DEBUG
+
+#ifdef HWC_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+struct counter {
+ int event_id;
+ bool enabled;
+ char *name;
+};
+
+#define RISCV_NCOUNTERS 32
+
+static struct counter counters[RISCV_NCOUNTERS];
+
+static uint64_t
+csr_read_num(int csr_num)
+{
+#define switchcase_csr_read(__csr_num, __val) {\
+ case __csr_num: \
+ __val = csr_read(__csr_num); \
+ break; }
+#define switchcase_csr_read_2(__csr_num, __val) {\
+ switchcase_csr_read(__csr_num + 0, __val) \
+ switchcase_csr_read(__csr_num + 1, __val)}
+#define switchcase_csr_read_4(__csr_num, __val) {\
+ switchcase_csr_read_2(__csr_num + 0, __val) \
+ switchcase_csr_read_2(__csr_num + 2, __val)}
+#define switchcase_csr_read_8(__csr_num, __val) {\
+ switchcase_csr_read_4(__csr_num + 0, __val) \
+ switchcase_csr_read_4(__csr_num + 4, __val)}
+#define switchcase_csr_read_16(__csr_num, __val) {\
+ switchcase_csr_read_8(__csr_num + 0, __val) \
+ switchcase_csr_read_8(__csr_num + 8, __val)}
+#define switchcase_csr_read_32(__csr_num, __val) {\
+ switchcase_csr_read_16(__csr_num + 0, __val) \
+ switchcase_csr_read_16(__csr_num + 16, __val)}
+
+ unsigned long ret = 0;
+
+ switch (csr_num) {
+ switchcase_csr_read_32(CSR_CYCLE, ret)
+ switchcase_csr_read_32(CSR_CYCLEH, ret)
+ default :
+ break;
+ }
+
+ return ret;
+#undef switchcase_csr_read_32
+#undef switchcase_csr_read_16
+#undef switchcase_csr_read_8
+#undef switchcase_csr_read_4
+#undef switchcase_csr_read_2
+#undef switchcase_csr_read
+}
+
+static int
+pmu_request_configure(struct hwc_context *tc, int mhpm_id, int event_id)
+{
+ struct hwc_configure hc;
+ int error;
+
+ /* Map event_id to a counter mhpm_id. */
+ hc.counter_id = mhpm_id;
+ hc.event_id = event_id;
+ hc.flags = SBI_PMU_CFG_FLAG_CLEAR_VALUE;
+ hc.flags |= SBI_PMU_CFG_FLAG_SET_SINH; /* S-mode Inhibit */
+ hc.flags |= SBI_PMU_CFG_FLAG_SET_MINH; /* M-mode Inhibit */
+ //hc.flags |= SBI_PMU_CFG_FLAG_SET_UINH; /* U-mode Inhibit */
+ hc.flags |= SBI_PMU_CFG_FLAG_SET_VUINH; /* VU-mode Inhibit */
+ hc.flags |= SBI_PMU_CFG_FLAG_SET_VSINH; /* VS-mode Inhibit */
+
+ error = ioctl(tc->ctx_fd, HWC_IOC_CONFIGURE, &hc);
+ if (error)
+ return (error);
+
+ return (0);
+}
+
+static int
+pmu_parse_counter(struct hwc_context *tc __unused, const ucl_object_t *top)
+{
+ const ucl_object_t *obj;
+ ucl_object_iter_t it = NULL;
+ const char *k;
+ int event_id;
+ int mhpm_id;
+ const char *name;
+ bool enabled;
+
+ mhpm_id = -1;
+ event_id = -1;
+ name = NULL;
+ enabled = false;
+
+ while ((obj = ucl_iterate_object (top, &it, true))) {
+ k = ucl_object_key(obj);
+ if (strcmp(k, "id") == 0)
+ mhpm_id = ucl_object_toint(obj);
+ if (strcmp(k, "event_id") == 0)
+ event_id = ucl_object_toint(obj);
+ if (strcmp(k, "name") == 0)
+ name = ucl_object_tostring(obj);
+ if (strcmp(k, "enabled") == 0)
+ enabled = ucl_object_toboolean(obj);
+ }
+
+ if (mhpm_id == -1) {
+ printf("The 'id' field not present, skipping\n");
+ return (0);
+ }
+
+ if (event_id == -1) {
+ printf("Counter %d: the 'event_id' field not present,"
+ " skipping\n", mhpm_id);
+ return (0);
+ }
+
+ if (name == NULL) {
+ printf("Counter %d: the 'name' field not present, skipping\n",
+ mhpm_id);
+ return (0);
+ }
+
+ if (enabled == false)
+ return (0);
+
+ if (counters[mhpm_id].enabled) {
+ printf("Counter %d already enabled, skipping\n", mhpm_id);
+ return (0);
+ }
+
+ counters[mhpm_id].event_id = event_id;
+ counters[mhpm_id].name = strdup(name);
+ counters[mhpm_id].enabled = true;
+
+ return (1);
+}
+
+static int
+pmu_parse_counters(struct hwc_context *tc, const ucl_object_t *top,
+ int *ncounters)
+{
+ ucl_object_iter_t it_obj = NULL;
+ ucl_object_iter_t it = NULL;
+ const ucl_object_t *obj;
+ const ucl_object_t *cur;
+ const char *k;
+ int error;
+ int i;
+
+ i = 0;
+
+ while ((obj = ucl_iterate_object (top, &it, true))) {
+ k = ucl_object_key(obj);
+ if (strcmp(k, "mhpmcounter") != 0)
+ continue;
+ while ((cur = ucl_iterate_object (obj, &it_obj, false))) {
+ error = pmu_parse_counter(tc, cur);
+ if (error > 0) {
+ i += 1;
+ continue;
+ }
+ if (error == 0)
+ continue;
+ if (error < 0)
+ return (error);
+ }
+ }
+
+ *ncounters = i;
+
+ return (0);
+}
+
+static int __unused
+pmu_stop_one(struct hwc_context *tc, int i)
+{
+ struct hwc_stop hs;
+ int error;
+
+ hs.counter_mask = (1 << i);
+ hs.flags = 0;
+
+ error = ioctl(tc->ctx_fd, HWC_IOC_STOP, &hs);
+ if (error) {
+ printf("%s: could not stop counter (mask) 0x%x, error %d\n",
+ __func__, hs.counter_mask, error);
+ return (error);
+ }
+
+ return (0);
+}
+
+static int
+pmu_stop_all(struct hwc_context *tc, int flags)
+{
+ struct hwc_stop hs;
+ int error;
+ int i;
+
+ hs.counter_mask = 0;
+ hs.flags = flags;
+
+ for (i = 0; i < RISCV_NCOUNTERS; i++)
+ if (counters[i].enabled == true)
+ hs.counter_mask |= (1 << i);
+
+ error = ioctl(tc->ctx_fd, HWC_IOC_STOP, &hs);
+ if (error)
+ return (error);
+
+ return (0);
+}
+
+static int __unused
+pmu_start_all(struct hwc_context *tc)
+{
+ struct hwc_start hs;
+ int error;
+ int i;
+
+ hs.counter_mask = 0;
+ hs.flags = SBI_PMU_START_FLAG_SET_INIT_VALUE;
+ hs.data = 0;
+
+ for (i = 0; i < RISCV_NCOUNTERS; i++)
+ if (counters[i].enabled == true)
+ hs.counter_mask |= (1 << i);
+
+ error = ioctl(tc->ctx_fd, HWC_IOC_START, &hs);
+ if (error) {
+ printf("%s: could not start counters (mask) 0x%x, error %d\n",
+ __func__, hs.counter_mask, error);
+ return (error);
+ }
+
+ return (0);
+}
+
+static int
+pmu_parse_config(struct hwc_context *tc, int *ncounters)
+{
+ struct ucl_parser *parser;
+ const ucl_object_t *obj;
+ ucl_object_t *top;
+ ucl_object_iter_t it = NULL;
+ const char *k;
+ int error;
+ bool ret;
+
+ parser = ucl_parser_new(0);
+
+ ret = ucl_parser_add_file(parser, tc->config_file);
+ if (ret == false) {
+ printf("can't read file\n");
+ return (-1);
+ }
+
+ top = ucl_parser_get_object(parser);
+
+ while ((obj = ucl_iterate_object(top, &it, true))) {
+ k = ucl_object_key(obj);
+ if (strcmp(k, "mhpmcounters") == 0) {
+ error = pmu_parse_counters(tc, obj, ncounters);
+ if (error)
+ return (error);
+ }
+ }
+
+ return (0);
+}
+
+static int
+pmu_configure(struct hwc_context *tc)
+{
+ struct counter *c;
+ int ncounters;
+ int error;
+ int i;
+
+ ncounters = 0;
+
+ error = pmu_parse_config(tc, &ncounters);
+ if (error)
+ return (error);
+
+ if (ncounters == 0) {
+ printf("%s: no counters to configure\n", __func__);
+ return (-1);
+ }
+
+ /* Stop and reset any previous mappings. */
+ pmu_stop_all(tc, SBI_PMU_STOP_FLAG_RESET);
+
+ for (i = 0; i < RISCV_NCOUNTERS; i++) {
+ c = &counters[i];
+ if (c->enabled == true) {
+ error = pmu_request_configure(tc, i, c->event_id);
+ if (error) {
+ printf("%s: cound not configure ctr id %d\n",
+ __func__, i);
+ return (error);
+ }
+ }
+ }
+
+ error = pmu_start_all(tc);
+
+ return (error);
+}
+
+static int
+pmu_init(struct hwc_context *tc __unused)
+{
+
+ dprintf("%s\n", __func__);
+
+ bzero(counters, sizeof(struct counter) * RISCV_NCOUNTERS);
+
+ return (0);
+}
+
+static void
+pmu_run_once(struct hwc_context *tc __unused)
+{
+
+}
+
+static void
+pmu_ucl_insert_entry(ucl_object_t *root, struct counter *c, int i)
+{
+ ucl_object_t *result;
+ uint64_t val;
+
+ val = csr_read_num(CSR_HPMCOUNTER3 - 3 + i);
+
+ result = ucl_object_typed_new(UCL_OBJECT);
+ ucl_object_insert_key(result, ucl_object_fromstring(c->name),
+ "name", 0, false);
+ ucl_object_insert_key(result, ucl_object_fromint(val), "count", 0,
+ false);
+
+ ucl_object_insert_key(root, result, "counters", 0, false);
+}
+
+static int
+pmu_dump(struct hwc_context *tc)
+{
+ unsigned char *json_str;
+ struct counter *c;
+ ucl_object_t *root;
+ FILE *fp;
+ int i;
+
+ if (tc->output_file == NULL)
+ return (0);
+
+ fp = fopen(tc->output_file, "w");
+ if (!fp) {
+ perror("fopen");
+ return (-1);
+ }
+
+ root = ucl_object_typed_new(UCL_OBJECT);
+
+ for (i = 0; i < RISCV_NCOUNTERS; i++) {
+ c = &counters[i];
+ if (c->enabled == true)
+ pmu_ucl_insert_entry(root, c, i);
+ }
+
+ json_str = ucl_object_emit(root, UCL_EMIT_JSON_COMPACT);
+ if (json_str) {
+ fputs((const char *)json_str, fp);
+ free(json_str);
+ }
+
+ fclose(fp);
+
+ ucl_object_unref(root);
+
+ return (0);
+}
+
+static void
+pmu_print(struct hwc_context *tc __unused)
+{
+ struct counter *c;
+ int i;
+
+ /* Print out standard counters. */
+ printf(" time == %ld\n", csr_read(time));
+ printf(" cycle == %ld\n", csr_read(cycle));
+ printf(" instructions == %ld\n", csr_read(instret));
+
+ for (i = 0; i < RISCV_NCOUNTERS; i++) {
+ c = &counters[i];
+ if (c->enabled == true)
+ printf(" %d: %s == %ld\n", i, c->name,
+ csr_read_num(CSR_HPMCOUNTER3 - 3 + i));
+ }
+}
+
+static int
+pmu_shutdown(struct hwc_context *tc)
+{
+
+ pmu_stop_all(tc, 0);
+ pmu_print(tc);
+ pmu_dump(tc);
+
+ return (0);
+}
+
+struct hwc_methods pmu_methods = {
+ .init = pmu_init,
+ .shutdown = pmu_shutdown,
+ .configure = pmu_configure,
+ .run_once = pmu_run_once,
+};

File Metadata

Mime Type
text/plain
Expires
Tue, Sep 8, 10:30 PM (2 h, 22 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
38542836
Default Alt Text
D54826.id170233.diff (109 KB)

Event Timeline