1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2023-2025 Ruslan Bukin <br@bsdpad.com> 5 * 6 * This work was supported by Innovate UK project 105694, "Digital Security 7 * by Design (DSbD) Technology Platform Prototype". 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 /* 32 * Hardware Tracing framework. 33 * 34 * The framework manages hardware tracing units that collect information 35 * about software execution and store it as events in highly compressed format 36 * into DRAM. The events cover information about control flow changes of a 37 * program, whether branches taken or not, exceptions taken, timing information, 38 * cycles elapsed and more. That allows us to restore entire program flow of a 39 * given application without performance impact. 40 * 41 * Design overview. 42 * 43 * The framework provides character devices for mmap(2) and ioctl(2) system 44 * calls to allow user to manage CPU (hardware) tracing units. 45 * 46 * /dev/hwt: 47 * .ioctl: 48 * hwt_ioctl(): 49 * a) HWT_IOC_ALLOC 50 * Allocates kernel tracing context CTX based on requested mode 51 * of operation. Verifies the information that comes with the 52 * request (pid, cpus), allocates unique ID for the context. 53 * Creates a new character device for CTX management. 54 * 55 * /dev/hwt_%d[_%d], ident[, thread_id] 56 * .mmap 57 * Maps tracing buffers of the corresponding thread to userspace. 58 * .ioctl 59 * hwt_thread_ioctl(): 60 * a) HWT_IOC_START 61 * Enables tracing unit for a given context. 62 * b) HWT_IOC_RECORD_GET 63 * Transfers (small) record entries collected during program 64 * execution for a given context to userspace, such as mmaping 65 * tables of executable and dynamic libraries, interpreter, 66 * kernel mappings, tid of threads created, etc. 67 * c) HWT_IOC_SET_CONFIG 68 * Allows to specify backend-specific configuration of the 69 * trace unit. 70 * d) HWT_IOC_WAKEUP 71 * Wakes up a thread that is currently sleeping. 72 * e) HWT_IOC_BUFPTR_GET 73 * Transfers current hardware pointer in the filling buffer 74 * to the userspace. 75 * f) HWT_IOC_SVC_BUF 76 * To avoid data loss, userspace may notify kernel it has 77 * copied out the given buffer, so kernel is ok to overwrite 78 * 79 * HWT context lifecycle in THREAD mode of operation: 80 * 1. User invokes HWT_IOC_ALLOC ioctl with information about pid to trace and 81 * size of the buffers for the trace data to allocate. 82 * Some architectures may have different tracing units supported, so user 83 * also provides backend name to use for this context, e.g. "coresight". 84 * 2. Kernel allocates context, lookups the proc for the given pid. Then it 85 * creates first hwt_thread in the context and allocates trace buffers for 86 * it. Immediately, kernel initializes tracing backend. 87 * Kernel creates character device and returns unique identificator of 88 * trace context to the user. 89 * 3. To manage the new context, user opens the character device created. 90 * User invokes HWT_IOC_START ioctl, kernel marks context as RUNNING. 91 * At this point any HWT hook invocation by scheduler enables/disables 92 * tracing for threads associated with the context (threads of the proc). 93 * Any new threads creation (of the target proc) procedures will be invoking 94 * corresponding hooks in HWT framework, so that new hwt_thread and buffers 95 * allocated, character device for mmap(2) created on the fly. 96 * 4. User issues HWT_IOC_RECORD_GET ioctl to fetch information about mmaping 97 * tables and threads created during application startup. 98 * 5. User mmaps tracing buffers of each thread to userspace (using 99 * /dev/hwt_%d_%d % (ident, thread_id) character devices). 100 * 6. User can repeat 4 if expected thread is not yet created during target 101 * application execution. 102 * 7. User issues HWT_IOC_BUFPTR_GET ioctl to get current filling level of the 103 * hardware buffer of a given thread. 104 * 8. User invokes trace decoder library to process available data and see the 105 * results in human readable form. 106 * 9. User repeats 7 if needed. 107 * 108 * HWT context lifecycle in CPU mode of operation: 109 * 1. User invokes HWT_IOC_ALLOC ioctl providing a set of CPU to trace within 110 * single CTX. 111 * 2. Kernel verifies the set of CPU and allocates tracing context, creates 112 * a buffer for each CPU. 113 * Kernel creates a character device for every CPU provided in the request. 114 * Kernel initialized tracing backend. 115 * 3. User opens character devices of interest to map the buffers to userspace. 116 * User can start tracing by invoking HWT_IOC_START on any of character 117 * device within the context, entire context will be marked as RUNNING. 118 * 4. The rest is similar to the THREAD mode. 119 * 120 */ 121 122 #include <sys/param.h> 123 #include <sys/conf.h> 124 #include <sys/eventhandler.h> 125 #include <sys/kernel.h> 126 #include <sys/module.h> 127 128 #include <dev/hwt/hwt_context.h> 129 #include <dev/hwt/hwt_contexthash.h> 130 #include <dev/hwt/hwt_thread.h> 131 #include <dev/hwt/hwt_owner.h> 132 #include <dev/hwt/hwt_ownerhash.h> 133 #include <dev/hwt/hwt_backend.h> 134 #include <dev/hwt/hwt_record.h> 135 #include <dev/hwt/hwt_ioctl.h> 136 #include <dev/hwt/hwt_hook.h> 137 138 #define HWT_DEBUG 139 #undef HWT_DEBUG 140 141 #ifdef HWT_DEBUG 142 #define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__) 143 #else 144 #define dprintf(fmt, ...) 145 #endif 146 147 static eventhandler_tag hwt_exit_tag; 148 static struct cdev *hwt_cdev; 149 static struct cdevsw hwt_cdevsw = { 150 .d_version = D_VERSION, 151 .d_name = "hwt", 152 .d_mmap_single = NULL, 153 .d_ioctl = hwt_ioctl 154 }; 155 156 static void 157 hwt_process_exit(void *arg __unused, struct proc *p) 158 { 159 struct hwt_owner *ho; 160 161 /* Stop HWTs associated with exiting owner, if any. */ 162 ho = hwt_ownerhash_lookup(p); 163 if (ho) 164 hwt_owner_shutdown(ho); 165 } 166 167 static int 168 hwt_load(void) 169 { 170 struct make_dev_args args; 171 int error; 172 173 make_dev_args_init(&args); 174 args.mda_devsw = &hwt_cdevsw; 175 args.mda_flags = MAKEDEV_CHECKNAME | MAKEDEV_WAITOK; 176 args.mda_uid = UID_ROOT; 177 args.mda_gid = GID_WHEEL; 178 args.mda_mode = 0660; 179 args.mda_si_drv1 = NULL; 180 181 hwt_backend_load(); 182 hwt_ctx_load(); 183 hwt_contexthash_load(); 184 hwt_ownerhash_load(); 185 hwt_record_load(); 186 187 error = make_dev_s(&args, &hwt_cdev, "hwt"); 188 if (error != 0) 189 return (error); 190 191 hwt_exit_tag = EVENTHANDLER_REGISTER(process_exit, hwt_process_exit, 192 NULL, EVENTHANDLER_PRI_ANY); 193 194 hwt_hook_load(); 195 196 return (0); 197 } 198 199 static int 200 hwt_unload(void) 201 { 202 203 hwt_hook_unload(); 204 EVENTHANDLER_DEREGISTER(process_exit, hwt_exit_tag); 205 destroy_dev(hwt_cdev); 206 hwt_record_unload(); 207 hwt_ownerhash_unload(); 208 hwt_contexthash_unload(); 209 hwt_ctx_unload(); 210 hwt_backend_unload(); 211 212 return (0); 213 } 214 215 static int 216 hwt_modevent(module_t mod, int type, void *data) 217 { 218 int error; 219 220 switch (type) { 221 case MOD_LOAD: 222 error = hwt_load(); 223 break; 224 case MOD_UNLOAD: 225 error = hwt_unload(); 226 break; 227 default: 228 error = 0; 229 break; 230 } 231 232 return (error); 233 } 234 235 static moduledata_t hwt_mod = { 236 "hwt", 237 hwt_modevent, 238 NULL 239 }; 240 241 DECLARE_MODULE(hwt, hwt_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); 242 MODULE_VERSION(hwt, 1); 243