subr_kcov.c revision 1.2 1 1.2 kamil /* $NetBSD: subr_kcov.c,v 1.2 2019/02/23 12:03:07 kamil Exp $ */
2 1.1 kamil
3 1.1 kamil /*
4 1.1 kamil * Copyright (c) 2019 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/atomic.h>
40 1.1 kamil #include <sys/conf.h>
41 1.1 kamil #include <sys/condvar.h>
42 1.1 kamil #include <sys/kmem.h>
43 1.1 kamil #include <sys/mutex.h>
44 1.1 kamil #include <sys/queue.h>
45 1.1 kamil
46 1.1 kamil #include <uvm/uvm_extern.h>
47 1.1 kamil #include <sys/kcov.h>
48 1.1 kamil
49 1.1 kamil #define KCOV_BUF_MAX_ENTRIES (256 << 10)
50 1.1 kamil
51 1.1 kamil /*
52 1.1 kamil * The KCOV descriptors are allocated during open(), and are associated with
53 1.1 kamil * the calling proc. They are freed lazily when their refcount reaches zero,
54 1.1 kamil * only when the process exits; this guarantees that kd->buf is not mmapped
55 1.1 kamil * in a currently running LWP. A KCOV descriptor is active on only one LWP
56 1.1 kamil * at the same time within the proc.
57 1.1 kamil *
58 1.1 kamil * In the refcount, one ref is for the proc, and one ref is for the LWP where
59 1.1 kamil * the descriptor is active. In each case, the descriptor is pointed to in
60 1.1 kamil * the proc's and LWP's specificdata.
61 1.1 kamil */
62 1.1 kamil
63 1.1 kamil typedef struct kcov_desc {
64 1.1 kamil kmutex_t lock;
65 1.1 kamil int refcnt;
66 1.1 kamil kcov_int_t *buf;
67 1.1 kamil size_t bufnent;
68 1.1 kamil size_t bufsize;
69 1.1 kamil TAILQ_ENTRY(kcov_desc) entry;
70 1.1 kamil } kcov_t;
71 1.1 kamil
72 1.1 kamil static specificdata_key_t kcov_proc_key;
73 1.1 kamil static specificdata_key_t kcov_lwp_key;
74 1.1 kamil
75 1.1 kamil static void
76 1.1 kamil kcov_lock(kcov_t *kd)
77 1.1 kamil {
78 1.1 kamil
79 1.1 kamil mutex_enter(&kd->lock);
80 1.1 kamil KASSERT(kd->refcnt > 0);
81 1.1 kamil }
82 1.1 kamil
83 1.1 kamil static void
84 1.1 kamil kcov_unlock(kcov_t *kd)
85 1.1 kamil {
86 1.1 kamil
87 1.1 kamil mutex_exit(&kd->lock);
88 1.1 kamil }
89 1.1 kamil
90 1.1 kamil static void
91 1.1 kamil kcov_lwp_take(kcov_t *kd)
92 1.1 kamil {
93 1.1 kamil
94 1.1 kamil kd->refcnt++;
95 1.1 kamil KASSERT(kd->refcnt == 2);
96 1.1 kamil lwp_setspecific(kcov_lwp_key, kd);
97 1.1 kamil }
98 1.1 kamil
99 1.1 kamil static void
100 1.1 kamil kcov_lwp_release(kcov_t *kd)
101 1.1 kamil {
102 1.1 kamil
103 1.1 kamil KASSERT(kd->refcnt == 2);
104 1.1 kamil kd->refcnt--;
105 1.1 kamil lwp_setspecific(kcov_lwp_key, NULL);
106 1.1 kamil }
107 1.1 kamil
108 1.1 kamil static inline bool
109 1.1 kamil kcov_is_owned(kcov_t *kd)
110 1.1 kamil {
111 1.1 kamil
112 1.1 kamil return (kd->refcnt > 1);
113 1.1 kamil }
114 1.1 kamil
115 1.1 kamil static void
116 1.1 kamil kcov_free(void *arg)
117 1.1 kamil {
118 1.1 kamil kcov_t *kd = (kcov_t *)arg;
119 1.1 kamil bool dofree;
120 1.1 kamil
121 1.1 kamil if (kd == NULL) {
122 1.1 kamil return;
123 1.1 kamil }
124 1.1 kamil
125 1.1 kamil kcov_lock(kd);
126 1.1 kamil kd->refcnt--;
127 1.1 kamil kcov_unlock(kd);
128 1.1 kamil dofree = (kd->refcnt == 0);
129 1.1 kamil
130 1.1 kamil if (!dofree) {
131 1.1 kamil return;
132 1.1 kamil }
133 1.1 kamil if (kd->buf != NULL) {
134 1.1 kamil uvm_km_free(kernel_map, (vaddr_t)kd->buf, kd->bufsize,
135 1.1 kamil UVM_KMF_WIRED);
136 1.1 kamil }
137 1.1 kamil mutex_destroy(&kd->lock);
138 1.1 kamil kmem_free(kd, sizeof(*kd));
139 1.1 kamil }
140 1.1 kamil
141 1.1 kamil static int
142 1.1 kamil kcov_allocbuf(kcov_t *kd, uint64_t nent)
143 1.1 kamil {
144 1.1 kamil size_t size;
145 1.1 kamil
146 1.1 kamil if (nent < 2 || nent > KCOV_BUF_MAX_ENTRIES)
147 1.1 kamil return EINVAL;
148 1.1 kamil if (kd->buf != NULL)
149 1.1 kamil return EEXIST;
150 1.1 kamil
151 1.1 kamil size = roundup(nent * KCOV_ENTRY_SIZE, PAGE_SIZE);
152 1.1 kamil kd->buf = (kcov_int_t *)uvm_km_alloc(kernel_map, size, 0,
153 1.1 kamil UVM_KMF_WIRED|UVM_KMF_ZERO);
154 1.1 kamil if (kd->buf == NULL)
155 1.1 kamil return ENOMEM;
156 1.1 kamil
157 1.1 kamil kd->bufnent = nent - 1;
158 1.1 kamil kd->bufsize = size;
159 1.1 kamil
160 1.1 kamil return 0;
161 1.1 kamil }
162 1.1 kamil
163 1.1 kamil /* -------------------------------------------------------------------------- */
164 1.1 kamil
165 1.1 kamil static int
166 1.1 kamil kcov_open(dev_t dev, int flag, int mode, struct lwp *l)
167 1.1 kamil {
168 1.1 kamil struct proc *p = l->l_proc;
169 1.1 kamil kcov_t *kd;
170 1.1 kamil
171 1.1 kamil kd = proc_getspecific(p, kcov_proc_key);
172 1.1 kamil if (kd != NULL)
173 1.1 kamil return EBUSY;
174 1.1 kamil
175 1.1 kamil kd = kmem_zalloc(sizeof(*kd), KM_SLEEP);
176 1.1 kamil mutex_init(&kd->lock, MUTEX_DEFAULT, IPL_NONE);
177 1.1 kamil kd->refcnt = 1;
178 1.1 kamil proc_setspecific(p, kcov_proc_key, kd);
179 1.1 kamil
180 1.1 kamil return 0;
181 1.1 kamil }
182 1.1 kamil
183 1.1 kamil static int
184 1.1 kamil kcov_close(dev_t dev, int flag, int mode, struct lwp *l)
185 1.1 kamil {
186 1.1 kamil
187 1.1 kamil return 0;
188 1.1 kamil }
189 1.1 kamil
190 1.1 kamil static int
191 1.1 kamil kcov_ioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l)
192 1.1 kamil {
193 1.1 kamil struct proc *p = l->l_proc;
194 1.1 kamil int error = 0;
195 1.1 kamil kcov_t *kd;
196 1.1 kamil
197 1.1 kamil kd = proc_getspecific(p, kcov_proc_key);
198 1.1 kamil if (kd == NULL)
199 1.1 kamil return ENXIO;
200 1.1 kamil kcov_lock(kd);
201 1.1 kamil
202 1.1 kamil switch (cmd) {
203 1.1 kamil case KCOV_IOC_SETBUFSIZE:
204 1.1 kamil if (kcov_is_owned(kd)) {
205 1.1 kamil error = EBUSY;
206 1.1 kamil break;
207 1.1 kamil }
208 1.1 kamil error = kcov_allocbuf(kd, *((uint64_t *)addr));
209 1.1 kamil break;
210 1.1 kamil case KCOV_IOC_ENABLE:
211 1.1 kamil if (kcov_is_owned(kd)) {
212 1.1 kamil error = EBUSY;
213 1.1 kamil break;
214 1.1 kamil }
215 1.1 kamil if (kd->buf == NULL) {
216 1.1 kamil error = ENOBUFS;
217 1.1 kamil break;
218 1.1 kamil }
219 1.1 kamil KASSERT(l == curlwp);
220 1.1 kamil kcov_lwp_take(kd);
221 1.1 kamil break;
222 1.1 kamil case KCOV_IOC_DISABLE:
223 1.1 kamil if (lwp_getspecific(kcov_lwp_key) == NULL) {
224 1.1 kamil error = ENOENT;
225 1.1 kamil break;
226 1.1 kamil }
227 1.1 kamil KASSERT(l == curlwp);
228 1.1 kamil kcov_lwp_release(kd);
229 1.1 kamil break;
230 1.1 kamil default:
231 1.1 kamil error = EINVAL;
232 1.1 kamil }
233 1.1 kamil
234 1.1 kamil kcov_unlock(kd);
235 1.1 kamil return error;
236 1.1 kamil }
237 1.1 kamil
238 1.1 kamil static paddr_t
239 1.1 kamil kcov_mmap(dev_t dev, off_t offset, int prot)
240 1.1 kamil {
241 1.1 kamil kcov_t *kd;
242 1.1 kamil paddr_t pa;
243 1.1 kamil vaddr_t va;
244 1.1 kamil
245 1.1 kamil kd = proc_getspecific(curproc, kcov_proc_key);
246 1.1 kamil KASSERT(kd != NULL);
247 1.1 kamil
248 1.1 kamil if ((offset < 0) || (offset >= kd->bufnent * KCOV_ENTRY_SIZE)) {
249 1.1 kamil return (paddr_t)-1;
250 1.1 kamil }
251 1.1 kamil if (offset & PAGE_MASK) {
252 1.1 kamil return (paddr_t)-1;
253 1.1 kamil }
254 1.1 kamil va = (vaddr_t)kd->buf + offset;
255 1.1 kamil if (!pmap_extract(pmap_kernel(), va, &pa)) {
256 1.1 kamil return (paddr_t)-1;
257 1.1 kamil }
258 1.1 kamil
259 1.1 kamil return atop(pa);
260 1.1 kamil }
261 1.1 kamil
262 1.1 kamil static inline bool
263 1.1 kamil in_interrupt(void)
264 1.1 kamil {
265 1.1 kamil return curcpu()->ci_idepth >= 0;
266 1.1 kamil }
267 1.1 kamil
268 1.1 kamil void __sanitizer_cov_trace_pc(void);
269 1.1 kamil
270 1.1 kamil void
271 1.1 kamil __sanitizer_cov_trace_pc(void)
272 1.1 kamil {
273 1.1 kamil extern int cold;
274 1.1 kamil uint64_t idx;
275 1.1 kamil kcov_t *kd;
276 1.1 kamil
277 1.1 kamil if (__predict_false(cold)) {
278 1.1 kamil /* Do not trace during boot. */
279 1.1 kamil return;
280 1.1 kamil }
281 1.1 kamil
282 1.1 kamil if (in_interrupt()) {
283 1.1 kamil /* Do not trace in interrupts. */
284 1.1 kamil return;
285 1.1 kamil }
286 1.1 kamil
287 1.1 kamil kd = lwp_getspecific(kcov_lwp_key);
288 1.1 kamil if (__predict_true(kd == NULL)) {
289 1.1 kamil /* Not traced. */
290 1.1 kamil return;
291 1.1 kamil }
292 1.1 kamil
293 1.1 kamil idx = kd->buf[0];
294 1.1 kamil if (idx < kd->bufnent) {
295 1.2 kamil kd->buf[idx+1] = (intptr_t)__builtin_return_address(0);
296 1.1 kamil kd->buf[0]++;
297 1.1 kamil }
298 1.1 kamil }
299 1.1 kamil
300 1.1 kamil /* -------------------------------------------------------------------------- */
301 1.1 kamil
302 1.1 kamil const struct cdevsw kcov_cdevsw = {
303 1.1 kamil .d_open = kcov_open,
304 1.1 kamil .d_close = kcov_close,
305 1.1 kamil .d_read = noread,
306 1.1 kamil .d_write = nowrite,
307 1.1 kamil .d_ioctl = kcov_ioctl,
308 1.1 kamil .d_stop = nostop,
309 1.1 kamil .d_tty = notty,
310 1.1 kamil .d_poll = nopoll,
311 1.1 kamil .d_mmap = kcov_mmap,
312 1.1 kamil .d_kqfilter = nokqfilter,
313 1.1 kamil .d_discard = nodiscard,
314 1.1 kamil .d_flag = D_OTHER | D_MPSAFE
315 1.1 kamil };
316 1.1 kamil
317 1.1 kamil MODULE(MODULE_CLASS_ANY, kcov, NULL);
318 1.1 kamil
319 1.1 kamil static void
320 1.1 kamil kcov_init(void)
321 1.1 kamil {
322 1.1 kamil
323 1.1 kamil proc_specific_key_create(&kcov_proc_key, kcov_free);
324 1.1 kamil lwp_specific_key_create(&kcov_lwp_key, kcov_free);
325 1.1 kamil }
326 1.1 kamil
327 1.1 kamil static int
328 1.1 kamil kcov_modcmd(modcmd_t cmd, void *arg)
329 1.1 kamil {
330 1.1 kamil
331 1.1 kamil switch (cmd) {
332 1.1 kamil case MODULE_CMD_INIT:
333 1.1 kamil kcov_init();
334 1.1 kamil return 0;
335 1.1 kamil case MODULE_CMD_FINI:
336 1.1 kamil return EINVAL;
337 1.1 kamil default:
338 1.1 kamil return ENOTTY;
339 1.1 kamil }
340 1.1 kamil }
341