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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 /* #pragma ident "@(#)fbt.c 1.18 07/01/10 SMI" */
30 #define _KERNEL /* Solaris vs. Darwin */
34 #include <mach-o/loader.h>
35 #include <libkern/kernel_mach_header.h>
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/errno.h>
41 #include <sys/ioctl.h>
43 #include <sys/fcntl.h>
44 #include <miscfs/devfs/devfs.h>
45 #include <pexpert/pexpert.h>
47 #include <sys/dtrace.h>
48 #include <sys/dtrace_impl.h>
51 #include <sys/dtrace_glue.h>
53 /* #include <machine/trap.h> */
54 struct savearea_t
; /* Used anonymously */
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);
61 #error Unknown architecture
64 #define FBT_ADDR2NDX(addr) ((((uintptr_t)(addr)) >> 4) & fbt_probetab_mask)
65 #define FBT_PROBETAB_SIZE 0x8000 /* 32k entries -- 128K total */
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;
74 void fbt_init( void );
78 fbt_destroy(void *arg
, dtrace_id_t id
, void *parg
)
80 #pragma unused(arg,id)
81 fbt_probe_t
*fbt
= parg
, *next
, *hash
, *last
;
86 * Now we need to remove this probe from the fbt_probetab.
88 ndx
= FBT_ADDR2NDX(fbt
->fbtp_patchpoint
);
90 hash
= fbt_probetab
[ndx
];
95 hash
= hash
->fbtp_hashnext
;
99 last
->fbtp_hashnext
= fbt
->fbtp_hashnext
;
101 fbt_probetab
[ndx
] = fbt
->fbtp_hashnext
;
104 next
= fbt
->fbtp_next
;
105 kmem_free(fbt
, sizeof (fbt_probe_t
));
108 } while (fbt
!= NULL
);
113 fbt_enable(void *arg
, dtrace_id_t id
, void *parg
)
115 #pragma unused(arg,id)
116 fbt_probe_t
*fbt
= parg
;
117 struct modctl
*ctl
= NULL
;
119 for (; fbt
!= NULL
; fbt
= fbt
->fbtp_next
) {
123 if (!ctl
->mod_loaded
) {
125 cmn_err(CE_NOTE
, "fbt is failing for probe %s "
126 "(module %s unloaded)",
127 fbt
->fbtp_name
, ctl
->mod_modname
);
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.
138 if (ctl
->mod_loadcnt
!= fbt
->fbtp_loadcnt
) {
140 cmn_err(CE_NOTE
, "fbt is failing for probe %s "
141 "(module %s reloaded)",
142 fbt
->fbtp_name
, ctl
->mod_modname
);
148 dtrace_casptr(&tempDTraceTrapHook
, NULL
, fbt_perfCallback
);
149 if (tempDTraceTrapHook
!= (perfCallback
)fbt_perfCallback
) {
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
);
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
));
162 * Make the patched instruction visible via a data + instruction
163 * cache flush for the platforms that need it
165 flush_dcache((vm_offset_t
)fbt
->fbtp_patchpoint
,(vm_size_t
)sizeof(fbt
->fbtp_patchval
), 0);
166 invalidate_icache((vm_offset_t
)fbt
->fbtp_patchpoint
,(vm_size_t
)sizeof(fbt
->fbtp_patchval
), 0);
167 fbt
->fbtp_currentval
= fbt
->fbtp_patchval
;
174 dtrace_membar_consumer();
181 fbt_disable(void *arg
, dtrace_id_t id
, void *parg
)
183 #pragma unused(arg,id)
184 fbt_probe_t
*fbt
= parg
;
185 struct modctl
*ctl
= NULL
;
187 for (; fbt
!= NULL
; fbt
= fbt
->fbtp_next
) {
190 if (!ctl
->mod_loaded
|| (ctl
->mod_loadcnt
!= fbt
->fbtp_loadcnt
))
193 if (fbt
->fbtp_currentval
!= fbt
->fbtp_savedval
) {
194 (void)ml_nofault_copy( (vm_offset_t
)&fbt
->fbtp_savedval
, (vm_offset_t
)fbt
->fbtp_patchpoint
,
195 sizeof(fbt
->fbtp_savedval
));
197 * Make the patched instruction visible via a data + instruction
198 * cache flush for the platforms that need it
200 flush_dcache((vm_offset_t
)fbt
->fbtp_patchpoint
,(vm_size_t
)sizeof(fbt
->fbtp_patchval
), 0);
201 invalidate_icache((vm_offset_t
)fbt
->fbtp_patchpoint
,(vm_size_t
)sizeof(fbt
->fbtp_patchval
), 0);
203 fbt
->fbtp_currentval
= fbt
->fbtp_savedval
;
204 ASSERT(ctl
->mod_nenabled
> 0);
208 dtrace_membar_consumer();
213 fbt_suspend(void *arg
, dtrace_id_t id
, void *parg
)
215 #pragma unused(arg,id)
216 fbt_probe_t
*fbt
= parg
;
217 struct modctl
*ctl
= NULL
;
219 for (; fbt
!= NULL
; fbt
= fbt
->fbtp_next
) {
222 ASSERT(ctl
->mod_nenabled
> 0);
223 if (!ctl
->mod_loaded
|| (ctl
->mod_loadcnt
!= fbt
->fbtp_loadcnt
))
226 (void)ml_nofault_copy( (vm_offset_t
)&fbt
->fbtp_savedval
, (vm_offset_t
)fbt
->fbtp_patchpoint
,
227 sizeof(fbt
->fbtp_savedval
));
230 * Make the patched instruction visible via a data + instruction
231 * cache flush for the platforms that need it
233 flush_dcache((vm_offset_t
)fbt
->fbtp_patchpoint
,(vm_size_t
)sizeof(fbt
->fbtp_savedval
), 0);
234 invalidate_icache((vm_offset_t
)fbt
->fbtp_patchpoint
,(vm_size_t
)sizeof(fbt
->fbtp_savedval
), 0);
236 fbt
->fbtp_currentval
= fbt
->fbtp_savedval
;
239 dtrace_membar_consumer();
244 fbt_resume(void *arg
, dtrace_id_t id
, void *parg
)
246 #pragma unused(arg,id)
247 fbt_probe_t
*fbt
= parg
;
248 struct modctl
*ctl
= NULL
;
250 for (; fbt
!= NULL
; fbt
= fbt
->fbtp_next
) {
253 ASSERT(ctl
->mod_nenabled
> 0);
254 if (!ctl
->mod_loaded
|| (ctl
->mod_loadcnt
!= fbt
->fbtp_loadcnt
))
257 dtrace_casptr(&tempDTraceTrapHook
, NULL
, fbt_perfCallback
);
258 if (tempDTraceTrapHook
!= (perfCallback
)fbt_perfCallback
) {
260 cmn_err(CE_NOTE
, "fbt_resume is failing for probe %s "
261 "in module %s: tempDTraceTrapHook already occupied.",
262 fbt
->fbtp_name
, ctl
->mod_modname
);
267 (void)ml_nofault_copy( (vm_offset_t
)&fbt
->fbtp_patchval
, (vm_offset_t
)fbt
->fbtp_patchpoint
,
268 sizeof(fbt
->fbtp_patchval
));
271 fbt
->fbtp_currentval
= fbt
->fbtp_patchval
;
274 dtrace_membar_consumer();
278 * APPLE NOTE: fbt_getargdesc not implemented
280 #if !defined(__APPLE__)
283 fbt_getargdesc(void *arg
, dtrace_id_t id
, void *parg
, dtrace_argdesc_t
*desc
)
285 fbt_probe_t
*fbt
= parg
;
286 struct modctl
*ctl
= fbt
->fbtp_ctl
;
287 struct module *mp
= ctl
->mod_mp
;
288 ctf_file_t
*fp
= NULL
, *pfp
;
291 ctf_id_t argv
[32], type
;
292 int argc
= sizeof (argv
) / sizeof (ctf_id_t
);
295 if (!ctl
->mod_loaded
|| (ctl
->mod_loadcnt
!= fbt
->fbtp_loadcnt
))
298 if (fbt
->fbtp_roffset
!= 0 && desc
->dtargd_ndx
== 0) {
299 (void) strlcpy(desc
->dtargd_native
, "int",
300 sizeof(desc
->dtargd_native
));
304 if ((fp
= ctf_modopen(mp
, &error
)) == NULL
) {
306 * We have no CTF information for this module -- and therefore
307 * no args[] information.
313 * If we have a parent container, we must manually import it.
315 if ((parent
= ctf_parent_name(fp
)) != NULL
) {
316 struct modctl
*mp
= &modules
;
317 struct modctl
*mod
= NULL
;
320 * We must iterate over all modules to find the module that
324 if (strcmp(mp
->mod_modname
, parent
) == 0) {
328 } while ((mp
= mp
->mod_next
) != &modules
);
333 if ((pfp
= ctf_modopen(mod
->mod_mp
, &error
)) == NULL
) {
337 if (ctf_import(fp
, pfp
) != 0) {
345 if (ctf_func_info(fp
, fbt
->fbtp_symndx
, &f
) == CTF_ERR
)
348 if (fbt
->fbtp_roffset
!= 0) {
349 if (desc
->dtargd_ndx
> 1)
352 ASSERT(desc
->dtargd_ndx
== 1);
355 if (desc
->dtargd_ndx
+ 1 > f
.ctc_argc
)
358 if (ctf_func_args(fp
, fbt
->fbtp_symndx
, argc
, argv
) == CTF_ERR
)
361 type
= argv
[desc
->dtargd_ndx
];
364 if (ctf_type_name(fp
, type
, desc
->dtargd_native
,
365 DTRACE_ARGTYPELEN
) != NULL
) {
373 desc
->dtargd_ndx
= DTRACE_ARGNONE
;
375 #endif /* __APPLE__ */
377 static dtrace_pattr_t fbt_attr
= {
378 { DTRACE_STABILITY_EVOLVING
, DTRACE_STABILITY_EVOLVING
, DTRACE_CLASS_ISA
},
379 { DTRACE_STABILITY_PRIVATE
, DTRACE_STABILITY_PRIVATE
, DTRACE_CLASS_UNKNOWN
},
380 { DTRACE_STABILITY_PRIVATE
, DTRACE_STABILITY_PRIVATE
, DTRACE_CLASS_UNKNOWN
},
381 { DTRACE_STABILITY_EVOLVING
, DTRACE_STABILITY_EVOLVING
, DTRACE_CLASS_ISA
},
382 { DTRACE_STABILITY_PRIVATE
, DTRACE_STABILITY_PRIVATE
, DTRACE_CLASS_ISA
},
385 static dtrace_pops_t fbt_pops
= {
392 NULL
, /* APPLE NOTE: fbt_getargdesc not implemented */
399 fbt_cleanup(dev_info_t
*devi
)
401 dtrace_invop_remove(fbt_invop
);
402 ddi_remove_minor_node(devi
, NULL
);
403 kmem_free(fbt_probetab
, fbt_probetab_size
* sizeof (fbt_probe_t
*));
405 fbt_probetab_mask
= 0;
409 fbt_attach(dev_info_t
*devi
, ddi_attach_cmd_t cmd
)
415 return (DDI_SUCCESS
);
417 return (DDI_FAILURE
);
420 if (fbt_probetab_size
== 0)
421 fbt_probetab_size
= FBT_PROBETAB_SIZE
;
423 fbt_probetab_mask
= fbt_probetab_size
- 1;
425 kmem_zalloc(fbt_probetab_size
* sizeof (fbt_probe_t
*), KM_SLEEP
);
427 dtrace_invop_add(fbt_invop
);
429 if (ddi_create_minor_node(devi
, "fbt", S_IFCHR
, 0,
430 DDI_PSEUDO
, 0) == DDI_FAILURE
||
431 dtrace_register("fbt", &fbt_attr
, DTRACE_PRIV_KERNEL
, NULL
,
432 &fbt_pops
, NULL
, &fbt_id
) != 0) {
434 return (DDI_FAILURE
);
437 ddi_report_dev(devi
);
440 return (DDI_SUCCESS
);
443 static d_open_t _fbt_open
;
446 _fbt_open(dev_t dev
, int flags
, int devtype
, struct proc
*p
)
448 #pragma unused(dev,flags,devtype,p)
452 #define FBT_MAJOR -24 /* let the kernel pick the device number */
455 * A struct describing which functions will get invoked for certain
458 static struct cdevsw fbt_cdevsw
=
460 _fbt_open
, /* open */
461 eno_opcl
, /* close */
462 eno_rdwrt
, /* read */
463 eno_rdwrt
, /* write */
464 eno_ioctl
, /* ioctl */
465 (stop_fcn_t
*)nulldev
, /* stop */
466 (reset_fcn_t
*)nulldev
, /* reset */
468 eno_select
, /* select */
470 eno_strat
, /* strategy */
476 int gIgnoreFBTBlacklist
= 0;
477 static int gFBTInited
= 0;
478 #undef kmem_alloc /* from its binding to dt_kmem_alloc glue */
479 #undef kmem_free /* from its binding to dt_kmem_free glue */
480 #include <vm/vm_kern.h>
487 int majdevno
= cdevsw_add(FBT_MAJOR
, &fbt_cdevsw
);
490 printf("fbt_init: failed to allocate a major number!\n");
494 PE_parse_boot_argn("IgnoreFBTBlacklist", &gIgnoreFBTBlacklist
, sizeof (gIgnoreFBTBlacklist
));
496 fbt_attach( (dev_info_t
*)(uintptr_t)majdevno
, DDI_ATTACH
);
498 gFBTInited
= 1; /* Ensure this initialization occurs just one time. */
501 panic("fbt_init: called twice!\n");