]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/dev/dtrace/fbt.c
xnu-3789.1.32.tar.gz
[apple/xnu.git] / bsd / dev / dtrace / fbt.c
index 94e15da001459abe9602ea8ee35aff75defa93ac..e05d5a92275868aeb561cf08dde21d94b4f1c526 100644 (file)
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 
 /* #include <machine/trap.h> */
 struct savearea_t; /* Used anonymously */
-typedef kern_return_t (*perfCallback)(int, struct savearea_t *, int, int);
 
-#if defined (__ppc__) || defined (__ppc64__)
-extern perfCallback tempDTraceTrapHook, tempDTraceIntHook;
-extern kern_return_t fbt_perfCallback(int, struct savearea_t *, int, int);
-extern kern_return_t fbt_perfIntCallback(int, struct savearea_t *, int, int);
-#else
+#if   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 *, int, int);
+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)
@@ -111,25 +109,42 @@ fbt_destroy(void *arg, dtrace_id_t id, void *parg)
 }
 
 /*ARGSUSED*/
-static void
+int
 fbt_enable(void *arg, dtrace_id_t id, void *parg)
 {
 #pragma unused(arg,id)
        fbt_probe_t *fbt = parg;
-       struct modctl *ctl = fbt->fbtp_ctl;
+       struct modctl *ctl = NULL;
+
+    for (; fbt != NULL; fbt = fbt->fbtp_next) {
 
-#if defined (__ppc__) || defined (__ppc64__)
-       dtrace_casptr(&tempDTraceIntHook, NULL, fbt_perfIntCallback);
-       if (tempDTraceIntHook != (perfCallback)fbt_perfIntCallback) {
+       ctl = fbt->fbtp_ctl;
+       
+       if (!ctl->mod_loaded) {
                if (fbt_verbose) {
-                       cmn_err(CE_NOTE, "fbt_enable is failing for probe %s "
-                           "in module %s: tempDTraceIntHook already occupied.",
+                       cmn_err(CE_NOTE, "fbt is failing for probe %s "
+                           "(module %s unloaded)",
                            fbt->fbtp_name, ctl->mod_modname);
                }
-               return;
+
+               continue;
        }
-#endif
-       
+
+       /*
+        * Now check that our modctl has the expected load count.  If it
+        * doesn't, this module must have been unloaded and reloaded -- and
+        * we're not going to touch it.
+        */
+       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) {
@@ -137,14 +152,28 @@ fbt_enable(void *arg, dtrace_id_t id, void *parg)
                            "in module %s: tempDTraceTrapHook already occupied.",
                            fbt->fbtp_name, ctl->mod_modname);
                }
-               return;
+               continue;
        }
 
-       for (; fbt != NULL; fbt = fbt->fbtp_next)
+       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));
-               
-       dtrace_membar_consumer();
+               /*
+                * Make the patched instruction visible via a data + instruction
+                * cache flush for the platforms that need it
+                */
+               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);
 }
 
 /*ARGSUSED*/
@@ -153,11 +182,29 @@ 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;
 
-       for (; fbt != NULL; fbt = fbt->fbtp_next)
+           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));
-               
+               /*
+                * Make the patched instruction visible via a data + instruction
+                * cache flush for the platforms that need it
+                */
+               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();
 }
 
@@ -167,11 +214,28 @@ 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)
-               (void)ml_nofault_copy( (vm_offset_t)&fbt->fbtp_savedval, (vm_offset_t)fbt->fbtp_patchpoint, 
+       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));
                
+               /*
+                * Make the patched instruction visible via a data + instruction
+                * cache flush for the platforms that need it
+                */
+               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();
 }
 
@@ -181,37 +245,38 @@ fbt_resume(void *arg, dtrace_id_t id, void *parg)
 {
 #pragma unused(arg,id)
        fbt_probe_t *fbt = parg;
-       struct modctl *ctl = fbt->fbtp_ctl;
+       struct modctl *ctl = NULL;
 
-#if defined (__ppc__) || defined (__ppc64__)
-       dtrace_casptr(&tempDTraceIntHook, NULL, fbt_perfIntCallback);
-       if (tempDTraceIntHook != (perfCallback)fbt_perfIntCallback) {
-               if (fbt_verbose) {
-                       cmn_err(CE_NOTE, "fbt_enable is failing for probe %s "
-                           "in module %s: tempDTraceIntHook already occupied.",
-                           fbt->fbtp_name, ctl->mod_modname);
-               }
-               return;
-       }
-#endif
+       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) {
+           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;
-       }
+           }
        
-       for (; fbt != NULL; fbt = fbt->fbtp_next)
-               (void)ml_nofault_copy( (vm_offset_t)&fbt->fbtp_patchval, (vm_offset_t)fbt->fbtp_patchpoint, 
+           (void)ml_nofault_copy( (vm_offset_t)&fbt->fbtp_patchval, (vm_offset_t)fbt->fbtp_patchpoint, 
                                                                sizeof(fbt->fbtp_patchval));
+
                
+           fbt->fbtp_currentval = fbt->fbtp_patchval;
+       }
+       
        dtrace_membar_consumer();
 }
 
+/*
+ * APPLE NOTE: fbt_getargdesc not implemented
+ */
 #if !defined(__APPLE__)
 /*ARGSUSED*/
 static void
