1 1.18 riastrad /* $NetBSD: subr_kcov.c,v 1.18 2022/10/26 23:24:21 riastradh Exp $ */ 2 1.1 kamil 3 1.1 kamil /* 4 1.12 maxv * Copyright (c) 2019-2020 The NetBSD Foundation, Inc. 5 1.1 kamil * All rights reserved. 6 1.1 kamil * 7 1.1 kamil * This code is derived from software contributed to The NetBSD Foundation 8 1.1 kamil * by Siddharth Muralee. 9 1.1 kamil * 10 1.1 kamil * Redistribution and use in source and binary forms, with or without 11 1.1 kamil * modification, are permitted provided that the following conditions 12 1.1 kamil * are met: 13 1.1 kamil * 1. Redistributions of source code must retain the above copyright 14 1.1 kamil * notice, this list of conditions and the following disclaimer. 15 1.1 kamil * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 kamil * notice, this list of conditions and the following disclaimer in the 17 1.1 kamil * documentation and/or other materials provided with the distribution. 18 1.1 kamil * 19 1.1 kamil * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 kamil * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 kamil * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 kamil * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 kamil * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 kamil * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 kamil * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 kamil * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 kamil * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 kamil * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 kamil * POSSIBILITY OF SUCH DAMAGE. 30 1.1 kamil */ 31 1.1 kamil 32 1.1 kamil #include <sys/cdefs.h> 33 1.1 kamil 34 1.1 kamil #include <sys/module.h> 35 1.1 kamil #include <sys/param.h> 36 1.1 kamil #include <sys/systm.h> 37 1.1 kamil #include <sys/kernel.h> 38 1.1 kamil 39 1.1 kamil #include <sys/conf.h> 40 1.1 kamil #include <sys/condvar.h> 41 1.4 kamil #include <sys/file.h> 42 1.4 kamil #include <sys/filedesc.h> 43 1.1 kamil #include <sys/kmem.h> 44 1.4 kamil #include <sys/mman.h> 45 1.1 kamil #include <sys/mutex.h> 46 1.1 kamil #include <sys/queue.h> 47 1.1 kamil 48 1.1 kamil #include <uvm/uvm_extern.h> 49 1.1 kamil #include <sys/kcov.h> 50 1.1 kamil 51 1.1 kamil #define KCOV_BUF_MAX_ENTRIES (256 << 10) 52 1.1 kamil 53 1.5 kamil #define KCOV_CMP_CONST 1 54 1.5 kamil #define KCOV_CMP_SIZE(x) ((x) << 1) 55 1.5 kamil 56 1.4 kamil static dev_type_open(kcov_open); 57 1.4 kamil 58 1.4 kamil const struct cdevsw kcov_cdevsw = { 59 1.4 kamil .d_open = kcov_open, 60 1.4 kamil .d_close = noclose, 61 1.4 kamil .d_read = noread, 62 1.4 kamil .d_write = nowrite, 63 1.4 kamil .d_ioctl = noioctl, 64 1.4 kamil .d_stop = nostop, 65 1.4 kamil .d_tty = notty, 66 1.4 kamil .d_poll = nopoll, 67 1.4 kamil .d_mmap = nommap, 68 1.4 kamil .d_kqfilter = nokqfilter, 69 1.4 kamil .d_discard = nodiscard, 70 1.4 kamil .d_flag = D_OTHER | D_MPSAFE 71 1.4 kamil }; 72 1.4 kamil 73 1.4 kamil static int kcov_fops_ioctl(file_t *, u_long, void *); 74 1.4 kamil static int kcov_fops_close(file_t *); 75 1.4 kamil static int kcov_fops_mmap(file_t *, off_t *, size_t, int, int *, int *, 76 1.4 kamil struct uvm_object **, int *); 77 1.4 kamil 78 1.4 kamil const struct fileops kcov_fileops = { 79 1.4 kamil .fo_read = fbadop_read, 80 1.4 kamil .fo_write = fbadop_write, 81 1.4 kamil .fo_ioctl = kcov_fops_ioctl, 82 1.4 kamil .fo_fcntl = fnullop_fcntl, 83 1.4 kamil .fo_poll = fnullop_poll, 84 1.4 kamil .fo_stat = fbadop_stat, 85 1.4 kamil .fo_close = kcov_fops_close, 86 1.4 kamil .fo_kqfilter = fnullop_kqfilter, 87 1.4 kamil .fo_restart = fnullop_restart, 88 1.4 kamil .fo_mmap = kcov_fops_mmap, 89 1.4 kamil }; 90 1.4 kamil 91 1.1 kamil /* 92 1.4 kamil * The KCOV descriptors (KD) are allocated during open(), and are associated 93 1.4 kamil * with a file descriptor. 94 1.4 kamil * 95 1.4 kamil * An LWP can 'enable' a KD. When this happens, this LWP becomes the owner of 96 1.4 kamil * the KD, and no LWP can 'disable' this KD except the owner. 97 1.1 kamil * 98 1.4 kamil * A KD is freed when its file descriptor is closed _iff_ the KD is not active 99 1.4 kamil * on an LWP. If it is, we ask the LWP to free it when it exits. 100 1.4 kamil * 101 1.4 kamil * The buffers mmapped are in a dedicated uobj, therefore there is no risk 102 1.4 kamil * that the kernel frees a buffer still mmapped in a process: the uobj 103 1.4 kamil * refcount will be non-zero, so the backing is not freed until an munmap 104 1.4 kamil * occurs on said process. 105 1.1 kamil */ 106 1.1 kamil 107 1.1 kamil typedef struct kcov_desc { 108 1.13 maxv /* Local only */ 109 1.1 kamil kmutex_t lock; 110 1.13 maxv bool lwpfree; 111 1.14 maxv bool silenced; 112 1.13 maxv 113 1.13 maxv /* Pointer to the end of the structure, if any */ 114 1.13 maxv struct kcov_desc *remote; 115 1.13 maxv 116 1.13 maxv /* Can be remote */ 117 1.1 kamil kcov_int_t *buf; 118 1.4 kamil struct uvm_object *uobj; 119 1.1 kamil size_t bufnent; 120 1.1 kamil size_t bufsize; 121 1.5 kamil int mode; 122 1.4 kamil bool enabled; 123 1.1 kamil } kcov_t; 124 1.1 kamil 125 1.13 maxv /* -------------------------------------------------------------------------- */ 126 1.13 maxv 127 1.1 kamil static void 128 1.1 kamil kcov_lock(kcov_t *kd) 129 1.1 kamil { 130 1.1 kamil 131 1.1 kamil mutex_enter(&kd->lock); 132 1.1 kamil } 133 1.1 kamil 134 1.1 kamil static void 135 1.1 kamil kcov_unlock(kcov_t *kd) 136 1.1 kamil { 137 1.1 kamil 138 1.1 kamil mutex_exit(&kd->lock); 139 1.1 kamil } 140 1.1 kamil 141 1.13 maxv static bool 142 1.13 maxv kcov_mode_is_valid(int mode) 143 1.13 maxv { 144 1.13 maxv switch (mode) { 145 1.13 maxv case KCOV_MODE_NONE: 146 1.13 maxv case KCOV_MODE_TRACE_PC: 147 1.13 maxv case KCOV_MODE_TRACE_CMP: 148 1.13 maxv return true; 149 1.13 maxv default: 150 1.13 maxv return false; 151 1.13 maxv } 152 1.13 maxv } 153 1.13 maxv 154 1.13 maxv /* -------------------------------------------------------------------------- */ 155 1.13 maxv 156 1.1 kamil static void 157 1.4 kamil kcov_free(kcov_t *kd) 158 1.1 kamil { 159 1.1 kamil 160 1.4 kamil KASSERT(kd != NULL); 161 1.4 kamil if (kd->buf != NULL) { 162 1.4 kamil uvm_deallocate(kernel_map, (vaddr_t)kd->buf, kd->bufsize); 163 1.4 kamil } 164 1.4 kamil mutex_destroy(&kd->lock); 165 1.4 kamil kmem_free(kd, sizeof(*kd)); 166 1.1 kamil } 167 1.1 kamil 168 1.12 maxv void 169 1.12 maxv kcov_lwp_free(struct lwp *l) 170 1.1 kamil { 171 1.12 maxv kcov_t *kd = (kcov_t *)l->l_kcov; 172 1.1 kamil 173 1.1 kamil if (kd == NULL) { 174 1.1 kamil return; 175 1.1 kamil } 176 1.1 kamil kcov_lock(kd); 177 1.4 kamil kd->enabled = false; 178 1.1 kamil kcov_unlock(kd); 179 1.4 kamil if (kd->lwpfree) { 180 1.4 kamil kcov_free(kd); 181 1.1 kamil } 182 1.1 kamil } 183 1.1 kamil 184 1.1 kamil static int 185 1.1 kamil kcov_allocbuf(kcov_t *kd, uint64_t nent) 186 1.1 kamil { 187 1.1 kamil size_t size; 188 1.4 kamil int error; 189 1.1 kamil 190 1.1 kamil if (nent < 2 || nent > KCOV_BUF_MAX_ENTRIES) 191 1.1 kamil return EINVAL; 192 1.1 kamil if (kd->buf != NULL) 193 1.1 kamil return EEXIST; 194 1.1 kamil 195 1.1 kamil size = roundup(nent * KCOV_ENTRY_SIZE, PAGE_SIZE); 196 1.1 kamil kd->bufnent = nent - 1; 197 1.1 kamil kd->bufsize = size; 198 1.4 kamil kd->uobj = uao_create(kd->bufsize, 0); 199 1.4 kamil 200 1.4 kamil /* Map the uobj into the kernel address space, as wired. */ 201 1.4 kamil kd->buf = NULL; 202 1.4 kamil error = uvm_map(kernel_map, (vaddr_t *)&kd->buf, kd->bufsize, kd->uobj, 203 1.4 kamil 0, 0, UVM_MAPFLAG(UVM_PROT_RW, UVM_PROT_RW, UVM_INH_SHARE, 204 1.4 kamil UVM_ADV_RANDOM, 0)); 205 1.4 kamil if (error) { 206 1.4 kamil uao_detach(kd->uobj); 207 1.4 kamil return error; 208 1.4 kamil } 209 1.4 kamil error = uvm_map_pageable(kernel_map, (vaddr_t)kd->buf, 210 1.4 kamil (vaddr_t)kd->buf + size, false, 0); 211 1.4 kamil if (error) { 212 1.4 kamil uvm_deallocate(kernel_map, (vaddr_t)kd->buf, size); 213 1.4 kamil return error; 214 1.4 kamil } 215 1.1 kamil 216 1.1 kamil return 0; 217 1.1 kamil } 218 1.1 kamil 219 1.1 kamil /* -------------------------------------------------------------------------- */ 220 1.1 kamil 221 1.13 maxv typedef struct kcov_remote { 222 1.13 maxv LIST_ENTRY(kcov_remote) list; 223 1.13 maxv uint64_t subsystem; 224 1.13 maxv uint64_t id; 225 1.13 maxv u_int refcount; 226 1.13 maxv kcov_t kd; 227 1.13 maxv } kcov_remote_t; 228 1.13 maxv 229 1.13 maxv typedef LIST_HEAD(, kcov_remote) kcov_remote_list_t; 230 1.13 maxv 231 1.13 maxv static kcov_remote_list_t kcov_remote_list; 232 1.13 maxv 233 1.13 maxv static kcov_remote_t * 234 1.13 maxv kcov_remote_find(uint64_t subsystem, uint64_t id) 235 1.13 maxv { 236 1.13 maxv kcov_remote_t *kr; 237 1.13 maxv 238 1.13 maxv LIST_FOREACH(kr, &kcov_remote_list, list) { 239 1.13 maxv if (kr->subsystem == subsystem && kr->id == id) 240 1.13 maxv return kr; 241 1.13 maxv } 242 1.13 maxv 243 1.13 maxv return NULL; 244 1.13 maxv } 245 1.13 maxv 246 1.13 maxv void 247 1.13 maxv kcov_remote_register(uint64_t subsystem, uint64_t id) 248 1.13 maxv { 249 1.13 maxv kcov_remote_t *kr; 250 1.13 maxv kcov_t *kd; 251 1.13 maxv int error; 252 1.13 maxv 253 1.13 maxv if (kcov_remote_find(subsystem, id) != NULL) { 254 1.13 maxv panic("%s: kr already exists", __func__); 255 1.13 maxv } 256 1.13 maxv 257 1.13 maxv kr = kmem_zalloc(sizeof(*kr), KM_SLEEP); 258 1.13 maxv kr->subsystem = subsystem; 259 1.13 maxv kr->id = id; 260 1.13 maxv kr->refcount = 0; 261 1.13 maxv kd = &kr->kd; 262 1.13 maxv 263 1.13 maxv mutex_init(&kd->lock, MUTEX_DEFAULT, IPL_NONE); 264 1.13 maxv error = kcov_allocbuf(kd, KCOV_BUF_MAX_ENTRIES); 265 1.13 maxv if (error != 0) 266 1.13 maxv panic("%s: failed to allocate buffer", __func__); 267 1.13 maxv 268 1.13 maxv LIST_INSERT_HEAD(&kcov_remote_list, kr, list); 269 1.13 maxv } 270 1.13 maxv 271 1.13 maxv void 272 1.13 maxv kcov_remote_enter(uint64_t subsystem, uint64_t id) 273 1.13 maxv { 274 1.13 maxv struct lwp *l = curlwp; 275 1.13 maxv kcov_remote_t *kr; 276 1.13 maxv kcov_t *kd; 277 1.15 hannken u_int refs __diagused; 278 1.13 maxv 279 1.13 maxv kr = kcov_remote_find(subsystem, id); 280 1.13 maxv if (__predict_false(kr == NULL)) { 281 1.13 maxv panic("%s: unable to find kr", __func__); 282 1.13 maxv } 283 1.13 maxv 284 1.13 maxv refs = atomic_inc_uint_nv(&kr->refcount); 285 1.13 maxv KASSERT(refs == 1); 286 1.13 maxv 287 1.13 maxv KASSERT(l->l_kcov == NULL); 288 1.13 maxv kd = &kr->kd; 289 1.13 maxv if (atomic_load_relaxed(&kd->enabled)) { 290 1.13 maxv l->l_kcov = kd; 291 1.13 maxv } 292 1.13 maxv } 293 1.13 maxv 294 1.13 maxv void 295 1.13 maxv kcov_remote_leave(uint64_t subsystem, uint64_t id) 296 1.13 maxv { 297 1.13 maxv struct lwp *l = curlwp; 298 1.13 maxv kcov_remote_t *kr; 299 1.15 hannken u_int refs __diagused; 300 1.13 maxv 301 1.13 maxv kr = kcov_remote_find(subsystem, id); 302 1.13 maxv if (__predict_false(kr == NULL)) { 303 1.13 maxv panic("%s: unable to find kr", __func__); 304 1.13 maxv } 305 1.13 maxv 306 1.13 maxv refs = atomic_dec_uint_nv(&kr->refcount); 307 1.13 maxv KASSERT(refs == 0); 308 1.13 maxv 309 1.13 maxv l->l_kcov = NULL; 310 1.13 maxv } 311 1.13 maxv 312 1.13 maxv static int 313 1.13 maxv kcov_remote_enable(kcov_t *kd, int mode) 314 1.13 maxv { 315 1.13 maxv kcov_lock(kd); 316 1.13 maxv if (kd->enabled) { 317 1.13 maxv kcov_unlock(kd); 318 1.13 maxv return EBUSY; 319 1.13 maxv } 320 1.13 maxv kd->mode = mode; 321 1.13 maxv atomic_store_relaxed(&kd->enabled, true); 322 1.13 maxv kcov_unlock(kd); 323 1.13 maxv 324 1.13 maxv return 0; 325 1.13 maxv } 326 1.13 maxv 327 1.13 maxv static int 328 1.13 maxv kcov_remote_disable(kcov_t *kd) 329 1.13 maxv { 330 1.13 maxv kcov_lock(kd); 331 1.13 maxv if (!kd->enabled) { 332 1.13 maxv kcov_unlock(kd); 333 1.13 maxv return ENOENT; 334 1.13 maxv } 335 1.13 maxv atomic_store_relaxed(&kd->enabled, false); 336 1.13 maxv kcov_unlock(kd); 337 1.13 maxv 338 1.13 maxv return 0; 339 1.13 maxv } 340 1.13 maxv 341 1.13 maxv static int 342 1.13 maxv kcov_remote_attach(kcov_t *kd, struct kcov_ioc_remote_attach *args) 343 1.13 maxv { 344 1.13 maxv kcov_remote_t *kr; 345 1.13 maxv 346 1.13 maxv if (kd->enabled) 347 1.13 maxv return EEXIST; 348 1.13 maxv 349 1.13 maxv kr = kcov_remote_find(args->subsystem, args->id); 350 1.13 maxv if (kr == NULL) 351 1.13 maxv return ENOENT; 352 1.13 maxv kd->remote = &kr->kd; 353 1.13 maxv 354 1.13 maxv return 0; 355 1.13 maxv } 356 1.13 maxv 357 1.13 maxv static int 358 1.13 maxv kcov_remote_detach(kcov_t *kd) 359 1.13 maxv { 360 1.13 maxv if (kd->enabled) 361 1.13 maxv return EEXIST; 362 1.13 maxv if (kd->remote == NULL) 363 1.13 maxv return ENOENT; 364 1.13 maxv (void)kcov_remote_disable(kd->remote); 365 1.13 maxv kd->remote = NULL; 366 1.13 maxv return 0; 367 1.13 maxv } 368 1.13 maxv 369 1.13 maxv /* -------------------------------------------------------------------------- */ 370 1.13 maxv 371 1.13 maxv static int 372 1.13 maxv kcov_setbufsize(kcov_t *kd, uint64_t *args) 373 1.13 maxv { 374 1.13 maxv if (kd->remote != NULL) 375 1.13 maxv return 0; /* buffer allocated remotely */ 376 1.13 maxv if (kd->enabled) 377 1.13 maxv return EBUSY; 378 1.13 maxv return kcov_allocbuf(kd, *((uint64_t *)args)); 379 1.13 maxv } 380 1.13 maxv 381 1.13 maxv static int 382 1.13 maxv kcov_enable(kcov_t *kd, uint64_t *args) 383 1.13 maxv { 384 1.13 maxv struct lwp *l = curlwp; 385 1.13 maxv int mode; 386 1.13 maxv 387 1.13 maxv mode = *((int *)args); 388 1.13 maxv if (!kcov_mode_is_valid(mode)) 389 1.13 maxv return EINVAL; 390 1.13 maxv 391 1.13 maxv if (kd->remote != NULL) 392 1.13 maxv return kcov_remote_enable(kd->remote, mode); 393 1.13 maxv 394 1.13 maxv if (kd->enabled) 395 1.13 maxv return EBUSY; 396 1.13 maxv if (l->l_kcov != NULL) 397 1.13 maxv return EBUSY; 398 1.13 maxv if (kd->buf == NULL) 399 1.13 maxv return ENOBUFS; 400 1.13 maxv 401 1.13 maxv l->l_kcov = kd; 402 1.13 maxv kd->mode = mode; 403 1.13 maxv kd->enabled = true; 404 1.13 maxv return 0; 405 1.13 maxv } 406 1.13 maxv 407 1.13 maxv static int 408 1.13 maxv kcov_disable(kcov_t *kd) 409 1.13 maxv { 410 1.13 maxv struct lwp *l = curlwp; 411 1.13 maxv 412 1.13 maxv if (kd->remote != NULL) 413 1.13 maxv return kcov_remote_disable(kd->remote); 414 1.13 maxv 415 1.13 maxv if (!kd->enabled) 416 1.13 maxv return ENOENT; 417 1.13 maxv if (l->l_kcov != kd) 418 1.13 maxv return ENOENT; 419 1.13 maxv 420 1.13 maxv l->l_kcov = NULL; 421 1.13 maxv kd->enabled = false; 422 1.13 maxv return 0; 423 1.13 maxv } 424 1.13 maxv 425 1.13 maxv /* -------------------------------------------------------------------------- */ 426 1.13 maxv 427 1.14 maxv void 428 1.14 maxv kcov_silence_enter(void) 429 1.14 maxv { 430 1.14 maxv kcov_t *kd = curlwp->l_kcov; 431 1.14 maxv 432 1.14 maxv if (kd != NULL) 433 1.14 maxv kd->silenced = true; 434 1.14 maxv } 435 1.14 maxv 436 1.14 maxv void 437 1.14 maxv kcov_silence_leave(void) 438 1.14 maxv { 439 1.14 maxv kcov_t *kd = curlwp->l_kcov; 440 1.14 maxv 441 1.14 maxv if (kd != NULL) 442 1.14 maxv kd->silenced = false; 443 1.14 maxv } 444 1.14 maxv 445 1.14 maxv /* -------------------------------------------------------------------------- */ 446 1.14 maxv 447 1.1 kamil static int 448 1.1 kamil kcov_open(dev_t dev, int flag, int mode, struct lwp *l) 449 1.1 kamil { 450 1.4 kamil struct file *fp; 451 1.4 kamil int error, fd; 452 1.1 kamil kcov_t *kd; 453 1.1 kamil 454 1.4 kamil error = fd_allocfile(&fp, &fd); 455 1.4 kamil if (error) 456 1.4 kamil return error; 457 1.1 kamil 458 1.1 kamil kd = kmem_zalloc(sizeof(*kd), KM_SLEEP); 459 1.1 kamil mutex_init(&kd->lock, MUTEX_DEFAULT, IPL_NONE); 460 1.1 kamil 461 1.4 kamil return fd_clone(fp, fd, flag, &kcov_fileops, kd); 462 1.1 kamil } 463 1.1 kamil 464 1.1 kamil static int 465 1.4 kamil kcov_fops_close(file_t *fp) 466 1.1 kamil { 467 1.4 kamil kcov_t *kd = fp->f_data; 468 1.4 kamil 469 1.4 kamil kcov_lock(kd); 470 1.13 maxv if (kd->remote != NULL) 471 1.13 maxv (void)kcov_remote_disable(kd->remote); 472 1.4 kamil if (kd->enabled) { 473 1.4 kamil kd->lwpfree = true; 474 1.4 kamil kcov_unlock(kd); 475 1.4 kamil } else { 476 1.4 kamil kcov_unlock(kd); 477 1.4 kamil kcov_free(kd); 478 1.4 kamil } 479 1.4 kamil fp->f_data = NULL; 480 1.1 kamil 481 1.1 kamil return 0; 482 1.1 kamil } 483 1.1 kamil 484 1.1 kamil static int 485 1.4 kamil kcov_fops_ioctl(file_t *fp, u_long cmd, void *addr) 486 1.1 kamil { 487 1.1 kamil kcov_t *kd; 488 1.13 maxv int error; 489 1.1 kamil 490 1.4 kamil kd = fp->f_data; 491 1.1 kamil if (kd == NULL) 492 1.1 kamil return ENXIO; 493 1.1 kamil kcov_lock(kd); 494 1.1 kamil 495 1.1 kamil switch (cmd) { 496 1.1 kamil case KCOV_IOC_SETBUFSIZE: 497 1.13 maxv error = kcov_setbufsize(kd, addr); 498 1.1 kamil break; 499 1.1 kamil case KCOV_IOC_ENABLE: 500 1.13 maxv error = kcov_enable(kd, addr); 501 1.1 kamil break; 502 1.1 kamil case KCOV_IOC_DISABLE: 503 1.13 maxv error = kcov_disable(kd); 504 1.13 maxv break; 505 1.13 maxv case KCOV_IOC_REMOTE_ATTACH: 506 1.13 maxv error = kcov_remote_attach(kd, addr); 507 1.13 maxv break; 508 1.13 maxv case KCOV_IOC_REMOTE_DETACH: 509 1.13 maxv error = kcov_remote_detach(kd); 510 1.1 kamil break; 511 1.1 kamil default: 512 1.1 kamil error = EINVAL; 513 1.1 kamil } 514 1.1 kamil 515 1.1 kamil kcov_unlock(kd); 516 1.1 kamil return error; 517 1.1 kamil } 518 1.1 kamil 519 1.4 kamil static int 520 1.4 kamil kcov_fops_mmap(file_t *fp, off_t *offp, size_t size, int prot, int *flagsp, 521 1.4 kamil int *advicep, struct uvm_object **uobjp, int *maxprotp) 522 1.1 kamil { 523 1.4 kamil off_t off = *offp; 524 1.13 maxv kcov_t *kd, *kdbuf; 525 1.4 kamil int error = 0; 526 1.4 kamil 527 1.17 riastrad KASSERT(size > 0); 528 1.17 riastrad 529 1.4 kamil if (prot & PROT_EXEC) 530 1.4 kamil return EACCES; 531 1.4 kamil if (off < 0) 532 1.4 kamil return EINVAL; 533 1.4 kamil if (size > KCOV_BUF_MAX_ENTRIES * KCOV_ENTRY_SIZE) 534 1.4 kamil return EINVAL; 535 1.4 kamil if (off > KCOV_BUF_MAX_ENTRIES * KCOV_ENTRY_SIZE) 536 1.4 kamil return EINVAL; 537 1.1 kamil 538 1.4 kamil kd = fp->f_data; 539 1.4 kamil if (kd == NULL) 540 1.4 kamil return ENXIO; 541 1.4 kamil kcov_lock(kd); 542 1.1 kamil 543 1.13 maxv if (kd->remote != NULL) 544 1.13 maxv kdbuf = kd->remote; 545 1.13 maxv else 546 1.13 maxv kdbuf = kd; 547 1.13 maxv 548 1.13 maxv if ((size + off) > kdbuf->bufsize) { 549 1.4 kamil error = ENOMEM; 550 1.4 kamil goto out; 551 1.1 kamil } 552 1.1 kamil 553 1.13 maxv uao_reference(kdbuf->uobj); 554 1.4 kamil 555 1.13 maxv *uobjp = kdbuf->uobj; 556 1.4 kamil *maxprotp = prot; 557 1.4 kamil *advicep = UVM_ADV_RANDOM; 558 1.4 kamil 559 1.4 kamil out: 560 1.4 kamil kcov_unlock(kd); 561 1.4 kamil return error; 562 1.1 kamil } 563 1.1 kamil 564 1.12 maxv /* -------------------------------------------------------------------------- */ 565 1.12 maxv 566 1.12 maxv /* 567 1.12 maxv * Constraints on the functions here: they must be marked with __nomsan, and 568 1.12 maxv * must not make any external call. 569 1.12 maxv */ 570 1.12 maxv 571 1.9 maxv static inline bool __nomsan 572 1.1 kamil in_interrupt(void) 573 1.1 kamil { 574 1.11 kamil return curcpu()->ci_idepth >= 0; 575 1.1 kamil } 576 1.1 kamil 577 1.1 kamil void __sanitizer_cov_trace_pc(void); 578 1.1 kamil 579 1.9 maxv void __nomsan 580 1.1 kamil __sanitizer_cov_trace_pc(void) 581 1.1 kamil { 582 1.1 kamil uint64_t idx; 583 1.1 kamil kcov_t *kd; 584 1.1 kamil 585 1.1 kamil if (__predict_false(cold)) { 586 1.1 kamil /* Do not trace during boot. */ 587 1.1 kamil return; 588 1.1 kamil } 589 1.1 kamil 590 1.1 kamil if (in_interrupt()) { 591 1.1 kamil /* Do not trace in interrupts. */ 592 1.1 kamil return; 593 1.1 kamil } 594 1.1 kamil 595 1.12 maxv kd = curlwp->l_kcov; 596 1.1 kamil if (__predict_true(kd == NULL)) { 597 1.1 kamil /* Not traced. */ 598 1.1 kamil return; 599 1.1 kamil } 600 1.1 kamil 601 1.4 kamil if (!kd->enabled) { 602 1.4 kamil /* Tracing not enabled */ 603 1.4 kamil return; 604 1.4 kamil } 605 1.4 kamil 606 1.14 maxv if (__predict_false(kd->silenced)) { 607 1.14 maxv /* Silenced. */ 608 1.14 maxv return; 609 1.14 maxv } 610 1.14 maxv 611 1.5 kamil if (kd->mode != KCOV_MODE_TRACE_PC) { 612 1.5 kamil /* PC tracing mode not enabled */ 613 1.5 kamil return; 614 1.5 kamil } 615 1.13 maxv KASSERT(kd->remote == NULL); 616 1.5 kamil 617 1.8 kamil idx = kd->buf[0]; 618 1.1 kamil if (idx < kd->bufnent) { 619 1.8 kamil kd->buf[idx+1] = 620 1.8 kamil (intptr_t)__builtin_return_address(0); 621 1.8 kamil kd->buf[0] = idx + 1; 622 1.1 kamil } 623 1.1 kamil } 624 1.1 kamil 625 1.9 maxv static void __nomsan 626 1.5 kamil trace_cmp(uint64_t type, uint64_t arg1, uint64_t arg2, intptr_t pc) 627 1.5 kamil { 628 1.5 kamil uint64_t idx; 629 1.5 kamil kcov_t *kd; 630 1.5 kamil 631 1.5 kamil if (__predict_false(cold)) { 632 1.5 kamil /* Do not trace during boot. */ 633 1.5 kamil return; 634 1.5 kamil } 635 1.5 kamil 636 1.5 kamil if (in_interrupt()) { 637 1.5 kamil /* Do not trace in interrupts. */ 638 1.5 kamil return; 639 1.5 kamil } 640 1.5 kamil 641 1.12 maxv kd = curlwp->l_kcov; 642 1.5 kamil if (__predict_true(kd == NULL)) { 643 1.5 kamil /* Not traced. */ 644 1.5 kamil return; 645 1.5 kamil } 646 1.5 kamil 647 1.5 kamil if (!kd->enabled) { 648 1.5 kamil /* Tracing not enabled */ 649 1.5 kamil return; 650 1.5 kamil } 651 1.5 kamil 652 1.16 maxv if (__predict_false(kd->silenced)) { 653 1.16 maxv /* Silenced. */ 654 1.16 maxv return; 655 1.16 maxv } 656 1.16 maxv 657 1.5 kamil if (kd->mode != KCOV_MODE_TRACE_CMP) { 658 1.6 kamil /* CMP tracing mode not enabled */ 659 1.5 kamil return; 660 1.5 kamil } 661 1.16 maxv KASSERT(kd->remote == NULL); 662 1.5 kamil 663 1.8 kamil idx = kd->buf[0]; 664 1.5 kamil if ((idx * 4 + 4) <= kd->bufnent) { 665 1.8 kamil kd->buf[idx * 4 + 1] = type; 666 1.8 kamil kd->buf[idx * 4 + 2] = arg1; 667 1.8 kamil kd->buf[idx * 4 + 3] = arg2; 668 1.8 kamil kd->buf[idx * 4 + 4] = pc; 669 1.8 kamil kd->buf[0] = idx + 1; 670 1.5 kamil } 671 1.5 kamil } 672 1.5 kamil 673 1.5 kamil void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2); 674 1.5 kamil 675 1.9 maxv void __nomsan 676 1.5 kamil __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2) 677 1.5 kamil { 678 1.5 kamil 679 1.5 kamil trace_cmp(KCOV_CMP_SIZE(0), arg1, arg2, 680 1.5 kamil (intptr_t)__builtin_return_address(0)); 681 1.5 kamil } 682 1.5 kamil 683 1.5 kamil void __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2); 684 1.5 kamil 685 1.9 maxv void __nomsan 686 1.5 kamil __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2) 687 1.5 kamil { 688 1.5 kamil 689 1.5 kamil trace_cmp(KCOV_CMP_SIZE(1), arg1, arg2, 690 1.5 kamil (intptr_t)__builtin_return_address(0)); 691 1.5 kamil } 692 1.5 kamil 693 1.5 kamil void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2); 694 1.5 kamil 695 1.9 maxv void __nomsan 696 1.5 kamil __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2) 697 1.5 kamil { 698 1.5 kamil 699 1.5 kamil trace_cmp(KCOV_CMP_SIZE(2), arg1, arg2, 700 1.5 kamil (intptr_t)__builtin_return_address(0)); 701 1.5 kamil } 702 1.5 kamil 703 1.5 kamil void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2); 704 1.5 kamil 705 1.9 maxv void __nomsan 706 1.5 kamil __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) 707 1.5 kamil { 708 1.5 kamil 709 1.5 kamil trace_cmp(KCOV_CMP_SIZE(3), arg1, arg2, 710 1.5 kamil (intptr_t)__builtin_return_address(0)); 711 1.5 kamil } 712 1.5 kamil 713 1.5 kamil void __sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2); 714 1.5 kamil 715 1.9 maxv void __nomsan 716 1.5 kamil __sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2) 717 1.5 kamil { 718 1.5 kamil 719 1.5 kamil trace_cmp(KCOV_CMP_SIZE(0) | KCOV_CMP_CONST, arg1, arg2, 720 1.5 kamil (intptr_t)__builtin_return_address(0)); 721 1.5 kamil } 722 1.5 kamil 723 1.5 kamil void __sanitizer_cov_trace_const_cmp2(uint16_t arg1, uint16_t arg2); 724 1.5 kamil 725 1.9 maxv void __nomsan 726 1.5 kamil __sanitizer_cov_trace_const_cmp2(uint16_t arg1, uint16_t arg2) 727 1.5 kamil { 728 1.5 kamil 729 1.5 kamil trace_cmp(KCOV_CMP_SIZE(1) | KCOV_CMP_CONST, arg1, arg2, 730 1.5 kamil (intptr_t)__builtin_return_address(0)); 731 1.5 kamil } 732 1.5 kamil 733 1.5 kamil void __sanitizer_cov_trace_const_cmp4(uint32_t arg1, uint32_t arg2); 734 1.5 kamil 735 1.9 maxv void __nomsan 736 1.5 kamil __sanitizer_cov_trace_const_cmp4(uint32_t arg1, uint32_t arg2) 737 1.5 kamil { 738 1.5 kamil 739 1.5 kamil trace_cmp(KCOV_CMP_SIZE(2) | KCOV_CMP_CONST, arg1, arg2, 740 1.5 kamil (intptr_t)__builtin_return_address(0)); 741 1.5 kamil } 742 1.5 kamil 743 1.5 kamil void __sanitizer_cov_trace_const_cmp8(uint64_t arg1, uint64_t arg2); 744 1.5 kamil 745 1.9 maxv void __nomsan 746 1.5 kamil __sanitizer_cov_trace_const_cmp8(uint64_t arg1, uint64_t arg2) 747 1.5 kamil { 748 1.5 kamil 749 1.5 kamil trace_cmp(KCOV_CMP_SIZE(3) | KCOV_CMP_CONST, arg1, arg2, 750 1.5 kamil (intptr_t)__builtin_return_address(0)); 751 1.5 kamil } 752 1.5 kamil 753 1.5 kamil void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases); 754 1.5 kamil 755 1.9 maxv void __nomsan 756 1.5 kamil __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) 757 1.5 kamil { 758 1.5 kamil uint64_t i, nbits, ncases, type; 759 1.5 kamil intptr_t pc; 760 1.5 kamil 761 1.5 kamil pc = (intptr_t)__builtin_return_address(0); 762 1.5 kamil ncases = cases[0]; 763 1.5 kamil nbits = cases[1]; 764 1.5 kamil 765 1.5 kamil switch (nbits) { 766 1.5 kamil case 8: 767 1.5 kamil type = KCOV_CMP_SIZE(0); 768 1.5 kamil break; 769 1.5 kamil case 16: 770 1.5 kamil type = KCOV_CMP_SIZE(1); 771 1.5 kamil break; 772 1.5 kamil case 32: 773 1.5 kamil type = KCOV_CMP_SIZE(2); 774 1.5 kamil break; 775 1.5 kamil case 64: 776 1.5 kamil type = KCOV_CMP_SIZE(3); 777 1.5 kamil break; 778 1.5 kamil default: 779 1.5 kamil return; 780 1.5 kamil } 781 1.5 kamil type |= KCOV_CMP_CONST; 782 1.5 kamil 783 1.5 kamil for (i = 0; i < ncases; i++) 784 1.5 kamil trace_cmp(type, cases[i + 2], val, pc); 785 1.5 kamil } 786 1.5 kamil 787 1.1 kamil /* -------------------------------------------------------------------------- */ 788 1.1 kamil 789 1.7 kamil MODULE(MODULE_CLASS_MISC, kcov, NULL); 790 1.1 kamil 791 1.1 kamil static int 792 1.1 kamil kcov_modcmd(modcmd_t cmd, void *arg) 793 1.1 kamil { 794 1.1 kamil 795 1.1 kamil switch (cmd) { 796 1.1 kamil case MODULE_CMD_INIT: 797 1.1 kamil return 0; 798 1.1 kamil case MODULE_CMD_FINI: 799 1.1 kamil return EINVAL; 800 1.1 kamil default: 801 1.1 kamil return ENOTTY; 802 1.1 kamil } 803 1.1 kamil } 804