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