Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F101987566
D40466.id123424.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
45 KB
Referenced Files
None
Subscribers
None
D40466.id123424.diff
View Options
Index: sys/conf/files.arm64
===================================================================
--- sys/conf/files.arm64
+++ sys/conf/files.arm64
@@ -325,6 +325,9 @@
dev/hyperv/netvsc/if_hn.c optional hyperv acpi
dev/hyperv/pcib/vmbus_pcib.c optional hyperv pci acpi
+dev/hwt/hwt.c optional hwt
+dev/hwt/hwt_record.c optional hwt
+
dev/ice/if_ice_iflib.c optional ice pci \
compile-with "${NORMAL_C} -I$S/dev/ice"
dev/ice/ice_lib.c optional ice pci \
Index: sys/conf/options
===================================================================
--- sys/conf/options
+++ sys/conf/options
@@ -906,6 +906,9 @@
HWPMC_HOOKS
HWPMC_MIPS_BACKTRACE opt_hwpmc_hooks.h
+# Hardware Trace (HWT) framework options
+HWT_HOOKS
+
# 802.11 support layer
IEEE80211_DEBUG opt_wlan.h
IEEE80211_DEBUG_REFCNT opt_wlan.h
Index: sys/dev/hwt/hwt.h
===================================================================
--- /dev/null
+++ sys/dev/hwt/hwt.h
@@ -0,0 +1,81 @@
+/*-
+ * Copyright (c) 2023 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* User-visible header. */
+
+#ifndef _DEV_HWT_HWT_H_
+#define _DEV_HWT_HWT_H_
+
+#include "hwt_record.h"
+
+#define HWT_MAGIC 0x42
+#define HWT_IOC_ALLOC \
+ _IOW(HWT_MAGIC, 0x00, struct hwt_alloc)
+#define HWT_IOC_START \
+ _IOW(HWT_MAGIC, 0x01, struct hwt_start)
+#define HWT_IOC_RECORD_GET \
+ _IOW(HWT_MAGIC, 0x02, struct hwt_record_get)
+#define HWT_IOC_BUFPTR_GET \
+ _IOW(HWT_MAGIC, 0x03, struct hwt_bufptr_get)
+
+#define HWT_BACKEND_MAXNAMELEN 256
+
+struct hwt_alloc {
+ size_t bufsize;
+ pid_t pid;
+ char *backend_name;
+} __aligned(16);
+
+struct hwt_start {
+ pid_t pid;
+} __aligned(16);
+
+struct hwt_record_user_entry {
+ enum hwt_record_type record_type;
+ char fullpath[MAXPATHLEN];
+ uintptr_t addr;
+ size_t size;
+ lwpid_t tid;
+} __aligned(16);
+
+struct hwt_record_get {
+ struct hwt_record_user_entry *records;
+ int *nentries;
+ pid_t pid;
+} __aligned(16);
+
+struct hwt_bufptr_get {
+ int *ptr;
+ int *curpage;
+ vm_offset_t *curpage_offset;
+ pid_t pid;
+} __aligned(16);
+
+#endif /* !_DEV_HWT_HWT_H_ */
Index: sys/dev/hwt/hwt.c
===================================================================
--- /dev/null
+++ sys/dev/hwt/hwt.c
@@ -0,0 +1,1179 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Hardware Trace (HWT) framework. */
+
+#include <sys/param.h>
+#include <sys/eventhandler.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/module.h>
+#include <sys/mutex.h>
+#include <sys/rwlock.h>
+
+#include <vm/vm.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/hwt/hwt_hook.h>
+#include <dev/hwt/hwtvar.h>
+#include <dev/hwt/hwt.h>
+
+#define HWT_DEBUG
+#undef HWT_DEBUG
+
+#ifdef HWT_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+#define HWT_PROCHASH_SIZE 1024
+#define HWT_OWNERHASH_SIZE 1024
+
+/* No real reason for this limitation except sanity checks. */
+#define HWT_MAXBUFSIZE (1U * 1024 * 1024 * 1024) /* 1 GB */
+
+/*
+ * 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 hwt_contexthash_mtx;
+static u_long hwt_contexthashmask;
+static LIST_HEAD(hwt_contexthash, hwt_context) *hwt_contexthash;
+
+static struct mtx hwt_ownerhash_mtx;
+static u_long hwt_ownerhashmask;
+static LIST_HEAD(hwt_ownerhash, hwt_owner) *hwt_ownerhash;
+
+static struct mtx hwt_backend_mtx;
+static LIST_HEAD(, hwt_backend) hwt_backends;
+
+static eventhandler_tag hwt_exit_tag;
+static struct cdev *hwt_cdev;
+
+static int
+hwt_event_init(struct hwt_thread *thr)
+{
+ struct hwt_context *ctx;
+
+ dprintf("%s\n", __func__);
+
+ ctx = thr->ctx;
+ ctx->hwt_backend->ops->hwt_event_init(thr);
+
+ return (0);
+}
+
+static int
+hwt_event_deinit(struct hwt_context *ctx)
+{
+
+ printf("%s\n", __func__);
+
+ mtx_lock_spin(&hwt_backend_mtx);
+ ctx->hwt_backend->ops->hwt_event_deinit();
+ mtx_unlock_spin(&hwt_backend_mtx);
+
+ return (0);
+}
+
+static int
+hwt_event_configure(struct hwt_thread *thr, int cpu_id)
+{
+ struct hwt_context *ctx;
+
+ dprintf("%s\n", __func__);
+
+ ctx = thr->ctx;
+
+ mtx_lock_spin(&hwt_backend_mtx);
+ ctx->hwt_backend->ops->hwt_event_configure(thr, cpu_id);
+ mtx_unlock_spin(&hwt_backend_mtx);
+
+ return (0);
+}
+
+static int
+hwt_event_enable(struct hwt_thread *thr, int cpu_id)
+{
+ struct hwt_context *ctx;
+
+ dprintf("%s\n", __func__);
+
+ ctx = thr->ctx;
+
+ mtx_lock_spin(&hwt_backend_mtx);
+ ctx->hwt_backend->ops->hwt_event_enable(thr, cpu_id);
+ mtx_unlock_spin(&hwt_backend_mtx);
+
+ return (0);
+}
+
+static int
+hwt_event_disable(struct hwt_thread *thr, int cpu_id)
+{
+ struct hwt_context *ctx;
+
+ dprintf("%s\n", __func__);
+
+ ctx = thr->ctx;
+
+ mtx_lock_spin(&hwt_backend_mtx);
+ ctx->hwt_backend->ops->hwt_event_disable(thr, cpu_id);
+ mtx_unlock_spin(&hwt_backend_mtx);
+
+ return (0);
+}
+
+static int __unused
+hwt_event_dump(struct hwt_thread *thr, int cpu_id)
+{
+ struct hwt_context *ctx;
+
+ dprintf("%s\n", __func__);
+
+ ctx = thr->ctx;
+
+ mtx_lock_spin(&hwt_backend_mtx);
+ ctx->hwt_backend->ops->hwt_event_dump(thr, cpu_id);
+ mtx_unlock_spin(&hwt_backend_mtx);
+
+ return (0);
+}
+
+static int
+hwt_event_read(struct hwt_thread *thr, int *curpage,
+ vm_offset_t *curpage_offset)
+{
+ struct hwt_context *ctx;
+ int error;
+
+ dprintf("%s\n", __func__);
+
+ ctx = thr->ctx;
+
+ mtx_lock_spin(&hwt_backend_mtx);
+ error = ctx->hwt_backend->ops->hwt_event_read(thr, 0, curpage,
+ curpage_offset);
+ mtx_unlock_spin(&hwt_backend_mtx);
+
+ return (error);
+}
+
+static struct hwt_backend *
+hwt_lookup_backend(const char *name)
+{
+ struct hwt_backend *backend;
+
+ mtx_lock_spin(&hwt_backend_mtx);
+ LIST_FOREACH(backend, &hwt_backends, next) {
+ if (strcmp(backend->name, name) == 0) {
+ mtx_unlock_spin(&hwt_backend_mtx);
+ return (backend);
+ }
+ }
+ mtx_unlock_spin(&hwt_backend_mtx);
+
+ return (NULL);
+}
+
+int
+hwt_register(struct hwt_backend *backend)
+{
+
+ if (backend == NULL ||
+ backend->name == NULL ||
+ backend->ops == NULL)
+ return (EINVAL);
+
+ mtx_lock_spin(&hwt_backend_mtx);
+ LIST_INSERT_HEAD(&hwt_backends, backend, next);
+ mtx_unlock_spin(&hwt_backend_mtx);
+
+ return (0);
+}
+
+static int
+hwt_fault(vm_object_t vm_obj, vm_ooffset_t offset,
+ int prot, vm_page_t *mres)
+{
+
+ return (0);
+}
+
+static int
+hwt_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot,
+ vm_ooffset_t foff, struct ucred *cred, u_short *color)
+{
+
+ *color = 0;
+
+ return (0);
+}
+
+static void
+hwt_dtor(void *handle)
+{
+
+}
+
+static struct cdev_pager_ops hwt_pager_ops = {
+ .cdev_pg_fault = hwt_fault,
+ .cdev_pg_ctor = hwt_ctor,
+ .cdev_pg_dtor = hwt_dtor
+};
+
+static int
+hwt_alloc_pages(struct hwt_thread *thr)
+{
+ vm_paddr_t low, high, boundary;
+ vm_memattr_t memattr;
+ int alignment;
+ vm_page_t m;
+ int pflags;
+ int tries;
+ int i;
+
+ alignment = PAGE_SIZE;
+ low = 0;
+ high = -1UL;
+ boundary = 0;
+ pflags = VM_ALLOC_NORMAL | VM_ALLOC_NOBUSY | VM_ALLOC_WIRED |
+ VM_ALLOC_ZERO;
+ memattr = VM_MEMATTR_DEVICE;
+
+ thr->obj = cdev_pager_allocate(thr, OBJT_MGTDEVICE, &hwt_pager_ops,
+ thr->npages * PAGE_SIZE, PROT_READ, 0, curthread->td_ucred);
+
+ for (i = 0; i < thr->npages; i++) {
+ tries = 0;
+retry:
+ m = vm_page_alloc_noobj_contig(pflags, 1, low, high,
+ alignment, boundary, memattr);
+ if (m == NULL) {
+ if (tries < 3) {
+ if (!vm_page_reclaim_contig(pflags, 1, low,
+ high, alignment, boundary))
+ vm_wait(NULL);
+ tries++;
+ goto retry;
+ }
+
+ return (ENOMEM);
+ }
+
+#if 0
+ /* TODO */
+ vm_pointer_t va;
+
+ if ((m->flags & PG_ZERO) == 0)
+ pmap_zero_page(m);
+
+ va = PHYS_TO_DMAP(VM_PAGE_TO_PHYS(m));
+ cpu_dcache_wb_range(va, PAGE_SIZE);
+#endif
+ m->valid = VM_PAGE_BITS_ALL;
+ m->oflags &= ~VPO_UNMANAGED;
+ m->flags |= PG_FICTITIOUS;
+ thr->pages[i] = m;
+
+ VM_OBJECT_WLOCK(thr->obj);
+ vm_page_insert(m, thr->obj, i);
+ VM_OBJECT_WUNLOCK(thr->obj);
+ }
+
+ return (0);
+}
+
+static int
+hwt_open(struct cdev *cdev, int oflags, int devtype, struct thread *td)
+{
+
+ dprintf("%s\n", __func__);
+
+ return (0);
+}
+
+static int
+hwt_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t mapsize,
+ struct vm_object **objp, int nprot)
+{
+ struct hwt_thread *thr;
+
+ thr = cdev->si_drv1;
+
+ if (nprot != PROT_READ || *offset != 0)
+ return (ENXIO);
+
+ *objp = thr->obj;
+
+ return (0);
+}
+
+static struct hwt_context *
+hwt_lookup_by_owner(struct hwt_owner *ho, pid_t pid)
+{
+ struct hwt_context *ctx;
+
+ mtx_lock(&ho->mtx);
+ LIST_FOREACH(ctx, &ho->hwts, next_hwts) {
+ if (ctx->pid == pid) {
+ mtx_unlock(&ho->mtx);
+ return (ctx);
+ }
+ }
+ mtx_unlock(&ho->mtx);
+
+ return (NULL);
+}
+
+static struct hwt_owner *
+hwt_lookup_ownerhash(struct proc *p)
+{
+ struct hwt_ownerhash *hoh;
+ struct hwt_owner *ho;
+ int hindex;
+
+ hindex = HWT_HASH_PTR(p, hwt_ownerhashmask);
+ hoh = &hwt_ownerhash[hindex];
+
+ mtx_lock_spin(&hwt_ownerhash_mtx);
+ LIST_FOREACH(ho, hoh, next) {
+ if (ho->p == p) {
+ mtx_unlock_spin(&hwt_ownerhash_mtx);
+ return (ho);
+ }
+ }
+ mtx_unlock_spin(&hwt_ownerhash_mtx);
+
+ return (NULL);
+}
+
+static struct hwt_context *
+hwt_lookup_by_owner_p(struct proc *owner_p, pid_t pid)
+{
+ struct hwt_context *ctx;
+ struct hwt_owner *ho;
+
+ ho = hwt_lookup_ownerhash(owner_p);
+ if (ho == NULL)
+ return (NULL);
+
+ ctx = hwt_lookup_by_owner(ho, pid);
+
+ return (ctx);
+}
+
+static int
+hwt_thread_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
+ struct thread *td)
+{
+ struct hwt_bufptr_get *ptr_get;
+ struct hwt_context *ctx;
+ vm_offset_t curpage_offset;
+ struct hwt_thread *thr;
+ int curpage;
+ int error;
+
+ thr = dev->si_drv1;
+
+ switch (cmd) {
+ case HWT_IOC_BUFPTR_GET:
+ ptr_get = (struct hwt_bufptr_get *)addr;
+
+ /* Check if process is registered owner of any HWTs. */
+ ctx = hwt_lookup_by_owner_p(td->td_proc, ptr_get->pid);
+ if (ctx == NULL)
+ return (ENXIO);
+
+ if (ctx != thr->ctx)
+ return (ENXIO);
+
+ hwt_event_read(thr, &curpage, &curpage_offset);
+
+ error = copyout(&curpage, ptr_get->curpage, sizeof(int));
+ if (error)
+ return (error);
+ error = copyout(&curpage_offset, ptr_get->curpage_offset,
+ sizeof(vm_offset_t));
+ if (error)
+ return (error);
+
+ break;
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static struct cdevsw hwt_thread_cdevsw = {
+ .d_version = D_VERSION,
+ .d_name = "hwt",
+ .d_open = hwt_open,
+ .d_mmap_single = hwt_mmap_single,
+ .d_ioctl = hwt_thread_ioctl,
+};
+
+static int
+hwt_create_cdev(struct hwt_thread *thr)
+{
+ struct make_dev_args args;
+ struct hwt_context *ctx;
+ int error;
+
+ ctx = thr->ctx;
+
+ printf("%s: pid %d tid %d\n", __func__, ctx->pid, thr->tid);
+
+ make_dev_args_init(&args);
+ args.mda_devsw = &hwt_thread_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 = thr;
+
+ error = make_dev_s(&args, &thr->cdev, "hwt_%d_%d", ctx->pid, thr->tid);
+ if (error != 0)
+ return (error);
+
+ return (0);
+}
+
+static int
+hwt_alloc_buffers(struct hwt_thread *thr)
+{
+ struct hwt_context *ctx;
+ int error;
+
+ ctx = thr->ctx;
+
+ thr->npages = ctx->bufsize / PAGE_SIZE;
+ thr->pages = malloc(sizeof(struct vm_page *) * thr->npages, M_HWT,
+ M_WAITOK | M_ZERO);
+
+ error = hwt_alloc_pages(thr);
+ if (error) {
+ printf("%s: could not alloc pages\n", __func__);
+ return (error);
+ }
+
+ return (0);
+}
+
+static void
+hwt_destroy_buffers(struct hwt_thread *thr)
+{
+ vm_page_t m;
+ int i;
+
+ VM_OBJECT_WLOCK(thr->obj);
+ for (i = 0; i < thr->npages; i++) {
+ m = thr->pages[i];
+ if (m == NULL)
+ break;
+
+ vm_page_busy_acquire(m, 0);
+ cdev_pager_free_page(thr->obj, m);
+ m->flags &= ~PG_FICTITIOUS;
+ vm_page_unwire_noq(m);
+ vm_page_free(m);
+
+ }
+ vm_pager_deallocate(thr->obj);
+ VM_OBJECT_WUNLOCK(thr->obj);
+
+ free(thr->pages, M_HWT);
+}
+
+static struct hwt_context *
+hwt_alloc(void)
+{
+ struct hwt_context *ctx;
+
+ ctx = malloc(sizeof(struct hwt_context), M_HWT, M_WAITOK | M_ZERO);
+ LIST_INIT(&ctx->records);
+ mtx_init(&ctx->mtx, "hwt records", NULL, MTX_SPIN);
+
+ LIST_INIT(&ctx->threads);
+ mtx_init(&ctx->mtx_threads, "hwt threads", NULL, MTX_SPIN);
+
+ return (ctx);
+}
+
+/*
+ * To use by hwt_switch_in/out only.
+ */
+struct hwt_context *
+hwt_lookup_contexthash(struct proc *p)
+{
+ struct hwt_contexthash *hch;
+ struct hwt_context *ctx;
+ int hindex;
+
+ hindex = HWT_HASH_PTR(p, hwt_contexthashmask);
+ hch = &hwt_contexthash[hindex];
+
+ mtx_lock_spin(&hwt_contexthash_mtx);
+ LIST_FOREACH(ctx, hch, next_hch) {
+ if (ctx->proc == p) {
+ mtx_unlock_spin(&hwt_contexthash_mtx);
+ return (ctx);
+ }
+ }
+ mtx_unlock_spin(&hwt_contexthash_mtx);
+
+ panic("ctx not found");
+}
+
+/*
+ * To use by hwt_switch_in/out only.
+ */
+static struct hwt_thread *
+hwt_lookup_thread(struct hwt_context *ctx, struct thread *td)
+{
+ struct hwt_thread *thr, *thr1;
+
+ mtx_lock_spin(&ctx->mtx_threads);
+ LIST_FOREACH_SAFE(thr, &ctx->threads, next, thr1) {
+ if (thr->tid == td->td_tid) {
+ mtx_unlock_spin(&ctx->mtx_threads);
+ return (thr);
+ }
+ }
+ mtx_unlock_spin(&ctx->mtx_threads);
+
+ panic("thread not found");
+}
+
+static void
+hwt_insert_contexthash(struct hwt_context *ctx)
+{
+ struct hwt_contexthash *hch;
+ int hindex;
+
+ PROC_LOCK_ASSERT(ctx->proc, MA_OWNED);
+
+ hindex = HWT_HASH_PTR(ctx->proc, hwt_contexthashmask);
+ hch = &hwt_contexthash[hindex];
+
+ mtx_lock_spin(&hwt_contexthash_mtx);
+ LIST_INSERT_HEAD(hch, ctx, next_hch);
+ mtx_unlock_spin(&hwt_contexthash_mtx);
+}
+
+static int
+hwt_send_records(struct hwt_record_get *record_get, struct hwt_context *ctx)
+{
+ struct hwt_record_entry *entry, *entry1;
+ struct hwt_record_user_entry *user_entry;
+ int nitems_req;
+ int error;
+ int i;
+
+ nitems_req = 0;
+
+ error = copyin(record_get->nentries, &nitems_req, sizeof(int));
+ if (error)
+ return (error);
+
+ if (nitems_req < 1 || nitems_req > 1024)
+ return (ENXIO);
+
+ user_entry = malloc(sizeof(struct hwt_record_user_entry) * nitems_req,
+ M_HWT, M_WAITOK);
+
+ i = 0;
+
+ mtx_lock_spin(&ctx->mtx);
+ LIST_FOREACH_SAFE(entry, &ctx->records, next, entry1) {
+ user_entry[i].addr = entry->addr;
+ user_entry[i].size = entry->size;
+ user_entry[i].record_type = entry->record_type;
+ user_entry[i].tid = entry->tid;
+ if (entry->fullpath != NULL)
+ strncpy(user_entry[i].fullpath, entry->fullpath,
+ MAXPATHLEN);
+ LIST_REMOVE(entry, next);
+
+ i += 1;
+
+ /* TODO: deallocate entry. */
+
+ if (i == nitems_req)
+ break;
+ }
+ mtx_unlock_spin(&ctx->mtx);
+
+ if (i != 0) {
+ error = copyout(user_entry, record_get->records,
+ sizeof(struct hwt_record_user_entry) * i);
+ if (error)
+ return (error);
+ }
+
+ error = copyout(&i, record_get->nentries, sizeof(int));
+
+ free(user_entry, M_HWT);
+
+ return (error);
+}
+
+/*
+ * Check if owner process *o can trace target process *t;
+ */
+
+static int
+hwt_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 HWT 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
+hwt_thread_alloc(struct hwt_context *ctx, struct hwt_thread **thr0)
+{
+ struct hwt_thread *thr;
+ int error;
+
+ thr = malloc(sizeof(struct hwt_thread), M_HWT, M_WAITOK | M_ZERO);
+ thr->ctx = ctx;
+
+ error = hwt_alloc_buffers(thr);
+ if (error) {
+ return (error);
+ }
+
+ *thr0 = thr;
+
+ return (0);
+}
+
+int
+hwt_thread_create(struct hwt_context *ctx, struct thread *td)
+{
+ struct hwt_thread *thr;
+ int error;
+
+ error = hwt_thread_alloc(ctx, &thr);
+ if (error)
+ return (error);
+
+ thr->tid = td->td_tid;
+
+ error = hwt_create_cdev(thr);
+ if (error) {
+ printf("%s: could not create cdev, error %d\n",
+ __func__, error);
+ return (error);
+ }
+
+ mtx_lock_spin(&ctx->mtx_threads);
+ LIST_INSERT_HEAD(&ctx->threads, thr, next);
+ mtx_unlock_spin(&ctx->mtx_threads);
+
+ return (0);
+}
+
+static int
+hwt_ioctl_alloc(struct thread *td, struct hwt_alloc *halloc)
+{
+ char backend_name[HWT_BACKEND_MAXNAMELEN];
+ struct hwt_backend *backend;
+ struct hwt_ownerhash *hoh;
+ struct hwt_context *ctx;
+ struct hwt_thread *thr;
+ struct hwt_owner *ho;
+ struct proc *p;
+ int hindex;
+ int error;
+
+ if (halloc->bufsize > HWT_MAXBUFSIZE)
+ return (EINVAL);
+ if (halloc->bufsize % PAGE_SIZE)
+ return (EINVAL);
+ if (halloc->backend_name == NULL)
+ return (EINVAL);
+
+ error = copyinstr(halloc->backend_name, (void *)backend_name,
+ HWT_BACKEND_MAXNAMELEN, NULL);
+ if (error)
+ return (error);
+
+ backend = hwt_lookup_backend(backend_name);
+ if (backend == NULL)
+ return (ENXIO);
+
+ /* First get the owner. */
+ ho = hwt_lookup_ownerhash(td->td_proc);
+ if (ho) {
+ /* Check if the owner already tracing this pid. */
+ ctx = hwt_lookup_by_owner(ho, halloc->pid);
+ if (ctx)
+ return (EEXIST);
+ } else {
+ /* Create a new owner. */
+ ho = malloc(sizeof(struct hwt_owner), M_HWT,
+ M_WAITOK | M_ZERO);
+ ho->p = td->td_proc;
+ LIST_INIT(&ho->hwts);
+ mtx_init(&ho->mtx, "hwts", NULL, MTX_DEF);
+
+ hindex = HWT_HASH_PTR(ho->p, hwt_ownerhashmask);
+ hoh = &hwt_ownerhash[hindex];
+
+ mtx_lock_spin(&hwt_ownerhash_mtx);
+ LIST_INSERT_HEAD(hoh, ho, next);
+ mtx_unlock_spin(&hwt_ownerhash_mtx);
+ }
+
+ /* Allocate a new HWT. */
+ ctx = hwt_alloc();
+ ctx->bufsize = halloc->bufsize;
+ ctx->hwt_backend = backend;
+
+ /* Allocate first thread and buffers. */
+ error = hwt_thread_alloc(ctx, &thr);
+ if (error)
+ return (error);
+
+ ctx->pid = halloc->pid;
+ ctx->hwt_owner = ho;
+
+ /* Since we done with malloc, now get the victim proc. */
+ p = pfind(halloc->pid);
+ if (p == NULL) {
+ /* TODO: deallocate resources. */
+ return (ENXIO);
+ }
+
+ thr->tid = FIRST_THREAD_IN_PROC(p)->td_tid;
+
+ error = hwt_priv_check(td->td_proc, p);
+ if (error) {
+ /* TODO: deallocate resources. */
+ PROC_UNLOCK(p);
+ return (error);
+ }
+
+ p->p_flag2 |= P2_HWT;
+
+ mtx_lock(&ho->mtx);
+ LIST_INSERT_HEAD(&ho->hwts, ctx, next_hwts);
+ mtx_unlock(&ho->mtx);
+ PROC_UNLOCK(p);
+
+ mtx_lock_spin(&ctx->mtx_threads);
+ LIST_INSERT_HEAD(&ctx->threads, thr, next);
+ mtx_unlock_spin(&ctx->mtx_threads);
+
+ error = hwt_event_init(thr);
+ if (error)
+ return (error);
+
+ error = hwt_create_cdev(thr);
+ if (error) {
+ /* TODO: destroy buffers and thr. */
+ return (error);
+ }
+
+ /* Pass thread ID to user for mmap. */
+
+ struct hwt_record_entry *entry;
+ entry = malloc(sizeof(struct hwt_record_entry), M_HWT,
+ M_WAITOK | M_ZERO);
+ entry->record_type = HWT_RECORD_THREAD_CREATE;
+ entry->tid = thr->tid;
+
+ mtx_lock_spin(&ctx->mtx);
+ LIST_INSERT_HEAD(&ctx->records, entry, next);
+ mtx_unlock_spin(&ctx->mtx);
+
+ return (0);
+}
+
+static int
+hwt_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
+ struct thread *td)
+{
+ struct hwt_record_get *rget;
+ struct hwt_alloc *halloc;
+ struct hwt_context *ctx;
+ struct hwt_start *s;
+ struct proc *p;
+ int error;
+
+ switch (cmd) {
+ case HWT_IOC_ALLOC:
+ /* Allocate HWT context. */
+ halloc = (struct hwt_alloc *)addr;
+ error = hwt_ioctl_alloc(td, halloc);
+ return (error);
+
+ case HWT_IOC_START:
+ /* Start tracing. */
+ s = (struct hwt_start *)addr;
+
+ dprintf("%s: start, pid %d\n", __func__, s->pid);
+
+ /* Check if process is registered owner of any HWTs. */
+ ctx = hwt_lookup_by_owner_p(td->td_proc, s->pid);
+ if (ctx == NULL)
+ return (ENXIO);
+
+ p = pfind(s->pid);
+ if (p == NULL)
+ return (ENXIO);
+
+ ctx->proc = p;
+
+ hwt_insert_contexthash(ctx);
+ PROC_UNLOCK(p);
+ return (0);
+
+ case HWT_IOC_RECORD_GET:
+ rget = (struct hwt_record_get *)addr;
+
+ /* Check if process is registered owner of any HWTs. */
+ ctx = hwt_lookup_by_owner_p(td->td_proc, rget->pid);
+ if (ctx == NULL)
+ return (ENXIO);
+
+ error = hwt_send_records(rget, ctx);
+ return (error);
+ default:
+ return (ENXIO);
+ };
+
+ /* Unreached. */
+
+ return (ENXIO);
+}
+
+void
+hwt_switch_in(struct thread *td)
+{
+ struct hwt_context *ctx;
+ struct hwt_thread *thr;
+ struct proc *p;
+ int cpu_id;
+
+ p = td->td_proc;
+ if ((p->p_flag2 & P2_HWT) == 0)
+ return;
+
+ cpu_id = PCPU_GET(cpuid);
+
+ ctx = hwt_lookup_contexthash(p);
+ thr = hwt_lookup_thread(ctx, td);
+
+ dprintf("%s: thr %p on cpu_id %d\n", __func__, thr, cpu_id);
+
+ hwt_event_configure(thr, cpu_id);
+ hwt_event_enable(thr, cpu_id);
+}
+
+void
+hwt_switch_out(struct thread *td)
+{
+ struct hwt_context *ctx;
+ struct hwt_thread *thr;
+ struct proc *p;
+ int cpu_id;
+
+ p = td->td_proc;
+ if ((p->p_flag2 & P2_HWT) == 0)
+ return;
+
+ cpu_id = PCPU_GET(cpuid);
+
+ ctx = hwt_lookup_contexthash(p);
+ thr = hwt_lookup_thread(ctx, td);
+
+ dprintf("%s: thr %p on cpu_id %d\n", __func__, thr, cpu_id);
+
+ hwt_event_disable(thr, cpu_id);
+}
+
+static struct cdevsw hwt_cdevsw = {
+ .d_version = D_VERSION,
+ .d_name = "hwt",
+ .d_mmap_single = NULL,
+ .d_ioctl = hwt_ioctl
+};
+
+static void
+hwt_stop_proc_hwts(struct hwt_contexthash *hch, struct proc *p)
+{
+ struct hwt_context *ctx, *ctx1;
+
+ printf("%s: stopping hwt proc\n", __func__);
+
+ PROC_LOCK(p);
+ p->p_flag2 &= ~P2_HWT;
+
+ mtx_lock_spin(&hwt_contexthash_mtx);
+ LIST_FOREACH_SAFE(ctx, hch, next_hch, ctx1) {
+ if (ctx->proc == p) {
+ printf("%s: stopping proc hwt\n", __func__);
+
+ LIST_REMOVE(ctx, next_hch);
+ hwt_event_deinit(ctx);
+
+ ctx->proc = NULL;
+ }
+ }
+ mtx_unlock_spin(&hwt_contexthash_mtx);
+ PROC_UNLOCK(p);
+}
+
+static void
+hwt_stop_owner_hwts(struct hwt_contexthash *hch, struct hwt_owner *ho)
+{
+ struct hwt_context *ctx;
+ struct hwt_thread *thr;
+ struct proc *p;
+
+ printf("%s: stopping hwt owner\n", __func__);
+
+ while (1) {
+ mtx_lock(&ho->mtx);
+ ctx = LIST_FIRST(&ho->hwts);
+ if (ctx)
+ LIST_REMOVE(ctx, next_hwts);
+ mtx_unlock(&ho->mtx);
+
+ if (ctx == NULL)
+ break;
+
+ p = pfind(ctx->pid);
+ if (p != NULL) {
+ printf("stopping hwt pid %d\n", ctx->pid);
+
+ if (ctx->proc) {
+ /* Remove it from contexthash now. */
+ mtx_lock_spin(&hwt_contexthash_mtx);
+ LIST_REMOVE(ctx, next_hch);
+
+ ctx->proc = NULL;
+ mtx_unlock_spin(&hwt_contexthash_mtx);
+ }
+
+ /* Stop it now on every CPU. */
+ hwt_event_deinit(ctx);
+
+ PROC_UNLOCK(p);
+ printf("pid %d stopped\n", ctx->pid);
+ }
+
+ printf("%s: remove threads\n", __func__);
+
+ while (1) {
+ mtx_lock_spin(&ctx->mtx_threads);
+ thr = LIST_FIRST(&ctx->threads);
+ if (thr)
+ LIST_REMOVE(thr, next);
+ mtx_unlock_spin(&ctx->mtx_threads);
+
+ if (thr == NULL)
+ break;
+
+ hwt_destroy_buffers(thr);
+ destroy_dev_sched(thr->cdev);
+ }
+
+ free(ctx, M_HWT);
+ }
+
+ /* Destroy hwt owner. */
+ mtx_lock_spin(&hwt_ownerhash_mtx);
+ LIST_REMOVE(ho, next);
+ mtx_unlock_spin(&hwt_ownerhash_mtx);
+
+ free(ho, M_HWT);
+}
+
+static void
+hwt_process_exit(void *arg __unused, struct proc *p)
+{
+ struct hwt_contexthash *hch;
+ struct hwt_owner *ho;
+ int hindex;
+
+ hindex = HWT_HASH_PTR(p, hwt_contexthashmask);
+ hch = &hwt_contexthash[hindex];
+
+ ho = hwt_lookup_ownerhash(p);
+ if (ho) {
+ /* Stop HWTs associated with exiting owner. */
+ hwt_stop_owner_hwts(hch, ho);
+ } else if (p->p_flag2 & P2_HWT) {
+ /* Stop HWTs associated with exiting proc. */
+ hwt_stop_proc_hwts(hch, p);
+ }
+}
+
+static int
+hwt_load(void)
+{
+ struct make_dev_args args;
+ int error;
+
+ LIST_INIT(&hwt_backends);
+
+ make_dev_args_init(&args);
+ args.mda_devsw = &hwt_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;
+
+ error = make_dev_s(&args, &hwt_cdev, "hwt");
+ if (error != 0)
+ return (error);
+
+ hwt_contexthash = hashinit(HWT_PROCHASH_SIZE, M_HWT,
+ &hwt_contexthashmask);
+ mtx_init(&hwt_contexthash_mtx, "hwt-proc-hash", "hwt-proc", MTX_SPIN);
+
+ hwt_ownerhash = hashinit(HWT_OWNERHASH_SIZE, M_HWT, &hwt_ownerhashmask);
+ mtx_init(&hwt_ownerhash_mtx, "hwt-owner-hash", "hwt-owner", MTX_SPIN);
+
+ mtx_init(&hwt_backend_mtx, "hwt backend", NULL, MTX_SPIN);
+
+ hwt_exit_tag = EVENTHANDLER_REGISTER(process_exit, hwt_process_exit,
+ NULL, EVENTHANDLER_PRI_ANY);
+
+ return (0);
+}
+
+static int
+hwt_unload(void)
+{
+
+ dprintf("%s\n", __func__);
+
+ destroy_dev(hwt_cdev);
+
+ return (0);
+}
+
+static int
+hwt_modevent(module_t mod, int type, void *data)
+{
+ int error;
+
+ switch (type) {
+ case MOD_LOAD:
+ error = hwt_load();
+ break;
+ case MOD_UNLOAD:
+ error = hwt_unload();
+ break;
+ default:
+ error = 0;
+ break;
+ }
+
+ return (error);
+}
+
+static moduledata_t hwt_mod = {
+ "hwt",
+ hwt_modevent,
+ NULL
+};
+
+DECLARE_MODULE(hwt, hwt_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(hwt, 1);
Index: sys/dev/hwt/hwt_hook.h
===================================================================
--- /dev/null
+++ sys/dev/hwt/hwt_hook.h
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 2023 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DEV_HWT_HWT_HOOK_H_
+#define _DEV_HWT_HWT_HOOK_H_
+
+#include "hwt_record.h"
+
+struct hwt_record_entry {
+ enum hwt_record_type record_type;
+ LIST_ENTRY(hwt_record_entry) next;
+ char *fullpath;
+ lwpid_t tid;
+ uintptr_t addr;
+ size_t size;
+};
+
+void hwt_switch_in(struct thread *td);
+void hwt_switch_out(struct thread *td);
+void hwt_record(struct thread *td, enum hwt_record_type record_type,
+ struct hwt_record_entry *ent);
+
+#endif /* !_DEV_HWT_HWT_HOOK_H_ */
Index: sys/dev/hwt/hwt_record.h
===================================================================
--- /dev/null
+++ sys/dev/hwt/hwt_record.h
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2023 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DEV_HWT_HWT_RECORD_H_
+#define _DEV_HWT_HWT_RECORD_H_
+
+enum hwt_record_type {
+ HWT_RECORD_MMAP,
+ HWT_RECORD_MUNMAP,
+ HWT_RECORD_EXECUTABLE,
+ HWT_RECORD_INTERP,
+ HWT_RECORD_THREAD_CREATE,
+ HWT_RECORD_THREAD_SET_NAME,
+};
+
+#endif /* !_DEV_HWT_HWT_RECORD_H_ */
Index: sys/dev/hwt/hwt_record.c
===================================================================
--- /dev/null
+++ sys/dev/hwt/hwt_record.c
@@ -0,0 +1,107 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+
+#include <vm/vm.h>
+
+#include <dev/hwt/hwt_hook.h>
+#include <dev/hwt/hwtvar.h>
+
+#define HWT_RECORD_DEBUG
+#undef HWT_RECORD_DEBUG
+
+#ifdef HWT_RECORD_DEBUG
+#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+void
+hwt_record(struct thread *td, enum hwt_record_type record_type,
+ struct hwt_record_entry *ent)
+{
+ struct hwt_record_entry *entry;
+ struct hwt_context *ctx;
+ struct proc *p;
+
+ p = td->td_proc;
+ if ((p->p_flag2 & P2_HWT) == 0)
+ return;
+
+ ctx = hwt_lookup_contexthash(p);
+ if (ctx == NULL)
+ return;
+
+ switch (record_type) {
+ case HWT_RECORD_MMAP:
+ dprintf("%s: MMAP path %s addr %lx size %lx\n", __func__,
+ ent->fullpath, (unsigned long)ent->addr, ent->size);
+ break;
+ case HWT_RECORD_EXECUTABLE:
+ dprintf("%s: EXEC path %s addr %lx size %lx\n", __func__,
+ ent->fullpath, (unsigned long)ent->addr, ent->size);
+ break;
+ case HWT_RECORD_INTERP:
+ dprintf("%s: INTP path %s addr %lx size %lx\n", __func__,
+ ent->fullpath, (unsigned long)ent->addr, ent->size);
+ break;
+ case HWT_RECORD_MUNMAP:
+ break;
+ case HWT_RECORD_THREAD_CREATE:
+ dprintf("%s: NEW thread %p, tid %d\n", __func__, td,
+ td->td_tid);
+ hwt_thread_create(ctx, td);
+ break;
+ case HWT_RECORD_THREAD_SET_NAME:
+ dprintf("%s: THREAD_SET_NAME %p\n", __func__, td);
+ break;
+ default:
+ return;
+ };
+
+ entry = malloc(sizeof(struct hwt_record_entry), M_HWT, M_WAITOK);
+ entry->record_type = record_type;
+ entry->tid = td->td_tid;
+
+ if (ent) {
+ entry->fullpath = strdup(ent->fullpath, M_HWT);
+ entry->addr = ent->addr;
+ entry->size = ent->size;
+ }
+
+ mtx_lock_spin(&ctx->mtx);
+ LIST_INSERT_HEAD(&ctx->records, entry, next);
+ mtx_unlock_spin(&ctx->mtx);
+}
Index: sys/dev/hwt/hwtvar.h
===================================================================
--- /dev/null
+++ sys/dev/hwt/hwtvar.h
@@ -0,0 +1,101 @@
+/*-
+ * Copyright (c) 2023 Ruslan Bukin <br@bsdpad.com>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security
+ * by Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DEV_HWT_HWTVAR_H_
+#define _DEV_HWT_HWTVAR_H_
+
+#ifndef LOCORE
+static MALLOC_DEFINE(M_HWT, "hwt", "Hardware Trace");
+
+#define HWT_LOCK(sc) mtx_lock(&(sc)->mtx)
+#define HWT_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
+#define HWT_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED)
+
+struct hwt_thread {
+ vm_page_t *pages;
+ int npages;
+ lwpid_t tid;
+ vm_object_t obj;
+ struct cdev *cdev;
+ struct hwt_context *ctx;
+ LIST_ENTRY(hwt_thread) next;
+};
+
+struct hwt_context {
+ LIST_HEAD(, hwt_thread) threads;
+ struct mtx mtx_threads; /* Protects threads. */
+ size_t bufsize; /* Applied to each hwt_thread*/
+
+ LIST_HEAD(, hwt_record_entry) records;
+ struct mtx mtx; /* Protects records. */
+
+ LIST_ENTRY(hwt_context) next_hch; /* Entry in contexthash. */
+ LIST_ENTRY(hwt_context) next_hwts; /* Entry in ho->hwts. */
+
+ struct proc *proc; /* Could be NULL if exited. */
+ pid_t pid;
+
+ struct hwt_owner *hwt_owner;
+ struct hwt_backend *hwt_backend;
+};
+
+struct hwt_owner {
+ struct proc *p;
+ struct mtx mtx; /* Protects hwts. */
+ LIST_HEAD(, hwt_context) hwts; /* Owned HWTs. */
+ LIST_ENTRY(hwt_owner) next; /* Entry in hwt owner hash. */
+};
+
+struct hwt_backend_ops {
+ void (*hwt_event_init)(struct hwt_thread *);
+ void (*hwt_event_deinit)(void);
+ void (*hwt_event_configure)(struct hwt_thread *, int cpu_id);
+ void (*hwt_event_enable)(struct hwt_thread *, int cpu_id);
+ void (*hwt_event_disable)(struct hwt_thread *, int cpu_id);
+ int (*hwt_event_read)(struct hwt_thread *, int cpu_id,
+ int *curpage, vm_offset_t *curpage_offset);
+
+ /* Debugging only. */
+ void (*hwt_event_dump)(struct hwt_thread *, int cpu_id);
+};
+
+struct hwt_backend {
+ const char *name;
+ struct hwt_backend_ops *ops;
+ LIST_ENTRY(hwt_backend) next;
+};
+
+int hwt_register(struct hwt_backend *);
+int hwt_thread_create(struct hwt_context *ctx, struct thread *td);
+
+struct hwt_context * hwt_lookup_contexthash(struct proc *p);
+
+#endif /* !LOCORE */
+#endif /* !_DEV_HWT_HWTVAR_H_ */
Index: sys/kern/imgact_elf.c
===================================================================
--- sys/kern/imgact_elf.c
+++ sys/kern/imgact_elf.c
@@ -35,6 +35,7 @@
__FBSDID("$FreeBSD$");
#include "opt_capsicum.h"
+#include "opt_hwt_hooks.h"
#include <sys/param.h>
#include <sys/capsicum.h>
@@ -83,6 +84,10 @@
#include <vm/vm_object.h>
#include <vm/vm_extern.h>
+#ifdef HWT_HOOKS
+#include <dev/hwt/hwt_hook.h>
+#endif
+
#if __has_feature(capabilities)
#include <cheri/cheri.h>
#endif
@@ -1307,6 +1312,9 @@
int32_t osrel;
bool free_interp;
int error, i, n;
+#ifdef HWT_HOOKS
+ struct proc *p;
+#endif
hdr = (const Elf_Ehdr *)imgp->image_header;
@@ -1609,6 +1617,17 @@
imgp->interp_start = 0;
imgp->interp_end = 0;
+#ifdef HWT_HOOKS
+ /* HWT: record main binary. */
+ struct hwt_record_entry ent;
+ if (td->td_proc->p_flag2 & P2_HWT) {
+ ent.fullpath = imgp->execpath;
+ ent.addr = (uintptr_t) entry;
+ ent.size = (size_t) (imgp->end_addr - imgp->start_addr);
+ hwt_record(td, HWT_RECORD_EXECUTABLE, &ent);
+ }
+#endif
+
if (interp != NULL) {
VOP_UNLOCK(imgp->vp);
if ((map->flags & MAP_ASLR) != 0) {
@@ -1624,6 +1643,18 @@
vn_lock(imgp->vp, LK_SHARED | LK_RETRY);
if (error != 0)
goto ret;
+
+#ifdef HWT_HOOKS
+ /* HWT: Record interp. */
+ struct hwt_record_entry ent;
+ p = td->td_proc;
+ if (p->p_flag2 & P2_HWT) {
+ ent.fullpath = interp;
+ ent.addr = (uintptr_t)imgp->entry_addr;
+ ent.size = (size_t) (imgp->interp_end - imgp->interp_start);
+ hwt_record(td, HWT_RECORD_INTERP, &ent);
+ }
+#endif
} else
addr = imgp->et_dyn_addr;
Index: sys/kern/kern_thr.c
===================================================================
--- sys/kern/kern_thr.c
+++ sys/kern/kern_thr.c
@@ -31,6 +31,7 @@
#include "opt_posix.h"
#include "opt_hwpmc_hooks.h"
+#include "opt_hwt_hooks.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/lock.h>
@@ -59,6 +60,9 @@
#ifdef HWPMC_HOOKS
#include <sys/pmckern.h>
#endif
+#ifdef HWT_HOOKS
+#include <dev/hwt/hwt_hook.h>
+#endif
#include <machine/frame.h>
@@ -269,6 +273,10 @@
PMC_CALL_HOOK_UNLOCKED(newtd, PMC_FN_THR_CREATE_LOG, NULL);
#endif
+#ifdef HWT_HOOKS
+ hwt_record(newtd, HWT_RECORD_THREAD_CREATE, NULL);
+#endif
+
tidhash_add(newtd);
/* ignore timesharing class */
@@ -611,6 +619,9 @@
if (PMC_PROC_IS_USING_PMCS(p) || PMC_SYSTEM_SAMPLING_ACTIVE())
PMC_CALL_HOOK_UNLOCKED(ttd, PMC_FN_THR_CREATE_LOG, NULL);
#endif
+#ifdef HWT_HOOKS
+ hwt_record(ttd, HWT_RECORD_THREAD_SET_NAME, NULL);
+#endif
#ifdef KTR
sched_clear_tdname(ttd);
#endif
Index: sys/kern/sched_ule.c
===================================================================
--- sys/kern/sched_ule.c
+++ sys/kern/sched_ule.c
@@ -41,6 +41,7 @@
__FBSDID("$FreeBSD$");
#include "opt_hwpmc_hooks.h"
+#include "opt_hwt_hooks.h"
#include "opt_sched.h"
#include <sys/param.h>
@@ -70,6 +71,10 @@
#include <sys/pmckern.h>
#endif
+#ifdef HWT_HOOKS
+#include <dev/hwt/hwt_hook.h>
+#endif
+
#ifdef KDTRACE_HOOKS
#include <sys/dtrace_bsd.h>
int __read_mostly dtrace_vtime_active;
@@ -2294,11 +2299,20 @@
if (dtrace_vtime_active)
(*dtrace_vtime_switch_func)(newtd);
#endif
+
+#ifdef HWT_HOOKS
+ hwt_switch_out(td);
+#endif
+
td->td_oncpu = NOCPU;
cpu_switch(td, newtd, mtx);
cpuid = td->td_oncpu = PCPU_GET(cpuid);
SDT_PROBE0(sched, , , on__cpu);
+#ifdef HWT_HOOKS
+ hwt_switch_in(td);
+#endif
+
#ifdef HWPMC_HOOKS
if (PMC_PROC_IS_USING_PMCS(td->td_proc))
PMC_SWITCH_CONTEXT(td, PMC_FN_CSW_IN);
Index: sys/kern/vfs_vnops.c
===================================================================
--- sys/kern/vfs_vnops.c
+++ sys/kern/vfs_vnops.c
@@ -46,6 +46,7 @@
__FBSDID("$FreeBSD$");
#include "opt_hwpmc_hooks.h"
+#include "opt_hwt_hooks.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -96,6 +97,10 @@
#include <sys/pmckern.h>
#endif
+#ifdef HWT_HOOKS
+#include <dev/hwt/hwt_hook.h>
+#endif
+
static fo_rdwr_t vn_read;
static fo_rdwr_t vn_write;
static fo_rdwr_t vn_io_fault;
@@ -2924,6 +2929,25 @@
}
}
#endif
+
+#ifdef HWT_HOOKS
+ /* HWT: record dynamic libs. */
+ struct hwt_record_entry ent;
+ char *fullpath;
+ char *freepath;
+
+ if ((prot & VM_PROT_EXECUTE) != 0 && error == 0) {
+ if (vn_fullpath(vp, &fullpath, &freepath) == 0) {
+ ent.fullpath = fullpath;
+ ent.addr = (uintptr_t) *addr;
+ ent.size = (size_t) size;
+ hwt_record(td, HWT_RECORD_MMAP, &ent);
+ if (freepath != NULL)
+ free(freepath, M_TEMP);
+ }
+ }
+#endif
+
return (error);
}
Index: sys/sys/proc.h
===================================================================
--- sys/sys/proc.h
+++ sys/sys/proc.h
@@ -885,6 +885,7 @@
#define P2_WEXIT 0x00040000 /* exit just started, no
external thread_single() is
permitted */
+#define P2_HWT 0x00080000 /* Process is using HWT. */
/* Flags protected by proctree_lock, kept in p_treeflags. */
#define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */
Index: sys/vm/vm_mmap.c
===================================================================
--- sys/vm/vm_mmap.c
+++ sys/vm/vm_mmap.c
@@ -110,6 +110,10 @@
#include <sys/pmckern.h>
#endif
+#ifdef HWT_HOOKS
+#include <dev/hwt/hwt_hook.h>
+#endif
+
#if __has_feature(capabilities)
#include <cheri/cheric.h>
#endif
@@ -1054,6 +1058,15 @@
#endif
rv = vm_map_remove_locked(map, addr, addr + size);
+#ifdef HWT_HOOKS
+ struct hwt_record_entry ent;
+ if (rv == KERN_SUCCESS) {
+ ent.addr = (uintptr_t) addr;
+ ent.size = (size_t) size;
+ hwt_record(td, HWT_RECORD_MUNMAP, &ent);
+ }
+#endif
+
#ifdef HWPMC_HOOKS
if (rv == KERN_SUCCESS && __predict_false(pmc_handled)) {
/* downgrade the lock to prevent a LOR with the pmc-sx lock */
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Nov 7, 5:38 AM (22 h, 14 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14505318
Default Alt Text
D40466.id123424.diff (45 KB)
Attached To
Mode
D40466: Hardware Trace (HWT) framework
Attached
Detach File
Event Timeline
Log In to Comment