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 #include <sys/param.h> 32 #include <sys/proc.h> 33 #include <sys/kernel.h> 34 #include <sys/malloc.h> 35 #include <sys/mutex.h> 36 #include <sys/hwt.h> 37 #include <sys/linker.h> 38 #include <sys/pmckern.h> /* linker_hwpmc_list_objects */ 39 40 #include <vm/vm.h> 41 #include <vm/uma.h> 42 43 #include <dev/hwt/hwt_hook.h> 44 #include <dev/hwt/hwt_context.h> 45 #include <dev/hwt/hwt_contexthash.h> 46 #include <dev/hwt/hwt_config.h> 47 #include <dev/hwt/hwt_thread.h> 48 #include <dev/hwt/hwt_record.h> 49 50 #define HWT_RECORD_DEBUG 51 #undef HWT_RECORD_DEBUG 52 53 #ifdef HWT_RECORD_DEBUG 54 #define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__) 55 #else 56 #define dprintf(fmt, ...) 57 #endif 58 59 static MALLOC_DEFINE(M_HWT_RECORD, "hwt_record", "Hardware Trace"); 60 static uma_zone_t record_zone = NULL; 61 62 static struct hwt_record_entry * 63 hwt_record_clone(struct hwt_record_entry *ent, int flags) 64 { 65 struct hwt_record_entry *entry; 66 67 entry = uma_zalloc(record_zone, flags); 68 if (entry == NULL) 69 return (NULL); 70 memcpy(entry, ent, sizeof(struct hwt_record_entry)); 71 switch (ent->record_type) { 72 case HWT_RECORD_MMAP: 73 case HWT_RECORD_EXECUTABLE: 74 case HWT_RECORD_KERNEL: 75 entry->fullpath = strdup(ent->fullpath, M_HWT_RECORD); 76 break; 77 default: 78 break; 79 } 80 81 return (entry); 82 } 83 84 static void 85 hwt_record_to_user(struct hwt_record_entry *ent, 86 struct hwt_record_user_entry *usr) 87 { 88 usr->record_type = ent->record_type; 89 switch (ent->record_type) { 90 case HWT_RECORD_MMAP: 91 case HWT_RECORD_EXECUTABLE: 92 case HWT_RECORD_KERNEL: 93 usr->addr = ent->addr; 94 usr->baseaddr = ent->baseaddr; 95 strncpy(usr->fullpath, ent->fullpath, MAXPATHLEN); 96 break; 97 case HWT_RECORD_BUFFER: 98 usr->buf_id = ent->buf_id; 99 usr->curpage = ent->curpage; 100 usr->offset = ent->offset; 101 break; 102 case HWT_RECORD_THREAD_CREATE: 103 case HWT_RECORD_THREAD_SET_NAME: 104 usr->thread_id = ent->thread_id; 105 break; 106 default: 107 break; 108 } 109 } 110 111 void 112 hwt_record_load(void) 113 { 114 record_zone = uma_zcreate("HWT records", 115 sizeof(struct hwt_record_entry), NULL, NULL, NULL, NULL, 0, 0); 116 } 117 118 void 119 hwt_record_unload(void) 120 { 121 uma_zdestroy(record_zone); 122 } 123 124 void 125 hwt_record_ctx(struct hwt_context *ctx, struct hwt_record_entry *ent, int flags) 126 { 127 struct hwt_record_entry *entry; 128 129 KASSERT(ent != NULL, ("ent is NULL")); 130 entry = hwt_record_clone(ent, flags); 131 if (entry == NULL) { 132 /* XXX: Not sure what to do here other than logging an error. */ 133 return; 134 } 135 136 HWT_CTX_LOCK(ctx); 137 TAILQ_INSERT_TAIL(&ctx->records, entry, next); 138 HWT_CTX_UNLOCK(ctx); 139 hwt_record_wakeup(ctx); 140 } 141 142 void 143 hwt_record_td(struct thread *td, struct hwt_record_entry *ent, int flags) 144 { 145 struct hwt_record_entry *entry; 146 struct hwt_context *ctx; 147 struct proc *p; 148 149 p = td->td_proc; 150 151 KASSERT(ent != NULL, ("ent is NULL")); 152 entry = hwt_record_clone(ent, flags); 153 if (entry == NULL) { 154 /* XXX: Not sure what to do here other than logging an error. */ 155 return; 156 } 157 ctx = hwt_contexthash_lookup(p); 158 if (ctx == NULL) { 159 hwt_record_entry_free(entry); 160 return; 161 } 162 HWT_CTX_LOCK(ctx); 163 TAILQ_INSERT_TAIL(&ctx->records, entry, next); 164 HWT_CTX_UNLOCK(ctx); 165 hwt_record_wakeup(ctx); 166 167 hwt_ctx_put(ctx); 168 } 169 170 struct hwt_record_entry * 171 hwt_record_entry_alloc(void) 172 { 173 return (uma_zalloc(record_zone, M_WAITOK | M_ZERO)); 174 } 175 176 void 177 hwt_record_entry_free(struct hwt_record_entry *entry) 178 { 179 180 switch (entry->record_type) { 181 case HWT_RECORD_MMAP: 182 case HWT_RECORD_EXECUTABLE: 183 case HWT_RECORD_KERNEL: 184 free(entry->fullpath, M_HWT_RECORD); 185 break; 186 default: 187 break; 188 } 189 190 uma_zfree(record_zone, entry); 191 } 192 193 static int 194 hwt_record_grab(struct hwt_context *ctx, 195 struct hwt_record_user_entry *user_entry, int nitems_req, int wait) 196 { 197 struct hwt_record_entry *entry; 198 int i; 199 200 if (wait) { 201 mtx_lock(&ctx->rec_mtx); 202 if (TAILQ_FIRST(&ctx->records) == NULL) { 203 /* Wait until we have new records. */ 204 msleep(ctx, &ctx->rec_mtx, PCATCH, "recsnd", 0); 205 } 206 mtx_unlock(&ctx->rec_mtx); 207 } 208 209 for (i = 0; i < nitems_req; i++) { 210 HWT_CTX_LOCK(ctx); 211 entry = TAILQ_FIRST(&ctx->records); 212 if (entry) 213 TAILQ_REMOVE_HEAD(&ctx->records, next); 214 HWT_CTX_UNLOCK(ctx); 215 216 if (entry == NULL) 217 break; 218 hwt_record_to_user(entry, &user_entry[i]); 219 hwt_record_entry_free(entry); 220 } 221 222 return (i); 223 } 224 225 void 226 hwt_record_free_all(struct hwt_context *ctx) 227 { 228 struct hwt_record_entry *entry; 229 230 while (1) { 231 HWT_CTX_LOCK(ctx); 232 entry = TAILQ_FIRST(&ctx->records); 233 if (entry) 234 TAILQ_REMOVE_HEAD(&ctx->records, next); 235 HWT_CTX_UNLOCK(ctx); 236 237 if (entry == NULL) 238 break; 239 240 hwt_record_entry_free(entry); 241 } 242 } 243 244 int 245 hwt_record_send(struct hwt_context *ctx, struct hwt_record_get *record_get) 246 { 247 struct hwt_record_user_entry *user_entry; 248 int nitems_req; 249 int error; 250 int i; 251 252 nitems_req = 0; 253 254 error = copyin(record_get->nentries, &nitems_req, sizeof(int)); 255 if (error) 256 return (error); 257 258 if (nitems_req < 1 || nitems_req > 1024) 259 return (ENXIO); 260 261 user_entry = malloc(sizeof(struct hwt_record_user_entry) * nitems_req, 262 M_HWT_RECORD, M_WAITOK | M_ZERO); 263 264 i = hwt_record_grab(ctx, user_entry, nitems_req, record_get->wait); 265 if (i > 0) 266 error = copyout(user_entry, record_get->records, 267 sizeof(struct hwt_record_user_entry) * i); 268 269 if (error == 0) 270 error = copyout(&i, record_get->nentries, sizeof(int)); 271 272 free(user_entry, M_HWT_RECORD); 273 274 return (error); 275 } 276 277 void 278 hwt_record_kernel_objects(struct hwt_context *ctx) 279 { 280 struct hwt_record_entry *entry; 281 struct pmckern_map_in *kobase; 282 int i; 283 284 kobase = linker_hwpmc_list_objects(); 285 for (i = 0; kobase[i].pm_file != NULL; i++) { 286 entry = hwt_record_entry_alloc(); 287 entry->record_type = HWT_RECORD_KERNEL; 288 entry->fullpath = strdup(kobase[i].pm_file, M_HWT_RECORD); 289 entry->addr = kobase[i].pm_address; 290 291 HWT_CTX_LOCK(ctx); 292 TAILQ_INSERT_HEAD(&ctx->records, entry, next); 293 HWT_CTX_UNLOCK(ctx); 294 } 295 free(kobase, M_LINKER); 296 } 297 298 void 299 hwt_record_wakeup(struct hwt_context *ctx) 300 { 301 wakeup(ctx); 302 } 303