@@ -324,11 +389,7 @@ static dtrace_pops_t fbt_pops = {
        fbt_disable,
        fbt_suspend,
        fbt_resume,
-#if !defined(__APPLE__)
-       fbt_getargdesc,
-#else
-       NULL, /* FIXME: where to look for xnu? */
-#endif /* __APPLE__ */
+       NULL, /*  APPLE NOTE: fbt_getargdesc not implemented */
        NULL,
        NULL,
        fbt_destroy
@@ -365,15 +426,6 @@ fbt_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
 
        dtrace_invop_add(fbt_invop);
 
-#if !defined(__APPLE__)
-       if (ddi_create_minor_node(devi, "fbt", S_IFCHR, 0,
-           DDI_PSEUDO, NULL) == DDI_FAILURE ||
-           dtrace_register("fbt", &fbt_attr, DTRACE_PRIV_KERNEL, NULL,
-           &fbt_pops, NULL, &fbt_id) != 0) {
-               fbt_cleanup(devi);
-               return (DDI_FAILURE);
-       }
-#else
        if (ddi_create_minor_node(devi, "fbt", S_IFCHR, 0,
            DDI_PSEUDO, 0) == DDI_FAILURE ||
            dtrace_register("fbt", &fbt_attr, DTRACE_PRIV_KERNEL, NULL,
@@ -381,7 +433,6 @@ fbt_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
                fbt_cleanup(devi);
                return (DDI_FAILURE);
        }
-#endif /* __APPLE__ */
 
        ddi_report_dev(devi);
        fbt_devi = devi;
@@ -422,8 +473,8 @@ static struct cdevsw fbt_cdevsw =
        0                                       /* type */
 };
 
-static int gDisableFBT = 0;
-struct modctl g_fbt_kernctl;
+int gIgnoreFBTBlacklist = 0;
+static int gFBTInited = 0;
 #undef kmem_alloc /* from its binding to dt_kmem_alloc glue */
 #undef kmem_free /* from its binding to dt_kmem_free glue */
 #include <vm/vm_kern.h>
@@ -431,66 +482,22 @@ struct modctl g_fbt_kernctl;
 void
 fbt_init( void )
 {
-
-       PE_parse_boot_argn("DisableFBT", &gDisableFBT, sizeof (gDisableFBT));
-
-       if (0 == gDisableFBT)
+       if (0 == gFBTInited)
        {
                int majdevno = cdevsw_add(FBT_MAJOR, &fbt_cdevsw);
-               unsigned long size = 0, header_size, round_size;
-               kern_return_t ret;
-               void *p, *q;
                
                if (majdevno < 0) {
                        printf("fbt_init: failed to allocate a major number!\n");
                        return;
                }
-
-               /*
-                * Capture the kernel's mach_header in its entirety and the contents of
-                * its LINKEDIT segment (and only that segment). This is sufficient to
-                * build all the fbt probes lazily the first time a client looks to
-                * the fbt provider. Remeber these on the global struct modctl g_fbt_kernctl.
-                */
-               header_size = sizeof(kernel_mach_header_t) + _mh_execute_header.sizeofcmds;
-               p = getsegdatafromheader(&_mh_execute_header, SEG_LINKEDIT, &size);
-
-        round_size = round_page(header_size + size);
-               /* "q" will accomodate copied kernel_mach_header_t, its load commands, and LINKEIT segment. */
-               ret = kmem_alloc_pageable(kernel_map, (vm_offset_t *)&q, round_size);
-
-               if (p && (ret == KERN_SUCCESS)) {
-                       kernel_segment_command_t *sgp;
-
-                       bcopy( (void *)&_mh_execute_header, q, header_size);
-                       bcopy( p, (char *)q + header_size, size);
-
-                       sgp = getsegbynamefromheader(q, SEG_LINKEDIT);
-
-                       if (sgp) {
-                               sgp->vmaddr = (uintptr_t)((char *)q + header_size);
-                               g_fbt_kernctl.address = (vm_address_t)q;
-                               g_fbt_kernctl.size = header_size + size;
-                       } else {
-                               kmem_free(kernel_map, (vm_offset_t)q, round_size);
-                               g_fbt_kernctl.address = (vm_address_t)NULL;
-                               g_fbt_kernctl.size = 0;
-                       }
-               } else {
-                       if (ret == KERN_SUCCESS)
-                               kmem_free(kernel_map, (vm_offset_t)q, round_size);
-                       g_fbt_kernctl.address = (vm_address_t)NULL;
-                       g_fbt_kernctl.size = 0;
-               }
-
-               strncpy((char *)&(g_fbt_kernctl.mod_modname), "mach_kernel", KMOD_MAX_NAME);
-               ((char *)&(g_fbt_kernctl.mod_modname))[KMOD_MAX_NAME -1] = '\0';
+               
+               PE_parse_boot_argn("IgnoreFBTBlacklist", &gIgnoreFBTBlacklist, sizeof (gIgnoreFBTBlacklist));
 
                fbt_attach( (dev_info_t *)(uintptr_t)majdevno, DDI_ATTACH );
-
-               gDisableFBT = 1; /* Ensure this initialization occurs just one time. */
+               
+               gFBTInited = 1; /* Ensure this initialization occurs just one time. */
        }
        else
-               printf("fbt_init: DisableFBT non-zero, no FBT probes will be provided.\n");
+               panic("fbt_init: called twice!\n");
 }
 #undef FBT_MAJOR