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