Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -1911,6 +1911,19 @@ dev/hwpmc/hwpmc_logging.c optional hwpmc dev/hwpmc/hwpmc_mod.c optional hwpmc dev/hwpmc/hwpmc_soft.c optional hwpmc +dev/hwt/hwt.c optional hwt +dev/hwt/hwt_backend.c optional hwt +dev/hwt/hwt_config.c optional hwt +dev/hwt/hwt_context.c optional hwt +dev/hwt/hwt_contexthash.c optional hwt +dev/hwt/hwt_cpu.c optional hwt +dev/hwt/hwt_hook.c optional hwt +dev/hwt/hwt_ioctl.c optional hwt +dev/hwt/hwt_owner.c optional hwt +dev/hwt/hwt_ownerhash.c optional hwt +dev/hwt/hwt_record.c optional hwt +dev/hwt/hwt_thread.c optional hwt +dev/hwt/hwt_vm.c optional hwt dev/ichiic/ig4_acpi.c optional ig4 acpi iicbus dev/ichiic/ig4_iic.c optional ig4 iicbus dev/ichiic/ig4_pci.c optional ig4 pci iicbus @@ -3894,6 +3907,7 @@ kern/kern_flag.c standard kern/kern_fork.c standard kern/kern_hhook.c standard +kern/kern_hwt.c standard kern/kern_idle.c standard kern/kern_intr.c standard kern/kern_jail.c standard 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.c =================================================================== --- /dev/null +++ sys/dev/hwt/hwt.c @@ -0,0 +1,229 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Hardware Tracing framework. + * + * The framework manages hardware tracing units that collect information + * about software execution and store it as events in highly compressed format + * into DRAM. The events cover information about control flow changes of a + * program, whether branches taken or not, exceptions taken, timing information, + * cycles elapsed and more. That allows us to restore entire program flow of a + * given application without performance impact. + * + * Design overview. + * + * The framework provides character devices for mmap(2) and ioctl(2) system + * calls to allow user to manage CPU (hardware) tracing units. + * + * /dev/hwt: + * .ioctl: + * hwt_ioctl(): + * a) HWT_IOC_ALLOC + * Allocates kernel tracing context ctx for a given pid, + * and for required mode of operation (CPU or Thread mode), + * returns unique ident. + * Creates a new character device for ctx management. + * + * /dev/hwt_%d[_%d], ident[, thread_id] + * .mmap + * Maps tracing buffers of the corresponding thread to userspace. + * .ioctl + * hwt_thread_ioctl(): + * a) HWT_IOC_START + * Enables tracing unit for a given context. + * b) HWT_IOC_RECORD_GET + * Transfers (small) record entries collected during program + * execution for a given context to userspace, such as mmaping + * tables of executable and dynamic libraries, interpreter, + * tid of threads created, etc. + * c) HWT_IOC_SET_CONFIG + * This allows to set backend-specific configuration of the + * trace unit. + * d) HWT_IOC_WAKEUP + * This wakes up a thread that is currently sleeping. + * e) HWT_IOC_BUFPTR_GET + * Transfers current hardware pointer in the filling buffer + * to userspace. + * + * HWT context lifecycle: + * 1. User invokes HWT_IOC_ALLOC ioctl with information about pid to trace and + * size of the buffers for the trace data to allocate. + * Some architectures may have different tracing units supported, so user + * also provides backend name to use for this context, e.g. "coresight". + * 2. Kernel allocates context, lookups the proc for the given pid. Creates + * first hwt_thread in the context and allocates trace buffers for it. + * Immediately, kernel initializes tracing backend. + * Kernel creates character device and returns unique identificator of + * trace context. + * 3. User opens new character device to operate with the new context. + * User invokes HWT_IOC_START ioctl, kernel marks context as RUNNING. + * At this point any HWT hook invocation by scheduler enables/disables + * tracing for threads associated with the context (threads of the proc). + * Any new threads creation (of the target proc) procedures will be invoking + * corresponding hooks in HWT framework, so that new hwt_thread and buffers + * allocated, character device for mmap(2) created on the fly. + * 4. User issues HWT_IOC_RECORD_GET ioctl to fetch information about mmaping + * tables and threads created during application startup. + * 5. User mmaps tracing buffers of each thread to userspace (using + * /dev/hwt_%d_%d % (ident, thread_id) character devices). + * 6. User can repeat 4 if expected thread is not yet created during target + * application execution. + * 7. User issues HWT_IOC_BUFPTR_GET ioctl to get current filling level of the + * hardware buffer of a given thread. + * 8. User invokes trace decoder library to process available data and see the + * results in human readable form. + * 9. User repeates 7 if needed. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define HWT_DEBUG +#undef HWT_DEBUG + +#ifdef HWT_DEBUG +#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__) +#else +#define dprintf(fmt, ...) +#endif + +static eventhandler_tag hwt_exit_tag; +static struct cdev *hwt_cdev; +static struct cdevsw hwt_cdevsw = { + .d_version = D_VERSION, + .d_name = "hwt", + .d_mmap_single = NULL, + .d_ioctl = hwt_ioctl +}; + +static void +hwt_process_exit(void *arg __unused, struct proc *p) +{ + struct hwt_owner *ho; + + /* Stop HWTs associated with exiting owner, if any. */ + ho = hwt_ownerhash_lookup(p); + if (ho) + hwt_owner_shutdown(ho); +} + +static int +hwt_load(void) +{ + struct make_dev_args args; + int error; + + 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; + + hwt_backend_load(); + hwt_ctx_load(); + hwt_contexthash_load(); + hwt_ownerhash_load(); + + error = make_dev_s(&args, &hwt_cdev, "hwt"); + if (error != 0) + return (error); + + hwt_exit_tag = EVENTHANDLER_REGISTER(process_exit, hwt_process_exit, + NULL, EVENTHANDLER_PRI_ANY); + + hwt_hook_load(); + + return (0); +} + +static int +hwt_unload(void) +{ + + hwt_hook_unload(); + EVENTHANDLER_DEREGISTER(process_exit, hwt_exit_tag); + destroy_dev(hwt_cdev); + hwt_ownerhash_unload(); + hwt_contexthash_unload(); + hwt_ctx_unload(); + hwt_backend_unload(); + + 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_backend.h =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_backend.h @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_HWT_HWT_BACKEND_H_ +#define _DEV_HWT_HWT_BACKEND_H_ + +struct hwt_backend_ops { + int (*hwt_backend_init)(struct hwt_context *); + void (*hwt_backend_deinit)(void); + int (*hwt_backend_configure)(struct hwt_context *, int cpu_id, + int thread_id); + void (*hwt_backend_enable)(int cpu_id); + void (*hwt_backend_disable)(int cpu_id); + int (*hwt_backend_read)(int cpu_id, int *curpage, + vm_offset_t *curpage_offset); + + /* Debugging only. */ + void (*hwt_backend_dump)(int cpu_id); +}; + +struct hwt_backend { + const char *name; + struct hwt_backend_ops *ops; +}; + +int hwt_backend_init(struct hwt_context *ctx); +void hwt_backend_deinit(struct hwt_context *ctx); +int hwt_backend_configure(struct hwt_context *ctx, int cpu_id, int thread_id); +void hwt_backend_enable(struct hwt_context *ctx, int cpu_id); +void hwt_backend_disable(struct hwt_context *ctx, int cpu_id); +void hwt_backend_dump(struct hwt_context *ctx, int cpu_id); +int hwt_backend_read(struct hwt_context *ctx, int cpu_id, int *curpage, + vm_offset_t *curpage_offset); +int hwt_backend_register(struct hwt_backend *); +int hwt_backend_unregister(struct hwt_backend *); +struct hwt_backend * hwt_backend_lookup(const char *name); + +void hwt_backend_load(void); +void hwt_backend_unload(void); + +#define HWT_BACKEND_LOCK() mtx_lock_spin(&hwt_backend_mtx) +#define HWT_BACKEND_UNLOCK() mtx_unlock_spin(&hwt_backend_mtx) + +#endif /* !_DEV_HWT_HWT_BACKEND_H_ */ + Index: sys/dev/hwt/hwt_backend.c =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_backend.c @@ -0,0 +1,230 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Hardware Trace (HWT) framework. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 hwt_backend_mtx; + +struct hwt_backend_entry { + struct hwt_backend *backend; + LIST_ENTRY(hwt_backend_entry) next; +}; + +static LIST_HEAD(, hwt_backend_entry) hwt_backends; + +static MALLOC_DEFINE(M_HWT_BACKEND, "hwt_backend", "HWT backend"); + +int +hwt_backend_init(struct hwt_context *ctx) +{ + int error; + + dprintf("%s\n", __func__); + + error = ctx->hwt_backend->ops->hwt_backend_init(ctx); + + return (error); +} + +void +hwt_backend_deinit(struct hwt_context *ctx) +{ + + dprintf("%s\n", __func__); + + ctx->hwt_backend->ops->hwt_backend_deinit(); +} + +int +hwt_backend_configure(struct hwt_context *ctx, int cpu_id, int thread_id) +{ + int error; + + dprintf("%s\n", __func__); + + error = ctx->hwt_backend->ops->hwt_backend_configure(ctx, cpu_id, + thread_id); + + return (error); +} + +void +hwt_backend_enable(struct hwt_context *ctx, int cpu_id) +{ + + dprintf("%s\n", __func__); + + ctx->hwt_backend->ops->hwt_backend_enable(cpu_id); +} + +void +hwt_backend_disable(struct hwt_context *ctx, int cpu_id) +{ + + dprintf("%s\n", __func__); + + ctx->hwt_backend->ops->hwt_backend_disable(cpu_id); +} + +void __unused +hwt_backend_dump(struct hwt_context *ctx, int cpu_id) +{ + + dprintf("%s\n", __func__); + + ctx->hwt_backend->ops->hwt_backend_dump(cpu_id); +} + +int +hwt_backend_read(struct hwt_context *ctx, int cpu_id, int *curpage, + vm_offset_t *curpage_offset) +{ + int error; + + dprintf("%s\n", __func__); + + error = ctx->hwt_backend->ops->hwt_backend_read(cpu_id, curpage, + curpage_offset); + + return (error); +} + +struct hwt_backend * +hwt_backend_lookup(const char *name) +{ + struct hwt_backend_entry *entry; + struct hwt_backend *backend; + + HWT_BACKEND_LOCK(); + LIST_FOREACH(entry, &hwt_backends, next) { + backend = entry->backend; + if (strcmp(backend->name, name) == 0) { + HWT_BACKEND_UNLOCK(); + return (backend); + } + } + HWT_BACKEND_UNLOCK(); + + return (NULL); +} + +int +hwt_backend_register(struct hwt_backend *backend) +{ + struct hwt_backend_entry *entry; + + if (backend == NULL || + backend->name == NULL || + backend->ops == NULL) + return (EINVAL); + +#if 0 + mtx_init(&entry->mtx, "BKND", NULL, MTX_DEF); +#endif + + entry = malloc(sizeof(struct hwt_backend_entry), M_HWT_BACKEND, + M_WAITOK | M_ZERO); + entry->backend = backend; + + HWT_BACKEND_LOCK(); + LIST_INSERT_HEAD(&hwt_backends, entry, next); + HWT_BACKEND_UNLOCK(); + + return (0); +} + +int +hwt_backend_unregister(struct hwt_backend *backend) +{ + struct hwt_backend_entry *entry, *tmp; + + if (backend == NULL) + return (EINVAL); + + /* TODO: check if not in use */ + + HWT_BACKEND_LOCK(); + LIST_FOREACH_SAFE(entry, &hwt_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 +hwt_backend_load(void) +{ + + mtx_init(&hwt_backend_mtx, "hwt backend", NULL, MTX_SPIN); + LIST_INIT(&hwt_backends); +} + +void +hwt_backend_unload(void) +{ + + /* TODO: ensure all unregistered */ + + mtx_destroy(&hwt_backend_mtx); +} Index: sys/dev/hwt/hwt_config.h =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_config.h @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_HWT_HWT_CONFIG_H_ +#define _DEV_HWT_HWT_CONFIG_H_ + +int hwt_config_set(struct thread *td, struct hwt_context *ctx, + struct hwt_set_config *sconf); +void hwt_config_free(struct hwt_context *ctx); + +#endif /* !_DEV_HWT_HWT_CONFIG_H_ */ Index: sys/dev/hwt/hwt_config.c =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_config.c @@ -0,0 +1,107 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#define HWT_MAXCONFIGSIZE PAGE_SIZE + +#define HWT_CONFIG_DEBUG +#undef HWT_CONFIG_DEBUG + +#ifdef HWT_CONFIG_DEBUG +#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__) +#else +#define dprintf(fmt, ...) +#endif + +static MALLOC_DEFINE(M_HWT_CONFIG, "hwt_config", "HWT config"); + +int +hwt_config_set(struct thread *td, struct hwt_context *ctx, + struct hwt_set_config *sconf) +{ + size_t config_size; + void *old_config; + void *config; + int error; + + config_size = sconf->config_size; + if (config_size == 0) + return (0); + + if (config_size > HWT_MAXCONFIGSIZE) + return (EFBIG); + + config = malloc(config_size, M_HWT_CONFIG, M_WAITOK | M_ZERO); + + error = copyin(sconf->config, config, config_size); + if (error) { + free(config, M_HWT_CONFIG); + return (error); + } + + HWT_CTX_LOCK(ctx); + old_config = ctx->config; + ctx->config = config; + ctx->config_size = sconf->config_size; + ctx->config_version = sconf->config_version; + HWT_CTX_UNLOCK(ctx); + + if (old_config != NULL) + free(old_config, M_HWT_CONFIG); + + return (error); +} + +void +hwt_config_free(struct hwt_context *ctx) +{ + + if (ctx->config == NULL) + return; + + free(ctx->config, M_HWT_CONFIG); + + ctx->config = NULL; +} Index: sys/dev/hwt/hwt_context.h =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_context.h @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_HWT_HWT_CONTEXT_H_ +#define _DEV_HWT_HWT_CONTEXT_H_ + +struct hwt_context { + LIST_HEAD(, hwt_record_entry) records; + + LIST_ENTRY(hwt_context) next_hch; /* Entry in contexthash. */ + LIST_ENTRY(hwt_context) next_hwts; /* Entry in ho->hwts. */ + + int mode; + int ident; + + /* CPU mode. */ + cpuset_t cpu_map; + TAILQ_HEAD(, hwt_cpu) cpus; + + /* Thread mode. */ + struct proc *proc; /* Target proc. */ + pid_t pid; /* Target pid. */ + TAILQ_HEAD(, hwt_thread) threads; + int thread_counter; + int pause_on_mmap; + + size_t bufsize; /* Trace bufsize for each vm.*/ + void *config; + size_t config_size; + int config_version; + + struct hwt_owner *hwt_owner; + struct hwt_backend *hwt_backend; + + struct mtx mtx; + int state; +#define CTX_STATE_RUNNING (1 << 0) +}; + +#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) + +struct hwt_context * hwt_ctx_alloc(void); +void hwt_ctx_free(struct hwt_context *ctx); + +void hwt_ctx_load(void); +void hwt_ctx_unload(void); + +#endif /* !_DEV_HWT_HWT_CONTEXT_H_ */ Index: sys/dev/hwt/hwt_context.c =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_context.c @@ -0,0 +1,140 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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, "hwt_ctx", "Hardware Trace"); + +static bitstr_t *ident_set; +static int ident_set_size; +static struct mtx ident_set_mutex; + +static int +hwt_ctx_ident_alloc(int *new_ident) +{ + + mtx_lock_spin(&ident_set_mutex); + bit_ffc(ident_set, ident_set_size, new_ident); + if (*new_ident == -1) { + mtx_unlock_spin(&ident_set_mutex); + return (ENOMEM); + } + bit_set(ident_set, *new_ident); + mtx_unlock_spin(&ident_set_mutex); + + return (0); +} + +static void +hwt_ctx_ident_free(int ident) +{ + + mtx_lock_spin(&ident_set_mutex); + bit_clear(ident_set, ident); + mtx_unlock_spin(&ident_set_mutex); +} + +struct hwt_context * +hwt_ctx_alloc(void) +{ + struct hwt_context *ctx; + int error; + + ctx = malloc(sizeof(struct hwt_context), M_HWT_CTX, M_WAITOK | M_ZERO); + ctx->thread_counter = 0; + + LIST_INIT(&ctx->records); + TAILQ_INIT(&ctx->threads); + TAILQ_INIT(&ctx->cpus); + mtx_init(&ctx->mtx, "ctx", NULL, MTX_SPIN); + + hwt_ctx_ident_alloc(&ctx->ident); + + error = hwt_ctx_ident_alloc(&ctx->ident); + if (error) { + printf("could not allocate ident bit str\n"); + return (NULL); + } + + return (ctx); +} + +void +hwt_ctx_free(struct hwt_context *ctx) +{ + + hwt_config_free(ctx); + hwt_ctx_ident_free(ctx->ident); + free(ctx, M_HWT_CTX); +} + +void +hwt_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_SPIN); +} + +void +hwt_ctx_unload(void) +{ + + mtx_destroy(&ident_set_mutex); + free(ident_set, M_HWT_CTX); +} Index: sys/dev/hwt/hwt_contexthash.h =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_contexthash.h @@ -0,0 +1,44 @@ +/*- + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_HWT_HWT_CONTEXTHASH_H_ +#define _DEV_HWT_HWT_CONTEXTHASH_H_ + +struct hwt_context * hwt_contexthash_lookup(struct proc *p); +void hwt_contexthash_insert(struct hwt_context *ctx); +void hwt_contexthash_remove(struct hwt_context *ctx); + +void hwt_contexthash_load(void); +void hwt_contexthash_unload(void); + +#define HWT_CTXHASH_LOCK() mtx_lock_spin(&hwt_contexthash_mtx) +#define HWT_CTXHASH_UNLOCK() mtx_unlock_spin(&hwt_contexthash_mtx) + +#endif /* !_DEV_HWT_HWT_CONTEXTHASH_H_ */ Index: sys/dev/hwt/hwt_contexthash.c =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_contexthash.c @@ -0,0 +1,139 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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, "hwt_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 hwt_contexthash_mtx; +static u_long hwt_contexthashmask; +static LIST_HEAD(hwt_contexthash, hwt_context) *hwt_contexthash; + +/* + * To use by hwt_switch_in/out() and hwt_record() only. + * This function returns mtx locked. + */ +struct hwt_context * +hwt_contexthash_lookup(struct proc *p) +{ + struct hwt_contexthash *hch; + struct hwt_context *ctx; + int hindex; + + hindex = HWT_HASH_PTR(p, hwt_contexthashmask); + hch = &hwt_contexthash[hindex]; + + HWT_CTXHASH_LOCK(); + LIST_FOREACH(ctx, hch, next_hch) { + if (ctx->proc == p) { + HWT_CTX_LOCK(ctx); + HWT_CTXHASH_UNLOCK(); + return (ctx); + } + } + HWT_CTXHASH_UNLOCK(); + + return (NULL); +} + +void +hwt_contexthash_insert(struct hwt_context *ctx) +{ + struct hwt_contexthash *hch; + int hindex; + + hindex = HWT_HASH_PTR(ctx->proc, hwt_contexthashmask); + hch = &hwt_contexthash[hindex]; + + HWT_CTXHASH_LOCK(); + LIST_INSERT_HEAD(hch, ctx, next_hch); + HWT_CTXHASH_UNLOCK(); +} + +void +hwt_contexthash_remove(struct hwt_context *ctx) +{ + + HWT_CTXHASH_LOCK(); + LIST_REMOVE(ctx, next_hch); + HWT_CTXHASH_UNLOCK(); +} + +void +hwt_contexthash_load(void) +{ + + hwt_contexthash = hashinit(HWT_CONTEXTHASH_SIZE, M_HWT_CONTEXTHASH, + &hwt_contexthashmask); + mtx_init(&hwt_contexthash_mtx, "hwt ctx hash", "hwt ctx", MTX_SPIN); +} + +void +hwt_contexthash_unload(void) +{ + + mtx_destroy(&hwt_contexthash_mtx); + hashdestroy(hwt_contexthash, M_HWT_CONTEXTHASH, hwt_contexthashmask); +} Index: sys/dev/hwt/hwt_cpu.h =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_cpu.h @@ -0,0 +1,46 @@ +/*- + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_HWT_HWT_CPU_H_ +#define _DEV_HWT_HWT_CPU_H_ + +struct hwt_cpu { + int cpu_id; + struct hwt_vm *vm; + TAILQ_ENTRY(hwt_cpu) next; +}; + +struct hwt_cpu * hwt_cpu_alloc(void); +void hwt_cpu_free(struct hwt_cpu *cpu); + +struct hwt_cpu * hwt_cpu_first(struct hwt_context *ctx); +void hwt_cpu_insert(struct hwt_context *ctx, struct hwt_cpu *cpu); + +#endif /* !_DEV_HWT_HWT_CPU_H_ */ Index: sys/dev/hwt/hwt_cpu.c =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_cpu.c @@ -0,0 +1,98 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define HWT_CPU_DEBUG +#undef HWT_CPU_DEBUG + +#ifdef HWT_CPU_DEBUG +#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__) +#else +#define dprintf(fmt, ...) +#endif + +static MALLOC_DEFINE(M_HWT_CPU, "hwt_cpu", "HWT cpu"); + +struct hwt_cpu * +hwt_cpu_alloc(void) +{ + struct hwt_cpu *cpu; + + cpu = malloc(sizeof(struct hwt_cpu), M_HWT_CPU, M_WAITOK | M_ZERO); + + return (cpu); +} + +void +hwt_cpu_free(struct hwt_cpu *cpu) +{ + + free(cpu, M_HWT_CPU); +} + +struct hwt_cpu * +hwt_cpu_first(struct hwt_context *ctx) +{ + struct hwt_cpu *cpu; + + HWT_CTX_ASSERT_LOCKED(ctx); + + cpu = TAILQ_FIRST(&ctx->cpus); + + KASSERT(cpu != NULL, ("cpu is NULL")); + + return (cpu); +} + +void +hwt_cpu_insert(struct hwt_context *ctx, struct hwt_cpu *cpu) +{ + + HWT_CTX_ASSERT_LOCKED(ctx); + + TAILQ_INSERT_TAIL(&ctx->cpus, cpu, next); +} Index: sys/dev/hwt/hwt_hook.h =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_hook.h @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include + +#ifndef _DEV_HWT_HWT_HOOK_H_ +#define _DEV_HWT_HWT_HOOK_H_ + +#define HWT_SWITCH_IN 0 +#define HWT_SWITCH_OUT 1 +#define HWT_THREAD_EXIT 2 +#define HWT_THREAD_CREATE 3 +#define HWT_THREAD_SET_NAME 4 +#define HWT_RECORD 5 +#define HWT_MMAP 6 +#define HWT_EXEC 7 + +#define HWT_CALL_HOOK(td, func, arg) \ +do { \ + if (hwt_hook != NULL) \ + (hwt_hook)((td), (func), (arg)); \ +} while (0) + +extern void (*hwt_hook)(struct thread *td, int func, void *arg); + +void hwt_hook_load(void); +void hwt_hook_unload(void); + +#endif /* !_DEV_HWT_HWT_HOOK_H_ */ Index: sys/dev/hwt/hwt_hook.c =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_hook.c @@ -0,0 +1,259 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Hardware Trace (HWT) framework. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define HWT_DEBUG +#undef HWT_DEBUG + +#ifdef HWT_DEBUG +#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__) +#else +#define dprintf(fmt, ...) +#endif + +static 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; + + cpu_id = PCPU_GET(cpuid); + + ctx = hwt_contexthash_lookup(p); + if (ctx == NULL) + return; + + if (ctx->state != CTX_STATE_RUNNING) { + HWT_CTX_UNLOCK(ctx); + return; + } + + thr = hwt_thread_lookup(ctx, td); + if (thr == NULL) { + HWT_CTX_UNLOCK(ctx); + return; + } + + dprintf("%s: thr %p index %d tid %d on cpu_id %d\n", __func__, thr, + thr->thread_id, td->td_tid, cpu_id); + + hwt_backend_configure(ctx, cpu_id, thr->thread_id); + hwt_backend_enable(ctx, cpu_id); + + HWT_THR_UNLOCK(thr); +} + +static 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; + + cpu_id = PCPU_GET(cpuid); + + ctx = hwt_contexthash_lookup(p); + if (ctx == NULL) + return; + + if (ctx->state != CTX_STATE_RUNNING) { + HWT_CTX_UNLOCK(ctx); + return; + } + thr = hwt_thread_lookup(ctx, td); + if (thr == NULL) { + HWT_CTX_UNLOCK(ctx); + return; + } + + dprintf("%s: thr %p index %d tid %d on cpu_id %d\n", __func__, thr, + thr->thread_id, td->td_tid, cpu_id); + + hwt_backend_disable(ctx, cpu_id); + HWT_THR_UNLOCK(thr); +} + +static void +hwt_thread_exit(struct thread *td) +{ + struct hwt_context *ctx; + struct hwt_thread *thr; + struct proc *p; + int cpu_id; + + p = td->td_proc; + + cpu_id = PCPU_GET(cpuid); + + ctx = hwt_contexthash_lookup(p); + if (ctx == NULL) + return; + + if (ctx->state != CTX_STATE_RUNNING) { + HWT_CTX_UNLOCK(ctx); + return; + } + thr = hwt_thread_lookup(ctx, td); + if (thr == NULL) { + HWT_CTX_UNLOCK(ctx); + return; + } + + thr->state = HWT_THREAD_STATE_EXITED; + + dprintf("%s: thr %p index %d tid %d on cpu_id %d\n", __func__, thr, + thr->thread_id, td->td_tid, cpu_id); + + hwt_backend_disable(ctx, cpu_id); + HWT_THR_UNLOCK(thr); +} + +static void +hwt_hook_mmap(struct thread *td) +{ + struct hwt_context *ctx; + struct hwt_thread *thr; + struct proc *p; + int pause; + + p = td->td_proc; + + ctx = hwt_contexthash_lookup(p); + if (ctx == NULL) + return; + + /* The ctx state could be any here. */ + + pause = ctx->pause_on_mmap ? 1 : 0; + + thr = hwt_thread_lookup(ctx, td); + if (thr == NULL) { + HWT_CTX_UNLOCK(ctx); + return; + } + + /* + * msleep(9) atomically releases the mtx lock, so take refcount + * to ensure that thr is not destroyed. + */ + refcount_acquire(&thr->refcnt); + + if (pause) + msleep_spin(thr, &thr->mtx, "hwt-mmap", 0); + + HWT_THR_UNLOCK(thr); + + if (refcount_release(&thr->refcnt)) + hwt_thread_free(thr); +} + +static void +hwt_hook_handler(struct thread *td, int func, void *arg) +{ + struct proc *p; + + p = td->td_proc; + if ((p->p_flag2 & P2_HWT) == 0) + return; + + switch (func) { + case HWT_SWITCH_IN: + hwt_switch_in(td); + break; + case HWT_SWITCH_OUT: + hwt_switch_out(td); + break; + case HWT_THREAD_CREATE: + hwt_thread_create(td); + break; + case HWT_THREAD_SET_NAME: + /* TODO. */ + break; + case HWT_THREAD_EXIT: + hwt_thread_exit(td); + break; + case HWT_EXEC: + hwt_record(td, arg); + hwt_hook_mmap(td); + break; + case HWT_MMAP: + hwt_record(td, arg); + hwt_hook_mmap(td); + break; + case HWT_RECORD: + hwt_record(td, arg); + break; + }; +} + +void +hwt_hook_load(void) +{ + + hwt_hook = hwt_hook_handler; +} + +void +hwt_hook_unload(void) +{ + + hwt_hook = NULL; +} Index: sys/dev/hwt/hwt_ioctl.h =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_ioctl.h @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_HWT_HWT_IOCTL_H +#define _DEV_HWT_HWT_IOCTL_H + +int hwt_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, + struct thread *td); + +#endif /* !_DEV_HWT_HWT_IOCTL_H */ Index: sys/dev/hwt/hwt_ioctl.c =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_ioctl.c @@ -0,0 +1,366 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Hardware Trace (HWT) framework. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HWT_IOCTL_DEBUG +#undef HWT_IOCTL_DEBUG + +#ifdef HWT_IOCTL_DEBUG +#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__) +#else +#define dprintf(fmt, ...) +#endif + +/* No real reason for these limitations just sanity checks. */ +#define HWT_MAXBUFSIZE (32UL * 1024 * 1024 * 1024) /* 32 GB */ + +static MALLOC_DEFINE(M_HWT_IOCTL, "hwt_ioctl", "Hardware Trace"); + +/* + * 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_ioctl_alloc_mode_thread(struct thread *td, struct hwt_owner *ho, + struct hwt_backend *backend, struct hwt_alloc *halloc) +{ + char path[MAXPATHLEN]; + struct hwt_context *ctx; + struct hwt_thread *thr; + struct proc *p; + int thread_id; + int error; + + /* Check if the owner have this pid configured already. */ + ctx = hwt_owner_lookup_ctx(ho, halloc->pid); + if (ctx) + return (EEXIST); + + /* Allocate a new HWT context. */ + ctx = hwt_ctx_alloc(); + ctx->bufsize = halloc->bufsize; + ctx->pid = halloc->pid; + ctx->hwt_backend = backend; + ctx->hwt_owner = ho; + ctx->mode = HWT_MODE_THREAD; + + error = copyout(&ctx->ident, halloc->ident, sizeof(int)); + if (error) { + hwt_ctx_free(ctx); + return (error); + } + + thread_id = atomic_fetchadd_int(&ctx->thread_counter, 1); + + /* Allocate first thread and buffers. */ + sprintf(path, "hwt_%d_%d", ctx->ident, thread_id); + error = hwt_thread_alloc(&thr, path, ctx->bufsize); + if (error) { + hwt_ctx_free(ctx); + return (error); + } + thr->vm->ctx = ctx; + + /* Since we done with malloc, now get the victim proc. */ + p = pfind(halloc->pid); + if (p == NULL) { + hwt_thread_free(thr); + hwt_ctx_free(ctx); + return (ENXIO); + } + + /* Ensure we can trace it. */ + error = hwt_priv_check(td->td_proc, p); + if (error) { + hwt_thread_free(thr); + hwt_ctx_free(ctx); + PROC_UNLOCK(p); + return (error); + } + p->p_flag2 |= P2_HWT; + thr->td = FIRST_THREAD_IN_PROC(p); + ctx->proc = p; + PROC_UNLOCK(p); + + /* All good. */ + thr->ctx = ctx; + thr->thread_id = thread_id; + + HWT_CTX_LOCK(ctx); + hwt_thread_insert(ctx, thr); + HWT_CTX_UNLOCK(ctx); + + error = hwt_backend_init(ctx); + if (error) { + hwt_thread_free(thr); + hwt_ctx_free(ctx); + /* TODO: remove P2_HWT from proc, if it is still there. */ + return (error); + } + + /* hwt_owner_insert_ctx? */ + mtx_lock(&ho->mtx); + LIST_INSERT_HEAD(&ho->hwts, ctx, next_hwts); + mtx_unlock(&ho->mtx); + + /* + * Hooks are now in action after this, but the ctx is not in RUNNING + * state. + */ + hwt_contexthash_insert(ctx); + + return (0); +} + +static int +hwt_ioctl_alloc_mode_cpu(struct thread *td, struct hwt_owner *ho, + struct hwt_backend *backend, struct hwt_alloc *halloc) +{ + struct hwt_context *ctx; + struct hwt_cpu *cpu; + struct hwt_vm *vm; + char path[MAXPATHLEN]; + int error; + int cpu_id; + + CPU_FOREACH(cpu_id) { + if (!CPU_ISSET(cpu_id, &halloc->cpu_map)) + continue; + /* Ensure CPU is not halted. */ + if (CPU_ISSET(cpu_id, &hlt_cpus_mask)) + return (ENXIO); +#if 0 + /* TODO: Check if the owner have this cpu configured already. */ + ctx = hwt_owner_lookup_ctx_by_cpu(ho, halloc->cpu); + if (ctx) + return (EEXIST); +#endif + } + + /* Allocate a new HWT context. */ + ctx = hwt_ctx_alloc(); + ctx->bufsize = halloc->bufsize; + ctx->hwt_backend = backend; + ctx->hwt_owner = ho; + ctx->mode = HWT_MODE_CPU; + ctx->cpu_map = halloc->cpu_map; + + error = copyout(&ctx->ident, halloc->ident, sizeof(int)); + if (error) { + hwt_ctx_free(ctx); + return (error); + } + + CPU_FOREACH(cpu_id) { + if (!CPU_ISSET(cpu_id, &halloc->cpu_map)) + continue; + + sprintf(path, "hwt_%d_%d", ctx->ident, cpu_id); + error = hwt_vm_alloc(ctx->bufsize, path, &vm); + if (error) { + /* TODO: remove all allocated cpus. */ + hwt_ctx_free(ctx); + return (error); + } + + cpu = hwt_cpu_alloc(); + cpu->cpu_id = cpu_id; + cpu->vm = vm; + + vm->cpu = cpu; + vm->ctx = ctx; + + HWT_CTX_LOCK(ctx); + hwt_cpu_insert(ctx, cpu); + HWT_CTX_UNLOCK(ctx); + } + + error = hwt_backend_init(ctx); + if (error) { + /* TODO: remove all allocated cpus. */ + hwt_ctx_free(ctx); + return (error); + } + + /* hwt_owner_insert_ctx? */ + mtx_lock(&ho->mtx); + LIST_INSERT_HEAD(&ho->hwts, ctx, next_hwts); + mtx_unlock(&ho->mtx); + + hwt_record_kernel_objects(ctx); + + 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_owner *ho; + 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_backend_lookup(backend_name); + if (backend == NULL) + return (ENODEV); + + /* First get the owner. */ + ho = hwt_ownerhash_lookup(td->td_proc); + if (ho == NULL) { + /* Create a new owner. */ + ho = hwt_owner_alloc(td->td_proc); + if (ho == NULL) + return (ENOMEM); + hwt_ownerhash_insert(ho); + } + + switch (halloc->mode) { + case HWT_MODE_THREAD: + error = hwt_ioctl_alloc_mode_thread(td, ho, backend, halloc); + break; + case HWT_MODE_CPU: + error = hwt_ioctl_alloc_mode_cpu(td, ho, backend, halloc); + break; + default: + error = ENXIO; + }; + + return (error); +} + +int +hwt_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, + struct thread *td) +{ + int error; + + switch (cmd) { + case HWT_IOC_ALLOC: + /* Allocate HWT context. */ + error = hwt_ioctl_alloc(td, (struct hwt_alloc *)addr); + return (error); + default: + return (ENXIO); + }; +} Index: sys/dev/hwt/hwt_owner.h =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_owner.h @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_HWT_HWT_OWNER_H_ +#define _DEV_HWT_HWT_OWNER_H_ + +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_context * hwt_owner_lookup_ctx(struct hwt_owner *ho, pid_t pid); +struct hwt_owner * hwt_owner_alloc(struct proc *p); +void hwt_owner_shutdown(struct hwt_owner *ho); +struct hwt_context * hwt_owner_lookup_ctx_by_cpu(struct hwt_owner *ho, int cpu); + +#endif /* !_DEV_HWT_HWT_OWNER_H_ */ Index: sys/dev/hwt/hwt_owner.c =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_owner.c @@ -0,0 +1,218 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, "hwt_owner", "Hardware Trace"); + +struct hwt_context * +hwt_owner_lookup_ctx(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); +} + +#if 0 +struct hwt_context * +hwt_owner_lookup_ctx_by_cpu(struct hwt_owner *ho, int cpu) +{ + struct hwt_context *ctx; + + mtx_lock(&ho->mtx); + LIST_FOREACH(ctx, &ho->hwts, next_hwts) { + if (ctx->cpu == cpu) { + mtx_unlock(&ho->mtx); + return (ctx); + } + } + mtx_unlock(&ho->mtx); + + return (NULL); +} +#endif + +struct hwt_owner * +hwt_owner_alloc(struct proc *p) +{ + struct hwt_owner *ho; + + ho = malloc(sizeof(struct hwt_owner), M_HWT_OWNER, + M_WAITOK | M_ZERO); + ho->p = p; + + LIST_INIT(&ho->hwts); + mtx_init(&ho->mtx, "hwts", NULL, MTX_DEF); + + return (ho); +} + +static void +hwt_owner_free_cpus(struct hwt_context *ctx) +{ + struct hwt_cpu *cpu; + + do { + HWT_CTX_LOCK(ctx); + cpu = TAILQ_FIRST(&ctx->cpus); + if (cpu) + TAILQ_REMOVE(&ctx->cpus, cpu, next); + HWT_CTX_UNLOCK(ctx); + + if (cpu == NULL) + break; + + hwt_vm_free(cpu->vm); + hwt_cpu_free(cpu); + } while (1); +} + +static void +hwt_owner_free_threads(struct hwt_context *ctx) +{ + struct hwt_thread *thr; + + dprintf("%s: remove threads\n", __func__); + + do { + HWT_CTX_LOCK(ctx); + thr = TAILQ_FIRST(&ctx->threads); + if (thr) { + TAILQ_REMOVE(&ctx->threads, thr, next); + HWT_THR_LOCK(thr); + } + HWT_CTX_UNLOCK(ctx); + + if (thr == NULL) + break; + + wakeup(thr); + + HWT_THR_UNLOCK(thr); + + if (refcount_release(&thr->refcnt)) + hwt_thread_free(thr); + } while (1); +} + +void +hwt_owner_shutdown(struct hwt_owner *ho) +{ + struct hwt_context *ctx; + struct hwt_thread *thr; + + dprintf("%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; + + if (ctx->mode == HWT_MODE_THREAD) + hwt_contexthash_remove(ctx); + + /* + * It could be that a hook has this ctx locked right here. + */ + + HWT_CTX_LOCK(ctx); + ctx->state = 0; + /* + * Ensure hook invocation is now completed. + */ + TAILQ_FOREACH(thr, &ctx->threads, next) { + HWT_THR_LOCK(thr); + HWT_THR_UNLOCK(thr); + } + HWT_CTX_UNLOCK(ctx); + + /* Note that a thread could be still sleeping on msleep_spin. */ + + hwt_backend_deinit(ctx); + + if (ctx->mode == HWT_MODE_CPU) + hwt_owner_free_cpus(ctx); + else + hwt_owner_free_threads(ctx); + + hwt_record_free_all(ctx); + hwt_ctx_free(ctx); + } + + hwt_ownerhash_remove(ho); + free(ho, M_HWT_OWNER); +} Index: sys/dev/hwt/hwt_ownerhash.h =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_ownerhash.h @@ -0,0 +1,44 @@ +/*- + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_HWT_HWT_OWNERHASH_H_ +#define _DEV_HWT_HWT_OWNERHASH_H_ + +struct hwt_owner * hwt_ownerhash_lookup(struct proc *p); +void hwt_ownerhash_insert(struct hwt_owner *ho); +void hwt_ownerhash_remove(struct hwt_owner *ho); + +void hwt_ownerhash_load(void); +void hwt_ownerhash_unload(void); + +#define HWT_OWNERHASH_LOCK() mtx_lock_spin(&hwt_ownerhash_mtx) +#define HWT_OWNERHASH_UNLOCK() mtx_unlock_spin(&hwt_ownerhash_mtx) + +#endif /* !_DEV_HWT_HWT_OWNERHASH_H_ */ Index: sys/dev/hwt/hwt_ownerhash.c =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_ownerhash.c @@ -0,0 +1,147 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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, "hwt_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 hwt_ownerhash_mtx; +static u_long hwt_ownerhashmask; +static LIST_HEAD(hwt_ownerhash, hwt_owner) *hwt_ownerhash; + +struct hwt_owner * +hwt_ownerhash_lookup(struct proc *p) +{ + struct hwt_ownerhash *hoh; + struct hwt_owner *ho; + int hindex; + + hindex = HWT_HASH_PTR(p, hwt_ownerhashmask); + hoh = &hwt_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 +hwt_ownerhash_insert(struct hwt_owner *ho) +{ + struct hwt_ownerhash *hoh; + int hindex; + + hindex = HWT_HASH_PTR(ho->p, hwt_ownerhashmask); + hoh = &hwt_ownerhash[hindex]; + + HWT_OWNERHASH_LOCK(); + LIST_INSERT_HEAD(hoh, ho, next); + HWT_OWNERHASH_UNLOCK(); +} + +void +hwt_ownerhash_remove(struct hwt_owner *ho) +{ + + /* Destroy hwt owner. */ + HWT_OWNERHASH_LOCK(); + LIST_REMOVE(ho, next); + HWT_OWNERHASH_UNLOCK(); +} + +void +hwt_ownerhash_load(void) +{ + + hwt_ownerhash = hashinit(HWT_OWNERHASH_SIZE, M_HWT_OWNERHASH, + &hwt_ownerhashmask); + mtx_init(&hwt_ownerhash_mtx, "hwt-owner-hash", "hwt-owner", MTX_SPIN); +} + +void +hwt_ownerhash_unload(void) +{ +#if 0 + struct hwt_ownerhash *hoh; + struct hwt_owner *ho, *tmp; + + HWT_OWNERHASH_LOCK(); + for (hoh = hwt_ownerhash; + hoh <= &hwt_ownerhash[hwt_ownerhashmask]; + hoh++) { + LIST_FOREACH_SAFE(ho, hoh, next, tmp) { + LIST_REMOVE(ho, next); + } + } + HWT_OWNERHASH_UNLOCK(); +#endif + mtx_destroy(&hwt_ownerhash_mtx); + hashdestroy(hwt_ownerhash, M_HWT_OWNERHASH, hwt_ownerhashmask); +} Index: sys/dev/hwt/hwt_record.h =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_record.h @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_HWT_HWT_RECORD_H_ +#define _DEV_HWT_HWT_RECORD_H_ + +int hwt_record_send(struct hwt_context *ctx, struct hwt_record_get *record_get); +void hwt_record(struct thread *td, struct hwt_record_entry *ent); + +struct hwt_record_entry * hwt_record_entry_alloc(void); +void hwt_record_entry_free(struct hwt_record_entry *entry); +void hwt_record_kernel_objects(struct hwt_context *ctx); +void hwt_record_free_all(struct hwt_context *ctx); + +#endif /* !_DEV_HWT_HWT_RECORD_H_ */ Index: sys/dev/hwt/hwt_record.c =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_record.c @@ -0,0 +1,213 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include /* linker_hwpmc_list_objects */ + +#include + +#include +#include +#include +#include +#include +#include + +#define HWT_RECORD_DEBUG +#undef HWT_RECORD_DEBUG + +#ifdef HWT_RECORD_DEBUG +#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__) +#else +#define dprintf(fmt, ...) +#endif + +static MALLOC_DEFINE(M_HWT_RECORD, "hwt_record", "Hardware Trace"); + +void +hwt_record(struct thread *td, struct hwt_record_entry *ent) +{ + struct hwt_record_entry *entry; + struct hwt_context *ctx; + struct proc *p; + + p = td->td_proc; + + KASSERT(ent != NULL, ("ent is NULL")); + KASSERT(ent->fullpath != NULL, ("fullpath is NULL")); + + entry = malloc(sizeof(struct hwt_record_entry), M_HWT_RECORD, M_WAITOK); + entry->record_type = ent->record_type; + entry->thread_id = -1; + entry->fullpath = strdup(ent->fullpath, M_HWT_RECORD); + entry->addr = ent->addr; + + ctx = hwt_contexthash_lookup(p); + if (ctx == NULL) { + free(entry->fullpath, M_HWT_RECORD); + free(entry, M_HWT_RECORD); + return; + } + LIST_INSERT_HEAD(&ctx->records, entry, next); + HWT_CTX_UNLOCK(ctx); +} + +struct hwt_record_entry * +hwt_record_entry_alloc(void) +{ + struct hwt_record_entry *entry; + + entry = malloc(sizeof(struct hwt_record_entry), M_HWT_RECORD, + M_WAITOK | M_ZERO); + + return (entry); +} + +void +hwt_record_entry_free(struct hwt_record_entry *entry) +{ + + free(entry, M_HWT_RECORD); +} + +static int +hwt_record_grab(struct hwt_context *ctx, + struct hwt_record_user_entry *user_entry, int nitems_req) +{ + struct hwt_record_entry *entry; + int i; + + for (i = 0; i < nitems_req; i++) { + HWT_CTX_LOCK(ctx); + entry = LIST_FIRST(&ctx->records); + if (entry) + LIST_REMOVE(entry, next); + HWT_CTX_UNLOCK(ctx); + + if (entry == NULL) + break; + + user_entry[i].addr = entry->addr; + user_entry[i].record_type = entry->record_type; + user_entry[i].thread_id = entry->thread_id; + if (entry->fullpath != NULL) { + strncpy(user_entry[i].fullpath, entry->fullpath, + MAXPATHLEN); + free(entry->fullpath, M_HWT_RECORD); + } + + free(entry, M_HWT_RECORD); + } + + return (i); +} + +void +hwt_record_free_all(struct hwt_context *ctx) +{ + struct hwt_record_entry *entry; + + while (1) { + HWT_CTX_LOCK(ctx); + entry = LIST_FIRST(&ctx->records); + if (entry) + LIST_REMOVE(entry, next); + HWT_CTX_UNLOCK(ctx); + + if (entry == NULL) + break; + + if (entry->fullpath != NULL) + free(entry->fullpath, M_HWT_RECORD); + + free(entry, M_HWT_RECORD); + } +} + +int +hwt_record_send(struct hwt_context *ctx, struct hwt_record_get *record_get) +{ + 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_RECORD, M_WAITOK | M_ZERO); + + i = hwt_record_grab(ctx, user_entry, nitems_req); + if (i > 0) + error = copyout(user_entry, record_get->records, + sizeof(struct hwt_record_user_entry) * i); + + if (error == 0) + error = copyout(&i, record_get->nentries, sizeof(int)); + + free(user_entry, M_HWT_RECORD); + + return (error); +} + +void +hwt_record_kernel_objects(struct hwt_context *ctx) +{ + struct hwt_record_entry *entry; + struct pmckern_map_in *kobase; + int i; + + kobase = linker_hwpmc_list_objects(); + for (i = 0; kobase[i].pm_file != NULL; i++) { + entry = malloc(sizeof(struct hwt_record_entry), M_HWT_RECORD, + M_WAITOK); + entry->record_type = HWT_RECORD_KERNEL; + entry->fullpath = strdup(kobase[i].pm_file, M_HWT_RECORD); + entry->addr = kobase[i].pm_address; + + HWT_CTX_LOCK(ctx); + LIST_INSERT_HEAD(&ctx->records, entry, next); + HWT_CTX_UNLOCK(ctx); + } + free(kobase, M_LINKER); +} Index: sys/dev/hwt/hwt_thread.h =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_thread.h @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_HWT_HWT_THREAD_H_ +#define _DEV_HWT_HWT_THREAD_H_ + +struct hwt_thread { + struct hwt_vm *vm; + struct hwt_context *ctx; + struct thread *td; + TAILQ_ENTRY(hwt_thread) next; + int thread_id; + int state; +#define HWT_THREAD_STATE_EXITED (1 << 0) + struct mtx mtx; + u_int refcnt; + int cpu_id; /* last cpu_id */ +}; + +/* Thread allocation. */ +int hwt_thread_alloc(struct hwt_thread **thr0, char *path, size_t bufsize); +int hwt_thread_create(struct thread *td); + +/* Thread de-allocation. */ +void hwt_thread_free(struct hwt_thread *thr); + +/* Thread list mgt. */ +void hwt_thread_insert(struct hwt_context *ctx, struct hwt_thread *thr); +struct hwt_thread * hwt_thread_first(struct hwt_context *ctx); +struct hwt_thread * hwt_thread_lookup(struct hwt_context *ctx, + struct thread *td); + +#define HWT_THR_LOCK(thr) mtx_lock_spin(&(thr)->mtx) +#define HWT_THR_UNLOCK(thr) mtx_unlock_spin(&(thr)->mtx) +#define HWT_THR_ASSERT_LOCKED(thr) mtx_assert(&(thr)->mtx, MA_OWNED) + +#endif /* !_DEV_HWT_HWT_THREAD_H_ */ Index: sys/dev/hwt/hwt_thread.c =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_thread.c @@ -0,0 +1,215 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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_THREAD, "hwt_thread", "Hardware Trace"); + +struct hwt_thread * +hwt_thread_first(struct hwt_context *ctx) +{ + struct hwt_thread *thr; + + HWT_CTX_ASSERT_LOCKED(ctx); + + thr = TAILQ_FIRST(&ctx->threads); + + KASSERT(thr != NULL, ("thr is NULL")); + + return (thr); +} + +/* + * To use by hwt_switch_in/out() only. + */ +struct hwt_thread * +hwt_thread_lookup(struct hwt_context *ctx, struct thread *td) +{ + struct hwt_thread *thr; + + HWT_CTX_ASSERT_LOCKED(ctx); + + TAILQ_FOREACH(thr, &ctx->threads, next) { + if (thr->td == td) { + HWT_THR_LOCK(thr); + HWT_CTX_UNLOCK(ctx); + return (thr); + } + } + + /* + * We are here because the hook on thread creation failed to allocate + * a thread. + */ + + return (NULL); +} + +int +hwt_thread_alloc(struct hwt_thread **thr0, char *path, size_t bufsize) +{ + struct hwt_thread *thr; + struct hwt_vm *vm; + int error; + + error = hwt_vm_alloc(bufsize, path, &vm); + if (error) + return (error); + + thr = malloc(sizeof(struct hwt_thread), M_HWT_THREAD, + M_WAITOK | M_ZERO); + thr->vm = vm; + + mtx_init(&thr->mtx, "thr", NULL, MTX_SPIN); + + refcount_init(&thr->refcnt, 1); + + vm->thr = thr; + + *thr0 = thr; + + return (0); +} + +void +hwt_thread_free(struct hwt_thread *thr) +{ + + hwt_vm_free(thr->vm); + + free(thr, M_HWT_THREAD); +} + +void +hwt_thread_insert(struct hwt_context *ctx, struct hwt_thread *thr) +{ + + HWT_CTX_ASSERT_LOCKED(ctx); + + TAILQ_INSERT_TAIL(&ctx->threads, thr, next); +} + +/* + * This is called by hooks only. + * TODO: Move to hwt_hook.c ? + */ +int +hwt_thread_create(struct thread *td) +{ + struct hwt_record_entry *entry; + struct hwt_context *ctx; + struct hwt_thread *thr; + struct proc *p; + size_t bufsize; + char path[MAXPATHLEN]; + int error; + int thread_id; + + p = td->td_proc; + + /* 1. First find our ctx and collect some information from it. */ + ctx = hwt_contexthash_lookup(p); + if (ctx == NULL) + return (ENXIO); + bufsize = ctx->bufsize; + thread_id = atomic_fetchadd_int(&ctx->thread_counter, 1); + sprintf(path, "hwt_%d_%d", ctx->ident, thread_id); + HWT_CTX_UNLOCK(ctx); + + /* 2. Now we can allocate some memory. */ + error = hwt_thread_alloc(&thr, path, bufsize); + if (error) { + printf("%s: could not allocate thread, error %d\n", + __func__, error); + return (error); + } + + entry = hwt_record_entry_alloc(); + entry->record_type = HWT_RECORD_THREAD_CREATE; + entry->thread_id = thread_id; + + /* 3. Take ctx again, as it may gone during previous step. */ + ctx = hwt_contexthash_lookup(p); + if (ctx == NULL) { + hwt_record_entry_free(entry); + hwt_thread_free(thr); + return (ENXIO); + } + thr->vm->ctx = ctx; + thr->ctx = ctx; + thr->thread_id = thread_id; + thr->td = td; + TAILQ_INSERT_TAIL(&ctx->threads, thr, next); + LIST_INSERT_HEAD(&ctx->records, entry, next); + HWT_CTX_UNLOCK(ctx); + + return (0); +} Index: sys/dev/hwt/hwt_vm.h =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_vm.h @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_HWT_HWT_VM_H_ +#define _DEV_HWT_HWT_VM_H_ + +struct hwt_vm { + vm_page_t *pages; + int npages; + vm_object_t obj; + struct cdev *cdev; + + struct hwt_context *ctx; + struct hwt_cpu *cpu; /* cpu mode only. */ + struct hwt_thread *thr; /* thr mode only. */ +}; + +int hwt_vm_alloc(size_t bufsize, char *path, struct hwt_vm **vm0); +void hwt_vm_free(struct hwt_vm *vm); + +#endif /* !_DEV_HWT_HWT_VM_H_ */ Index: sys/dev/hwt/hwt_vm.c =================================================================== --- /dev/null +++ sys/dev/hwt/hwt_vm.c @@ -0,0 +1,418 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, "hwt_vm", "Hardware Trace"); + +static int +hwt_vm_fault(vm_object_t vm_obj, vm_ooffset_t offset, + int prot, vm_page_t *mres) +{ + + return (0); +} + +static int +hwt_vm_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_vm_dtor(void *handle) +{ + +} + +static struct cdev_pager_ops hwt_vm_pager_ops = { + .cdev_pg_fault = hwt_vm_fault, + .cdev_pg_ctor = hwt_vm_ctor, + .cdev_pg_dtor = hwt_vm_dtor +}; + +static int +hwt_vm_alloc_pages(struct hwt_vm *vm) +{ + vm_paddr_t low, high, boundary; + vm_memattr_t memattr; + vm_pointer_t va; + 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; + + vm->obj = cdev_pager_allocate(vm, OBJT_MGTDEVICE, + &hwt_vm_pager_ops, vm->npages * PAGE_SIZE, PROT_READ, 0, + curthread->td_ucred); + + for (i = 0; i < vm->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: could not clean device memory on arm64. */ + if ((m->flags & PG_ZERO) == 0) + pmap_zero_page(m); +#endif + + va = PHYS_TO_DMAP(VM_PAGE_TO_PHYS(m)); + cpu_dcache_wb_range(va, PAGE_SIZE); + + m->valid = VM_PAGE_BITS_ALL; + m->oflags &= ~VPO_UNMANAGED; + m->flags |= PG_FICTITIOUS; + vm->pages[i] = m; + + VM_OBJECT_WLOCK(vm->obj); + vm_page_insert(m, vm->obj, i); + VM_OBJECT_WUNLOCK(vm->obj); + } + + return (0); +} + +static int +hwt_vm_open(struct cdev *cdev, int oflags, int devtype, struct thread *td) +{ + + dprintf("%s\n", __func__); + + return (0); +} + +static int +hwt_vm_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, + vm_size_t mapsize, struct vm_object **objp, int nprot) +{ + struct hwt_vm *vm; + + vm = cdev->si_drv1; + + if (nprot != PROT_READ || *offset != 0) + return (ENXIO); + + *objp = vm->obj; + + return (0); +} + +static int +hwt_vm_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, + struct thread *td) +{ + struct hwt_record_get *rget; + struct hwt_set_config *sconf; + struct hwt_bufptr_get *ptr_get; + + struct hwt_context *ctx; + struct hwt_vm *vm; + struct hwt_owner *ho; + struct hwt_cpu *cpu; + + vm_offset_t curpage_offset; + int cpu_id; + int curpage; + 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 = hwt_ownerhash_lookup(td->td_proc); + if (ho == NULL) + return (ENXIO); + + if (ctx->hwt_owner != ho) + return (EPERM); + + switch (cmd) { + case HWT_IOC_START: + /* Start tracing. */ + dprintf("%s: start\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); + + if (ctx->mode == HWT_MODE_CPU) { + cpu = vm->cpu; + hwt_backend_configure(ctx, cpu->cpu_id, cpu->cpu_id); + hwt_backend_enable(ctx, cpu->cpu_id); + } else { + /* + * Tracing backend will be configured and enabled + * during hook invocation. See hwt_hook.c. + */ + } + + break; + + case HWT_IOC_STOP: + /* TODO */ + break; + + case HWT_IOC_RECORD_GET: + rget = (struct hwt_record_get *)addr; + error = hwt_record_send(ctx, rget); + if (error) + return (error); + break; + + case HWT_IOC_SET_CONFIG: + sconf = (struct hwt_set_config *)addr; + error = hwt_config_set(td, ctx, sconf); + if (error) + return (error); + ctx->pause_on_mmap = sconf->pause_on_mmap ? 1 : 0; + break; + + case HWT_IOC_WAKEUP: + + if (ctx->mode == HWT_MODE_CPU) + return (ENXIO); + + KASSERT(vm->thr != NULL, ("thr is NULL")); + + wakeup(vm->thr); + + break; + + case HWT_IOC_BUFPTR_GET: + ptr_get = (struct hwt_bufptr_get *)addr; + + if (ctx->mode == HWT_MODE_THREAD) + cpu_id = vm->thr->cpu_id; + else + cpu_id = vm->cpu->cpu_id; + + error = hwt_backend_read(ctx, cpu_id, &curpage, + &curpage_offset); + if (error) + return (error); + + 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_vm_cdevsw = { + .d_version = D_VERSION, + .d_name = "hwt", + .d_open = hwt_vm_open, + .d_mmap_single = hwt_vm_mmap_single, + .d_ioctl = hwt_vm_ioctl, +}; + +static int +hwt_vm_create_cdev(struct hwt_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 = &hwt_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); +} + +static int +hwt_vm_alloc_buffers(struct hwt_vm *vm) +{ + int error; + + vm->pages = malloc(sizeof(struct vm_page *) * vm->npages, + M_HWT_VM, M_WAITOK | M_ZERO); + + error = hwt_vm_alloc_pages(vm); + if (error) { + printf("%s: could not alloc pages\n", __func__); + return (error); + } + + return (0); +} + +static void +hwt_vm_destroy_buffers(struct hwt_vm *vm) +{ + vm_page_t m; + int i; + + VM_OBJECT_WLOCK(vm->obj); + for (i = 0; i < vm->npages; i++) { + m = vm->pages[i]; + if (m == NULL) + break; + + vm_page_busy_acquire(m, 0); + cdev_pager_free_page(vm->obj, m); + m->flags &= ~PG_FICTITIOUS; + vm_page_unwire_noq(m); + vm_page_free(m); + + } + vm_pager_deallocate(vm->obj); + VM_OBJECT_WUNLOCK(vm->obj); + + free(vm->pages, M_HWT_VM); +} + +void +hwt_vm_free(struct hwt_vm *vm) +{ + + if (vm->cdev) + destroy_dev_sched(vm->cdev); + hwt_vm_destroy_buffers(vm); + free(vm, M_HWT_VM); +} + +int +hwt_vm_alloc(size_t bufsize, char *path, struct hwt_vm **vm0) +{ + struct hwt_vm *vm; + int error; + + vm = malloc(sizeof(struct hwt_vm), M_HWT_VM, M_WAITOK | M_ZERO); + vm->npages = bufsize / PAGE_SIZE; + + error = hwt_vm_alloc_buffers(vm); + if (error) { + free(vm, M_HWT_VM); + return (error); + } + + error = hwt_vm_create_cdev(vm, path); + if (error) { + hwt_vm_free(vm); + return (error); + } + + *vm0 = vm; + + return (0); +} 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 #include @@ -84,6 +85,10 @@ #include #include +#ifdef HWT_HOOKS +#include +#endif + #if __has_feature(capabilities) #include #endif @@ -1603,6 +1608,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.record_type = HWT_RECORD_EXECUTABLE; + HWT_CALL_HOOK(td, HWT_EXEC, &ent); + } +#endif + if (interp != NULL) { VOP_UNLOCK(imgp->vp); if ((map->flags & MAP_ASLR) != 0) { @@ -1618,6 +1634,17 @@ vn_lock(imgp->vp, LK_SHARED | LK_RETRY); if (error != 0) goto ret; + +#ifdef HWT_HOOKS + /* HWT: Record interp. */ + struct hwt_record_entry ent; + if (td->td_proc->p_flag2 & P2_HWT) { + ent.fullpath = interp; + ent.addr = (uintptr_t)imgp->entry_addr; + ent.record_type = HWT_RECORD_INTERP; + HWT_CALL_HOOK(td, HWT_EXEC, &ent); + } +#endif } else addr = imgp->et_dyn_addr; Index: sys/kern/kern_hwt.c =================================================================== --- /dev/null +++ sys/kern/kern_hwt.c @@ -0,0 +1,46 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +void __read_mostly (*hwt_hook)(struct thread *td, int func, void *arg) = NULL; Index: sys/kern/kern_linker.c =================================================================== --- sys/kern/kern_linker.c +++ sys/kern/kern_linker.c @@ -32,6 +32,7 @@ #include "opt_ddb.h" #include "opt_kld.h" #include "opt_hwpmc_hooks.h" +#include "opt_hwt_hooks.h" #include #include @@ -66,7 +67,7 @@ #include "linker_if.h" -#ifdef HWPMC_HOOKS +#if defined(HWPMC_HOOKS) || defined(HWT_HOOKS) #include #endif @@ -2131,7 +2132,7 @@ return (filename); } -#ifdef HWPMC_HOOKS +#if defined(HWPMC_HOOKS) || defined(HWT_HOOKS) /* * Inform hwpmc about the set of kernel modules currently loaded. */ 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 #include #include @@ -59,6 +60,9 @@ #ifdef HWPMC_HOOKS #include #endif +#ifdef HWT_HOOKS +#include +#endif #include @@ -269,6 +273,10 @@ PMC_CALL_HOOK_UNLOCKED(newtd, PMC_FN_THR_CREATE_LOG, NULL); #endif +#ifdef HWT_HOOKS + HWT_CALL_HOOK(newtd, HWT_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_CALL_HOOK(ttd, HWT_THREAD_SET_NAME, NULL); +#endif #ifdef KTR sched_clear_tdname(ttd); #endif Index: sys/kern/kern_thread.c =================================================================== --- sys/kern/kern_thread.c +++ sys/kern/kern_thread.c @@ -30,6 +30,7 @@ #include "opt_witness.h" #include "opt_hwpmc_hooks.h" +#include "opt_hwt_hooks.h" #include __FBSDID("$FreeBSD$"); @@ -63,6 +64,9 @@ #ifdef HWPMC_HOOKS #include #endif +#ifdef HWT_HOOKS +#include +#endif #include #include @@ -971,6 +975,11 @@ } else if (PMC_SYSTEM_SAMPLING_ACTIVE()) PMC_CALL_HOOK_UNLOCKED(td, PMC_FN_THR_EXIT_LOG, NULL); #endif + +#ifdef HWT_HOOKS + HWT_CALL_HOOK(td, HWT_THREAD_EXIT, NULL); +#endif + PROC_UNLOCK(p); PROC_STATLOCK(p); thread_lock(td); 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 @@ -70,6 +71,10 @@ #include #endif +#ifdef HWT_HOOKS +#include +#endif + #ifdef KDTRACE_HOOKS #include int __read_mostly dtrace_vtime_active; @@ -2294,6 +2299,12 @@ if (dtrace_vtime_active) (*dtrace_vtime_switch_func)(newtd); #endif + +#ifdef HWT_HOOKS + HWT_CALL_HOOK(td, HWT_SWITCH_OUT, NULL); + HWT_CALL_HOOK(newtd, HWT_SWITCH_IN, NULL); +#endif + td->td_oncpu = NOCPU; cpu_switch(td, newtd, mtx); cpuid = td->td_oncpu = PCPU_GET(cpuid); @@ -3123,6 +3134,10 @@ newtd = sched_throw_grab(tdq); +#ifdef HWT_HOOKS + HWT_CALL_HOOK(newtd, HWT_SWITCH_IN, NULL); +#endif + /* doesn't return */ cpu_throw(NULL, newtd); } @@ -3149,6 +3164,10 @@ newtd = sched_throw_grab(tdq); +#ifdef HWT_HOOKS + HWT_CALL_HOOK(newtd, HWT_SWITCH_IN, NULL); +#endif + /* doesn't return */ cpu_switch(td, newtd, TDQ_LOCKPTR(tdq)); } 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 #include @@ -97,6 +98,10 @@ #include #endif +#ifdef HWT_HOOKS +#include +#endif + static fo_rdwr_t vn_read; static fo_rdwr_t vn_write; static fo_rdwr_t vn_io_fault; @@ -2922,6 +2927,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.record_type = HWT_RECORD_MMAP; + HWT_CALL_HOOK(td, HWT_MMAP, &ent); + if (freepath != NULL) + free(freepath, M_TEMP); + } + } +#endif + return (error); } Index: sys/modules/hwt/Makefile =================================================================== --- /dev/null +++ sys/modules/hwt/Makefile @@ -0,0 +1,23 @@ +# $FreeBSD$ + +.PATH: ${SRCTOP}/sys/dev/hwt + +KMOD = hwt +SRCS = \ + hwt.c \ + hwt_backend.c \ + hwt_config.c \ + hwt_context.c \ + hwt_contexthash.c \ + hwt_cpu.c \ + hwt_hook.c \ + hwt_ioctl.c \ + hwt_owner.c \ + hwt_ownerhash.c \ + hwt_record.c \ + hwt_thread.c \ + hwt_vm.c + +# EXPORT_SYMS= hwt_backend_register + +.include Index: sys/sys/hwt.h =================================================================== --- /dev/null +++ sys/sys/hwt.h @@ -0,0 +1,103 @@ +/*- + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* User-visible header. */ + +#include +#include +#include +#include + +#ifndef _SYS_HWT_H_ +#define _SYS_HWT_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_STOP _IOW(HWT_MAGIC, 0x02, struct hwt_stop) +#define HWT_IOC_RECORD_GET _IOW(HWT_MAGIC, 0x03, struct hwt_record_get) +#define HWT_IOC_BUFPTR_GET _IOW(HWT_MAGIC, 0x04, struct hwt_bufptr_get) +#define HWT_IOC_SET_CONFIG _IOW(HWT_MAGIC, 0x05, struct hwt_set_config) +#define HWT_IOC_WAKEUP _IOW(HWT_MAGIC, 0x06, struct hwt_wakeup) + +#define HWT_BACKEND_MAXNAMELEN 256 + +#define HWT_MODE_THREAD 1 +#define HWT_MODE_CPU 2 + +struct hwt_alloc { + size_t bufsize; + int mode; + pid_t pid; /* thread mode */ + cpuset_t cpu_map; /* cpu mode only */ + const char *backend_name; + int *ident; +} __aligned(16); + +struct hwt_start { + int reserved; +} __aligned(16); + +struct hwt_stop { + int reserved; +} __aligned(16); + +struct hwt_wakeup { + int reserved; +} __aligned(16); + +struct hwt_record_user_entry { + enum hwt_record_type record_type; + char fullpath[MAXPATHLEN]; + uintptr_t addr; + int thread_id; +} __aligned(16); + +struct hwt_record_get { + struct hwt_record_user_entry *records; + int *nentries; +} __aligned(16); + +struct hwt_bufptr_get { + int *curpage; + vm_offset_t *curpage_offset; +} __aligned(16); + +struct hwt_set_config { + /* Configuration of ctx. */ + int pause_on_mmap; + + /* The following passed to backend as is. */ + void *config; + size_t config_size; + int config_version; +} __aligned(16); + +#endif /* !_SYS_HWT_H_ */ Index: sys/sys/hwt_record.h =================================================================== --- /dev/null +++ sys/sys/hwt_record.h @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 2023 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* User-visible header. */ + +#ifndef _SYS_HWT_RECORD_H_ +#define _SYS_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, + HWT_RECORD_KERNEL, +}; + +#ifdef _KERNEL +struct hwt_record_entry { + enum hwt_record_type record_type; + LIST_ENTRY(hwt_record_entry) next; + char *fullpath; + int thread_id; + uintptr_t addr; +}; +#endif + +#endif /* !_SYS_HWT_RECORD_H_ */ Index: sys/sys/proc.h =================================================================== --- sys/sys/proc.h +++ sys/sys/proc.h @@ -886,6 +886,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 #endif +#ifdef HWT_HOOKS +#include +#endif + #if __has_feature(capabilities) #include #endif @@ -1054,6 +1058,16 @@ #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; + ent.record_type = HWT_RECORD_MUNMAP; + HWT_CALL_HOOK(td, HWT_RECORD, &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 */