#ifdef KERNEL
#ifndef _KERNEL
#define _KERNEL
#endif
#endif
#include <mach-o/loader.h>
#include <libkern/kernel_mach_header.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/errno.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
#include <miscfs/devfs/devfs.h>
#include <pexpert/pexpert.h>
#include <sys/dtrace.h>
#include <sys/dtrace_impl.h>
#include <sys/fbt.h>
#include <sys/dtrace_glue.h>
struct savearea_t;
#if defined(__arm__) || defined(__arm64__)
typedef kern_return_t (*perfCallback)(int, struct savearea_t *, __unused int, __unused int);
extern perfCallback tempDTraceTrapHook;
extern kern_return_t fbt_perfCallback(int, struct savearea_t *, __unused int, __unused int);
#elif defined(__x86_64__)
typedef kern_return_t (*perfCallback)(int, struct savearea_t *, uintptr_t *, __unused int);
extern perfCallback tempDTraceTrapHook;
extern kern_return_t fbt_perfCallback(int, struct savearea_t *, uintptr_t *, __unused int);
#else
#error Unknown architecture
#endif
#define FBT_ADDR2NDX(addr) ((((uintptr_t)(addr)) >> 4) & fbt_probetab_mask)
#define FBT_PROBETAB_SIZE 0x8000
static dev_info_t *fbt_devi;
static int fbt_probetab_size;
dtrace_provider_id_t fbt_id;
fbt_probe_t **fbt_probetab;
int fbt_probetab_mask;
static int fbt_verbose = 0;
int ignore_fbt_blacklist = 0;
extern int dtrace_kernel_symbol_mode;
void fbt_init( void );
static const char * critical_blacklist[] =
{
"Call_DebuggerC",
"SysChoked",
"_ZN9IOService14newTemperatureElPS_",
"_ZN9IOService26temperatureCriticalForZoneEPS_",
"_ZNK6OSData14getBytesNoCopyEv",
"_disable_preemption",
"_enable_preemption",
"bcopy_phys",
"console_cpu_alloc",
"console_cpu_free",
"cpu_IA32e_disable",
"cpu_IA32e_enable",
"cpu_NMI_interrupt",
"cpu_control",
"cpu_data_alloc",
"cpu_desc_init",
"cpu_desc_init64",
"cpu_desc_load",
"cpu_desc_load64",
"cpu_exit_wait",
"cpu_info",
"cpu_info_count",
"cpu_init",
"cpu_interrupt",
"cpu_machine_init",
"cpu_mode_init",
"cpu_processor_alloc",
"cpu_processor_free",
"cpu_signal_handler",
"cpu_sleep",
"cpu_start",
"cpu_subtype",
"cpu_thread_alloc",
"cpu_thread_halt",
"cpu_thread_init",
"cpu_threadtype",
"cpu_to_processor",
"cpu_topology_sort",
"cpu_topology_start_cpu",
"cpu_type",
"cpuid_cpu_display",
"cpuid_extfeatures",
"dtrace_invop",
"enter_lohandler",
"fbt_invop",
"fbt_perfCallback",
"get_threadtask",
"handle_pending_TLB_flushes",
"hw_compare_and_store",
"interrupt",
"kernel_trap",
"kprintf",
"lo_alltraps",
"lock_debugger",
"machine_idle_cstate",
"machine_thread_get_kern_state",
"mca_cpu_alloc",
"mca_cpu_init",
"ml_nofault_copy",
"nanoseconds_to_absolutetime",
"nanotime_to_absolutetime",
"packA",
"panic",
"pmKextRegister",
"pmMarkAllCPUsOff",
"pmSafeMode",
"pmTimerRestore",
"pmTimerSave",
"pmUnRegister",
"pmap_cpu_alloc",
"pmap_cpu_free",
"pmap_cpu_high_map_vaddr",
"pmap_cpu_high_shared_remap",
"pmap_cpu_init",
"power_management_init",
"preemption_underflow_panic",
"register_cpu_setup_func",
"sdt_invop",
"sprlock",
"sprunlock",
"t_invop",
"tmrCvt",
"uread",
"uwrite",
"unlock_debugger",
"unpackA",
"unregister_cpu_setup_func",
"vstart"
};
#define CRITICAL_BLACKLIST_COUNT (sizeof(critical_blacklist)/sizeof(critical_blacklist[0]))
static const char * probe_ctx_closure[] =
{
"ClearIdlePop",
"Debugger",
"IS_64BIT_PROCESS",
"OSCompareAndSwap",
"SetIdlePop",
"absolutetime_to_microtime",
"act_set_astbsd",
"arm_init_idle_cpu",
"ast_dtrace_on",
"ast_pending",
"clean_dcache",
"clean_mmu_dcache",
"clock_get_calendar_nanotime_nowait",
"copyin",
"copyin_kern",
"copyin_user",
"copyinstr",
"copyout",
"copyoutstr",
"cpu_number",
"current_proc",
"current_processor",
"current_task",
"current_thread",
"debug_enter",
"drain_write_buffer",
"find_user_regs",
"flush_dcache",
"flush_tlb64",
"get_bsdtask_info",
"get_bsdthread_info",
"hertz_tick",
"hw_atomic_and",
"invalidate_mmu_icache",
"kauth_cred_get",
"kauth_getgid",
"kauth_getuid",
"kernel_preempt_check",
"kvtophys",
"mach_absolute_time",
"max_valid_stack_address",
"memcpy",
"memmove",
"ml_at_interrupt_context",
"ml_phys_write_byte_64",
"ml_phys_write_half_64",
"ml_phys_write_word_64",
"ml_set_interrupts_enabled",
"mt_core_snap",
"mt_cur_cpu_cycles",
"mt_cur_cpu_instrs",
"mt_cur_thread_cycles",
"mt_cur_thread_instrs",
"mt_fixed_counts",
"mt_fixed_counts_internal",
"mt_mtc_update_count",
"mt_update_thread",
"ovbcopy",
"panic",
"pmap64_pde",
"pmap64_pdpt",
"pmap_find_phys",
"pmap_get_mapwindow",
"pmap_pde",
"pmap_pte",
"pmap_put_mapwindow",
"pmap_valid_page",
"prf",
"proc_is64bit",
"proc_selfname",
"psignal_lock",
"rtc_nanotime_load",
"rtc_nanotime_read",
"sdt_getargdesc",
"setPop",
"strlcpy",
"sync_iss_to_iks_unconditionally",
"systrace_stub",
"timer_grab"
};
#define PROBE_CTX_CLOSURE_COUNT (sizeof(probe_ctx_closure)/sizeof(probe_ctx_closure[0]))
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wcast-qual"
static int _cmp(const void *a, const void *b)
{
return strncmp((const char *)a, *(const char **)b, strlen((const char *)a) + 1);
}
#pragma clang diagnostic pop
int
fbt_module_excluded(struct modctl* ctl)
{
ASSERT(!MOD_FBT_DONE(ctl));
if (ctl->mod_address == 0 || ctl->mod_size == 0) {
return TRUE;
}
if (ctl->mod_loaded == 0) {
return TRUE;
}
if (ignore_fbt_blacklist)
return FALSE;
#ifdef __x86_64__
if (strstr(ctl->mod_modname, "AppleACPIEC") != NULL)
return TRUE;
if (strstr(ctl->mod_modname, "AppleACPIPlatform") != NULL)
return TRUE;
if (strstr(ctl->mod_modname, "AppleRTC") != NULL)
return TRUE;
if (strstr(ctl->mod_modname, "IOACPIFamily") != NULL)
return TRUE;
if (strstr(ctl->mod_modname, "AppleIntelCPUPowerManagement") != NULL)
return TRUE;
if (strstr(ctl->mod_modname, "AppleProfile") != NULL)
return TRUE;
if (strstr(ctl->mod_modname, "AppleIntelProfile") != NULL)
return TRUE;
if (strstr(ctl->mod_modname, "AppleEFI") != NULL)
return TRUE;
#elif __arm__ || __arm64__
if (LIT_STRNEQL(ctl->mod_modname, "com.apple.driver.AppleARMPlatform") ||
LIT_STRNEQL(ctl->mod_modname, "com.apple.driver.AppleARMPL192VIC") ||
LIT_STRNEQL(ctl->mod_modname, "com.apple.driver.AppleInterruptController"))
return TRUE;
#endif
return FALSE;
}
int
fbt_excluded(const char* name)
{
if (ignore_fbt_blacklist)
return FALSE;
if (LIT_STRNSTART(name, "dtrace_") && !LIT_STRNSTART(name, "dtrace_safe_")) {
return TRUE;
}
if (bsearch( name, critical_blacklist, CRITICAL_BLACKLIST_COUNT, sizeof(name), _cmp ) != NULL)
return TRUE;
if (bsearch( name, probe_ctx_closure, PROBE_CTX_CLOSURE_COUNT, sizeof(name), _cmp ) != NULL) {
return TRUE;
}
if (LIT_STRNSTART(name, "cpu_") ||
LIT_STRNSTART(name, "platform_") ||
LIT_STRNSTART(name, "machine_") ||
LIT_STRNSTART(name, "ml_") ||
LIT_STRNSTART(name, "PE_") ||
LIT_STRNSTART(name, "rtc_") ||
LIT_STRNSTART(name, "_rtc_") ||
LIT_STRNSTART(name, "rtclock_") ||
LIT_STRNSTART(name, "clock_") ||
LIT_STRNSTART(name, "bcopy") ||
LIT_STRNSTART(name, "pmap_") ||
LIT_STRNSTART(name, "hw_") ||
LIT_STRNSTART(name, "lapic_") ||
LIT_STRNSTART(name, "OSAdd") ||
LIT_STRNSTART(name, "OSBit") ||
LIT_STRNSTART(name, "OSDecrement") ||
LIT_STRNSTART(name, "OSIncrement") ||
LIT_STRNSTART(name, "OSCompareAndSwap") ||
LIT_STRNSTART(name, "etimer_") ||
LIT_STRNSTART(name, "dtxnu_kern_") ||
LIT_STRNSTART(name, "flush_mmu_tlb_"))
return TRUE;
if (LIT_STRNSTART(name, "fasttrap_") ||
LIT_STRNSTART(name, "fuword") ||
LIT_STRNSTART(name, "suword"))
return TRUE;
if (LIT_STRNSTART(name, "_dtrace"))
return TRUE;
if (LIT_STRNSTART(name, "hibernate_"))
return TRUE;
#if __arm__ || __arm64__
if (LIT_STRNSTART(name, "fleh_") ||
LIT_STRNSTART(name, "sleh_") ||
LIT_STRNSTART(name, "timer_state_event") ||
LIT_STRNEQL(name, "get_vfp_enabled"))
return TRUE;
if (LIT_STRNSTART(name, "_ZNK15OSMetaClassBase8metaCastEPK11OSMetaClass") ||
LIT_STRNSTART(name, "_ZN15OSMetaClassBase12safeMetaCastEPKS_PK11OSMetaClass") ||
LIT_STRNSTART(name, "_ZNK11OSMetaClass13checkMetaCastEPK15OSMetaClassBase"))
return TRUE;
#endif
#ifdef __x86_64__
if (LIT_STRNSTART(name, "machine_") ||
LIT_STRNSTART(name, "mapping_") ||
LIT_STRNSTART(name, "tsc_") ||
LIT_STRNSTART(name, "pmCPU") ||
LIT_STRNSTART(name, "pms") ||
LIT_STRNSTART(name, "usimple_") ||
LIT_STRNSTART(name, "lck_spin_lock") ||
LIT_STRNSTART(name, "lck_spin_unlock") ||
LIT_STRNSTART(name, "absolutetime_to_") ||
LIT_STRNSTART(name, "commpage_") ||
LIT_STRNSTART(name, "ml_") ||
LIT_STRNSTART(name, "PE_") ||
LIT_STRNSTART(name, "act_machine") ||
LIT_STRNSTART(name, "acpi_") ||
LIT_STRNSTART(name, "pal_")) {
return TRUE;
}
if (LIT_STRNSTART(name, "dsmos_"))
return TRUE;
#endif
if (LIT_STRNSTART(name, "kdp_") ||
LIT_STRNSTART(name, "kdb_") ||
LIT_STRNSTART(name, "debug_")) {
return TRUE;
}
if (NULL != strstr(name, "panic_"))
return TRUE;
return FALSE;
}
static void
fbt_destroy(void *arg, dtrace_id_t id, void *parg)
{
#pragma unused(arg,id)
fbt_probe_t *fbt = parg, *next, *hash, *last;
int ndx;
do {
ndx = FBT_ADDR2NDX(fbt->fbtp_patchpoint);
last = NULL;
hash = fbt_probetab[ndx];
while (hash != fbt) {
ASSERT(hash != NULL);
last = hash;
hash = hash->fbtp_hashnext;
}
if (last != NULL) {
last->fbtp_hashnext = fbt->fbtp_hashnext;
} else {
fbt_probetab[ndx] = fbt->fbtp_hashnext;
}
next = fbt->fbtp_next;
kmem_free(fbt, sizeof (fbt_probe_t));
fbt = next;
} while (fbt != NULL);
}
int
fbt_enable(void *arg, dtrace_id_t id, void *parg)
{
#pragma unused(arg,id)
fbt_probe_t *fbt = parg;
struct modctl *ctl = NULL;
for (; fbt != NULL; fbt = fbt->fbtp_next) {
ctl = fbt->fbtp_ctl;
if (!ctl->mod_loaded) {
if (fbt_verbose) {
cmn_err(CE_NOTE, "fbt is failing for probe %s "
"(module %s unloaded)",
fbt->fbtp_name, ctl->mod_modname);
}
continue;
}
if (ctl->mod_loadcnt != fbt->fbtp_loadcnt) {
if (fbt_verbose) {
cmn_err(CE_NOTE, "fbt is failing for probe %s "
"(module %s reloaded)",
fbt->fbtp_name, ctl->mod_modname);
}
continue;
}
dtrace_casptr(&tempDTraceTrapHook, NULL, fbt_perfCallback);
if (tempDTraceTrapHook != (perfCallback)fbt_perfCallback) {
if (fbt_verbose) {
cmn_err(CE_NOTE, "fbt_enable is failing for probe %s "
"in module %s: tempDTraceTrapHook already occupied.",
fbt->fbtp_name, ctl->mod_modname);
}
continue;
}
if (fbt->fbtp_currentval != fbt->fbtp_patchval) {
(void)ml_nofault_copy( (vm_offset_t)&fbt->fbtp_patchval, (vm_offset_t)fbt->fbtp_patchpoint,
sizeof(fbt->fbtp_patchval));
flush_dcache((vm_offset_t)fbt->fbtp_patchpoint,(vm_size_t)sizeof(fbt->fbtp_patchval), 0);
invalidate_icache((vm_offset_t)fbt->fbtp_patchpoint,(vm_size_t)sizeof(fbt->fbtp_patchval), 0);
fbt->fbtp_currentval = fbt->fbtp_patchval;
ctl->mod_nenabled++;
}
}
dtrace_membar_consumer();
return (0);
}
static void
fbt_disable(void *arg, dtrace_id_t id, void *parg)
{
#pragma unused(arg,id)
fbt_probe_t *fbt = parg;
struct modctl *ctl = NULL;
for (; fbt != NULL; fbt = fbt->fbtp_next) {
ctl = fbt->fbtp_ctl;
if (!ctl->mod_loaded || (ctl->mod_loadcnt != fbt->fbtp_loadcnt))
continue;
if (fbt->fbtp_currentval != fbt->fbtp_savedval) {
(void)ml_nofault_copy( (vm_offset_t)&fbt->fbtp_savedval, (vm_offset_t)fbt->fbtp_patchpoint,
sizeof(fbt->fbtp_savedval));
flush_dcache((vm_offset_t)fbt->fbtp_patchpoint,(vm_size_t)sizeof(fbt->fbtp_patchval), 0);
invalidate_icache((vm_offset_t)fbt->fbtp_patchpoint,(vm_size_t)sizeof(fbt->fbtp_patchval), 0);
fbt->fbtp_currentval = fbt->fbtp_savedval;
ASSERT(ctl->mod_nenabled > 0);
ctl->mod_nenabled--;
}
}
dtrace_membar_consumer();
}
static void
fbt_suspend(void *arg, dtrace_id_t id, void *parg)
{
#pragma unused(arg,id)
fbt_probe_t *fbt = parg;
struct modctl *ctl = NULL;
for (; fbt != NULL; fbt = fbt->fbtp_next) {
ctl = fbt->fbtp_ctl;
ASSERT(ctl->mod_nenabled > 0);
if (!ctl->mod_loaded || (ctl->mod_loadcnt != fbt->fbtp_loadcnt))
continue;
(void)ml_nofault_copy( (vm_offset_t)&fbt->fbtp_savedval, (vm_offset_t)fbt->fbtp_patchpoint,
sizeof(fbt->fbtp_savedval));
flush_dcache((vm_offset_t)fbt->fbtp_patchpoint,(vm_size_t)sizeof(fbt->fbtp_savedval), 0);
invalidate_icache((vm_offset_t)fbt->fbtp_patchpoint,(vm_size_t)sizeof(fbt->fbtp_savedval), 0);
fbt->fbtp_currentval = fbt->fbtp_savedval;
}
dtrace_membar_consumer();
}
static void
fbt_resume(void *arg, dtrace_id_t id, void *parg)
{
#pragma unused(arg,id)
fbt_probe_t *fbt = parg;
struct modctl *ctl = NULL;
for (; fbt != NULL; fbt = fbt->fbtp_next) {
ctl = fbt->fbtp_ctl;
ASSERT(ctl->mod_nenabled > 0);
if (!ctl->mod_loaded || (ctl->mod_loadcnt != fbt->fbtp_loadcnt))
continue;
dtrace_casptr(&tempDTraceTrapHook, NULL, fbt_perfCallback);
if (tempDTraceTrapHook != (perfCallback)fbt_perfCallback) {
if (fbt_verbose) {
cmn_err(CE_NOTE, "fbt_resume is failing for probe %s "
"in module %s: tempDTraceTrapHook already occupied.",
fbt->fbtp_name, ctl->mod_modname);
}
return;
}
(void)ml_nofault_copy( (vm_offset_t)&fbt->fbtp_patchval, (vm_offset_t)fbt->fbtp_patchpoint,
sizeof(fbt->fbtp_patchval));
#if CONFIG_EMBEDDED
flush_dcache((vm_offset_t)fbt->fbtp_patchpoint,(vm_size_t)sizeof(fbt->fbtp_patchval), 0);
invalidate_icache((vm_offset_t)fbt->fbtp_patchpoint,(vm_size_t)sizeof(fbt->fbtp_patchval), 0);
#endif
fbt->fbtp_currentval = fbt->fbtp_patchval;
}
dtrace_membar_consumer();
}
#if !defined(__APPLE__)
static void
fbt_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
{
fbt_probe_t *fbt = parg;
struct modctl *ctl = fbt->fbtp_ctl;
struct module *mp = ctl->mod_mp;
ctf_file_t *fp = NULL, *pfp;
ctf_funcinfo_t f;
int error;
ctf_id_t argv[32], type;
int argc = sizeof (argv) / sizeof (ctf_id_t);
const char *parent;
if (!ctl->mod_loaded || (ctl->mod_loadcnt != fbt->fbtp_loadcnt))
goto err;
if (fbt->fbtp_roffset != 0 && desc->dtargd_ndx == 0) {
(void) strlcpy(desc->dtargd_native, "int",
sizeof(desc->dtargd_native));
return;
}
if ((fp = ctf_modopen(mp, &error)) == NULL) {
goto err;
}
if ((parent = ctf_parent_name(fp)) != NULL) {
struct modctl *mp = &modules;
struct modctl *mod = NULL;
do {
if (strcmp(mp->mod_modname, parent) == 0) {
mod = mp;
break;
}
} while ((mp = mp->mod_next) != &modules);
if (mod == NULL)
goto err;
if ((pfp = ctf_modopen(mod->mod_mp, &error)) == NULL) {
goto err;
}
if (ctf_import(fp, pfp) != 0) {
ctf_close(pfp);
goto err;
}
ctf_close(pfp);
}
if (ctf_func_info(fp, fbt->fbtp_symndx, &f) == CTF_ERR)
goto err;
if (fbt->fbtp_roffset != 0) {
if (desc->dtargd_ndx > 1)
goto err;
ASSERT(desc->dtargd_ndx == 1);
type = f.ctc_return;
} else {
if (desc->dtargd_ndx + 1 > f.ctc_argc)
goto err;
if (ctf_func_args(fp, fbt->fbtp_symndx, argc, argv) == CTF_ERR)
goto err;
type = argv[desc->dtargd_ndx];
}
if (ctf_type_name(fp, type, desc->dtargd_native,
DTRACE_ARGTYPELEN) != NULL) {
ctf_close(fp);
return;
}
err:
if (fp != NULL)
ctf_close(fp);
desc->dtargd_ndx = DTRACE_ARGNONE;
}
#endif
static void
fbt_provide_module_user_syms(struct modctl *ctl)
{
unsigned int i;
char *modname = ctl->mod_modname;
dtrace_module_symbols_t* module_symbols = ctl->mod_user_symbols;
if (module_symbols) {
for (i=0; i<module_symbols->dtmodsyms_count; i++) {
dtrace_symbol_t* symbol = &module_symbols->dtmodsyms_symbols[i];
char* name = symbol->dtsym_name;
if (*name == '_')
name += 1;
if (MOD_IS_MACH_KERNEL(ctl) && fbt_excluded(name))
continue;
if (!symbol->dtsym_addr)
continue;
fbt_provide_probe(ctl, (uintptr_t)symbol->dtsym_addr, (uintptr_t)(symbol->dtsym_addr + symbol->dtsym_size), modname, name, (machine_inst_t*)(uintptr_t)symbol->dtsym_addr);
}
}
}
void
fbt_provide_module(void *arg, struct modctl *ctl)
{
#pragma unused(arg)
ASSERT(ctl != NULL);
ASSERT(dtrace_kernel_symbol_mode != DTRACE_KERNEL_SYMBOLS_NEVER);
LCK_MTX_ASSERT(&mod_lock, LCK_MTX_ASSERT_OWNED);
if (ignore_fbt_blacklist)
ctl->mod_flags |= MODCTL_FBT_PROVIDE_BLACKLISTED_PROBES;
if (MOD_FBT_DONE(ctl))
return;
if (fbt_module_excluded(ctl)) {
ctl->mod_flags |= MODCTL_FBT_INVALID;
return;
}
if (MOD_HAS_KERNEL_SYMBOLS(ctl)) {
fbt_provide_module_kernel_syms(ctl);
ctl->mod_flags |= MODCTL_FBT_PROBES_PROVIDED;
if (MOD_FBT_PROVIDE_BLACKLISTED_PROBES(ctl))
ctl->mod_flags |= MODCTL_FBT_BLACKLISTED_PROBES_PROVIDED;
return;
}
if (MOD_HAS_USERSPACE_SYMBOLS(ctl)) {
fbt_provide_module_user_syms(ctl);
ctl->mod_flags |= MODCTL_FBT_PROBES_PROVIDED;
if (MOD_FBT_PROVIDE_PRIVATE_PROBES(ctl))
ctl->mod_flags |= MODCTL_FBT_PRIVATE_PROBES_PROVIDED;
if (MOD_FBT_PROVIDE_BLACKLISTED_PROBES(ctl))
ctl->mod_flags |= MODCTL_FBT_BLACKLISTED_PROBES_PROVIDED;
return;
}
}
static dtrace_pattr_t fbt_attr = {
{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
};
static dtrace_pops_t fbt_pops = {
NULL,
fbt_provide_module,
fbt_enable,
fbt_disable,
fbt_suspend,
fbt_resume,
NULL,
NULL,
NULL,
fbt_destroy
};
static void
fbt_cleanup(dev_info_t *devi)
{
dtrace_invop_remove(fbt_invop);
ddi_remove_minor_node(devi, NULL);
kmem_free(fbt_probetab, fbt_probetab_size * sizeof (fbt_probe_t *));
fbt_probetab = NULL;
fbt_probetab_mask = 0;
}
static int
fbt_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
if (fbt_probetab_size == 0)
fbt_probetab_size = FBT_PROBETAB_SIZE;
fbt_probetab_mask = fbt_probetab_size - 1;
fbt_probetab =
kmem_zalloc(fbt_probetab_size * sizeof (fbt_probe_t *), KM_SLEEP);
dtrace_invop_add(fbt_invop);
if (ddi_create_minor_node(devi, "fbt", S_IFCHR, 0,
DDI_PSEUDO, 0) == DDI_FAILURE ||
dtrace_register("fbt", &fbt_attr, DTRACE_PRIV_KERNEL, NULL,
&fbt_pops, NULL, &fbt_id) != 0) {
fbt_cleanup(devi);
return (DDI_FAILURE);
}
ddi_report_dev(devi);
fbt_devi = devi;
return (DDI_SUCCESS);
}
static d_open_t _fbt_open;
static int
_fbt_open(dev_t dev, int flags, int devtype, struct proc *p)
{
#pragma unused(dev,flags,devtype,p)
return 0;
}
#define FBT_MAJOR -24
SYSCTL_DECL(_kern_dtrace);
static int
sysctl_dtrace_ignore_fbt_blacklist SYSCTL_HANDLER_ARGS
{
#pragma unused(oidp, arg2)
int err;
int value = *(int*)arg1;
err = sysctl_io_number(req, value, sizeof(value), &value, NULL);
if (err)
return (err);
if (req->newptr) {
if (!(value == 0 || value == 1))
return (ERANGE);
if (value != 1 || dtrace_kernel_symbol_mode == DTRACE_KERNEL_SYMBOLS_NEVER ||
dtrace_kernel_symbol_mode == DTRACE_KERNEL_SYMBOLS_ALWAYS_FROM_KERNEL)
return (EPERM);
ignore_fbt_blacklist = 1;
}
return (0);
}
SYSCTL_PROC(_kern_dtrace, OID_AUTO, ignore_fbt_blacklist,
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
&ignore_fbt_blacklist, 0,
sysctl_dtrace_ignore_fbt_blacklist, "I", "fbt provider ignore blacklist");
static struct cdevsw fbt_cdevsw =
{
_fbt_open,
eno_opcl,
eno_rdwrt,
eno_rdwrt,
eno_ioctl,
(stop_fcn_t *)nulldev,
(reset_fcn_t *)nulldev,
NULL,
eno_select,
eno_mmap,
eno_strat,
eno_getc,
eno_putc,
0
};
static int fbt_inited = 0;
#undef kmem_alloc
#undef kmem_free
#include <vm/vm_kern.h>
void
fbt_init( void )
{
if (0 == fbt_inited)
{
int majdevno = cdevsw_add(FBT_MAJOR, &fbt_cdevsw);
if (majdevno < 0) {
printf("fbt_init: failed to allocate a major number!\n");
return;
}
PE_parse_boot_argn("IgnoreFBTBlacklist", &ignore_fbt_blacklist, sizeof (ignore_fbt_blacklist));
fbt_attach( (dev_info_t *)(uintptr_t)majdevno, DDI_ATTACH );
fbt_inited = 1;
}
else
panic("fbt_init: called twice!\n");
}
#undef FBT_MAJOR