]> git.saurik.com Git - apple/xnu.git/blame - bsd/dev/dtrace/fbt.c
xnu-1504.15.3.tar.gz
[apple/xnu.git] / bsd / dev / dtrace / fbt.c
CommitLineData
2d21ac55
A
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
b0d623f7
A
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
2d21ac55
A
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/*
b0d623f7 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
2d21ac55
A
23 * Use is subject to license terms.
24 */
25
b0d623f7 26/* #pragma ident "@(#)fbt.c 1.18 07/01/10 SMI" */
2d21ac55
A
27
28#ifdef KERNEL
29#ifndef _KERNEL
30#define _KERNEL /* Solaris vs. Darwin */
31#endif
32#endif
33
34#include <mach-o/loader.h>
b0d623f7 35#include <libkern/kernel_mach_header.h>
2d21ac55
A
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> */
54struct savearea_t; /* Used anonymously */
55typedef kern_return_t (*perfCallback)(int, struct savearea_t *, int, int);
56
57#if defined (__ppc__) || defined (__ppc64__)
58extern perfCallback tempDTraceTrapHook, tempDTraceIntHook;
59extern kern_return_t fbt_perfCallback(int, struct savearea_t *, int, int);
60extern kern_return_t fbt_perfIntCallback(int, struct savearea_t *, int, int);
61#else
62extern perfCallback tempDTraceTrapHook;
63extern kern_return_t fbt_perfCallback(int, struct savearea_t *, int, int);
64#endif
65
66#define FBT_ADDR2NDX(addr) ((((uintptr_t)(addr)) >> 4) & fbt_probetab_mask)
67#define FBT_PROBETAB_SIZE 0x8000 /* 32k entries -- 128K total */
68
69static dev_info_t *fbt_devi;
70static int fbt_probetab_size;
71dtrace_provider_id_t fbt_id;
72fbt_probe_t **fbt_probetab;
73int fbt_probetab_mask;
74static int fbt_verbose = 0;
75
76void fbt_init( void );
77
78/*ARGSUSED*/
79static void
80fbt_destroy(void *arg, dtrace_id_t id, void *parg)
81{
82#pragma unused(arg,id)
83 fbt_probe_t *fbt = parg, *next, *hash, *last;
84 int ndx;
85
86 do {
87 /*
88 * Now we need to remove this probe from the fbt_probetab.
89 */
90 ndx = FBT_ADDR2NDX(fbt->fbtp_patchpoint);
91 last = NULL;
92 hash = fbt_probetab[ndx];
93
94 while (hash != fbt) {
95 ASSERT(hash != NULL);
96 last = hash;
97 hash = hash->fbtp_hashnext;
98 }
99
100 if (last != NULL) {
101 last->fbtp_hashnext = fbt->fbtp_hashnext;
102 } else {
103 fbt_probetab[ndx] = fbt->fbtp_hashnext;
104 }
105
106 next = fbt->fbtp_next;
107 kmem_free(fbt, sizeof (fbt_probe_t));
108
109 fbt = next;
110 } while (fbt != NULL);
111}
112
113/*ARGSUSED*/
114static void
115fbt_enable(void *arg, dtrace_id_t id, void *parg)
116{
117#pragma unused(arg,id)
118 fbt_probe_t *fbt = parg;
119 struct modctl *ctl = fbt->fbtp_ctl;
120
121#if defined (__ppc__) || defined (__ppc64__)
122 dtrace_casptr(&tempDTraceIntHook, NULL, fbt_perfIntCallback);
123 if (tempDTraceIntHook != (perfCallback)fbt_perfIntCallback) {
124 if (fbt_verbose) {
125 cmn_err(CE_NOTE, "fbt_enable is failing for probe %s "
126 "in module %s: tempDTraceIntHook already occupied.",
127 fbt->fbtp_name, ctl->mod_modname);
128 }
129 return;
130 }
131#endif
132
133 dtrace_casptr(&tempDTraceTrapHook, NULL, fbt_perfCallback);
134 if (tempDTraceTrapHook != (perfCallback)fbt_perfCallback) {
135 if (fbt_verbose) {
136 cmn_err(CE_NOTE, "fbt_enable is failing for probe %s "
137 "in module %s: tempDTraceTrapHook already occupied.",
138 fbt->fbtp_name, ctl->mod_modname);
139 }
140 return;
141 }
142
143 for (; fbt != NULL; fbt = fbt->fbtp_next)
144 (void)ml_nofault_copy( (vm_offset_t)&fbt->fbtp_patchval, (vm_offset_t)fbt->fbtp_patchpoint,
145 sizeof(fbt->fbtp_patchval));
146
147 dtrace_membar_consumer();
148}
149
150/*ARGSUSED*/
151static void
152fbt_disable(void *arg, dtrace_id_t id, void *parg)
153{
154#pragma unused(arg,id)
155 fbt_probe_t *fbt = parg;
156
157 for (; fbt != NULL; fbt = fbt->fbtp_next)
158 (void)ml_nofault_copy( (vm_offset_t)&fbt->fbtp_savedval, (vm_offset_t)fbt->fbtp_patchpoint,
159 sizeof(fbt->fbtp_savedval));
160
161 dtrace_membar_consumer();
162}
163
164/*ARGSUSED*/
165static void
166fbt_suspend(void *arg, dtrace_id_t id, void *parg)
167{
168#pragma unused(arg,id)
169 fbt_probe_t *fbt = parg;
170
171 for (; fbt != NULL; fbt = fbt->fbtp_next)
172 (void)ml_nofault_copy( (vm_offset_t)&fbt->fbtp_savedval, (vm_offset_t)fbt->fbtp_patchpoint,
173 sizeof(fbt->fbtp_savedval));
174
175 dtrace_membar_consumer();
176}
177
178/*ARGSUSED*/
179static void
180fbt_resume(void *arg, dtrace_id_t id, void *parg)
181{
182#pragma unused(arg,id)
183 fbt_probe_t *fbt = parg;
184 struct modctl *ctl = fbt->fbtp_ctl;
185
186#if defined (__ppc__) || defined (__ppc64__)
187 dtrace_casptr(&tempDTraceIntHook, NULL, fbt_perfIntCallback);
188 if (tempDTraceIntHook != (perfCallback)fbt_perfIntCallback) {
189 if (fbt_verbose) {
190 cmn_err(CE_NOTE, "fbt_enable is failing for probe %s "
191 "in module %s: tempDTraceIntHook already occupied.",
192 fbt->fbtp_name, ctl->mod_modname);
193 }
194 return;
195 }
196#endif
197
198 dtrace_casptr(&tempDTraceTrapHook, NULL, fbt_perfCallback);
199 if (tempDTraceTrapHook != (perfCallback)fbt_perfCallback) {
200 if (fbt_verbose) {
201 cmn_err(CE_NOTE, "fbt_resume is failing for probe %s "
202 "in module %s: tempDTraceTrapHook already occupied.",
203 fbt->fbtp_name, ctl->mod_modname);
204 }
205 return;
206 }
207
208 for (; fbt != NULL; fbt = fbt->fbtp_next)
209 (void)ml_nofault_copy( (vm_offset_t)&fbt->fbtp_patchval, (vm_offset_t)fbt->fbtp_patchpoint,
210 sizeof(fbt->fbtp_patchval));
211
212 dtrace_membar_consumer();
213}
214
215#if !defined(__APPLE__)
216/*ARGSUSED*/
217static void
218fbt_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
219{
220 fbt_probe_t *fbt = parg;
221 struct modctl *ctl = fbt->fbtp_ctl;
222 struct module *mp = ctl->mod_mp;
223 ctf_file_t *fp = NULL, *pfp;
224 ctf_funcinfo_t f;
225 int error;
226 ctf_id_t argv[32], type;
227 int argc = sizeof (argv) / sizeof (ctf_id_t);
228 const char *parent;
229
230 if (!ctl->mod_loaded || (ctl->mod_loadcnt != fbt->fbtp_loadcnt))
231 goto err;
232
233 if (fbt->fbtp_roffset != 0 && desc->dtargd_ndx == 0) {
234 (void) strlcpy(desc->dtargd_native, "int",
235 sizeof(desc->dtargd_native));
236 return;
237 }
238
239 if ((fp = ctf_modopen(mp, &error)) == NULL) {
240 /*
241 * We have no CTF information for this module -- and therefore
242 * no args[] information.
243 */
244 goto err;
245 }
246
247 /*
248 * If we have a parent container, we must manually import it.
249 */
250 if ((parent = ctf_parent_name(fp)) != NULL) {
b0d623f7
A
251 struct modctl *mp = &modules;
252 struct modctl *mod = NULL;
2d21ac55
A
253
254 /*
255 * We must iterate over all modules to find the module that
256 * is our parent.
257 */
b0d623f7
A
258 do {
259 if (strcmp(mp->mod_modname, parent) == 0) {
260 mod = mp;
2d21ac55 261 break;
b0d623f7
A
262 }
263 } while ((mp = mp->mod_next) != &modules);
2d21ac55
A
264
265 if (mod == NULL)
266 goto err;
267
b0d623f7 268 if ((pfp = ctf_modopen(mod->mod_mp, &error)) == NULL) {
2d21ac55 269 goto err;
b0d623f7 270 }
2d21ac55
A
271
272 if (ctf_import(fp, pfp) != 0) {
273 ctf_close(pfp);
274 goto err;
275 }
276
277 ctf_close(pfp);
278 }
279
280 if (ctf_func_info(fp, fbt->fbtp_symndx, &f) == CTF_ERR)
281 goto err;
282
283 if (fbt->fbtp_roffset != 0) {
284 if (desc->dtargd_ndx > 1)
285 goto err;
286
287 ASSERT(desc->dtargd_ndx == 1);
288 type = f.ctc_return;
289 } else {
290 if (desc->dtargd_ndx + 1 > f.ctc_argc)
291 goto err;
292
293 if (ctf_func_args(fp, fbt->fbtp_symndx, argc, argv) == CTF_ERR)
294 goto err;
295
296 type = argv[desc->dtargd_ndx];
297 }
298
299 if (ctf_type_name(fp, type, desc->dtargd_native,
300 DTRACE_ARGTYPELEN) != NULL) {
301 ctf_close(fp);
302 return;
303 }
304err:
305 if (fp != NULL)
306 ctf_close(fp);
307
308 desc->dtargd_ndx = DTRACE_ARGNONE;
309}
310#endif /* __APPLE__ */
311
312static dtrace_pattr_t fbt_attr = {
313{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
314{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
315{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
316{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
317{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
318};
319
320static dtrace_pops_t fbt_pops = {
321 NULL,
322 fbt_provide_module,
323 fbt_enable,
324 fbt_disable,
325 fbt_suspend,
326 fbt_resume,
327#if !defined(__APPLE__)
328 fbt_getargdesc,
329#else
b0d623f7 330 NULL, /* FIXME: where to look for xnu? */
2d21ac55
A
331#endif /* __APPLE__ */
332 NULL,
333 NULL,
334 fbt_destroy
335};
336
337static void
338fbt_cleanup(dev_info_t *devi)
339{
340 dtrace_invop_remove(fbt_invop);
341 ddi_remove_minor_node(devi, NULL);
342 kmem_free(fbt_probetab, fbt_probetab_size * sizeof (fbt_probe_t *));
343 fbt_probetab = NULL;
344 fbt_probetab_mask = 0;
345}
346
347static int
348fbt_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
349{
350 switch (cmd) {
351 case DDI_ATTACH:
352 break;
353 case DDI_RESUME:
354 return (DDI_SUCCESS);
355 default:
356 return (DDI_FAILURE);
357 }
358
359 if (fbt_probetab_size == 0)
360 fbt_probetab_size = FBT_PROBETAB_SIZE;
361
362 fbt_probetab_mask = fbt_probetab_size - 1;
363 fbt_probetab =
364 kmem_zalloc(fbt_probetab_size * sizeof (fbt_probe_t *), KM_SLEEP);
365
366 dtrace_invop_add(fbt_invop);
367
b0d623f7 368#if !defined(__APPLE__)
2d21ac55
A
369 if (ddi_create_minor_node(devi, "fbt", S_IFCHR, 0,
370 DDI_PSEUDO, NULL) == DDI_FAILURE ||
371 dtrace_register("fbt", &fbt_attr, DTRACE_PRIV_KERNEL, NULL,
372 &fbt_pops, NULL, &fbt_id) != 0) {
373 fbt_cleanup(devi);
374 return (DDI_FAILURE);
375 }
b0d623f7
A
376#else
377 if (ddi_create_minor_node(devi, "fbt", S_IFCHR, 0,
378 DDI_PSEUDO, 0) == DDI_FAILURE ||
379 dtrace_register("fbt", &fbt_attr, DTRACE_PRIV_KERNEL, NULL,
380 &fbt_pops, NULL, &fbt_id) != 0) {
381 fbt_cleanup(devi);
382 return (DDI_FAILURE);
383 }
384#endif /* __APPLE__ */
2d21ac55
A
385
386 ddi_report_dev(devi);
387 fbt_devi = devi;
388
389 return (DDI_SUCCESS);
390}
391
392static d_open_t _fbt_open;
393
394static int
395_fbt_open(dev_t dev, int flags, int devtype, struct proc *p)
396{
397#pragma unused(dev,flags,devtype,p)
398 return 0;
399}
400
401#define FBT_MAJOR -24 /* let the kernel pick the device number */
402
403/*
404 * A struct describing which functions will get invoked for certain
405 * actions.
406 */
407static struct cdevsw fbt_cdevsw =
408{
409 _fbt_open, /* open */
410 eno_opcl, /* close */
411 eno_rdwrt, /* read */
412 eno_rdwrt, /* write */
413 eno_ioctl, /* ioctl */
414 (stop_fcn_t *)nulldev, /* stop */
415 (reset_fcn_t *)nulldev, /* reset */
416 NULL, /* tty's */
417 eno_select, /* select */
418 eno_mmap, /* mmap */
419 eno_strat, /* strategy */
420 eno_getc, /* getc */
421 eno_putc, /* putc */
422 0 /* type */
423};
424
425static int gDisableFBT = 0;
426struct modctl g_fbt_kernctl;
427#undef kmem_alloc /* from its binding to dt_kmem_alloc glue */
428#undef kmem_free /* from its binding to dt_kmem_free glue */
429#include <vm/vm_kern.h>
430
431void
432fbt_init( void )
433{
434
593a1d5f 435 PE_parse_boot_argn("DisableFBT", &gDisableFBT, sizeof (gDisableFBT));
2d21ac55
A
436
437 if (0 == gDisableFBT)
438 {
439 int majdevno = cdevsw_add(FBT_MAJOR, &fbt_cdevsw);
b0d623f7 440 unsigned long size = 0, header_size, round_size;
2d21ac55
A
441 kern_return_t ret;
442 void *p, *q;
443
444 if (majdevno < 0) {
445 printf("fbt_init: failed to allocate a major number!\n");
446 return;
447 }
448
449 /*
450 * Capture the kernel's mach_header in its entirety and the contents of
451 * its LINKEDIT segment (and only that segment). This is sufficient to
452 * build all the fbt probes lazily the first time a client looks to
b0d623f7 453 * the fbt provider. Remeber these on the global struct modctl g_fbt_kernctl.
2d21ac55 454 */
b0d623f7 455 header_size = sizeof(kernel_mach_header_t) + _mh_execute_header.sizeofcmds;
2d21ac55
A
456 p = getsegdatafromheader(&_mh_execute_header, SEG_LINKEDIT, &size);
457
b0d623f7
A
458 round_size = round_page(header_size + size);
459 /* "q" will accomodate copied kernel_mach_header_t, its load commands, and LINKEIT segment. */
2d21ac55
A
460 ret = kmem_alloc_pageable(kernel_map, (vm_offset_t *)&q, round_size);
461
462 if (p && (ret == KERN_SUCCESS)) {
b0d623f7 463 kernel_segment_command_t *sgp;
2d21ac55
A
464
465 bcopy( (void *)&_mh_execute_header, q, header_size);
466 bcopy( p, (char *)q + header_size, size);
467
468 sgp = getsegbynamefromheader(q, SEG_LINKEDIT);
469
470 if (sgp) {
b0d623f7 471 sgp->vmaddr = (uintptr_t)((char *)q + header_size);
2d21ac55
A
472 g_fbt_kernctl.address = (vm_address_t)q;
473 g_fbt_kernctl.size = header_size + size;
474 } else {
475 kmem_free(kernel_map, (vm_offset_t)q, round_size);
476 g_fbt_kernctl.address = (vm_address_t)NULL;
477 g_fbt_kernctl.size = 0;
478 }
479 } else {
480 if (ret == KERN_SUCCESS)
481 kmem_free(kernel_map, (vm_offset_t)q, round_size);
482 g_fbt_kernctl.address = (vm_address_t)NULL;
483 g_fbt_kernctl.size = 0;
484 }
485
486 strncpy((char *)&(g_fbt_kernctl.mod_modname), "mach_kernel", KMOD_MAX_NAME);
b0d623f7 487 ((char *)&(g_fbt_kernctl.mod_modname))[KMOD_MAX_NAME -1] = '\0';
2d21ac55 488
b0d623f7 489 fbt_attach( (dev_info_t *)(uintptr_t)majdevno, DDI_ATTACH );
2d21ac55
A
490
491 gDisableFBT = 1; /* Ensure this initialization occurs just one time. */
492 }
493 else
494 printf("fbt_init: DisableFBT non-zero, no FBT probes will be provided.\n");
495}
496#undef FBT_MAJOR