]>
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 | ||
5ba3f43e | 26 | #include <mach-o/loader.h> |
b0d623f7 | 27 | #include <libkern/kernel_mach_header.h> |
2d21ac55 A |
28 | |
29 | #include <sys/param.h> | |
30 | #include <sys/systm.h> | |
5ba3f43e | 31 | #include <sys/sysctl.h> |
2d21ac55 A |
32 | #include <sys/errno.h> |
33 | #include <sys/stat.h> | |
34 | #include <sys/ioctl.h> | |
35 | #include <sys/conf.h> | |
36 | #include <sys/fcntl.h> | |
37 | #include <miscfs/devfs/devfs.h> | |
38 | #include <pexpert/pexpert.h> | |
39 | ||
40 | #include <sys/dtrace.h> | |
41 | #include <sys/dtrace_impl.h> | |
42 | #include <sys/fbt.h> | |
43 | ||
44 | #include <sys/dtrace_glue.h> | |
a39ff7e2 | 45 | #include <san/kasan.h> |
2d21ac55 | 46 | |
f427ee49 A |
47 | #include <ptrauth.h> |
48 | ||
2d21ac55 A |
49 | /* #include <machine/trap.h> */ |
50 | struct savearea_t; /* Used anonymously */ | |
2d21ac55 | 51 | |
5ba3f43e A |
52 | #if defined(__arm__) || defined(__arm64__) |
53 | typedef kern_return_t (*perfCallback)(int, struct savearea_t *, __unused int, __unused int); | |
54 | extern perfCallback tempDTraceTrapHook; | |
55 | extern kern_return_t fbt_perfCallback(int, struct savearea_t *, __unused int, __unused int); | |
56 | #elif defined(__x86_64__) | |
39236c6e | 57 | typedef kern_return_t (*perfCallback)(int, struct savearea_t *, uintptr_t *, __unused int); |
2d21ac55 | 58 | extern perfCallback tempDTraceTrapHook; |
39236c6e | 59 | extern kern_return_t fbt_perfCallback(int, struct savearea_t *, uintptr_t *, __unused int); |
fe8ab488 A |
60 | #else |
61 | #error Unknown architecture | |
62 | #endif | |
2d21ac55 | 63 | |
d9a64523 A |
64 | __private_extern__ |
65 | void | |
66 | qsort(void *a, size_t n, size_t es, int (*cmp)(const void *, const void *)); | |
67 | ||
0a7de745 A |
68 | #define FBT_ADDR2NDX(addr) ((((uintptr_t)(addr)) >> 4) & fbt_probetab_mask) |
69 | #define FBT_PROBETAB_SIZE 0x8000 /* 32k entries -- 128K total */ | |
2d21ac55 | 70 | |
0a7de745 A |
71 | static int fbt_probetab_size; |
72 | dtrace_provider_id_t fbt_id; | |
73 | fbt_probe_t **fbt_probetab; | |
74 | int fbt_probetab_mask; | |
75 | static int fbt_verbose = 0; | |
2d21ac55 | 76 | |
cb323159 | 77 | extern int ignore_fbt_blacklist; |
5ba3f43e A |
78 | |
79 | extern int dtrace_kernel_symbol_mode; | |
80 | ||
81 | ||
2d21ac55 A |
82 | void fbt_init( void ); |
83 | ||
84 | /*ARGSUSED*/ | |
85 | static void | |
86 | fbt_destroy(void *arg, dtrace_id_t id, void *parg) | |
87 | { | |
88 | #pragma unused(arg,id) | |
89 | fbt_probe_t *fbt = parg, *next, *hash, *last; | |
90 | int ndx; | |
91 | ||
92 | do { | |
93 | /* | |
94 | * Now we need to remove this probe from the fbt_probetab. | |
95 | */ | |
96 | ndx = FBT_ADDR2NDX(fbt->fbtp_patchpoint); | |
97 | last = NULL; | |
98 | hash = fbt_probetab[ndx]; | |
99 | ||
100 | while (hash != fbt) { | |
101 | ASSERT(hash != NULL); | |
102 | last = hash; | |
103 | hash = hash->fbtp_hashnext; | |
104 | } | |
105 | ||
106 | if (last != NULL) { | |
107 | last->fbtp_hashnext = fbt->fbtp_hashnext; | |
108 | } else { | |
109 | fbt_probetab[ndx] = fbt->fbtp_hashnext; | |
110 | } | |
111 | ||
112 | next = fbt->fbtp_next; | |
0a7de745 | 113 | kmem_free(fbt, sizeof(fbt_probe_t)); |
2d21ac55 A |
114 | |
115 | fbt = next; | |
116 | } while (fbt != NULL); | |
117 | } | |
118 | ||
119 | /*ARGSUSED*/ | |
6d2010ae | 120 | int |
2d21ac55 A |
121 | fbt_enable(void *arg, dtrace_id_t id, void *parg) |
122 | { | |
123 | #pragma unused(arg,id) | |
124 | fbt_probe_t *fbt = parg; | |
6d2010ae A |
125 | struct modctl *ctl = NULL; |
126 | ||
0a7de745 A |
127 | for (; fbt != NULL; fbt = fbt->fbtp_next) { |
128 | ctl = fbt->fbtp_ctl; | |
2d21ac55 | 129 | |
0a7de745 A |
130 | if (!ctl->mod_loaded) { |
131 | if (fbt_verbose) { | |
132 | cmn_err(CE_NOTE, "fbt is failing for probe %s " | |
133 | "(module %s unloaded)", | |
134 | fbt->fbtp_name, ctl->mod_modname); | |
135 | } | |
d9a64523 | 136 | |
0a7de745 | 137 | continue; |
2d21ac55 | 138 | } |
6d2010ae | 139 | |
0a7de745 A |
140 | /* |
141 | * Now check that our modctl has the expected load count. If it | |
142 | * doesn't, this module must have been unloaded and reloaded -- and | |
143 | * we're not going to touch it. | |
144 | */ | |
145 | if (ctl->mod_loadcnt != fbt->fbtp_loadcnt) { | |
146 | if (fbt_verbose) { | |
147 | cmn_err(CE_NOTE, "fbt is failing for probe %s " | |
148 | "(module %s reloaded)", | |
149 | fbt->fbtp_name, ctl->mod_modname); | |
150 | } | |
6d2010ae | 151 | |
0a7de745 | 152 | continue; |
6d2010ae A |
153 | } |
154 | ||
f427ee49 | 155 | dtrace_casptr(&tempDTraceTrapHook, NULL, ptrauth_nop_cast(void *, &fbt_perfCallback)); |
0a7de745 A |
156 | if (tempDTraceTrapHook != (perfCallback)fbt_perfCallback) { |
157 | if (fbt_verbose) { | |
158 | cmn_err(CE_NOTE, "fbt_enable is failing for probe %s " | |
159 | "in module %s: tempDTraceTrapHook already occupied.", | |
160 | fbt->fbtp_name, ctl->mod_modname); | |
161 | } | |
162 | continue; | |
2d21ac55 | 163 | } |
2d21ac55 | 164 | |
0a7de745 | 165 | if (fbt->fbtp_currentval != fbt->fbtp_patchval) { |
a39ff7e2 | 166 | #if KASAN |
0a7de745 A |
167 | /* Since dtrace probes can call into KASan and vice versa, things can get |
168 | * very slow if we have a lot of probes. This call will disable the KASan | |
169 | * fakestack after a threshold of probes is reached. */ | |
170 | kasan_fakestack_suspend(); | |
a39ff7e2 A |
171 | #endif |
172 | ||
0a7de745 A |
173 | (void)ml_nofault_copy((vm_offset_t)&fbt->fbtp_patchval, (vm_offset_t)fbt->fbtp_patchpoint, |
174 | sizeof(fbt->fbtp_patchval)); | |
175 | /* | |
176 | * Make the patched instruction visible via a data + instruction | |
177 | * cache flush for the platforms that need it | |
178 | */ | |
179 | flush_dcache((vm_offset_t)fbt->fbtp_patchpoint, (vm_size_t)sizeof(fbt->fbtp_patchval), 0); | |
180 | invalidate_icache((vm_offset_t)fbt->fbtp_patchpoint, (vm_size_t)sizeof(fbt->fbtp_patchval), 0); | |
181 | fbt->fbtp_currentval = fbt->fbtp_patchval; | |
39037602 | 182 | |
0a7de745 A |
183 | ctl->mod_nenabled++; |
184 | } | |
6d2010ae A |
185 | } |
186 | ||
0a7de745 | 187 | dtrace_membar_consumer(); |
d9a64523 | 188 | |
0a7de745 | 189 | return 0; |
2d21ac55 A |
190 | } |
191 | ||
192 | /*ARGSUSED*/ | |
193 | static void | |
194 | fbt_disable(void *arg, dtrace_id_t id, void *parg) | |
195 | { | |
196 | #pragma unused(arg,id) | |
197 | fbt_probe_t *fbt = parg; | |
6d2010ae A |
198 | struct modctl *ctl = NULL; |
199 | ||
200 | for (; fbt != NULL; fbt = fbt->fbtp_next) { | |
0a7de745 | 201 | ctl = fbt->fbtp_ctl; |
d9a64523 | 202 | |
0a7de745 A |
203 | if (!ctl->mod_loaded || (ctl->mod_loadcnt != fbt->fbtp_loadcnt)) { |
204 | continue; | |
205 | } | |
2d21ac55 | 206 | |
0a7de745 A |
207 | if (fbt->fbtp_currentval != fbt->fbtp_savedval) { |
208 | (void)ml_nofault_copy((vm_offset_t)&fbt->fbtp_savedval, (vm_offset_t)fbt->fbtp_patchpoint, | |
209 | sizeof(fbt->fbtp_savedval)); | |
210 | /* | |
211 | * Make the patched instruction visible via a data + instruction | |
212 | * cache flush for the platforms that need it | |
213 | */ | |
214 | flush_dcache((vm_offset_t)fbt->fbtp_patchpoint, (vm_size_t)sizeof(fbt->fbtp_patchval), 0); | |
215 | invalidate_icache((vm_offset_t)fbt->fbtp_patchpoint, (vm_size_t)sizeof(fbt->fbtp_patchval), 0); | |
39037602 | 216 | |
0a7de745 A |
217 | fbt->fbtp_currentval = fbt->fbtp_savedval; |
218 | ASSERT(ctl->mod_nenabled > 0); | |
219 | ctl->mod_nenabled--; | |
a39ff7e2 A |
220 | |
221 | #if KASAN | |
0a7de745 | 222 | kasan_fakestack_resume(); |
a39ff7e2 | 223 | #endif |
0a7de745 | 224 | } |
6d2010ae | 225 | } |
2d21ac55 A |
226 | dtrace_membar_consumer(); |
227 | } | |
228 | ||
229 | /*ARGSUSED*/ | |
230 | static void | |
231 | fbt_suspend(void *arg, dtrace_id_t id, void *parg) | |
232 | { | |
233 | #pragma unused(arg,id) | |
234 | fbt_probe_t *fbt = parg; | |
6d2010ae | 235 | struct modctl *ctl = NULL; |
2d21ac55 | 236 | |
6d2010ae | 237 | for (; fbt != NULL; fbt = fbt->fbtp_next) { |
0a7de745 | 238 | ctl = fbt->fbtp_ctl; |
6d2010ae | 239 | |
0a7de745 A |
240 | ASSERT(ctl->mod_nenabled > 0); |
241 | if (!ctl->mod_loaded || (ctl->mod_loadcnt != fbt->fbtp_loadcnt)) { | |
242 | continue; | |
243 | } | |
6d2010ae | 244 | |
0a7de745 A |
245 | (void)ml_nofault_copy((vm_offset_t)&fbt->fbtp_savedval, (vm_offset_t)fbt->fbtp_patchpoint, |
246 | sizeof(fbt->fbtp_savedval)); | |
d9a64523 | 247 | |
39037602 A |
248 | /* |
249 | * Make the patched instruction visible via a data + instruction | |
250 | * cache flush for the platforms that need it | |
251 | */ | |
0a7de745 A |
252 | flush_dcache((vm_offset_t)fbt->fbtp_patchpoint, (vm_size_t)sizeof(fbt->fbtp_savedval), 0); |
253 | invalidate_icache((vm_offset_t)fbt->fbtp_patchpoint, (vm_size_t)sizeof(fbt->fbtp_savedval), 0); | |
d9a64523 | 254 | |
39037602 | 255 | fbt->fbtp_currentval = fbt->fbtp_savedval; |
6d2010ae | 256 | } |
d9a64523 | 257 | |
2d21ac55 A |
258 | dtrace_membar_consumer(); |
259 | } | |
260 | ||
261 | /*ARGSUSED*/ | |
262 | static void | |
263 | fbt_resume(void *arg, dtrace_id_t id, void *parg) | |
264 | { | |
265 | #pragma unused(arg,id) | |
266 | fbt_probe_t *fbt = parg; | |
6d2010ae | 267 | struct modctl *ctl = NULL; |
2d21ac55 | 268 | |
6d2010ae | 269 | for (; fbt != NULL; fbt = fbt->fbtp_next) { |
0a7de745 A |
270 | ctl = fbt->fbtp_ctl; |
271 | ||
272 | ASSERT(ctl->mod_nenabled > 0); | |
273 | if (!ctl->mod_loaded || (ctl->mod_loadcnt != fbt->fbtp_loadcnt)) { | |
274 | continue; | |
2d21ac55 | 275 | } |
d9a64523 | 276 | |
f427ee49 | 277 | dtrace_casptr(&tempDTraceTrapHook, NULL, ptrauth_nop_cast(void *, &fbt_perfCallback)); |
0a7de745 A |
278 | if (tempDTraceTrapHook != (perfCallback)fbt_perfCallback) { |
279 | if (fbt_verbose) { | |
280 | cmn_err(CE_NOTE, "fbt_resume is failing for probe %s " | |
281 | "in module %s: tempDTraceTrapHook already occupied.", | |
282 | fbt->fbtp_name, ctl->mod_modname); | |
283 | } | |
284 | return; | |
285 | } | |
286 | ||
287 | (void)ml_nofault_copy((vm_offset_t)&fbt->fbtp_patchval, (vm_offset_t)fbt->fbtp_patchpoint, | |
288 | sizeof(fbt->fbtp_patchval)); | |
39236c6e | 289 | |
5ba3f43e A |
290 | /* |
291 | * Make the patched instruction visible via a data + instruction cache flush. | |
292 | */ | |
0a7de745 A |
293 | flush_dcache((vm_offset_t)fbt->fbtp_patchpoint, (vm_size_t)sizeof(fbt->fbtp_patchval), 0); |
294 | invalidate_icache((vm_offset_t)fbt->fbtp_patchpoint, (vm_size_t)sizeof(fbt->fbtp_patchval), 0); | |
2d21ac55 | 295 | |
0a7de745 | 296 | fbt->fbtp_currentval = fbt->fbtp_patchval; |
2d21ac55 | 297 | } |
2d21ac55 | 298 | |
d9a64523 | 299 | dtrace_membar_consumer(); |
2d21ac55 | 300 | } |
2d21ac55 | 301 | |
5ba3f43e A |
302 | static void |
303 | fbt_provide_module_user_syms(struct modctl *ctl) | |
304 | { | |
305 | unsigned int i; | |
306 | char *modname = ctl->mod_modname; | |
307 | ||
308 | dtrace_module_symbols_t* module_symbols = ctl->mod_user_symbols; | |
309 | if (module_symbols) { | |
0a7de745 A |
310 | for (i = 0; i < module_symbols->dtmodsyms_count; i++) { |
311 | /* | |
5ba3f43e A |
312 | * symbol->dtsym_addr (the symbol address) passed in from |
313 | * user space, is already slid for both kexts and kernel. | |
314 | */ | |
315 | dtrace_symbol_t* symbol = &module_symbols->dtmodsyms_symbols[i]; | |
316 | ||
317 | char* name = symbol->dtsym_name; | |
318 | ||
319 | /* Lop off omnipresent leading underscore. */ | |
0a7de745 | 320 | if (*name == '_') { |
5ba3f43e | 321 | name += 1; |
0a7de745 | 322 | } |
5ba3f43e | 323 | |
cb323159 | 324 | if (fbt_excluded(name)) { |
d9a64523 | 325 | continue; |
0a7de745 | 326 | } |
5ba3f43e A |
327 | |
328 | /* | |
329 | * Ignore symbols with a null address | |
330 | */ | |
0a7de745 | 331 | if (!symbol->dtsym_addr) { |
5ba3f43e | 332 | continue; |
0a7de745 | 333 | } |
5ba3f43e | 334 | |
d9a64523 A |
335 | /* |
336 | * Ignore symbols not part of this module | |
337 | */ | |
0a7de745 | 338 | if (!dtrace_addr_in_module((void*)symbol->dtsym_addr, ctl)) { |
d9a64523 | 339 | continue; |
0a7de745 | 340 | } |
d9a64523 A |
341 | |
342 | fbt_provide_probe(ctl, modname, name, (machine_inst_t*)(uintptr_t)symbol->dtsym_addr, (machine_inst_t*)(uintptr_t)(symbol->dtsym_addr + symbol->dtsym_size)); | |
5ba3f43e A |
343 | } |
344 | } | |
345 | } | |
d9a64523 A |
346 | static void |
347 | fbt_provide_kernel_section(struct modctl *ctl, kernel_section_t *sect, kernel_nlist_t *sym, uint32_t nsyms, const char *strings) | |
348 | { | |
349 | uintptr_t sect_start = (uintptr_t)sect->addr; | |
350 | uintptr_t sect_end = (uintptr_t)sect->size + sect->addr; | |
351 | unsigned int i; | |
352 | ||
353 | if ((sect->flags & S_ATTR_PURE_INSTRUCTIONS) != S_ATTR_PURE_INSTRUCTIONS) { | |
354 | return; | |
355 | } | |
356 | ||
357 | for (i = 0; i < nsyms; i++) { | |
358 | uint8_t n_type = sym[i].n_type & (N_TYPE | N_EXT); | |
359 | const char *name = strings + sym[i].n_un.n_strx; | |
360 | uint64_t limit; | |
361 | ||
0a7de745 | 362 | if (sym[i].n_value < sect_start || sym[i].n_value > sect_end) { |
d9a64523 | 363 | continue; |
0a7de745 | 364 | } |
d9a64523 A |
365 | |
366 | /* Check that the symbol is a global and that it has a name. */ | |
0a7de745 | 367 | if (((N_SECT | N_EXT) != n_type && (N_ABS | N_EXT) != n_type)) { |
d9a64523 | 368 | continue; |
0a7de745 | 369 | } |
d9a64523 | 370 | |
0a7de745 | 371 | if (0 == sym[i].n_un.n_strx) { /* iff a null, "", name. */ |
d9a64523 | 372 | continue; |
0a7de745 | 373 | } |
d9a64523 A |
374 | |
375 | /* Lop off omnipresent leading underscore. */ | |
0a7de745 | 376 | if (*name == '_') { |
d9a64523 | 377 | name += 1; |
0a7de745 | 378 | } |
d9a64523 A |
379 | |
380 | #if defined(__arm__) | |
381 | // Skip non-thumb functions on arm32 | |
382 | if (sym[i].n_sect == 1 && !(sym[i].n_desc & N_ARM_THUMB_DEF)) { | |
383 | continue; | |
384 | } | |
385 | #endif /* defined(__arm__) */ | |
386 | ||
cb323159 | 387 | if (fbt_excluded(name)) { |
d9a64523 | 388 | continue; |
0a7de745 | 389 | } |
d9a64523 A |
390 | |
391 | /* | |
392 | * Find the function boundary by looking at either the | |
393 | * end of the section or the beginning of the next symbol | |
394 | */ | |
395 | if (i == nsyms - 1) { | |
396 | limit = sect_end; | |
0a7de745 | 397 | } else { |
d9a64523 A |
398 | limit = sym[i + 1].n_value; |
399 | } | |
400 | ||
401 | fbt_provide_probe(ctl, ctl->mod_modname, name, (machine_inst_t*)sym[i].n_value, (machine_inst_t*)limit); | |
402 | } | |
d9a64523 A |
403 | } |
404 | ||
405 | static int | |
406 | fbt_sym_cmp(const void *ap, const void *bp) | |
407 | { | |
408 | return (int)(((const kernel_nlist_t*)ap)->n_value - ((const kernel_nlist_t*)bp)->n_value); | |
409 | } | |
410 | ||
411 | static void | |
412 | fbt_provide_module_kernel_syms(struct modctl *ctl) | |
413 | { | |
414 | kernel_mach_header_t *mh = (kernel_mach_header_t *)(ctl->mod_address); | |
415 | kernel_segment_command_t *seg; | |
416 | struct load_command *cmd; | |
417 | kernel_segment_command_t *linkedit = NULL; | |
418 | struct symtab_command *symtab = NULL; | |
419 | kernel_nlist_t *syms = NULL, *sorted_syms = NULL; | |
420 | const char *strings; | |
421 | unsigned int i; | |
422 | size_t symlen; | |
423 | ||
0a7de745 | 424 | if (mh->magic != MH_MAGIC_KERNEL) { |
d9a64523 | 425 | return; |
0a7de745 | 426 | } |
d9a64523 A |
427 | |
428 | cmd = (struct load_command *) &mh[1]; | |
429 | for (i = 0; i < mh->ncmds; i++) { | |
430 | if (cmd->cmd == LC_SEGMENT_KERNEL) { | |
431 | kernel_segment_command_t *orig_sg = (kernel_segment_command_t *) cmd; | |
0a7de745 | 432 | if (LIT_STRNEQL(orig_sg->segname, SEG_LINKEDIT)) { |
d9a64523 | 433 | linkedit = orig_sg; |
0a7de745 | 434 | } |
d9a64523 A |
435 | } else if (cmd->cmd == LC_SYMTAB) { |
436 | symtab = (struct symtab_command *) cmd; | |
437 | } | |
438 | if (symtab && linkedit) { | |
439 | break; | |
440 | } | |
441 | cmd = (struct load_command *) ((caddr_t) cmd + cmd->cmdsize); | |
442 | } | |
5ba3f43e | 443 | |
d9a64523 A |
444 | if ((symtab == NULL) || (linkedit == NULL)) { |
445 | return; | |
446 | } | |
447 | ||
448 | syms = (kernel_nlist_t *)(linkedit->vmaddr + symtab->symoff - linkedit->fileoff); | |
449 | strings = (const char *)(linkedit->vmaddr + symtab->stroff - linkedit->fileoff); | |
450 | ||
451 | /* | |
452 | * Make a copy of the symbol table and sort it to not cross into the next function | |
453 | * when disassembling the function | |
454 | */ | |
455 | symlen = sizeof(kernel_nlist_t) * symtab->nsyms; | |
456 | sorted_syms = kmem_alloc(symlen, KM_SLEEP); | |
457 | bcopy(syms, sorted_syms, symlen); | |
458 | qsort(sorted_syms, symtab->nsyms, sizeof(kernel_nlist_t), fbt_sym_cmp); | |
459 | ||
460 | for (seg = firstsegfromheader(mh); seg != NULL; seg = nextsegfromheader(mh, seg)) { | |
461 | kernel_section_t *sect = firstsect(seg); | |
462 | ||
463 | if (strcmp(seg->segname, "__KLD") == 0) { | |
464 | continue; | |
465 | } | |
466 | ||
467 | for (sect = firstsect(seg); sect != NULL; sect = nextsect(seg, sect)) { | |
468 | fbt_provide_kernel_section(ctl, sect, sorted_syms, symtab->nsyms, strings); | |
469 | } | |
470 | } | |
471 | ||
472 | kmem_free(sorted_syms, symlen); | |
473 | } | |
5ba3f43e A |
474 | |
475 | void | |
476 | fbt_provide_module(void *arg, struct modctl *ctl) | |
477 | { | |
478 | #pragma unused(arg) | |
479 | ASSERT(ctl != NULL); | |
480 | ASSERT(dtrace_kernel_symbol_mode != DTRACE_KERNEL_SYMBOLS_NEVER); | |
481 | LCK_MTX_ASSERT(&mod_lock, LCK_MTX_ASSERT_OWNED); | |
482 | ||
f427ee49 A |
483 | if (dtrace_fbt_probes_restricted()) { |
484 | return; | |
485 | } | |
486 | ||
5ba3f43e | 487 | // Update the "ignore blacklist" bit |
0a7de745 | 488 | if (ignore_fbt_blacklist) { |
5ba3f43e | 489 | ctl->mod_flags |= MODCTL_FBT_PROVIDE_BLACKLISTED_PROBES; |
0a7de745 | 490 | } |
5ba3f43e | 491 | |
0a7de745 | 492 | if (MOD_FBT_DONE(ctl)) { |
5ba3f43e | 493 | return; |
0a7de745 | 494 | } |
5ba3f43e A |
495 | |
496 | if (fbt_module_excluded(ctl)) { | |
497 | ctl->mod_flags |= MODCTL_FBT_INVALID; | |
498 | return; | |
499 | } | |
500 | ||
501 | if (MOD_HAS_KERNEL_SYMBOLS(ctl)) { | |
502 | fbt_provide_module_kernel_syms(ctl); | |
503 | ctl->mod_flags |= MODCTL_FBT_PROBES_PROVIDED; | |
0a7de745 | 504 | if (MOD_FBT_PROVIDE_BLACKLISTED_PROBES(ctl)) { |
5ba3f43e | 505 | ctl->mod_flags |= MODCTL_FBT_BLACKLISTED_PROBES_PROVIDED; |
0a7de745 | 506 | } |
5ba3f43e A |
507 | return; |
508 | } | |
509 | ||
510 | if (MOD_HAS_USERSPACE_SYMBOLS(ctl)) { | |
511 | fbt_provide_module_user_syms(ctl); | |
512 | ctl->mod_flags |= MODCTL_FBT_PROBES_PROVIDED; | |
0a7de745 | 513 | if (MOD_FBT_PROVIDE_BLACKLISTED_PROBES(ctl)) { |
5ba3f43e | 514 | ctl->mod_flags |= MODCTL_FBT_BLACKLISTED_PROBES_PROVIDED; |
0a7de745 | 515 | } |
5ba3f43e A |
516 | return; |
517 | } | |
518 | } | |
519 | ||
2d21ac55 | 520 | static dtrace_pattr_t fbt_attr = { |
0a7de745 A |
521 | { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA }, |
522 | { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, | |
523 | { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, | |
524 | { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA }, | |
525 | { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA }, | |
2d21ac55 A |
526 | }; |
527 | ||
528 | static dtrace_pops_t fbt_pops = { | |
0a7de745 A |
529 | .dtps_provide = NULL, |
530 | .dtps_provide_module = fbt_provide_module, | |
531 | .dtps_enable = fbt_enable, | |
532 | .dtps_disable = fbt_disable, | |
533 | .dtps_suspend = fbt_suspend, | |
534 | .dtps_resume = fbt_resume, | |
535 | .dtps_getargdesc = NULL, /* APPLE NOTE: fbt_getargdesc implemented in userspace */ | |
536 | .dtps_getargval = NULL, | |
537 | .dtps_usermode = NULL, | |
538 | .dtps_destroy = fbt_destroy | |
2d21ac55 A |
539 | }; |
540 | ||
541 | static void | |
542 | fbt_cleanup(dev_info_t *devi) | |
543 | { | |
544 | dtrace_invop_remove(fbt_invop); | |
545 | ddi_remove_minor_node(devi, NULL); | |
0a7de745 | 546 | kmem_free(fbt_probetab, fbt_probetab_size * sizeof(fbt_probe_t *)); |
2d21ac55 A |
547 | fbt_probetab = NULL; |
548 | fbt_probetab_mask = 0; | |
549 | } | |
550 | ||
551 | static int | |
d9a64523 | 552 | fbt_attach(dev_info_t *devi) |
2d21ac55 | 553 | { |
0a7de745 | 554 | if (fbt_probetab_size == 0) { |
2d21ac55 | 555 | fbt_probetab_size = FBT_PROBETAB_SIZE; |
0a7de745 | 556 | } |
2d21ac55 A |
557 | |
558 | fbt_probetab_mask = fbt_probetab_size - 1; | |
559 | fbt_probetab = | |
0a7de745 | 560 | kmem_zalloc(fbt_probetab_size * sizeof(fbt_probe_t *), KM_SLEEP); |
2d21ac55 A |
561 | |
562 | dtrace_invop_add(fbt_invop); | |
563 | ||
b0d623f7 A |
564 | if (ddi_create_minor_node(devi, "fbt", S_IFCHR, 0, |
565 | DDI_PSEUDO, 0) == DDI_FAILURE || | |
566 | dtrace_register("fbt", &fbt_attr, DTRACE_PRIV_KERNEL, NULL, | |
567 | &fbt_pops, NULL, &fbt_id) != 0) { | |
568 | fbt_cleanup(devi); | |
0a7de745 | 569 | return DDI_FAILURE; |
b0d623f7 | 570 | } |
2d21ac55 | 571 | |
0a7de745 | 572 | return DDI_SUCCESS; |
2d21ac55 A |
573 | } |
574 | ||
575 | static d_open_t _fbt_open; | |
576 | ||
577 | static int | |
578 | _fbt_open(dev_t dev, int flags, int devtype, struct proc *p) | |
579 | { | |
580 | #pragma unused(dev,flags,devtype,p) | |
581 | return 0; | |
582 | } | |
583 | ||
584 | #define FBT_MAJOR -24 /* let the kernel pick the device number */ | |
585 | ||
f427ee49 | 586 | static const struct cdevsw fbt_cdevsw = |
2d21ac55 | 587 | { |
f427ee49 A |
588 | .d_open = _fbt_open, |
589 | .d_close = eno_opcl, | |
590 | .d_read = eno_rdwrt, | |
591 | .d_write = eno_rdwrt, | |
592 | .d_ioctl = eno_ioctl, | |
593 | .d_stop = (stop_fcn_t *)nulldev, | |
594 | .d_reset = (reset_fcn_t *)nulldev, | |
595 | .d_select = eno_select, | |
596 | .d_mmap = eno_mmap, | |
597 | .d_strategy = eno_strat, | |
598 | .d_reserved_1 = eno_getc, | |
599 | .d_reserved_2 = eno_putc, | |
2d21ac55 A |
600 | }; |
601 | ||
2d21ac55 A |
602 | #undef kmem_alloc /* from its binding to dt_kmem_alloc glue */ |
603 | #undef kmem_free /* from its binding to dt_kmem_free glue */ | |
604 | #include <vm/vm_kern.h> | |
605 | ||
cb323159 | 606 | |
2d21ac55 A |
607 | void |
608 | fbt_init( void ) | |
609 | { | |
d9a64523 | 610 | int majdevno = cdevsw_add(FBT_MAJOR, &fbt_cdevsw); |
2d21ac55 | 611 | |
d9a64523 A |
612 | if (majdevno < 0) { |
613 | printf("fbt_init: failed to allocate a major number!\n"); | |
614 | return; | |
2d21ac55 | 615 | } |
d9a64523 | 616 | |
cb323159 | 617 | fbt_blacklist_init(); |
d9a64523 | 618 | fbt_attach((dev_info_t*)(uintptr_t)majdevno); |
2d21ac55 A |
619 | } |
620 | #undef FBT_MAJOR |