subr_kcov.c revision 1.15 1 1.15 hannken /* $NetBSD: subr_kcov.c,v 1.15 2020/05/16 17:42:06 hannken 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.4 kamil if (prot & PROT_EXEC)
528 1.4 kamil return EACCES;
529 1.4 kamil if (off < 0)
530 1.4 kamil return EINVAL;
531 1.4 kamil if (size > KCOV_BUF_MAX_ENTRIES * KCOV_ENTRY_SIZE)
532 1.4 kamil return EINVAL;
533 1.4 kamil if (off > KCOV_BUF_MAX_ENTRIES * KCOV_ENTRY_SIZE)
534 1.4 kamil return EINVAL;
535 1.1 kamil
536 1.4 kamil kd = fp->f_data;
537 1.4 kamil if (kd == NULL)
538 1.4 kamil return ENXIO;
539 1.4 kamil kcov_lock(kd);
540 1.1 kamil
541 1.13 maxv if (kd->remote != NULL)
542 1.13 maxv kdbuf = kd->remote;
543 1.13 maxv else
544 1.13 maxv kdbuf = kd;
545 1.13 maxv
546 1.13 maxv if ((size + off) > kdbuf->bufsize) {
547 1.4 kamil error = ENOMEM;
548 1.4 kamil goto out;
549 1.1 kamil }
550 1.1 kamil
551 1.13 maxv uao_reference(kdbuf->uobj);
552 1.4 kamil
553 1.13 maxv *uobjp = kdbuf->uobj;
554 1.4 kamil *maxprotp = prot;
555 1.4 kamil *advicep = UVM_ADV_RANDOM;
556 1.4 kamil
557 1.4 kamil out:
558 1.4 kamil kcov_unlock(kd);
559 1.4 kamil return error;
560 1.1 kamil }
561 1.1 kamil
562 1.12 maxv /* -------------------------------------------------------------------------- */
563 1.12 maxv
564 1.12 maxv /*
565 1.12 maxv * Constraints on the functions here: they must be marked with __nomsan, and
566 1.12 maxv * must not make any external call.
567 1.12 maxv */
568 1.12 maxv
569 1.9 maxv static inline bool __nomsan
570 1.1 kamil in_interrupt(void)
571 1.1 kamil {
572 1.11 kamil return curcpu()->ci_idepth >= 0;
573 1.1 kamil }
574 1.1 kamil
575 1.1 kamil void __sanitizer_cov_trace_pc(void);
576 1.1 kamil
577 1.9 maxv void __nomsan
578 1.1 kamil __sanitizer_cov_trace_pc(void)
579 1.1 kamil {
580 1.1 kamil extern int cold;
581 1.1 kamil uint64_t idx;
582 1.1 kamil kcov_t *kd;
583 1.1 kamil
584 1.1 kamil if (__predict_false(cold)) {
585 1.1 kamil /* Do not trace during boot. */
586 1.1 kamil return;
587 1.1 kamil }
588 1.1 kamil
589 1.1 kamil if (in_interrupt()) {
590 1.1 kamil /* Do not trace in interrupts. */
591 1.1 kamil return;
592 1.1 kamil }
593 1.1 kamil
594 1.12 maxv kd = curlwp->l_kcov;
595 1.1 kamil if (__predict_true(kd == NULL)) {
596 1.1 kamil /* Not traced. */
597 1.1 kamil return;
598 1.1 kamil }
599 1.1 kamil
600 1.4 kamil if (!kd->enabled) {
601 1.4 kamil /* Tracing not enabled */
602 1.4 kamil return;
603 1.4 kamil }
604 1.4 kamil
605 1.14 maxv if (__predict_false(kd->silenced)) {
606 1.14 maxv /* Silenced. */
607 1.14 maxv return;
608 1.14 maxv }
609 1.14 maxv
610 1.5 kamil if (kd->mode != KCOV_MODE_TRACE_PC) {
611 1.5 kamil /* PC tracing mode not enabled */
612 1.5 kamil return;
613 1.5 kamil }
614 1.13 maxv KASSERT(kd->remote == NULL);
615 1.5 kamil
616 1.8 kamil idx = kd->buf[0];
617 1.1 kamil if (idx < kd->bufnent) {
618 1.8 kamil kd->buf[idx+1] =
619 1.8 kamil (intptr_t)__builtin_return_address(0);
620 1.8 kamil kd->buf[0] = idx + 1;
621 1.1 kamil }
622 1.1 kamil }
623 1.1 kamil
624 1.9 maxv static void __nomsan
625 1.5 kamil trace_cmp(uint64_t type, uint64_t arg1, uint64_t arg2, intptr_t pc)
626 1.5 kamil {
627 1.5 kamil extern int cold;
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.5 kamil if (kd->mode != KCOV_MODE_TRACE_CMP) {
653 1.6 kamil /* CMP tracing mode not enabled */
654 1.5 kamil return;
655 1.5 kamil }
656 1.5 kamil
657 1.8 kamil idx = kd->buf[0];
658 1.5 kamil if ((idx * 4 + 4) <= kd->bufnent) {
659 1.8 kamil kd->buf[idx * 4 + 1] = type;
660 1.8 kamil kd->buf[idx * 4 + 2] = arg1;
661 1.8 kamil kd->buf[idx * 4 + 3] = arg2;
662 1.8 kamil kd->buf[idx * 4 + 4] = pc;
663 1.8 kamil kd->buf[0] = idx + 1;
664 1.5 kamil }
665 1.5 kamil }
666 1.5 kamil
667 1.5 kamil void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2);
668 1.5 kamil
669 1.9 maxv void __nomsan
670 1.5 kamil __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2)
671 1.5 kamil {
672 1.5 kamil
673 1.5 kamil trace_cmp(KCOV_CMP_SIZE(0), arg1, arg2,
674 1.5 kamil (intptr_t)__builtin_return_address(0));
675 1.5 kamil }
676 1.5 kamil
677 1.5 kamil void __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2);
678 1.5 kamil
679 1.9 maxv void __nomsan
680 1.5 kamil __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2)
681 1.5 kamil {
682 1.5 kamil
683 1.5 kamil trace_cmp(KCOV_CMP_SIZE(1), arg1, arg2,
684 1.5 kamil (intptr_t)__builtin_return_address(0));
685 1.5 kamil }
686 1.5 kamil
687 1.5 kamil void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2);
688 1.5 kamil
689 1.9 maxv void __nomsan
690 1.5 kamil __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2)
691 1.5 kamil {
692 1.5 kamil
693 1.5 kamil trace_cmp(KCOV_CMP_SIZE(2), arg1, arg2,
694 1.5 kamil (intptr_t)__builtin_return_address(0));
695 1.5 kamil }
696 1.5 kamil
697 1.5 kamil void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2);
698 1.5 kamil
699 1.9 maxv void __nomsan
700 1.5 kamil __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2)
701 1.5 kamil {
702 1.5 kamil
703 1.5 kamil trace_cmp(KCOV_CMP_SIZE(3), arg1, arg2,
704 1.5 kamil (intptr_t)__builtin_return_address(0));
705 1.5 kamil }
706 1.5 kamil
707 1.5 kamil void __sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2);
708 1.5 kamil
709 1.9 maxv void __nomsan
710 1.5 kamil __sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2)
711 1.5 kamil {
712 1.5 kamil
713 1.5 kamil trace_cmp(KCOV_CMP_SIZE(0) | KCOV_CMP_CONST, arg1, arg2,
714 1.5 kamil (intptr_t)__builtin_return_address(0));
715 1.5 kamil }
716 1.5 kamil
717 1.5 kamil void __sanitizer_cov_trace_const_cmp2(uint16_t arg1, uint16_t arg2);
718 1.5 kamil
719 1.9 maxv void __nomsan
720 1.5 kamil __sanitizer_cov_trace_const_cmp2(uint16_t arg1, uint16_t arg2)
721 1.5 kamil {
722 1.5 kamil
723 1.5 kamil trace_cmp(KCOV_CMP_SIZE(1) | KCOV_CMP_CONST, arg1, arg2,
724 1.5 kamil (intptr_t)__builtin_return_address(0));
725 1.5 kamil }
726 1.5 kamil
727 1.5 kamil void __sanitizer_cov_trace_const_cmp4(uint32_t arg1, uint32_t arg2);
728 1.5 kamil
729 1.9 maxv void __nomsan
730 1.5 kamil __sanitizer_cov_trace_const_cmp4(uint32_t arg1, uint32_t arg2)
731 1.5 kamil {
732 1.5 kamil
733 1.5 kamil trace_cmp(KCOV_CMP_SIZE(2) | KCOV_CMP_CONST, arg1, arg2,
734 1.5 kamil (intptr_t)__builtin_return_address(0));
735 1.5 kamil }
736 1.5 kamil
737 1.5 kamil void __sanitizer_cov_trace_const_cmp8(uint64_t arg1, uint64_t arg2);
738 1.5 kamil
739 1.9 maxv void __nomsan
740 1.5 kamil __sanitizer_cov_trace_const_cmp8(uint64_t arg1, uint64_t arg2)
741 1.5 kamil {
742 1.5 kamil
743 1.5 kamil trace_cmp(KCOV_CMP_SIZE(3) | KCOV_CMP_CONST, arg1, arg2,
744 1.5 kamil (intptr_t)__builtin_return_address(0));
745 1.5 kamil }
746 1.5 kamil
747 1.5 kamil void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases);
748 1.5 kamil
749 1.9 maxv void __nomsan
750 1.5 kamil __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases)
751 1.5 kamil {
752 1.5 kamil uint64_t i, nbits, ncases, type;
753 1.5 kamil intptr_t pc;
754 1.5 kamil
755 1.5 kamil pc = (intptr_t)__builtin_return_address(0);
756 1.5 kamil ncases = cases[0];
757 1.5 kamil nbits = cases[1];
758 1.5 kamil
759 1.5 kamil switch (nbits) {
760 1.5 kamil case 8:
761 1.5 kamil type = KCOV_CMP_SIZE(0);
762 1.5 kamil break;
763 1.5 kamil case 16:
764 1.5 kamil type = KCOV_CMP_SIZE(1);
765 1.5 kamil break;
766 1.5 kamil case 32:
767 1.5 kamil type = KCOV_CMP_SIZE(2);
768 1.5 kamil break;
769 1.5 kamil case 64:
770 1.5 kamil type = KCOV_CMP_SIZE(3);
771 1.5 kamil break;
772 1.5 kamil default:
773 1.5 kamil return;
774 1.5 kamil }
775 1.5 kamil type |= KCOV_CMP_CONST;
776 1.5 kamil
777 1.5 kamil for (i = 0; i < ncases; i++)
778 1.5 kamil trace_cmp(type, cases[i + 2], val, pc);
779 1.5 kamil }
780 1.5 kamil
781 1.1 kamil /* -------------------------------------------------------------------------- */
782 1.1 kamil
783 1.7 kamil MODULE(MODULE_CLASS_MISC, kcov, NULL);
784 1.1 kamil
785 1.1 kamil static int
786 1.1 kamil kcov_modcmd(modcmd_t cmd, void *arg)
787 1.1 kamil {
788 1.1 kamil
789 1.1 kamil switch (cmd) {
790 1.1 kamil case MODULE_CMD_INIT:
791 1.1 kamil return 0;
792 1.1 kamil case MODULE_CMD_FINI:
793 1.1 kamil return EINVAL;
794 1.1 kamil default:
795 1.1 kamil return ENOTTY;
796 1.1 kamil }
797 1.1 kamil }
798