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
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.
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]
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* #pragma ident "@(#)fbt.c 1.15 05/09/19 SMI" */
31 #define _KERNEL /* Solaris vs. Darwin */
35 #include <mach-o/loader.h>
36 #include <kern/mach_header.h>
38 extern struct mach_header _mh_execute_header
; /* the kernel's mach header */
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/errno.h>
44 #include <sys/ioctl.h>
46 #include <sys/fcntl.h>
47 #include <miscfs/devfs/devfs.h>
48 #include <pexpert/pexpert.h>
50 #include <sys/dtrace.h>
51 #include <sys/dtrace_impl.h>
54 #include <sys/dtrace_glue.h>
56 /* #include <machine/trap.h> */
57 struct savearea_t
; /* Used anonymously */
58 typedef kern_return_t (*perfCallback
)(int, struct savearea_t
*, int, int);
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);
65 extern perfCallback tempDTraceTrapHook
;
66 extern kern_return_t
fbt_perfCallback(int, struct savearea_t
*, int, int);
69 #define FBT_ADDR2NDX(addr) ((((uintptr_t)(addr)) >> 4) & fbt_probetab_mask)
70 #define FBT_PROBETAB_SIZE 0x8000 /* 32k entries -- 128K total */
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;
79 void fbt_init( void );
83 fbt_destroy(void *arg
, dtrace_id_t id
, void *parg
)
85 #pragma unused(arg,id)
86 fbt_probe_t
*fbt
= parg
, *next
, *hash
, *last
;
91 * Now we need to remove this probe from the fbt_probetab.
93 ndx
= FBT_ADDR2NDX(fbt
->fbtp_patchpoint
);
95 hash
= fbt_probetab
[ndx
];
100 hash
= hash
->fbtp_hashnext
;
104 last
->fbtp_hashnext
= fbt
->fbtp_hashnext
;
106 fbt_probetab
[ndx
] = fbt
->fbtp_hashnext
;
109 next
= fbt
->fbtp_next
;
110 kmem_free(fbt
, sizeof (fbt_probe_t
));
113 } while (fbt
!= NULL
);
118 fbt_enable(void *arg
, dtrace_id_t id
, void *parg
)
120 #pragma unused(arg,id)
121 fbt_probe_t
*fbt
= parg
;
122 struct modctl
*ctl
= fbt
->fbtp_ctl
;
124 #if defined (__ppc__) || defined (__ppc64__)
125 dtrace_casptr(&tempDTraceIntHook
, NULL
, fbt_perfIntCallback
);
126 if (tempDTraceIntHook
!= (perfCallback
)fbt_perfIntCallback
) {
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
);
136 dtrace_casptr(&tempDTraceTrapHook
, NULL
, fbt_perfCallback
);
137 if (tempDTraceTrapHook
!= (perfCallback
)fbt_perfCallback
) {
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
);
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
));
150 dtrace_membar_consumer();
155 fbt_disable(void *arg
, dtrace_id_t id
, void *parg
)
157 #pragma unused(arg,id)
158 fbt_probe_t
*fbt
= parg
;
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
));
164 dtrace_membar_consumer();
169 fbt_suspend(void *arg
, dtrace_id_t id
, void *parg
)
171 #pragma unused(arg,id)
172 fbt_probe_t
*fbt
= parg
;
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
));
178 dtrace_membar_consumer();
183 fbt_resume(void *arg
, dtrace_id_t id
, void *parg
)
185 #pragma unused(arg,id)
186 fbt_probe_t
*fbt
= parg
;
187 struct modctl
*ctl
= fbt
->fbtp_ctl
;
189 #if defined (__ppc__) || defined (__ppc64__)
190 dtrace_casptr(&tempDTraceIntHook
, NULL
, fbt_perfIntCallback
);
191 if (tempDTraceIntHook
!= (perfCallback
)fbt_perfIntCallback
) {
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
);
201 dtrace_casptr(&tempDTraceTrapHook
, NULL
, fbt_perfCallback
);
202 if (tempDTraceTrapHook
!= (perfCallback
)fbt_perfCallback
) {
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
);
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
));
215 dtrace_membar_consumer();
218 #if !defined(__APPLE__)
221 fbt_getargdesc(void *arg
, dtrace_id_t id
, void *parg
, dtrace_argdesc_t
*desc
)
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
;
229 ctf_id_t argv
[32], type
;
230 int argc
= sizeof (argv
) / sizeof (ctf_id_t
);
233 if (!ctl
->mod_loaded
|| (ctl
->mod_loadcnt
!= fbt
->fbtp_loadcnt
))
236 if (fbt
->fbtp_roffset
!= 0 && desc
->dtargd_ndx
== 0) {
237 (void) strlcpy(desc
->dtargd_native
, "int",
238 sizeof(desc
->dtargd_native
));
242 if ((fp
= ctf_modopen(mp
, &error
)) == NULL
) {
244 * We have no CTF information for this module -- and therefore
245 * no args[] information.
251 * If we have a parent container, we must manually import it.
253 if ((parent
= ctf_parent_name(fp
)) != NULL
) {
257 * We must iterate over all modules to find the module that
260 for (mod
= &modules
; mod
!= NULL
; mod
= mod
->mod_next
) {
261 if (strcmp(mod
->mod_filename
, parent
) == 0)
268 if ((pfp
= ctf_modopen(mod
->mod_mp
, &error
)) == NULL
)
271 if (ctf_import(fp
, pfp
) != 0) {
279 if (ctf_func_info(fp
, fbt
->fbtp_symndx
, &f
) == CTF_ERR
)
282 if (fbt
->fbtp_roffset
!= 0) {
283 if (desc
->dtargd_ndx
> 1)
286 ASSERT(desc
->dtargd_ndx
== 1);
289 if (desc
->dtargd_ndx
+ 1 > f
.ctc_argc
)
292 if (ctf_func_args(fp
, fbt
->fbtp_symndx
, argc
, argv
) == CTF_ERR
)
295 type
= argv
[desc
->dtargd_ndx
];
298 if (ctf_type_name(fp
, type
, desc
->dtargd_native
,
299 DTRACE_ARGTYPELEN
) != NULL
) {
307 desc
->dtargd_ndx
= DTRACE_ARGNONE
;
309 #endif /* __APPLE__ */
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
},
319 static dtrace_pops_t fbt_pops
= {
326 #if !defined(__APPLE__)
329 NULL
, /* XXX where to look for xnu? */
330 #endif /* __APPLE__ */
337 fbt_cleanup(dev_info_t
*devi
)
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
*));
343 fbt_probetab_mask
= 0;
347 fbt_attach(dev_info_t
*devi
, ddi_attach_cmd_t cmd
)
353 return (DDI_SUCCESS
);
355 return (DDI_FAILURE
);
358 if (fbt_probetab_size
== 0)
359 fbt_probetab_size
= FBT_PROBETAB_SIZE
;
361 fbt_probetab_mask
= fbt_probetab_size
- 1;
363 kmem_zalloc(fbt_probetab_size
* sizeof (fbt_probe_t
*), KM_SLEEP
);
365 dtrace_invop_add(fbt_invop
);
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) {
372 return (DDI_FAILURE
);
375 ddi_report_dev(devi
);
378 return (DDI_SUCCESS
);
381 static d_open_t _fbt_open
;
384 _fbt_open(dev_t dev
, int flags
, int devtype
, struct proc
*p
)
386 #pragma unused(dev,flags,devtype,p)
390 #define FBT_MAJOR -24 /* let the kernel pick the device number */
393 * A struct describing which functions will get invoked for certain
396 static struct cdevsw fbt_cdevsw
=
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 */
406 eno_select
, /* select */
408 eno_strat
, /* strategy */
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>
424 PE_parse_boot_argn("DisableFBT", &gDisableFBT
, sizeof (gDisableFBT
));
426 if (0 == gDisableFBT
)
428 int majdevno
= cdevsw_add(FBT_MAJOR
, &fbt_cdevsw
);
429 int size
= 0, header_size
, round_size
;
434 printf("fbt_init: failed to allocate a major number!\n");
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.
444 header_size
= sizeof(struct mach_header
) + _mh_execute_header
.sizeofcmds
;
445 p
= getsegdatafromheader(&_mh_execute_header
, SEG_LINKEDIT
, &size
);
447 round_size
= round_page_32(header_size
+ size
);
448 ret
= kmem_alloc_pageable(kernel_map
, (vm_offset_t
*)&q
, round_size
);
450 if (p
&& (ret
== KERN_SUCCESS
)) {
451 struct segment_command
*sgp
;
453 bcopy( (void *)&_mh_execute_header
, q
, header_size
);
454 bcopy( p
, (char *)q
+ header_size
, size
);
456 sgp
= getsegbynamefromheader(q
, SEG_LINKEDIT
);
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
;
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;
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;
474 strncpy((char *)&(g_fbt_kernctl
.mod_modname
), "mach_kernel", KMOD_MAX_NAME
);
476 fbt_attach( (dev_info_t
*)majdevno
, DDI_ATTACH
);
478 gDisableFBT
= 1; /* Ensure this initialization occurs just one time. */
481 printf("fbt_init: DisableFBT non-zero, no FBT probes will be provided.\n");