Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F170969618
D54826.id170233.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
109 KB
Referenced Files
None
Subscribers
None
D54826.id170233.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D54826: Import the Hardware Counters (HWC) Framework
Attached
Detach File
Event Timeline
Log In to Comment