]>
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 */ | |
6d2010ae | 55 | typedef kern_return_t (*perfCallback)(int, struct savearea_t *, uintptr_t *, int); |
2d21ac55 | 56 | |
2d21ac55 | 57 | extern perfCallback tempDTraceTrapHook; |
6d2010ae | 58 | extern kern_return_t fbt_perfCallback(int, struct savearea_t *, uintptr_t *); |
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)); |
6d2010ae A |
210 | fbt->fbtp_currentval = fbt->fbtp_savedval; |
211 | } | |
212 | ||
2d21ac55 A |
213 | dtrace_membar_consumer(); |
214 | } | |
215 | ||
216 | /*ARGSUSED*/ | |
217 | static void | |
218 | fbt_resume(void *arg, dtrace_id_t id, void *parg) | |
219 | { | |
220 | #pragma unused(arg,id) | |
221 | fbt_probe_t *fbt = parg; | |
6d2010ae | 222 | struct modctl *ctl = NULL; |
2d21ac55 | 223 | |
6d2010ae A |
224 | for (; fbt != NULL; fbt = fbt->fbtp_next) { |
225 | ctl = fbt->fbtp_ctl; | |
226 | ||
227 | ASSERT(ctl->mod_nenabled > 0); | |
228 | if (!ctl->mod_loaded || (ctl->mod_loadcnt != fbt->fbtp_loadcnt)) | |
229 | continue; | |
2d21ac55 | 230 | |
6d2010ae A |
231 | dtrace_casptr(&tempDTraceTrapHook, NULL, fbt_perfCallback); |
232 | if (tempDTraceTrapHook != (perfCallback)fbt_perfCallback) { | |
2d21ac55 A |
233 | if (fbt_verbose) { |
234 | cmn_err(CE_NOTE, "fbt_resume is failing for probe %s " | |
235 | "in module %s: tempDTraceTrapHook already occupied.", | |
236 | fbt->fbtp_name, ctl->mod_modname); | |
237 | } | |
238 | return; | |
6d2010ae | 239 | } |
2d21ac55 | 240 | |
6d2010ae | 241 | (void)ml_nofault_copy( (vm_offset_t)&fbt->fbtp_patchval, (vm_offset_t)fbt->fbtp_patchpoint, |
2d21ac55 | 242 | sizeof(fbt->fbtp_patchval)); |
6d2010ae A |
243 | fbt->fbtp_currentval = fbt->fbtp_patchval; |
244 | } | |
245 | ||
2d21ac55 A |
246 | dtrace_membar_consumer(); |
247 | } | |
248 | ||
249 | #if !defined(__APPLE__) | |
250 | /*ARGSUSED*/ | |
251 | static void | |
252 | fbt_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc) | |
253 | { | |
254 | fbt_probe_t *fbt = parg; | |
255 | struct modctl *ctl = fbt->fbtp_ctl; | |
256 | struct module *mp = ctl->mod_mp; | |
257 | ctf_file_t *fp = NULL, *pfp; | |
258 | ctf_funcinfo_t f; | |
259 | int error; | |
260 | ctf_id_t argv[32], type; | |
261 | int argc = sizeof (argv) / sizeof (ctf_id_t); | |
262 | const char *parent; | |
263 | ||
264 | if (!ctl->mod_loaded || (ctl->mod_loadcnt != fbt->fbtp_loadcnt)) | |
265 | goto err; | |
266 | ||
267 | if (fbt->fbtp_roffset != 0 && desc->dtargd_ndx == 0) { | |
268 | (void) strlcpy(desc->dtargd_native, "int", | |
269 | sizeof(desc->dtargd_native)); | |
270 | return; | |
271 | } | |
272 | ||
273 | if ((fp = ctf_modopen(mp, &error)) == NULL) { | |
274 | /* | |
275 | * We have no CTF information for this module -- and therefore | |
276 | * no args[] information. | |
277 | */ | |
278 | goto err; | |
279 | } | |
280 | ||
281 | /* | |
282 | * If we have a parent container, we must manually import it. | |
283 | */ | |
284 | if ((parent = ctf_parent_name(fp)) != NULL) { | |
b0d623f7 A |
285 | struct modctl *mp = &modules; |
286 | struct modctl *mod = NULL; | |
2d21ac55 A |
287 | |
288 | /* | |
289 | * We must iterate over all modules to find the module that | |
290 | * is our parent. | |
291 | */ | |
b0d623f7 A |
292 | do { |
293 | if (strcmp(mp->mod_modname, parent) == 0) { | |
294 | mod = mp; | |
2d21ac55 | 295 | break; |
b0d623f7 A |
296 | } |
297 | } while ((mp = mp->mod_next) != &modules); | |
2d21ac55 A |
298 | |
299 | if (mod == NULL) | |
300 | goto err; | |
301 | ||
b0d623f7 | 302 | if ((pfp = ctf_modopen(mod->mod_mp, &error)) == NULL) { |
2d21ac55 | 303 | goto err; |
b0d623f7 | 304 | } |
2d21ac55 A |
305 | |
306 | if (ctf_import(fp, pfp) != 0) { | |
307 | ctf_close(pfp); | |
308 | goto err; | |
309 | } | |
310 | ||
311 | ctf_close(pfp); | |
312 | } | |
313 | ||
314 | if (ctf_func_info(fp, fbt->fbtp_symndx, &f) == CTF_ERR) | |
315 | goto err; | |
316 | ||
317 | if (fbt->fbtp_roffset != 0) { | |
318 | if (desc->dtargd_ndx > 1) | |
319 | goto err; | |
320 | ||
321 | ASSERT(desc->dtargd_ndx == 1); | |
322 | type = f.ctc_return; | |
323 | } else { | |
324 | if (desc->dtargd_ndx + 1 > f.ctc_argc) | |
325 | goto err; | |
326 | ||
327 | if (ctf_func_args(fp, fbt->fbtp_symndx, argc, argv) == CTF_ERR) | |
328 | goto err; | |
329 | ||
330 | type = argv[desc->dtargd_ndx]; | |
331 | } | |
332 | ||
333 | if (ctf_type_name(fp, type, desc->dtargd_native, | |
334 | DTRACE_ARGTYPELEN) != NULL) { | |
335 | ctf_close(fp); | |
336 | return; | |
337 | } | |
338 | err: | |
339 | if (fp != NULL) | |
340 | ctf_close(fp); | |
341 | ||
342 | desc->dtargd_ndx = DTRACE_ARGNONE; | |
343 | } | |
344 | #endif /* __APPLE__ */ | |
345 | ||
346 | static dtrace_pattr_t fbt_attr = { | |
347 | { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA }, | |
348 | { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, | |
349 | { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, | |
350 | { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA }, | |
351 | { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA }, | |
352 | }; | |
353 | ||
354 | static dtrace_pops_t fbt_pops = { | |
355 | NULL, | |
356 | fbt_provide_module, | |
357 | fbt_enable, | |
358 | fbt_disable, | |
359 | fbt_suspend, | |
360 | fbt_resume, | |
361 | #if !defined(__APPLE__) | |
362 | fbt_getargdesc, | |
363 | #else | |
b0d623f7 | 364 | NULL, /* FIXME: where to look for xnu? */ |
2d21ac55 A |
365 | #endif /* __APPLE__ */ |
366 | NULL, | |
367 | NULL, | |
368 | fbt_destroy | |
369 | }; | |
370 | ||
371 | static void | |
372 | fbt_cleanup(dev_info_t *devi) | |
373 | { | |
374 | dtrace_invop_remove(fbt_invop); | |
375 | ddi_remove_minor_node(devi, NULL); | |
376 | kmem_free(fbt_probetab, fbt_probetab_size * sizeof (fbt_probe_t *)); | |
377 | fbt_probetab = NULL; | |
378 | fbt_probetab_mask = 0; | |
379 | } | |
380 | ||
381 | static int | |
382 | fbt_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) | |
383 | { | |
384 | switch (cmd) { | |
385 | case DDI_ATTACH: | |
386 | break; | |
387 | case DDI_RESUME: | |
388 | return (DDI_SUCCESS); | |
389 | default: | |
390 | return (DDI_FAILURE); | |
391 | } | |
392 | ||
393 | if (fbt_probetab_size == 0) | |
394 | fbt_probetab_size = FBT_PROBETAB_SIZE; | |
395 | ||
396 | fbt_probetab_mask = fbt_probetab_size - 1; | |
397 | fbt_probetab = | |
398 | kmem_zalloc(fbt_probetab_size * sizeof (fbt_probe_t *), KM_SLEEP); | |
399 | ||
400 | dtrace_invop_add(fbt_invop); | |
401 | ||
b0d623f7 | 402 | #if !defined(__APPLE__) |
2d21ac55 A |
403 | if (ddi_create_minor_node(devi, "fbt", S_IFCHR, 0, |
404 | DDI_PSEUDO, NULL) == DDI_FAILURE || | |
405 | dtrace_register("fbt", &fbt_attr, DTRACE_PRIV_KERNEL, NULL, | |
406 | &fbt_pops, NULL, &fbt_id) != 0) { | |
407 | fbt_cleanup(devi); | |
408 | return (DDI_FAILURE); | |
409 | } | |
b0d623f7 A |
410 | #else |
411 | if (ddi_create_minor_node(devi, "fbt", S_IFCHR, 0, | |
412 | DDI_PSEUDO, 0) == DDI_FAILURE || | |
413 | dtrace_register("fbt", &fbt_attr, DTRACE_PRIV_KERNEL, NULL, | |
414 | &fbt_pops, NULL, &fbt_id) != 0) { | |
415 | fbt_cleanup(devi); | |
416 | return (DDI_FAILURE); | |
417 | } | |
418 | #endif /* __APPLE__ */ | |
2d21ac55 A |
419 | |
420 | ddi_report_dev(devi); | |
421 | fbt_devi = devi; | |
422 | ||
423 | return (DDI_SUCCESS); | |
424 | } | |
425 | ||
426 | static d_open_t _fbt_open; | |
427 | ||
428 | static int | |
429 | _fbt_open(dev_t dev, int flags, int devtype, struct proc *p) | |
430 | { | |
431 | #pragma unused(dev,flags,devtype,p) | |
432 | return 0; | |
433 | } | |
434 | ||
435 | #define FBT_MAJOR -24 /* let the kernel pick the device number */ | |
436 | ||
437 | /* | |
438 | * A struct describing which functions will get invoked for certain | |
439 | * actions. | |
440 | */ | |
441 | static struct cdevsw fbt_cdevsw = | |
442 | { | |
443 | _fbt_open, /* open */ | |
444 | eno_opcl, /* close */ | |
445 | eno_rdwrt, /* read */ | |
446 | eno_rdwrt, /* write */ | |
447 | eno_ioctl, /* ioctl */ | |
448 | (stop_fcn_t *)nulldev, /* stop */ | |
449 | (reset_fcn_t *)nulldev, /* reset */ | |
450 | NULL, /* tty's */ | |
451 | eno_select, /* select */ | |
452 | eno_mmap, /* mmap */ | |
453 | eno_strat, /* strategy */ | |
454 | eno_getc, /* getc */ | |
455 | eno_putc, /* putc */ | |
456 | 0 /* type */ | |
457 | }; | |
458 | ||
6d2010ae A |
459 | int gIgnoreFBTBlacklist = 0; |
460 | static int gFBTInited = 0; | |
2d21ac55 A |
461 | #undef kmem_alloc /* from its binding to dt_kmem_alloc glue */ |
462 | #undef kmem_free /* from its binding to dt_kmem_free glue */ | |
463 | #include <vm/vm_kern.h> | |
464 | ||
465 | void | |
466 | fbt_init( void ) | |
467 | { | |
6d2010ae | 468 | if (0 == gFBTInited) |
2d21ac55 A |
469 | { |
470 | int majdevno = cdevsw_add(FBT_MAJOR, &fbt_cdevsw); | |
2d21ac55 A |
471 | |
472 | if (majdevno < 0) { | |
473 | printf("fbt_init: failed to allocate a major number!\n"); | |
474 | return; | |
475 | } | |
6d2010ae A |
476 | |
477 | PE_parse_boot_argn("IgnoreFBTBlacklist", &gIgnoreFBTBlacklist, sizeof (gIgnoreFBTBlacklist)); | |
2d21ac55 | 478 | |
b0d623f7 | 479 | fbt_attach( (dev_info_t *)(uintptr_t)majdevno, DDI_ATTACH ); |
6d2010ae A |
480 | |
481 | gFBTInited = 1; /* Ensure this initialization occurs just one time. */ | |
2d21ac55 A |
482 | } |
483 | else | |
6d2010ae | 484 | panic("fbt_init: called twice!\n"); |
2d21ac55 A |
485 | } |
486 | #undef FBT_MAJOR |