]>
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 | ||
5ba3f43e | 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> | |
5ba3f43e | 39 | #include <sys/sysctl.h> |
2d21ac55 A |
40 | #include <sys/errno.h> |
41 | #include <sys/stat.h> | |
42 | #include <sys/ioctl.h> | |
43 | #include <sys/conf.h> | |
44 | #include <sys/fcntl.h> | |
45 | #include <miscfs/devfs/devfs.h> | |
46 | #include <pexpert/pexpert.h> | |
47 | ||
48 | #include <sys/dtrace.h> | |
49 | #include <sys/dtrace_impl.h> | |
50 | #include <sys/fbt.h> | |
51 | ||
52 | #include <sys/dtrace_glue.h> | |
a39ff7e2 | 53 | #include <san/kasan.h> |
2d21ac55 A |
54 | |
55 | /* #include <machine/trap.h> */ | |
56 | struct savearea_t; /* Used anonymously */ | |
2d21ac55 | 57 | |
5ba3f43e A |
58 | #if defined(__arm__) || defined(__arm64__) |
59 | typedef kern_return_t (*perfCallback)(int, struct savearea_t *, __unused int, __unused int); | |
60 | extern perfCallback tempDTraceTrapHook; | |
61 | extern kern_return_t fbt_perfCallback(int, struct savearea_t *, __unused int, __unused int); | |
62 | #elif defined(__x86_64__) | |
39236c6e | 63 | typedef kern_return_t (*perfCallback)(int, struct savearea_t *, uintptr_t *, __unused int); |
2d21ac55 | 64 | extern perfCallback tempDTraceTrapHook; |
39236c6e | 65 | extern kern_return_t fbt_perfCallback(int, struct savearea_t *, uintptr_t *, __unused int); |
fe8ab488 A |
66 | #else |
67 | #error Unknown architecture | |
68 | #endif | |
2d21ac55 | 69 | |
d9a64523 A |
70 | __private_extern__ |
71 | void | |
72 | qsort(void *a, size_t n, size_t es, int (*cmp)(const void *, const void *)); | |
73 | ||
0a7de745 A |
74 | #define FBT_ADDR2NDX(addr) ((((uintptr_t)(addr)) >> 4) & fbt_probetab_mask) |
75 | #define FBT_PROBETAB_SIZE 0x8000 /* 32k entries -- 128K total */ | |
2d21ac55 | 76 | |
0a7de745 A |
77 | static int fbt_probetab_size; |
78 | dtrace_provider_id_t fbt_id; | |
79 | fbt_probe_t **fbt_probetab; | |
80 | int fbt_probetab_mask; | |
81 | static int fbt_verbose = 0; | |
2d21ac55 | 82 | |
5ba3f43e A |
83 | int ignore_fbt_blacklist = 0; |
84 | ||
85 | extern int dtrace_kernel_symbol_mode; | |
86 | ||
87 | ||
2d21ac55 A |
88 | void fbt_init( void ); |
89 | ||
5ba3f43e A |
90 | /* |
91 | * Critical routines that must not be probed. PR_5221096, PR_5379018. | |
92 | * The blacklist must be kept in alphabetic order for purposes of bsearch(). | |
93 | */ | |
94 | static const char * critical_blacklist[] = | |
95 | { | |
96 | "Call_DebuggerC", | |
d9a64523 A |
97 | "DebuggerCall", |
98 | "DebuggerTrapWithState", | |
99 | "DebuggerXCallEnter", | |
100 | "IOCPURunPlatformPanicActions", | |
101 | "PEARMDebugPanicHook", | |
102 | "PEHaltRestart", | |
103 | "SavePanicInfo", | |
5ba3f43e A |
104 | "SysChoked", |
105 | "_ZN9IOService14newTemperatureElPS_", /* IOService::newTemperature */ | |
106 | "_ZN9IOService26temperatureCriticalForZoneEPS_", /* IOService::temperatureCriticalForZone */ | |
107 | "_ZNK6OSData14getBytesNoCopyEv", /* Data::getBytesNoCopy, IOHibernateSystemWake path */ | |
d9a64523 A |
108 | "__ZN16IOPlatformExpert11haltRestartEj", |
109 | "__ZN18IODTPlatformExpert11haltRestartEj", | |
0a7de745 | 110 | "__ZN9IODTNVRAM13savePanicInfoEPhy", |
5ba3f43e A |
111 | "_disable_preemption", |
112 | "_enable_preemption", | |
d9a64523 | 113 | "alternate_debugger_enter", |
5ba3f43e A |
114 | "bcopy_phys", |
115 | "console_cpu_alloc", | |
116 | "console_cpu_free", | |
117 | "cpu_IA32e_disable", | |
118 | "cpu_IA32e_enable", | |
119 | "cpu_NMI_interrupt", | |
120 | "cpu_control", | |
121 | "cpu_data_alloc", | |
122 | "cpu_desc_init", | |
123 | "cpu_desc_init64", | |
124 | "cpu_desc_load", | |
125 | "cpu_desc_load64", | |
126 | "cpu_exit_wait", | |
127 | "cpu_info", | |
128 | "cpu_info_count", | |
129 | "cpu_init", | |
130 | "cpu_interrupt", | |
131 | "cpu_machine_init", | |
132 | "cpu_mode_init", | |
133 | "cpu_processor_alloc", | |
134 | "cpu_processor_free", | |
135 | "cpu_signal_handler", | |
136 | "cpu_sleep", | |
137 | "cpu_start", | |
138 | "cpu_subtype", | |
139 | "cpu_thread_alloc", | |
140 | "cpu_thread_halt", | |
141 | "cpu_thread_init", | |
142 | "cpu_threadtype", | |
143 | "cpu_to_processor", | |
144 | "cpu_topology_sort", | |
145 | "cpu_topology_start_cpu", | |
146 | "cpu_type", | |
147 | "cpuid_cpu_display", | |
148 | "cpuid_extfeatures", | |
149 | "dtrace_invop", | |
150 | "enter_lohandler", | |
151 | "fbt_invop", | |
152 | "fbt_perfCallback", | |
d9a64523 | 153 | "get_preemption_level" |
5ba3f43e A |
154 | "get_threadtask", |
155 | "handle_pending_TLB_flushes", | |
156 | "hw_compare_and_store", | |
157 | "interrupt", | |
d9a64523 A |
158 | "is_saved_state32", |
159 | "kernel_preempt_check", | |
5ba3f43e A |
160 | "kernel_trap", |
161 | "kprintf", | |
d9a64523 A |
162 | "ks_dispatch_kernel", |
163 | "ks_dispatch_user", | |
164 | "ks_kernel_trap", | |
5ba3f43e A |
165 | "lo_alltraps", |
166 | "lock_debugger", | |
167 | "machine_idle_cstate", | |
168 | "machine_thread_get_kern_state", | |
169 | "mca_cpu_alloc", | |
170 | "mca_cpu_init", | |
171 | "ml_nofault_copy", | |
172 | "nanoseconds_to_absolutetime", | |
173 | "nanotime_to_absolutetime", | |
174 | "packA", | |
175 | "panic", | |
d9a64523 A |
176 | "phystokv", |
177 | "phystokv_range", | |
178 | "pltrace", | |
5ba3f43e A |
179 | "pmKextRegister", |
180 | "pmMarkAllCPUsOff", | |
181 | "pmSafeMode", | |
182 | "pmTimerRestore", | |
183 | "pmTimerSave", | |
184 | "pmUnRegister", | |
185 | "pmap_cpu_alloc", | |
186 | "pmap_cpu_free", | |
187 | "pmap_cpu_high_map_vaddr", | |
188 | "pmap_cpu_high_shared_remap", | |
189 | "pmap_cpu_init", | |
190 | "power_management_init", | |
191 | "preemption_underflow_panic", | |
192 | "register_cpu_setup_func", | |
d9a64523 A |
193 | "ret64_iret" |
194 | "ret_to_user" | |
195 | "return_to_kernel", | |
196 | "return_to_user", | |
197 | "saved_state64", | |
5ba3f43e A |
198 | "sdt_invop", |
199 | "sprlock", | |
200 | "sprunlock", | |
d9a64523 A |
201 | "strlen", |
202 | "strncmp", | |
5ba3f43e A |
203 | "t_invop", |
204 | "tmrCvt", | |
d9a64523 A |
205 | "trap_from_kernel", |
206 | "uart_putc", | |
5ba3f43e A |
207 | "unlock_debugger", |
208 | "unpackA", | |
209 | "unregister_cpu_setup_func", | |
d9a64523 A |
210 | "uread", |
211 | "uwrite", | |
5ba3f43e A |
212 | "vstart" |
213 | }; | |
d9a64523 | 214 | |
5ba3f43e A |
215 | #define CRITICAL_BLACKLIST_COUNT (sizeof(critical_blacklist)/sizeof(critical_blacklist[0])) |
216 | ||
217 | /* | |
218 | * The transitive closure of entry points that can be reached from probe context. | |
219 | * (Apart from routines whose names begin with dtrace_). | |
220 | */ | |
221 | static const char * probe_ctx_closure[] = | |
222 | { | |
223 | "ClearIdlePop", | |
224 | "Debugger", | |
225 | "IS_64BIT_PROCESS", | |
226 | "OSCompareAndSwap", | |
227 | "SetIdlePop", | |
d9a64523 | 228 | "__dtrace_probe", |
5ba3f43e A |
229 | "absolutetime_to_microtime", |
230 | "act_set_astbsd", | |
231 | "arm_init_idle_cpu", | |
232 | "ast_dtrace_on", | |
233 | "ast_pending", | |
234 | "clean_dcache", | |
235 | "clean_mmu_dcache", | |
236 | "clock_get_calendar_nanotime_nowait", | |
237 | "copyin", | |
238 | "copyin_kern", | |
239 | "copyin_user", | |
240 | "copyinstr", | |
241 | "copyout", | |
242 | "copyoutstr", | |
243 | "cpu_number", | |
244 | "current_proc", | |
245 | "current_processor", | |
246 | "current_task", | |
247 | "current_thread", | |
248 | "debug_enter", | |
249 | "drain_write_buffer", | |
250 | "find_user_regs", | |
251 | "flush_dcache", | |
252 | "flush_tlb64", | |
253 | "get_bsdtask_info", | |
254 | "get_bsdthread_info", | |
255 | "hertz_tick", | |
256 | "hw_atomic_and", | |
257 | "invalidate_mmu_icache", | |
258 | "kauth_cred_get", | |
259 | "kauth_getgid", | |
260 | "kauth_getuid", | |
261 | "kernel_preempt_check", | |
262 | "kvtophys", | |
263 | "mach_absolute_time", | |
264 | "max_valid_stack_address", | |
265 | "memcpy", | |
266 | "memmove", | |
267 | "ml_at_interrupt_context", | |
268 | "ml_phys_write_byte_64", | |
269 | "ml_phys_write_half_64", | |
270 | "ml_phys_write_word_64", | |
271 | "ml_set_interrupts_enabled", | |
272 | "mt_core_snap", | |
273 | "mt_cur_cpu_cycles", | |
274 | "mt_cur_cpu_instrs", | |
275 | "mt_cur_thread_cycles", | |
276 | "mt_cur_thread_instrs", | |
277 | "mt_fixed_counts", | |
278 | "mt_fixed_counts_internal", | |
279 | "mt_mtc_update_count", | |
280 | "mt_update_thread", | |
281 | "ovbcopy", | |
282 | "panic", | |
5ba3f43e A |
283 | "pmap64_pdpt", |
284 | "pmap_find_phys", | |
285 | "pmap_get_mapwindow", | |
286 | "pmap_pde", | |
0a7de745 A |
287 | "pmap_pde_internal0", |
288 | "pmap_pde_internal1", | |
5ba3f43e | 289 | "pmap_pte", |
0a7de745 | 290 | "pmap_pte_internal", |
5ba3f43e A |
291 | "pmap_put_mapwindow", |
292 | "pmap_valid_page", | |
293 | "prf", | |
294 | "proc_is64bit", | |
295 | "proc_selfname", | |
296 | "psignal_lock", | |
297 | "rtc_nanotime_load", | |
298 | "rtc_nanotime_read", | |
299 | "sdt_getargdesc", | |
300 | "setPop", | |
301 | "strlcpy", | |
302 | "sync_iss_to_iks_unconditionally", | |
303 | "systrace_stub", | |
304 | "timer_grab" | |
305 | }; | |
306 | #define PROBE_CTX_CLOSURE_COUNT (sizeof(probe_ctx_closure)/sizeof(probe_ctx_closure[0])) | |
307 | ||
308 | #pragma clang diagnostic push | |
309 | #pragma clang diagnostic ignored "-Wcast-qual" | |
0a7de745 A |
310 | static int |
311 | _cmp(const void *a, const void *b) | |
5ba3f43e | 312 | { |
0a7de745 | 313 | return strncmp((const char *)a, *(const char **)b, strlen((const char *)a) + 1); |
5ba3f43e A |
314 | } |
315 | #pragma clang diagnostic pop | |
316 | /* | |
317 | * Module validation | |
318 | */ | |
319 | int | |
320 | fbt_module_excluded(struct modctl* ctl) | |
321 | { | |
322 | ASSERT(!MOD_FBT_DONE(ctl)); | |
323 | ||
324 | if (ctl->mod_address == 0 || ctl->mod_size == 0) { | |
325 | return TRUE; | |
326 | } | |
d9a64523 | 327 | |
5ba3f43e | 328 | if (ctl->mod_loaded == 0) { |
0a7de745 | 329 | return TRUE; |
5ba3f43e A |
330 | } |
331 | ||
0a7de745 | 332 | /* |
5ba3f43e A |
333 | * If the user sets this, trust they know what they are doing. |
334 | */ | |
0a7de745 | 335 | if (ignore_fbt_blacklist) { |
5ba3f43e | 336 | return FALSE; |
0a7de745 | 337 | } |
5ba3f43e A |
338 | |
339 | /* | |
340 | * These drivers control low level functions that when traced | |
341 | * cause problems often in the sleep/wake paths as well as | |
342 | * critical debug and panic paths. | |
343 | * If somebody really wants to drill in on one of these kexts, then | |
344 | * they can override blacklisting using the boot-arg above. | |
345 | */ | |
346 | ||
347 | #ifdef __x86_64__ | |
0a7de745 | 348 | if (strstr(ctl->mod_modname, "AppleACPIEC") != NULL) { |
5ba3f43e | 349 | return TRUE; |
0a7de745 | 350 | } |
5ba3f43e | 351 | |
0a7de745 | 352 | if (strstr(ctl->mod_modname, "AppleACPIPlatform") != NULL) { |
5ba3f43e | 353 | return TRUE; |
0a7de745 | 354 | } |
5ba3f43e | 355 | |
0a7de745 | 356 | if (strstr(ctl->mod_modname, "AppleRTC") != NULL) { |
5ba3f43e | 357 | return TRUE; |
0a7de745 | 358 | } |
5ba3f43e | 359 | |
0a7de745 | 360 | if (strstr(ctl->mod_modname, "IOACPIFamily") != NULL) { |
5ba3f43e | 361 | return TRUE; |
0a7de745 | 362 | } |
5ba3f43e | 363 | |
0a7de745 | 364 | if (strstr(ctl->mod_modname, "AppleIntelCPUPowerManagement") != NULL) { |
5ba3f43e | 365 | return TRUE; |
0a7de745 | 366 | } |
5ba3f43e | 367 | |
0a7de745 | 368 | if (strstr(ctl->mod_modname, "AppleProfile") != NULL) { |
5ba3f43e | 369 | return TRUE; |
0a7de745 | 370 | } |
5ba3f43e | 371 | |
0a7de745 | 372 | if (strstr(ctl->mod_modname, "AppleIntelProfile") != NULL) { |
5ba3f43e | 373 | return TRUE; |
0a7de745 | 374 | } |
5ba3f43e | 375 | |
0a7de745 | 376 | if (strstr(ctl->mod_modname, "AppleEFI") != NULL) { |
5ba3f43e | 377 | return TRUE; |
0a7de745 | 378 | } |
5ba3f43e A |
379 | |
380 | #elif __arm__ || __arm64__ | |
381 | if (LIT_STRNEQL(ctl->mod_modname, "com.apple.driver.AppleARMPlatform") || | |
0a7de745 A |
382 | LIT_STRNEQL(ctl->mod_modname, "com.apple.driver.AppleARMPL192VIC") || |
383 | LIT_STRNEQL(ctl->mod_modname, "com.apple.driver.AppleInterruptController")) { | |
5ba3f43e | 384 | return TRUE; |
0a7de745 | 385 | } |
5ba3f43e A |
386 | #endif |
387 | ||
388 | return FALSE; | |
389 | } | |
390 | ||
391 | /* | |
392 | * FBT probe name validation | |
393 | */ | |
394 | int | |
395 | fbt_excluded(const char* name) | |
396 | { | |
397 | /* | |
398 | * If the user set this, trust they know what they are doing. | |
399 | */ | |
0a7de745 | 400 | if (ignore_fbt_blacklist) { |
5ba3f43e | 401 | return FALSE; |
0a7de745 | 402 | } |
5ba3f43e A |
403 | |
404 | if (LIT_STRNSTART(name, "dtrace_") && !LIT_STRNSTART(name, "dtrace_safe_")) { | |
405 | /* | |
406 | * Anything beginning with "dtrace_" may be called | |
407 | * from probe context unless it explitly indicates | |
408 | * that it won't be called from probe context by | |
409 | * using the prefix "dtrace_safe_". | |
410 | */ | |
411 | return TRUE; | |
412 | } | |
413 | ||
414 | /* | |
0a7de745 A |
415 | * Place no probes on critical routines (5221096) |
416 | */ | |
417 | if (bsearch( name, critical_blacklist, CRITICAL_BLACKLIST_COUNT, sizeof(name), _cmp ) != NULL) { | |
5ba3f43e | 418 | return TRUE; |
0a7de745 | 419 | } |
5ba3f43e A |
420 | |
421 | /* | |
0a7de745 A |
422 | * Place no probes that could be hit in probe context. |
423 | */ | |
5ba3f43e A |
424 | if (bsearch( name, probe_ctx_closure, PROBE_CTX_CLOSURE_COUNT, sizeof(name), _cmp ) != NULL) { |
425 | return TRUE; | |
426 | } | |
427 | ||
428 | /* | |
0a7de745 A |
429 | * Place no probes that could be hit in probe context. |
430 | * In the interests of safety, some of these may be overly cautious. | |
431 | * Also exclude very low-level "firmware" class calls. | |
432 | */ | |
433 | if (LIT_STRNSTART(name, "cpu_") || /* Coarse */ | |
434 | LIT_STRNSTART(name, "platform_") || /* Coarse */ | |
435 | LIT_STRNSTART(name, "machine_") || /* Coarse */ | |
436 | LIT_STRNSTART(name, "ml_") || /* Coarse */ | |
437 | LIT_STRNSTART(name, "PE_") || /* Coarse */ | |
438 | LIT_STRNSTART(name, "rtc_") || /* Coarse */ | |
439 | LIT_STRNSTART(name, "_rtc_") || | |
440 | LIT_STRNSTART(name, "rtclock_") || | |
441 | LIT_STRNSTART(name, "clock_") || | |
442 | LIT_STRNSTART(name, "bcopy") || | |
443 | LIT_STRNSTART(name, "pmap_") || | |
444 | LIT_STRNSTART(name, "hw_") || /* Coarse */ | |
445 | LIT_STRNSTART(name, "lapic_") || /* Coarse */ | |
446 | LIT_STRNSTART(name, "OSAdd") || | |
447 | LIT_STRNSTART(name, "OSBit") || | |
448 | LIT_STRNSTART(name, "OSDecrement") || | |
449 | LIT_STRNSTART(name, "OSIncrement") || | |
450 | LIT_STRNSTART(name, "OSCompareAndSwap") || | |
451 | LIT_STRNSTART(name, "etimer_") || | |
452 | LIT_STRNSTART(name, "dtxnu_kern_") || | |
453 | LIT_STRNSTART(name, "flush_mmu_tlb_")) { | |
5ba3f43e | 454 | return TRUE; |
0a7de745 | 455 | } |
5ba3f43e A |
456 | /* |
457 | * Fasttrap inner-workings we can't instrument | |
458 | * on Intel (6230149) | |
0a7de745 | 459 | */ |
5ba3f43e | 460 | if (LIT_STRNSTART(name, "fasttrap_") || |
0a7de745 A |
461 | LIT_STRNSTART(name, "fuword") || |
462 | LIT_STRNSTART(name, "suword")) { | |
5ba3f43e | 463 | return TRUE; |
0a7de745 | 464 | } |
5ba3f43e | 465 | |
0a7de745 | 466 | if (LIT_STRNSTART(name, "_dtrace")) { |
5ba3f43e | 467 | return TRUE; /* Shims in dtrace.c */ |
0a7de745 A |
468 | } |
469 | if (LIT_STRNSTART(name, "hibernate_")) { | |
5ba3f43e | 470 | return TRUE; |
0a7de745 | 471 | } |
5ba3f43e A |
472 | |
473 | /* | |
474 | * Place no probes in the exception handling path | |
475 | */ | |
476 | #if __arm__ || __arm64__ | |
477 | if (LIT_STRNSTART(name, "fleh_") || | |
0a7de745 A |
478 | LIT_STRNSTART(name, "sleh_") || |
479 | LIT_STRNSTART(name, "timer_state_event") || | |
480 | LIT_STRNEQL(name, "get_vfp_enabled")) { | |
5ba3f43e | 481 | return TRUE; |
0a7de745 | 482 | } |
5ba3f43e A |
483 | |
484 | if (LIT_STRNSTART(name, "_ZNK15OSMetaClassBase8metaCastEPK11OSMetaClass") || | |
0a7de745 A |
485 | LIT_STRNSTART(name, "_ZN15OSMetaClassBase12safeMetaCastEPKS_PK11OSMetaClass") || |
486 | LIT_STRNSTART(name, "_ZNK11OSMetaClass13checkMetaCastEPK15OSMetaClassBase")) { | |
5ba3f43e | 487 | return TRUE; |
0a7de745 | 488 | } |
5ba3f43e A |
489 | #endif |
490 | ||
5ba3f43e A |
491 | #ifdef __x86_64__ |
492 | if (LIT_STRNSTART(name, "machine_") || | |
0a7de745 A |
493 | LIT_STRNSTART(name, "idt64") || |
494 | LIT_STRNSTART(name, "ks_") || | |
495 | LIT_STRNSTART(name, "hndl_") || | |
496 | LIT_STRNSTART(name, "_intr_") || | |
497 | LIT_STRNSTART(name, "mapping_") || | |
498 | LIT_STRNSTART(name, "tsc_") || | |
499 | LIT_STRNSTART(name, "pmCPU") || | |
500 | LIT_STRNSTART(name, "pms") || | |
501 | LIT_STRNSTART(name, "usimple_") || | |
502 | LIT_STRNSTART(name, "lck_spin_lock") || | |
503 | LIT_STRNSTART(name, "lck_spin_unlock") || | |
504 | LIT_STRNSTART(name, "absolutetime_to_") || | |
505 | LIT_STRNSTART(name, "commpage_") || | |
506 | LIT_STRNSTART(name, "ml_") || | |
507 | LIT_STRNSTART(name, "PE_") || | |
508 | LIT_STRNSTART(name, "act_machine") || | |
509 | LIT_STRNSTART(name, "acpi_") || | |
510 | LIT_STRNSTART(name, "pal_")) { | |
5ba3f43e A |
511 | return TRUE; |
512 | } | |
513 | // Don't Steal Mac OS X | |
0a7de745 | 514 | if (LIT_STRNSTART(name, "dsmos_")) { |
5ba3f43e | 515 | return TRUE; |
0a7de745 | 516 | } |
5ba3f43e A |
517 | |
518 | #endif | |
519 | ||
520 | /* | |
0a7de745 A |
521 | * Place no probes that could be hit on the way to the debugger. |
522 | */ | |
5ba3f43e | 523 | if (LIT_STRNSTART(name, "kdp_") || |
0a7de745 A |
524 | LIT_STRNSTART(name, "kdb_") || |
525 | LIT_STRNSTART(name, "debug_")) { | |
5ba3f43e A |
526 | return TRUE; |
527 | } | |
528 | ||
a39ff7e2 A |
529 | #if KASAN |
530 | if (LIT_STRNSTART(name, "kasan") || | |
0a7de745 A |
531 | LIT_STRNSTART(name, "__kasan") || |
532 | LIT_STRNSTART(name, "__asan")) { | |
a39ff7e2 A |
533 | return TRUE; |
534 | } | |
535 | #endif | |
536 | ||
5ba3f43e A |
537 | /* |
538 | * Place no probes that could be hit on the way to a panic. | |
539 | */ | |
0a7de745 | 540 | if (NULL != strstr(name, "panic_")) { |
5ba3f43e | 541 | return TRUE; |
0a7de745 | 542 | } |
5ba3f43e A |
543 | |
544 | return FALSE; | |
545 | } | |
546 | ||
547 | ||
2d21ac55 A |
548 | /*ARGSUSED*/ |
549 | static void | |
550 | fbt_destroy(void *arg, dtrace_id_t id, void *parg) | |
551 | { | |
552 | #pragma unused(arg,id) | |
553 | fbt_probe_t *fbt = parg, *next, *hash, *last; | |
554 | int ndx; | |
555 | ||
556 | do { | |
557 | /* | |
558 | * Now we need to remove this probe from the fbt_probetab. | |
559 | */ | |
560 | ndx = FBT_ADDR2NDX(fbt->fbtp_patchpoint); | |
561 | last = NULL; | |
562 | hash = fbt_probetab[ndx]; | |
563 | ||
564 | while (hash != fbt) { | |
565 | ASSERT(hash != NULL); | |
566 | last = hash; | |
567 | hash = hash->fbtp_hashnext; | |
568 | } | |
569 | ||
570 | if (last != NULL) { | |
571 | last->fbtp_hashnext = fbt->fbtp_hashnext; | |
572 | } else { | |
573 | fbt_probetab[ndx] = fbt->fbtp_hashnext; | |
574 | } | |
575 | ||
576 | next = fbt->fbtp_next; | |
0a7de745 | 577 | kmem_free(fbt, sizeof(fbt_probe_t)); |
2d21ac55 A |
578 | |
579 | fbt = next; | |
580 | } while (fbt != NULL); | |
581 | } | |
582 | ||
583 | /*ARGSUSED*/ | |
6d2010ae | 584 | int |
2d21ac55 A |
585 | fbt_enable(void *arg, dtrace_id_t id, void *parg) |
586 | { | |
587 | #pragma unused(arg,id) | |
588 | fbt_probe_t *fbt = parg; | |
6d2010ae A |
589 | struct modctl *ctl = NULL; |
590 | ||
0a7de745 A |
591 | for (; fbt != NULL; fbt = fbt->fbtp_next) { |
592 | ctl = fbt->fbtp_ctl; | |
2d21ac55 | 593 | |
0a7de745 A |
594 | if (!ctl->mod_loaded) { |
595 | if (fbt_verbose) { | |
596 | cmn_err(CE_NOTE, "fbt is failing for probe %s " | |
597 | "(module %s unloaded)", | |
598 | fbt->fbtp_name, ctl->mod_modname); | |
599 | } | |
d9a64523 | 600 | |
0a7de745 | 601 | continue; |
2d21ac55 | 602 | } |
6d2010ae | 603 | |
0a7de745 A |
604 | /* |
605 | * Now check that our modctl has the expected load count. If it | |
606 | * doesn't, this module must have been unloaded and reloaded -- and | |
607 | * we're not going to touch it. | |
608 | */ | |
609 | if (ctl->mod_loadcnt != fbt->fbtp_loadcnt) { | |
610 | if (fbt_verbose) { | |
611 | cmn_err(CE_NOTE, "fbt is failing for probe %s " | |
612 | "(module %s reloaded)", | |
613 | fbt->fbtp_name, ctl->mod_modname); | |
614 | } | |
6d2010ae | 615 | |
0a7de745 | 616 | continue; |
6d2010ae A |
617 | } |
618 | ||
0a7de745 A |
619 | dtrace_casptr(&tempDTraceTrapHook, NULL, fbt_perfCallback); |
620 | if (tempDTraceTrapHook != (perfCallback)fbt_perfCallback) { | |
621 | if (fbt_verbose) { | |
622 | cmn_err(CE_NOTE, "fbt_enable is failing for probe %s " | |
623 | "in module %s: tempDTraceTrapHook already occupied.", | |
624 | fbt->fbtp_name, ctl->mod_modname); | |
625 | } | |
626 | continue; | |
2d21ac55 | 627 | } |
2d21ac55 | 628 | |
0a7de745 | 629 | if (fbt->fbtp_currentval != fbt->fbtp_patchval) { |
a39ff7e2 | 630 | #if KASAN |
0a7de745 A |
631 | /* Since dtrace probes can call into KASan and vice versa, things can get |
632 | * very slow if we have a lot of probes. This call will disable the KASan | |
633 | * fakestack after a threshold of probes is reached. */ | |
634 | kasan_fakestack_suspend(); | |
a39ff7e2 A |
635 | #endif |
636 | ||
0a7de745 A |
637 | (void)ml_nofault_copy((vm_offset_t)&fbt->fbtp_patchval, (vm_offset_t)fbt->fbtp_patchpoint, |
638 | sizeof(fbt->fbtp_patchval)); | |
639 | /* | |
640 | * Make the patched instruction visible via a data + instruction | |
641 | * cache flush for the platforms that need it | |
642 | */ | |
643 | flush_dcache((vm_offset_t)fbt->fbtp_patchpoint, (vm_size_t)sizeof(fbt->fbtp_patchval), 0); | |
644 | invalidate_icache((vm_offset_t)fbt->fbtp_patchpoint, (vm_size_t)sizeof(fbt->fbtp_patchval), 0); | |
645 | fbt->fbtp_currentval = fbt->fbtp_patchval; | |
39037602 | 646 | |
0a7de745 A |
647 | ctl->mod_nenabled++; |
648 | } | |
6d2010ae A |
649 | } |
650 | ||
0a7de745 | 651 | dtrace_membar_consumer(); |
d9a64523 | 652 | |
0a7de745 | 653 | return 0; |
2d21ac55 A |
654 | } |
655 | ||
656 | /*ARGSUSED*/ | |
657 | static void | |
658 | fbt_disable(void *arg, dtrace_id_t id, void *parg) | |
659 | { | |
660 | #pragma unused(arg,id) | |
661 | fbt_probe_t *fbt = parg; | |
6d2010ae A |
662 | struct modctl *ctl = NULL; |
663 | ||
664 | for (; fbt != NULL; fbt = fbt->fbtp_next) { | |
0a7de745 | 665 | ctl = fbt->fbtp_ctl; |
d9a64523 | 666 | |
0a7de745 A |
667 | if (!ctl->mod_loaded || (ctl->mod_loadcnt != fbt->fbtp_loadcnt)) { |
668 | continue; | |
669 | } | |
2d21ac55 | 670 | |
0a7de745 A |
671 | if (fbt->fbtp_currentval != fbt->fbtp_savedval) { |
672 | (void)ml_nofault_copy((vm_offset_t)&fbt->fbtp_savedval, (vm_offset_t)fbt->fbtp_patchpoint, | |
673 | sizeof(fbt->fbtp_savedval)); | |
674 | /* | |
675 | * Make the patched instruction visible via a data + instruction | |
676 | * cache flush for the platforms that need it | |
677 | */ | |
678 | flush_dcache((vm_offset_t)fbt->fbtp_patchpoint, (vm_size_t)sizeof(fbt->fbtp_patchval), 0); | |
679 | invalidate_icache((vm_offset_t)fbt->fbtp_patchpoint, (vm_size_t)sizeof(fbt->fbtp_patchval), 0); | |
39037602 | 680 | |
0a7de745 A |
681 | fbt->fbtp_currentval = fbt->fbtp_savedval; |
682 | ASSERT(ctl->mod_nenabled > 0); | |
683 | ctl->mod_nenabled--; | |
a39ff7e2 A |
684 | |
685 | #if KASAN | |
0a7de745 | 686 | kasan_fakestack_resume(); |
a39ff7e2 | 687 | #endif |
0a7de745 | 688 | } |
6d2010ae | 689 | } |
2d21ac55 A |
690 | dtrace_membar_consumer(); |
691 | } | |
692 | ||
693 | /*ARGSUSED*/ | |
694 | static void | |
695 | fbt_suspend(void *arg, dtrace_id_t id, void *parg) | |
696 | { | |
697 | #pragma unused(arg,id) | |
698 | fbt_probe_t *fbt = parg; | |
6d2010ae | 699 | struct modctl *ctl = NULL; |
2d21ac55 | 700 | |
6d2010ae | 701 | for (; fbt != NULL; fbt = fbt->fbtp_next) { |
0a7de745 | 702 | ctl = fbt->fbtp_ctl; |
6d2010ae | 703 | |
0a7de745 A |
704 | ASSERT(ctl->mod_nenabled > 0); |
705 | if (!ctl->mod_loaded || (ctl->mod_loadcnt != fbt->fbtp_loadcnt)) { | |
706 | continue; | |
707 | } | |
6d2010ae | 708 | |
0a7de745 A |
709 | (void)ml_nofault_copy((vm_offset_t)&fbt->fbtp_savedval, (vm_offset_t)fbt->fbtp_patchpoint, |
710 | sizeof(fbt->fbtp_savedval)); | |
d9a64523 | 711 | |
39037602 A |
712 | /* |
713 | * Make the patched instruction visible via a data + instruction | |
714 | * cache flush for the platforms that need it | |
715 | */ | |
0a7de745 A |
716 | flush_dcache((vm_offset_t)fbt->fbtp_patchpoint, (vm_size_t)sizeof(fbt->fbtp_savedval), 0); |
717 | invalidate_icache((vm_offset_t)fbt->fbtp_patchpoint, (vm_size_t)sizeof(fbt->fbtp_savedval), 0); | |
d9a64523 | 718 | |
39037602 | 719 | fbt->fbtp_currentval = fbt->fbtp_savedval; |
6d2010ae | 720 | } |
d9a64523 | 721 | |
2d21ac55 A |
722 | dtrace_membar_consumer(); |
723 | } | |
724 | ||
725 | /*ARGSUSED*/ | |
726 | static void | |
727 | fbt_resume(void *arg, dtrace_id_t id, void *parg) | |
728 | { | |
729 | #pragma unused(arg,id) | |
730 | fbt_probe_t *fbt = parg; | |
6d2010ae | 731 | struct modctl *ctl = NULL; |
2d21ac55 | 732 | |
6d2010ae | 733 | for (; fbt != NULL; fbt = fbt->fbtp_next) { |
0a7de745 A |
734 | ctl = fbt->fbtp_ctl; |
735 | ||
736 | ASSERT(ctl->mod_nenabled > 0); | |
737 | if (!ctl->mod_loaded || (ctl->mod_loadcnt != fbt->fbtp_loadcnt)) { | |
738 | continue; | |
2d21ac55 | 739 | } |
d9a64523 | 740 | |
0a7de745 A |
741 | dtrace_casptr(&tempDTraceTrapHook, NULL, fbt_perfCallback); |
742 | if (tempDTraceTrapHook != (perfCallback)fbt_perfCallback) { | |
743 | if (fbt_verbose) { | |
744 | cmn_err(CE_NOTE, "fbt_resume is failing for probe %s " | |
745 | "in module %s: tempDTraceTrapHook already occupied.", | |
746 | fbt->fbtp_name, ctl->mod_modname); | |
747 | } | |
748 | return; | |
749 | } | |
750 | ||
751 | (void)ml_nofault_copy((vm_offset_t)&fbt->fbtp_patchval, (vm_offset_t)fbt->fbtp_patchpoint, | |
752 | sizeof(fbt->fbtp_patchval)); | |
39236c6e | 753 | |
5ba3f43e A |
754 | /* |
755 | * Make the patched instruction visible via a data + instruction cache flush. | |
756 | */ | |
0a7de745 A |
757 | flush_dcache((vm_offset_t)fbt->fbtp_patchpoint, (vm_size_t)sizeof(fbt->fbtp_patchval), 0); |
758 | invalidate_icache((vm_offset_t)fbt->fbtp_patchpoint, (vm_size_t)sizeof(fbt->fbtp_patchval), 0); | |
2d21ac55 | 759 | |
0a7de745 | 760 | fbt->fbtp_currentval = fbt->fbtp_patchval; |
2d21ac55 | 761 | } |
2d21ac55 | 762 | |
d9a64523 | 763 | dtrace_membar_consumer(); |
2d21ac55 | 764 | } |
2d21ac55 | 765 | |
5ba3f43e A |
766 | static void |
767 | fbt_provide_module_user_syms(struct modctl *ctl) | |
768 | { | |
769 | unsigned int i; | |
770 | char *modname = ctl->mod_modname; | |
771 | ||
772 | dtrace_module_symbols_t* module_symbols = ctl->mod_user_symbols; | |
773 | if (module_symbols) { | |
0a7de745 A |
774 | for (i = 0; i < module_symbols->dtmodsyms_count; i++) { |
775 | /* | |
5ba3f43e A |
776 | * symbol->dtsym_addr (the symbol address) passed in from |
777 | * user space, is already slid for both kexts and kernel. | |
778 | */ | |
779 | dtrace_symbol_t* symbol = &module_symbols->dtmodsyms_symbols[i]; | |
780 | ||
781 | char* name = symbol->dtsym_name; | |
782 | ||
783 | /* Lop off omnipresent leading underscore. */ | |
0a7de745 | 784 | if (*name == '_') { |
5ba3f43e | 785 | name += 1; |
0a7de745 | 786 | } |
5ba3f43e | 787 | |
0a7de745 | 788 | if (MOD_IS_MACH_KERNEL(ctl) && fbt_excluded(name)) { |
d9a64523 | 789 | continue; |
0a7de745 | 790 | } |
5ba3f43e A |
791 | |
792 | /* | |
793 | * Ignore symbols with a null address | |
794 | */ | |
0a7de745 | 795 | if (!symbol->dtsym_addr) { |
5ba3f43e | 796 | continue; |
0a7de745 | 797 | } |
5ba3f43e | 798 | |
d9a64523 A |
799 | /* |
800 | * Ignore symbols not part of this module | |
801 | */ | |
0a7de745 | 802 | if (!dtrace_addr_in_module((void*)symbol->dtsym_addr, ctl)) { |
d9a64523 | 803 | continue; |
0a7de745 | 804 | } |
d9a64523 A |
805 | |
806 | 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 |
807 | } |
808 | } | |
809 | } | |
d9a64523 A |
810 | static void |
811 | fbt_provide_kernel_section(struct modctl *ctl, kernel_section_t *sect, kernel_nlist_t *sym, uint32_t nsyms, const char *strings) | |
812 | { | |
813 | uintptr_t sect_start = (uintptr_t)sect->addr; | |
814 | uintptr_t sect_end = (uintptr_t)sect->size + sect->addr; | |
815 | unsigned int i; | |
816 | ||
817 | if ((sect->flags & S_ATTR_PURE_INSTRUCTIONS) != S_ATTR_PURE_INSTRUCTIONS) { | |
818 | return; | |
819 | } | |
820 | ||
821 | for (i = 0; i < nsyms; i++) { | |
822 | uint8_t n_type = sym[i].n_type & (N_TYPE | N_EXT); | |
823 | const char *name = strings + sym[i].n_un.n_strx; | |
824 | uint64_t limit; | |
825 | ||
0a7de745 | 826 | if (sym[i].n_value < sect_start || sym[i].n_value > sect_end) { |
d9a64523 | 827 | continue; |
0a7de745 | 828 | } |
d9a64523 A |
829 | |
830 | /* Check that the symbol is a global and that it has a name. */ | |
0a7de745 | 831 | if (((N_SECT | N_EXT) != n_type && (N_ABS | N_EXT) != n_type)) { |
d9a64523 | 832 | continue; |
0a7de745 | 833 | } |
d9a64523 | 834 | |
0a7de745 | 835 | if (0 == sym[i].n_un.n_strx) { /* iff a null, "", name. */ |
d9a64523 | 836 | continue; |
0a7de745 | 837 | } |
d9a64523 A |
838 | |
839 | /* Lop off omnipresent leading underscore. */ | |
0a7de745 | 840 | if (*name == '_') { |
d9a64523 | 841 | name += 1; |
0a7de745 | 842 | } |
d9a64523 A |
843 | |
844 | #if defined(__arm__) | |
845 | // Skip non-thumb functions on arm32 | |
846 | if (sym[i].n_sect == 1 && !(sym[i].n_desc & N_ARM_THUMB_DEF)) { | |
847 | continue; | |
848 | } | |
849 | #endif /* defined(__arm__) */ | |
850 | ||
0a7de745 | 851 | if (MOD_IS_MACH_KERNEL(ctl) && fbt_excluded(name)) { |
d9a64523 | 852 | continue; |
0a7de745 | 853 | } |
d9a64523 A |
854 | |
855 | /* | |
856 | * Find the function boundary by looking at either the | |
857 | * end of the section or the beginning of the next symbol | |
858 | */ | |
859 | if (i == nsyms - 1) { | |
860 | limit = sect_end; | |
0a7de745 | 861 | } else { |
d9a64523 A |
862 | limit = sym[i + 1].n_value; |
863 | } | |
864 | ||
865 | fbt_provide_probe(ctl, ctl->mod_modname, name, (machine_inst_t*)sym[i].n_value, (machine_inst_t*)limit); | |
866 | } | |
d9a64523 A |
867 | } |
868 | ||
869 | static int | |
870 | fbt_sym_cmp(const void *ap, const void *bp) | |
871 | { | |
872 | return (int)(((const kernel_nlist_t*)ap)->n_value - ((const kernel_nlist_t*)bp)->n_value); | |
873 | } | |
874 | ||
875 | static void | |
876 | fbt_provide_module_kernel_syms(struct modctl *ctl) | |
877 | { | |
878 | kernel_mach_header_t *mh = (kernel_mach_header_t *)(ctl->mod_address); | |
879 | kernel_segment_command_t *seg; | |
880 | struct load_command *cmd; | |
881 | kernel_segment_command_t *linkedit = NULL; | |
882 | struct symtab_command *symtab = NULL; | |
883 | kernel_nlist_t *syms = NULL, *sorted_syms = NULL; | |
884 | const char *strings; | |
885 | unsigned int i; | |
886 | size_t symlen; | |
887 | ||
0a7de745 | 888 | if (mh->magic != MH_MAGIC_KERNEL) { |
d9a64523 | 889 | return; |
0a7de745 | 890 | } |
d9a64523 A |
891 | |
892 | cmd = (struct load_command *) &mh[1]; | |
893 | for (i = 0; i < mh->ncmds; i++) { | |
894 | if (cmd->cmd == LC_SEGMENT_KERNEL) { | |
895 | kernel_segment_command_t *orig_sg = (kernel_segment_command_t *) cmd; | |
0a7de745 | 896 | if (LIT_STRNEQL(orig_sg->segname, SEG_LINKEDIT)) { |
d9a64523 | 897 | linkedit = orig_sg; |
0a7de745 | 898 | } |
d9a64523 A |
899 | } else if (cmd->cmd == LC_SYMTAB) { |
900 | symtab = (struct symtab_command *) cmd; | |
901 | } | |
902 | if (symtab && linkedit) { | |
903 | break; | |
904 | } | |
905 | cmd = (struct load_command *) ((caddr_t) cmd + cmd->cmdsize); | |
906 | } | |
5ba3f43e | 907 | |
d9a64523 A |
908 | if ((symtab == NULL) || (linkedit == NULL)) { |
909 | return; | |
910 | } | |
911 | ||
912 | syms = (kernel_nlist_t *)(linkedit->vmaddr + symtab->symoff - linkedit->fileoff); | |
913 | strings = (const char *)(linkedit->vmaddr + symtab->stroff - linkedit->fileoff); | |
914 | ||
915 | /* | |
916 | * Make a copy of the symbol table and sort it to not cross into the next function | |
917 | * when disassembling the function | |
918 | */ | |
919 | symlen = sizeof(kernel_nlist_t) * symtab->nsyms; | |
920 | sorted_syms = kmem_alloc(symlen, KM_SLEEP); | |
921 | bcopy(syms, sorted_syms, symlen); | |
922 | qsort(sorted_syms, symtab->nsyms, sizeof(kernel_nlist_t), fbt_sym_cmp); | |
923 | ||
924 | for (seg = firstsegfromheader(mh); seg != NULL; seg = nextsegfromheader(mh, seg)) { | |
925 | kernel_section_t *sect = firstsect(seg); | |
926 | ||
927 | if (strcmp(seg->segname, "__KLD") == 0) { | |
928 | continue; | |
929 | } | |
930 | ||
931 | for (sect = firstsect(seg); sect != NULL; sect = nextsect(seg, sect)) { | |
932 | fbt_provide_kernel_section(ctl, sect, sorted_syms, symtab->nsyms, strings); | |
933 | } | |
934 | } | |
935 | ||
936 | kmem_free(sorted_syms, symlen); | |
937 | } | |
5ba3f43e A |
938 | |
939 | void | |
940 | fbt_provide_module(void *arg, struct modctl *ctl) | |
941 | { | |
942 | #pragma unused(arg) | |
943 | ASSERT(ctl != NULL); | |
944 | ASSERT(dtrace_kernel_symbol_mode != DTRACE_KERNEL_SYMBOLS_NEVER); | |
945 | LCK_MTX_ASSERT(&mod_lock, LCK_MTX_ASSERT_OWNED); | |
946 | ||
947 | // Update the "ignore blacklist" bit | |
0a7de745 | 948 | if (ignore_fbt_blacklist) { |
5ba3f43e | 949 | ctl->mod_flags |= MODCTL_FBT_PROVIDE_BLACKLISTED_PROBES; |
0a7de745 | 950 | } |
5ba3f43e | 951 | |
0a7de745 | 952 | if (MOD_FBT_DONE(ctl)) { |
5ba3f43e | 953 | return; |
0a7de745 | 954 | } |
5ba3f43e A |
955 | |
956 | if (fbt_module_excluded(ctl)) { | |
957 | ctl->mod_flags |= MODCTL_FBT_INVALID; | |
958 | return; | |
959 | } | |
960 | ||
961 | if (MOD_HAS_KERNEL_SYMBOLS(ctl)) { | |
962 | fbt_provide_module_kernel_syms(ctl); | |
963 | ctl->mod_flags |= MODCTL_FBT_PROBES_PROVIDED; | |
0a7de745 | 964 | if (MOD_FBT_PROVIDE_BLACKLISTED_PROBES(ctl)) { |
5ba3f43e | 965 | ctl->mod_flags |= MODCTL_FBT_BLACKLISTED_PROBES_PROVIDED; |
0a7de745 | 966 | } |
5ba3f43e A |
967 | return; |
968 | } | |
969 | ||
970 | if (MOD_HAS_USERSPACE_SYMBOLS(ctl)) { | |
971 | fbt_provide_module_user_syms(ctl); | |
972 | ctl->mod_flags |= MODCTL_FBT_PROBES_PROVIDED; | |
0a7de745 | 973 | if (MOD_FBT_PROVIDE_PRIVATE_PROBES(ctl)) { |
5ba3f43e | 974 | ctl->mod_flags |= MODCTL_FBT_PRIVATE_PROBES_PROVIDED; |
0a7de745 A |
975 | } |
976 | if (MOD_FBT_PROVIDE_BLACKLISTED_PROBES(ctl)) { | |
5ba3f43e | 977 | ctl->mod_flags |= MODCTL_FBT_BLACKLISTED_PROBES_PROVIDED; |
0a7de745 | 978 | } |
5ba3f43e A |
979 | return; |
980 | } | |
981 | } | |
982 | ||
2d21ac55 | 983 | static dtrace_pattr_t fbt_attr = { |
0a7de745 A |
984 | { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA }, |
985 | { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, | |
986 | { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, | |
987 | { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA }, | |
988 | { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA }, | |
2d21ac55 A |
989 | }; |
990 | ||
991 | static dtrace_pops_t fbt_pops = { | |
0a7de745 A |
992 | .dtps_provide = NULL, |
993 | .dtps_provide_module = fbt_provide_module, | |
994 | .dtps_enable = fbt_enable, | |
995 | .dtps_disable = fbt_disable, | |
996 | .dtps_suspend = fbt_suspend, | |
997 | .dtps_resume = fbt_resume, | |
998 | .dtps_getargdesc = NULL, /* APPLE NOTE: fbt_getargdesc implemented in userspace */ | |
999 | .dtps_getargval = NULL, | |
1000 | .dtps_usermode = NULL, | |
1001 | .dtps_destroy = fbt_destroy | |
2d21ac55 A |
1002 | }; |
1003 | ||
1004 | static void | |
1005 | fbt_cleanup(dev_info_t *devi) | |
1006 | { | |
1007 | dtrace_invop_remove(fbt_invop); | |
1008 | ddi_remove_minor_node(devi, NULL); | |
0a7de745 | 1009 | kmem_free(fbt_probetab, fbt_probetab_size * sizeof(fbt_probe_t *)); |
2d21ac55 A |
1010 | fbt_probetab = NULL; |
1011 | fbt_probetab_mask = 0; | |
1012 | } | |
1013 | ||
1014 | static int | |
d9a64523 | 1015 | fbt_attach(dev_info_t *devi) |
2d21ac55 | 1016 | { |
0a7de745 | 1017 | if (fbt_probetab_size == 0) { |
2d21ac55 | 1018 | fbt_probetab_size = FBT_PROBETAB_SIZE; |
0a7de745 | 1019 | } |
2d21ac55 A |
1020 | |
1021 | fbt_probetab_mask = fbt_probetab_size - 1; | |
1022 | fbt_probetab = | |
0a7de745 | 1023 | kmem_zalloc(fbt_probetab_size * sizeof(fbt_probe_t *), KM_SLEEP); |
2d21ac55 A |
1024 | |
1025 | dtrace_invop_add(fbt_invop); | |
1026 | ||
b0d623f7 A |
1027 | if (ddi_create_minor_node(devi, "fbt", S_IFCHR, 0, |
1028 | DDI_PSEUDO, 0) == DDI_FAILURE || | |
1029 | dtrace_register("fbt", &fbt_attr, DTRACE_PRIV_KERNEL, NULL, | |
1030 | &fbt_pops, NULL, &fbt_id) != 0) { | |
1031 | fbt_cleanup(devi); | |
0a7de745 | 1032 | return DDI_FAILURE; |
b0d623f7 | 1033 | } |
2d21ac55 | 1034 | |
0a7de745 | 1035 | return DDI_SUCCESS; |
2d21ac55 A |
1036 | } |
1037 | ||
1038 | static d_open_t _fbt_open; | |
1039 | ||
1040 | static int | |
1041 | _fbt_open(dev_t dev, int flags, int devtype, struct proc *p) | |
1042 | { | |
1043 | #pragma unused(dev,flags,devtype,p) | |
1044 | return 0; | |
1045 | } | |
1046 | ||
1047 | #define FBT_MAJOR -24 /* let the kernel pick the device number */ | |
1048 | ||
5ba3f43e A |
1049 | SYSCTL_DECL(_kern_dtrace); |
1050 | ||
1051 | static int | |
1052 | sysctl_dtrace_ignore_fbt_blacklist SYSCTL_HANDLER_ARGS | |
1053 | { | |
1054 | #pragma unused(oidp, arg2) | |
1055 | int err; | |
1056 | int value = *(int*)arg1; | |
1057 | ||
1058 | err = sysctl_io_number(req, value, sizeof(value), &value, NULL); | |
0a7de745 A |
1059 | if (err) { |
1060 | return err; | |
1061 | } | |
5ba3f43e | 1062 | if (req->newptr) { |
0a7de745 A |
1063 | if (!(value == 0 || value == 1)) { |
1064 | return ERANGE; | |
1065 | } | |
5ba3f43e A |
1066 | |
1067 | /* | |
1068 | * We do not allow setting the blacklist back to on, as we have no way | |
1069 | * of knowing if those unsafe probes are still used. | |
1070 | * | |
1071 | * If we are using kernel symbols, we also do not allow any change, | |
1072 | * since the symbols are jettison'd after the first pass. | |
1073 | * | |
1074 | * We do not need to take any locks here because those symbol modes | |
1075 | * are permanent and do not change after boot. | |
1076 | */ | |
1077 | if (value != 1 || dtrace_kernel_symbol_mode == DTRACE_KERNEL_SYMBOLS_NEVER || | |
0a7de745 A |
1078 | dtrace_kernel_symbol_mode == DTRACE_KERNEL_SYMBOLS_ALWAYS_FROM_KERNEL) { |
1079 | return EPERM; | |
1080 | } | |
5ba3f43e A |
1081 | |
1082 | ignore_fbt_blacklist = 1; | |
1083 | } | |
1084 | ||
0a7de745 | 1085 | return 0; |
5ba3f43e A |
1086 | } |
1087 | ||
1088 | SYSCTL_PROC(_kern_dtrace, OID_AUTO, ignore_fbt_blacklist, | |
0a7de745 A |
1089 | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, |
1090 | &ignore_fbt_blacklist, 0, | |
1091 | sysctl_dtrace_ignore_fbt_blacklist, "I", "fbt provider ignore blacklist"); | |
5ba3f43e | 1092 | |
2d21ac55 A |
1093 | /* |
1094 | * A struct describing which functions will get invoked for certain | |
1095 | * actions. | |
1096 | */ | |
1097 | static struct cdevsw fbt_cdevsw = | |
1098 | { | |
0a7de745 A |
1099 | _fbt_open, /* open */ |
1100 | eno_opcl, /* close */ | |
1101 | eno_rdwrt, /* read */ | |
1102 | eno_rdwrt, /* write */ | |
1103 | eno_ioctl, /* ioctl */ | |
2d21ac55 A |
1104 | (stop_fcn_t *)nulldev, /* stop */ |
1105 | (reset_fcn_t *)nulldev, /* reset */ | |
0a7de745 A |
1106 | NULL, /* tty's */ |
1107 | eno_select, /* select */ | |
1108 | eno_mmap, /* mmap */ | |
1109 | eno_strat, /* strategy */ | |
1110 | eno_getc, /* getc */ | |
1111 | eno_putc, /* putc */ | |
1112 | 0 /* type */ | |
2d21ac55 A |
1113 | }; |
1114 | ||
2d21ac55 A |
1115 | #undef kmem_alloc /* from its binding to dt_kmem_alloc glue */ |
1116 | #undef kmem_free /* from its binding to dt_kmem_free glue */ | |
1117 | #include <vm/vm_kern.h> | |
1118 | ||
1119 | void | |
1120 | fbt_init( void ) | |
1121 | { | |
d9a64523 | 1122 | int majdevno = cdevsw_add(FBT_MAJOR, &fbt_cdevsw); |
2d21ac55 | 1123 | |
d9a64523 A |
1124 | if (majdevno < 0) { |
1125 | printf("fbt_init: failed to allocate a major number!\n"); | |
1126 | return; | |
2d21ac55 | 1127 | } |
d9a64523 | 1128 | |
0a7de745 | 1129 | PE_parse_boot_argn("IgnoreFBTBlacklist", &ignore_fbt_blacklist, sizeof(ignore_fbt_blacklist)); |
d9a64523 A |
1130 | |
1131 | fbt_attach((dev_info_t*)(uintptr_t)majdevno); | |
2d21ac55 A |
1132 | } |
1133 | #undef FBT_MAJOR |