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 /* Hardware Trace (HWT) framework. */ 32 33 #include <sys/param.h> 34 #include <sys/proc.h> 35 #include <sys/malloc.h> 36 #include <sys/mman.h> 37 #include <sys/refcount.h> 38 #include <sys/hwt.h> 39 40 #include <dev/hwt/hwt_hook.h> 41 #include <dev/hwt/hwt_context.h> 42 #include <dev/hwt/hwt_contexthash.h> 43 #include <dev/hwt/hwt_config.h> 44 #include <dev/hwt/hwt_thread.h> 45 #include <dev/hwt/hwt_owner.h> 46 #include <dev/hwt/hwt_backend.h> 47 #include <dev/hwt/hwt_record.h> 48 #include <dev/hwt/hwt_vm.h> 49 50 #define HWT_DEBUG 51 #undef HWT_DEBUG 52 53 #ifdef HWT_DEBUG 54 #define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__) 55 #else 56 #define dprintf(fmt, ...) 57 #endif 58 59 static void 60 hwt_switch_in(struct thread *td) 61 { 62 struct hwt_context *ctx; 63 struct hwt_thread *thr; 64 struct proc *p; 65 int cpu_id; 66 67 p = td->td_proc; 68 69 cpu_id = PCPU_GET(cpuid); 70 71 ctx = hwt_contexthash_lookup(p); 72 if (ctx == NULL) 73 return; 74 75 if (ctx->state != CTX_STATE_RUNNING) { 76 hwt_ctx_put(ctx); 77 return; 78 } 79 80 thr = hwt_thread_lookup(ctx, td); 81 if (thr == NULL) { 82 hwt_ctx_put(ctx); 83 return; 84 } 85 86 dprintf("%s: thr %p index %d tid %d on cpu_id %d\n", __func__, thr, 87 thr->thread_id, td->td_tid, cpu_id); 88 89 hwt_backend_configure(ctx, cpu_id, thr->thread_id); 90 hwt_backend_enable(ctx, cpu_id); 91 92 hwt_ctx_put(ctx); 93 } 94 95 static void 96 hwt_switch_out(struct thread *td) 97 { 98 struct hwt_context *ctx; 99 struct hwt_thread *thr; 100 struct proc *p; 101 int cpu_id; 102 103 p = td->td_proc; 104 105 cpu_id = PCPU_GET(cpuid); 106 107 ctx = hwt_contexthash_lookup(p); 108 if (ctx == NULL) 109 return; 110 111 if (ctx->state != CTX_STATE_RUNNING) { 112 hwt_ctx_put(ctx); 113 return; 114 } 115 thr = hwt_thread_lookup(ctx, td); 116 if (thr == NULL) { 117 hwt_ctx_put(ctx); 118 return; 119 } 120 121 dprintf("%s: thr %p index %d tid %d on cpu_id %d\n", __func__, thr, 122 thr->thread_id, td->td_tid, cpu_id); 123 124 hwt_backend_disable(ctx, cpu_id); 125 126 hwt_ctx_put(ctx); 127 } 128 129 static void 130 hwt_hook_thread_exit(struct thread *td) 131 { 132 struct hwt_context *ctx; 133 struct hwt_thread *thr; 134 struct proc *p; 135 int cpu_id; 136 137 p = td->td_proc; 138 139 cpu_id = PCPU_GET(cpuid); 140 141 ctx = hwt_contexthash_lookup(p); 142 if (ctx == NULL) 143 return; 144 145 thr = hwt_thread_lookup(ctx, td); 146 if (thr == NULL) { 147 hwt_ctx_put(ctx); 148 return; 149 } 150 151 thr->state = HWT_THREAD_STATE_EXITED; 152 153 dprintf("%s: thr %p index %d tid %d on cpu_id %d\n", __func__, thr, 154 thr->thread_id, td->td_tid, cpu_id); 155 156 if (ctx->state == CTX_STATE_RUNNING) 157 hwt_backend_disable(ctx, cpu_id); 158 159 hwt_ctx_put(ctx); 160 } 161 162 static void 163 hwt_hook_mmap(struct thread *td) 164 { 165 struct hwt_context *ctx; 166 struct hwt_thread *thr; 167 struct proc *p; 168 int pause; 169 170 p = td->td_proc; 171 172 ctx = hwt_contexthash_lookup(p); 173 if (ctx == NULL) 174 return; 175 176 /* The ctx state could be any here. */ 177 178 pause = ctx->pause_on_mmap ? 1 : 0; 179 180 thr = hwt_thread_lookup(ctx, td); 181 if (thr == NULL) { 182 hwt_ctx_put(ctx); 183 return; 184 } 185 186 /* 187 * msleep(9) atomically releases the mtx lock, so take refcount 188 * to ensure that thr is not destroyed. 189 * It could not be destroyed prior to this call as we are holding ctx 190 * refcnt. 191 */ 192 refcount_acquire(&thr->refcnt); 193 hwt_ctx_put(ctx); 194 195 if (pause) { 196 HWT_THR_LOCK(thr); 197 msleep(thr, &thr->mtx, PCATCH, "hwt-mmap", 0); 198 HWT_THR_UNLOCK(thr); 199 } 200 201 if (refcount_release(&thr->refcnt)) 202 hwt_thread_free(thr); 203 } 204 205 static int 206 hwt_hook_thread_create(struct thread *td) 207 { 208 struct hwt_record_entry *entry; 209 struct hwt_context *ctx; 210 struct hwt_thread *thr; 211 char path[MAXPATHLEN]; 212 size_t bufsize; 213 struct proc *p; 214 int thread_id, kva_req; 215 int error; 216 217 p = td->td_proc; 218 219 /* Step 1. Get CTX and collect information needed. */ 220 ctx = hwt_contexthash_lookup(p); 221 if (ctx == NULL) 222 return (ENXIO); 223 thread_id = atomic_fetchadd_int(&ctx->thread_counter, 1); 224 bufsize = ctx->bufsize; 225 kva_req = ctx->hwt_backend->kva_req; 226 sprintf(path, "hwt_%d_%d", ctx->ident, thread_id); 227 hwt_ctx_put(ctx); 228 229 /* Step 2. Allocate some memory without holding ctx ref. */ 230 error = hwt_thread_alloc(&thr, path, bufsize, kva_req); 231 if (error) { 232 printf("%s: could not allocate thread, error %d\n", 233 __func__, error); 234 return (error); 235 } 236 237 entry = hwt_record_entry_alloc(); 238 entry->record_type = HWT_RECORD_THREAD_CREATE; 239 entry->thread_id = thread_id; 240 241 /* Step 3. Get CTX once again. */ 242 ctx = hwt_contexthash_lookup(p); 243 if (ctx == NULL) { 244 hwt_record_entry_free(entry); 245 hwt_thread_free(thr); 246 /* ctx->thread_counter does not matter. */ 247 return (ENXIO); 248 } 249 /* Allocate backend-specific thread data. */ 250 error = hwt_backend_thread_alloc(ctx, thr); 251 if (error != 0) { 252 dprintf("%s: failed to allocate backend thread data\n", 253 __func__); 254 return (error); 255 } 256 257 thr->vm->ctx = ctx; 258 thr->ctx = ctx; 259 thr->backend = ctx->hwt_backend; 260 thr->thread_id = thread_id; 261 thr->td = td; 262 263 HWT_CTX_LOCK(ctx); 264 hwt_thread_insert(ctx, thr, entry); 265 HWT_CTX_UNLOCK(ctx); 266 267 /* Notify userspace. */ 268 hwt_record_wakeup(ctx); 269 270 hwt_ctx_put(ctx); 271 272 return (0); 273 } 274 275 static void 276 hwt_hook_handler(struct thread *td, int func, void *arg) 277 { 278 struct proc *p; 279 280 p = td->td_proc; 281 if ((p->p_flag2 & P2_HWT) == 0) 282 return; 283 284 switch (func) { 285 case HWT_SWITCH_IN: 286 hwt_switch_in(td); 287 break; 288 case HWT_SWITCH_OUT: 289 hwt_switch_out(td); 290 break; 291 case HWT_THREAD_CREATE: 292 hwt_hook_thread_create(td); 293 break; 294 case HWT_THREAD_SET_NAME: 295 /* TODO. */ 296 break; 297 case HWT_THREAD_EXIT: 298 hwt_hook_thread_exit(td); 299 break; 300 case HWT_EXEC: 301 case HWT_MMAP: 302 hwt_record_td(td, arg, M_WAITOK | M_ZERO); 303 hwt_hook_mmap(td); 304 break; 305 case HWT_RECORD: 306 hwt_record_td(td, arg, M_WAITOK | M_ZERO); 307 break; 308 }; 309 } 310 311 void 312 hwt_hook_load(void) 313 { 314 315 hwt_hook = hwt_hook_handler; 316 } 317 318 void 319 hwt_hook_unload(void) 320 { 321 322 hwt_hook = NULL; 323 } 324