]> git.saurik.com Git - apple/xnu.git/blob - bsd/dev/dtrace/fbt.c
xnu-2782.1.97.tar.gz
[apple/xnu.git] / bsd / dev / dtrace / fbt.c
1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /* #pragma ident "@(#)fbt.c 1.18 07/01/10 SMI" */
27
28 #ifdef KERNEL
29 #ifndef _KERNEL
30 #define _KERNEL /* Solaris vs. Darwin */
31 #endif
32 #endif
33
34 #include <mach-o/loader.h>
35 #include <libkern/kernel_mach_header.h>
36
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/errno.h>
40 #include <sys/stat.h>
41 #include <sys/ioctl.h>
42 #include <sys/conf.h>
43 #include <sys/fcntl.h>
44 #include <miscfs/devfs/devfs.h>
45 #include <pexpert/pexpert.h>
46
47 #include <sys/dtrace.h>
48 #include <sys/dtrace_impl.h>
49 #include <sys/fbt.h>
50
51 #include <sys/dtrace_glue.h>
52
53 /* #include <machine/trap.h> */
54 struct savearea_t; /* Used anonymously */
55
56 #if defined(__x86_64__)
57 typedef kern_return_t (*perfCallback)(int, struct savearea_t *, uintptr_t *, __unused int);
58 extern perfCallback tempDTraceTrapHook;
59 extern kern_return_t fbt_perfCallback(int, struct savearea_t *, uintptr_t *, __unused int);
60 #else
61 #error Unknown architecture
62 #endif
63
64 #define FBT_ADDR2NDX(addr) ((((uintptr_t)(addr)) >> 4) & fbt_probetab_mask)
65 #define FBT_PROBETAB_SIZE 0x8000 /* 32k entries -- 128K total */
66
67 static dev_info_t *fbt_devi;
68 static int fbt_probetab_size;
69 dtrace_provider_id_t fbt_id;
70 fbt_probe_t **fbt_probetab;
71 int fbt_probetab_mask;
72 static int fbt_verbose = 0;
73
74 void fbt_init( void );
75
76 /*ARGSUSED*/
77 static void
78 fbt_destroy(void *arg, dtrace_id_t id, void *parg)
79 {
80 #pragma unused(arg,id)
81 fbt_probe_t *fbt = parg, *next, *hash, *last;
82 int ndx;
83
84 do {
85 /*
86 * Now we need to remove this probe from the fbt_probetab.
87 */
88 ndx = FBT_ADDR2NDX(fbt->fbtp_patchpoint);
89 last = NULL;
90 hash = fbt_probetab[ndx];
91
92 while (hash != fbt) {
93 ASSERT(hash != NULL);
94 last = hash;
95 hash = hash->fbtp_hashnext;
96 }
97
98 if (last != NULL) {
99 last->fbtp_hashnext = fbt->fbtp_hashnext;
100 } else {
101 fbt_probetab[ndx] = fbt->fbtp_hashnext;
102 }
103
104 next = fbt->fbtp_next;
105 kmem_free(fbt, sizeof (fbt_probe_t));
106
107 fbt = next;
108 } while (fbt != NULL);
109 }
110
111 /*ARGSUSED*/
112 int
113 fbt_enable(void *arg, dtrace_id_t id, void *parg)
114 {
115 #pragma unused(arg,id)
116 fbt_probe_t *fbt = parg;
117 struct modctl *ctl = NULL;
118
119 for (; fbt != NULL; fbt = fbt->fbtp_next) {
120
121 ctl = fbt->fbtp_ctl;
122
123 if (!ctl->mod_loaded) {
124 if (fbt_verbose) {
125 cmn_err(CE_NOTE, "fbt is failing for probe %s "
126 "(module %s unloaded)",
127 fbt->fbtp_name, ctl->mod_modname);
128 }
129
130 continue;
131 }
132
133 /*
134 * Now check that our modctl has the expected load count. If it
135 * doesn't, this module must have been unloaded and reloaded -- and
136 * we're not going to touch it.
137 */
138 if (ctl->mod_loadcnt != fbt->fbtp_loadcnt) {
139 if (fbt_verbose) {
140 cmn_err(CE_NOTE, "fbt is failing for probe %s "
141 "(module %s reloaded)",
142 fbt->fbtp_name, ctl->mod_modname);
143 }
144
145 continue;
146 }
147
148 dtrace_casptr(&tempDTraceTrapHook, NULL, fbt_perfCallback);
149 if (tempDTraceTrapHook != (perfCallback)fbt_perfCallback) {
150 if (fbt_verbose) {
151 cmn_err(CE_NOTE, "fbt_enable is failing for probe %s "
152 "in module %s: tempDTraceTrapHook already occupied.",
153 fbt->fbtp_name, ctl->mod_modname);
154 }
155 continue;
156 }
157
158 if (fbt->fbtp_currentval != fbt->fbtp_patchval) {
159 (void)ml_nofault_copy( (vm_offset_t)&fbt->fbtp_patchval, (vm_offset_t)fbt->fbtp_patchpoint,
160 sizeof(fbt->fbtp_patchval));
161 fbt->fbtp_currentval = fbt->fbtp_patchval;
162 ctl->mod_nenabled++;
163 }
164
165 }
166
167 dtrace_membar_consumer();
168
169 return (0);
170 }
171
172 /*ARGSUSED*/
173 static void
174 fbt_disable(void *arg, dtrace_id_t id, void *parg)
175 {
176 #pragma unused(arg,id)
177 fbt_probe_t *fbt = parg;
178 struct modctl *ctl = NULL;
179
180 for (; fbt != NULL; fbt = fbt->fbtp_next) {
181 ctl = fbt->fbtp_ctl;
182
183 if (!ctl->mod_loaded || (ctl->mod_loadcnt != fbt->fbtp_loadcnt))
184 continue;
185
186 if (fbt->fbtp_currentval != fbt->fbtp_savedval) {
187 (void)ml_nofault_copy( (vm_offset_t)&fbt->fbtp_savedval, (vm_offset_t)fbt->fbtp_patchpoint,
188 sizeof(fbt->fbtp_savedval));
189 fbt->fbtp_currentval = fbt->fbtp_savedval;
190 ASSERT(ctl->mod_nenabled > 0);
191 ctl->mod_nenabled--;
192 }
193 }
194 dtrace_membar_consumer();
195 }
196
197 /*ARGSUSED*/
198 static void
199 fbt_suspend(void *arg, dtrace_id_t id, void *parg)
200 {
201 #pragma unused(arg,id)
202 fbt_probe_t *fbt = parg;
203 struct modctl *ctl = NULL;
204
205 for (; fbt != NULL; fbt = fbt->fbtp_next) {
206 ctl = fbt->fbtp_ctl;
207
208 ASSERT(ctl->mod_nenabled > 0);
209 if (!ctl->mod_loaded || (ctl->mod_loadcnt != fbt->fbtp_loadcnt))
210 continue;
211
212 (void)ml_nofault_copy( (vm_offset_t)&fbt->fbtp_savedval, (vm_offset_t)fbt->fbtp_patchpoint,
213 sizeof(fbt->fbtp_savedval));
214
215
216 fbt->fbtp_currentval = fbt->fbtp_savedval;
217 }
218
219 dtrace_membar_consumer();
220 }
221
222 /*ARGSUSED*/
223 static void
224 fbt_resume(void *arg, dtrace_id_t id, void *parg)
225 {
226 #pragma unused(arg,id)
227 fbt_probe_t *fbt = parg;
228 struct modctl *ctl = NULL;
229
230 for (; fbt != NULL; fbt = fbt->fbtp_next) {
231 ctl = fbt->fbtp_ctl;
232
233 ASSERT(ctl->mod_nenabled > 0);
234 if (!ctl->mod_loaded || (ctl->mod_loadcnt != fbt->fbtp_loadcnt))
235 continue;
236
237 dtrace_casptr(&tempDTraceTrapHook, NULL, fbt_perfCallback);
238 if (tempDTraceTrapHook != (perfCallback)fbt_perfCallback) {
239 if (fbt_verbose) {
240 cmn_err(CE_NOTE, "fbt_resume is failing for probe %s "
241 "in module %s: tempDTraceTrapHook already occupied.",
242 fbt->fbtp_name, ctl->mod_modname);
243 }
244 return;
245 }
246
247 (void)ml_nofault_copy( (vm_offset_t)&fbt->fbtp_patchval, (vm_offset_t)fbt->fbtp_patchpoint,
248 sizeof(fbt->fbtp_patchval));
249
250
251 fbt->fbtp_currentval = fbt->fbtp_patchval;
252 }
253
254 dtrace_membar_consumer();
255 }
256
257 /*
258 * APPLE NOTE: fbt_getargdesc not implemented
259 */
260 #if !defined(__APPLE__)
261 /*ARGSUSED*/
262 static void
263 fbt_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
264 {
265 fbt_probe_t *fbt = parg;
266 struct modctl *ctl = fbt->fbtp_ctl;
267 struct module *mp = ctl->mod_mp;
268 ctf_file_t *fp = NULL, *pfp;
269 ctf_funcinfo_t f;
270 int error;
271 ctf_id_t argv[32], type;
272 int argc = sizeof (argv) / sizeof (ctf_id_t);
273 const char *parent;
274
275 if (!ctl->mod_loaded || (ctl->mod_loadcnt != fbt->fbtp_loadcnt))
276 goto err;
277
278 if (fbt->fbtp_roffset != 0 && desc->dtargd_ndx == 0) {
279 (void) strlcpy(desc->dtargd_native, "int",
280 sizeof(desc->dtargd_native));
281 return;
282 }
283
284 if ((fp = ctf_modopen(mp, &error)) == NULL) {
285 /*
286 * We have no CTF information for this module -- and therefore
287 * no args[] information.
288 */
289 goto err;
290 }
291
292 /*
293 * If we have a parent container, we must manually import it.
294 */
295 if ((parent = ctf_parent_name(fp)) != NULL) {
296 struct modctl *mp = &modules;
297 struct modctl *mod = NULL;
298
299 /*
300 * We must iterate over all modules to find the module that
301 * is our parent.
302 */
303 do {
304 if (strcmp(mp->mod_modname, parent) == 0) {
305 mod = mp;
306 break;
307 }
308 } while ((mp = mp->mod_next) != &modules);
309
310 if (mod == NULL)
311 goto err;
312
313 if ((pfp = ctf_modopen(mod->mod_mp, &error)) == NULL) {
314 goto err;
315 }
316
317 if (ctf_import(fp, pfp) != 0) {
318 ctf_close(pfp);
319 goto err;
320 }
321
322 ctf_close(pfp);
323 }
324
325 if (ctf_func_info(fp, fbt->fbtp_symndx, &f) == CTF_ERR)
326 goto err;
327
328 if (fbt->fbtp_roffset != 0) {
329 if (desc->dtargd_ndx > 1)
330 goto err;
331
332 ASSERT(desc->dtargd_ndx == 1);
333 type = f.ctc_return;
334 } else {
335 if (desc->dtargd_ndx + 1 > f.ctc_argc)
336 goto err;
337
338 if (ctf_func_args(fp, fbt->fbtp_symndx, argc, argv) == CTF_ERR)
339 goto err;
340
341 type = argv[desc->dtargd_ndx];
342 }
343
344 if (ctf_type_name(fp, type, desc->dtargd_native,
345 DTRACE_ARGTYPELEN) != NULL) {
346 ctf_close(fp);
347 return;
348 }
349 err:
350 if (fp != NULL)
351 ctf_close(fp);
352
353 desc->dtargd_ndx = DTRACE_ARGNONE;
354 }
355 #endif /* __APPLE__ */
356
357 static dtrace_pattr_t fbt_attr = {
358 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
359 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
360 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
361 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
362 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
363 };
364
365 static dtrace_pops_t fbt_pops = {
366 NULL,
367 fbt_provide_module,
368 fbt_enable,
369 fbt_disable,
370 fbt_suspend,
371 fbt_resume,
372 NULL, /* APPLE NOTE: fbt_getargdesc not implemented */
373 NULL,
374 NULL,
375 fbt_destroy
376 };
377
378 static void
379 fbt_cleanup(dev_info_t *devi)
380 {
381 dtrace_invop_remove(fbt_invop);
382 ddi_remove_minor_node(devi, NULL);
383 kmem_free(fbt_probetab, fbt_probetab_size * sizeof (fbt_probe_t *));
384 fbt_probetab = NULL;
385 fbt_probetab_mask = 0;
386 }
387
388 static int
389 fbt_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
390 {
391 switch (cmd) {
392 case DDI_ATTACH:
393 break;
394 case DDI_RESUME:
395 return (DDI_SUCCESS);
396 default:
397 return (DDI_FAILURE);
398 }
399
400 if (fbt_probetab_size == 0)
401 fbt_probetab_size = FBT_PROBETAB_SIZE;
402
403 fbt_probetab_mask = fbt_probetab_size - 1;
404 fbt_probetab =
405 kmem_zalloc(fbt_probetab_size * sizeof (fbt_probe_t *), KM_SLEEP);
406
407 dtrace_invop_add(fbt_invop);
408
409 if (ddi_create_minor_node(devi, "fbt", S_IFCHR, 0,
410 DDI_PSEUDO, 0) == DDI_FAILURE ||
411 dtrace_register("fbt", &fbt_attr, DTRACE_PRIV_KERNEL, NULL,
412 &fbt_pops, NULL, &fbt_id) != 0) {
413 fbt_cleanup(devi);
414 return (DDI_FAILURE);
415 }
416
417 ddi_report_dev(devi);
418 fbt_devi = devi;
419
420 return (DDI_SUCCESS);
421 }
422
423 static d_open_t _fbt_open;
424
425 static int
426 _fbt_open(dev_t dev, int flags, int devtype, struct proc *p)
427 {
428 #pragma unused(dev,flags,devtype,p)
429 return 0;
430 }
431
432 #define FBT_MAJOR -24 /* let the kernel pick the device number */
433
434 /*
435 * A struct describing which functions will get invoked for certain
436 * actions.
437 */
438 static struct cdevsw fbt_cdevsw =
439 {
440 _fbt_open, /* open */
441 eno_opcl, /* close */
442 eno_rdwrt, /* read */
443 eno_rdwrt, /* write */
444 eno_ioctl, /* ioctl */
445 (stop_fcn_t *)nulldev, /* stop */
446 (reset_fcn_t *)nulldev, /* reset */
447 NULL, /* tty's */
448 eno_select, /* select */
449 eno_mmap, /* mmap */
450 eno_strat, /* strategy */
451 eno_getc, /* getc */
452 eno_putc, /* putc */
453 0 /* type */
454 };
455
456 int gIgnoreFBTBlacklist = 0;
457 static int gFBTInited = 0;
458 #undef kmem_alloc /* from its binding to dt_kmem_alloc glue */
459 #undef kmem_free /* from its binding to dt_kmem_free glue */
460 #include <vm/vm_kern.h>
461
462 void
463 fbt_init( void )
464 {
465 if (0 == gFBTInited)
466 {
467 int majdevno = cdevsw_add(FBT_MAJOR, &fbt_cdevsw);
468
469 if (majdevno < 0) {
470 printf("fbt_init: failed to allocate a major number!\n");
471 return;
472 }
473
474 PE_parse_boot_argn("IgnoreFBTBlacklist", &gIgnoreFBTBlacklist, sizeof (gIgnoreFBTBlacklist));
475
476 fbt_attach( (dev_info_t *)(uintptr_t)majdevno, DDI_ATTACH );
477
478 gFBTInited = 1; /* Ensure this initialization occurs just one time. */
479 }
480 else
481 panic("fbt_init: called twice!\n");
482 }
483 #undef FBT_MAJOR