Author: jhibbits
Date: Sat Aug 31 16:30:20 2013
New Revision: 255099
URL: http://svnweb.freebsd.org/changeset/base/255099

Log:
  Fixes for DTrace on PowerPC:
  
  - Implement dtrace_getarg()
  - Sync fbt with x86, and fix a typo.
  - Pull in the time synchronization code from amd64.

Modified:
  head/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c
  head/sys/cddl/dev/dtrace/powerpc/dtrace_subr.c
  head/sys/cddl/dev/fbt/fbt_powerpc.c

Modified: head/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c
==============================================================================
--- head/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c       Sat Aug 31 16:21:13 
2013        (r255098)
+++ head/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c       Sat Aug 31 16:30:20 
2013        (r255099)
@@ -349,50 +349,84 @@ zero:
 uint64_t
 dtrace_getarg(int arg, int aframes)
 {
-       return (0);
-}
-
-#ifdef notyet
-{
-       int depth = 0;
-       register_t sp;
-       vm_offset_t callpc;
-       pc_t caller = (pc_t) solaris_cpu[curcpu].cpu_dtrace_caller;
-
-       if (intrpc != 0)
-               pcstack[depth++] = (pc_t) intrpc;
-
-       aframes++;
-
-       sp = dtrace_getfp();
-
-       while (depth < pcstack_limit) {
-               if (!INKERNEL((long) frame))
-                       break;
-
-               callpc = *(void **)(sp + RETURN_OFFSET);
-
-               if (!INKERNEL(callpc))
-                       break;
+       uintptr_t val;
+       uintptr_t *fp = (uintptr_t *)dtrace_getfp();
+       uintptr_t *stack;
+       int i;
+
+       /*
+        * A total of 8 arguments are passed via registers; any argument with
+        * index of 7 or lower is therefore in a register.
+        */
+       int inreg = 7;
+
+       for (i = 1; i <= aframes; i++) {
+               fp = (uintptr_t *)*fp;
+
+               /*
+                * On ppc32 AIM, and booke, trapexit() is the immediately 
following
+                * label.  On ppc64 AIM trapexit() follows a nop.
+                */
+               if (((long)(fp[1]) == (long)trapexit) ||
+                               (((long)(fp[1]) + 4 == (long)trapexit))) {
+                       /*
+                        * In the case of powerpc, we will use the pointer to 
the regs
+                        * structure that was pushed when we took the trap.  To 
get this
+                        * structure, we must increment beyond the frame 
structure.  If the
+                        * argument that we're seeking is passed on the stack, 
we'll pull
+                        * the true stack pointer out of the saved registers 
and decrement
+                        * our argument by the number of arguments passed in 
registers; if
+                        * the argument we're seeking is passed in regsiters, 
we can just
+                        * load it directly.
+                        */
+#ifdef __powerpc64__
+                       struct reg *rp = (struct reg *)((uintptr_t)fp[0] + 48);
+#else
+                       struct reg *rp = (struct reg *)((uintptr_t)fp[0] + 8);
+#endif
 
-               if (aframes > 0) {
-                       aframes--;
-                       if ((aframes == 0) && (caller != 0)) {
-                               pcstack[depth++] = caller;
+                       if (arg <= inreg) {
+                               stack = &rp->fixreg[3];
+                       } else {
+                               stack = (uintptr_t *)(rp->fixreg[1]);
+                               arg -= inreg;
                        }
-               }
-               else {
-                       pcstack[depth++] = callpc;
+                       goto load;
                }
 
-               sp = *(void **)sp;
        }
 
-       for (; depth < pcstack_limit; depth++) {
-               pcstack[depth] = 0;
+       /*
+        * We know that we did not come through a trap to get into
+        * dtrace_probe() -- the provider simply called dtrace_probe()
+        * directly.  As this is the case, we need to shift the argument
+        * that we're looking for:  the probe ID is the first argument to
+        * dtrace_probe(), so the argument n will actually be found where
+        * one would expect to find argument (n + 1).
+        */
+       arg++;
+
+       if (arg <= inreg) {
+               /*
+                * This shouldn't happen.  If the argument is passed in a
+                * register then it should have been, well, passed in a
+                * register...
+                */
+               DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
+               return (0);
        }
+
+       arg -= (inreg + 1);
+       stack = fp + 2;
+
+load:
+       DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+       val = stack[arg];
+       DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
+
+       return (val);
+       return (0);
 }
-#endif
 
 int
 dtrace_getstackdepth(int aframes)

Modified: head/sys/cddl/dev/dtrace/powerpc/dtrace_subr.c
==============================================================================
--- head/sys/cddl/dev/dtrace/powerpc/dtrace_subr.c      Sat Aug 31 16:21:13 
2013        (r255098)
+++ head/sys/cddl/dev/dtrace/powerpc/dtrace_subr.c      Sat Aug 31 16:30:20 
2013        (r255099)
@@ -51,6 +51,8 @@ extern int            dtrace_in_probe;
 extern dtrace_id_t     dtrace_probeid_error;
 extern int (*dtrace_invop_jump_addr)(struct trapframe *);
 
+extern void dtrace_getnanotime(struct timespec *tsp);
+
 int dtrace_invop(uintptr_t, uintptr_t *, uintptr_t);
 void dtrace_invop_init(void);
 void dtrace_invop_uninit(void);
@@ -63,13 +65,13 @@ typedef struct dtrace_invop_hdlr {
 dtrace_invop_hdlr_t *dtrace_invop_hdlr;
 
 int
-dtrace_invop(uintptr_t addr, uintptr_t *stack, uintptr_t eax)
+dtrace_invop(uintptr_t addr, uintptr_t *stack, uintptr_t arg0)
 {
        dtrace_invop_hdlr_t *hdlr;
        int rval;
 
        for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next)
-               if ((rval = hdlr->dtih_func(addr, stack, eax)) != 0)
+               if ((rval = hdlr->dtih_func(addr, stack, arg0)) != 0)
                        return (rval);
 
        return (0);
@@ -134,7 +136,7 @@ dtrace_xcall(processorid_t cpu, dtrace_x
                CPU_SETOF(cpu, &cpus);
 
        smp_rendezvous_cpus(cpus, smp_no_rendevous_barrier, func,
-           smp_no_rendevous_barrier, arg);
+                       smp_no_rendevous_barrier, arg);
 }
 
 static void
@@ -145,9 +147,82 @@ dtrace_sync_func(void)
 void
 dtrace_sync(void)
 {
-        dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_sync_func, NULL);
+       dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_sync_func, NULL);
+}
+
+static int64_t tgt_cpu_tsc;
+static int64_t hst_cpu_tsc;
+static int64_t timebase_skew[MAXCPU];
+static uint64_t        nsec_scale;
+
+/* See below for the explanation of this macro. */
+/* This is taken from the amd64 dtrace_subr, to provide a synchronized timer
+ * between multiple processors in dtrace.  Since PowerPC Timebases can be much
+ * lower than x86, the scale shift is 26 instead of 28, allowing for a 15.63MHz
+ * timebase.
+ */
+#define SCALE_SHIFT    26
+
+static void
+dtrace_gethrtime_init_cpu(void *arg)
+{
+       uintptr_t cpu = (uintptr_t) arg;
+
+       if (cpu == curcpu)
+               tgt_cpu_tsc = mftb();
+       else
+               hst_cpu_tsc = mftb();
+}
+
+static void
+dtrace_gethrtime_init(void *arg)
+{
+       struct pcpu *pc;
+       uint64_t tb_f;
+       cpuset_t map;
+       int i;
+
+       tb_f = cpu_tickrate();
+
+       /*
+        * The following line checks that nsec_scale calculated below
+        * doesn't overflow 32-bit unsigned integer, so that it can multiply
+        * another 32-bit integer without overflowing 64-bit.
+        * Thus minimum supported Timebase frequency is 15.63MHz.
+        */
+       KASSERT(tb_f > (NANOSEC >> (32 - SCALE_SHIFT)), ("Timebase frequency is 
too low"));
+
+       /*
+        * We scale up NANOSEC/tb_f ratio to preserve as much precision
+        * as possible.
+        * 2^26 factor was chosen quite arbitrarily from practical
+        * considerations:
+        * - it supports TSC frequencies as low as 15.63MHz (see above);
+        */
+       nsec_scale = ((uint64_t)NANOSEC << SCALE_SHIFT) / tb_f;
+
+       /* The current CPU is the reference one. */
+       sched_pin();
+       timebase_skew[curcpu] = 0;
+       CPU_FOREACH(i) {
+               if (i == curcpu)
+                       continue;
+
+               pc = pcpu_find(i);
+               CPU_SETOF(PCPU_GET(cpuid), &map);
+               CPU_SET(pc->pc_cpuid, &map);
+
+               smp_rendezvous_cpus(map, NULL,
+                               dtrace_gethrtime_init_cpu,
+                               smp_no_rendevous_barrier, (void *)(uintptr_t) 
i);
+
+               timebase_skew[i] = tgt_cpu_tsc - hst_cpu_tsc;
+       }
+       sched_unpin();
 }
 
+SYSINIT(dtrace_gethrtime_init, SI_SUB_SMP, SI_ORDER_ANY, 
dtrace_gethrtime_init, NULL);
+
 /*
  * DTrace needs a high resolution time function which can
  * be called from a probe context and guaranteed not to have
@@ -158,12 +233,21 @@ dtrace_sync(void)
 uint64_t
 dtrace_gethrtime()
 {
-       struct      timespec curtime;
-
-       nanouptime(&curtime);
-
-       return (curtime.tv_sec * 1000000000UL + curtime.tv_nsec);
+       uint64_t timebase;
+       uint32_t lo;
+       uint32_t hi;
 
+       /*
+        * We split timebase value into lower and higher 32-bit halves and 
separately
+        * scale them with nsec_scale, then we scale them down by 2^28
+        * (see nsec_scale calculations) taking into account 32-bit shift of
+        * the higher half and finally add.
+        */
+       timebase = mftb() - timebase_skew[curcpu];
+       lo = timebase;
+       hi = timebase >> 32;
+       return (((lo * nsec_scale) >> SCALE_SHIFT) +
+               ((hi * nsec_scale) << (32 - SCALE_SHIFT)));
 }
 
 uint64_t
@@ -171,12 +255,12 @@ dtrace_gethrestime(void)
 {
        struct      timespec curtime;
 
-       getnanotime(&curtime);
+       dtrace_getnanotime(&curtime);
 
        return (curtime.tv_sec * 1000000000UL + curtime.tv_nsec);
 }
 
-/* Function to handle DTrace traps during probes. See amd64/amd64/trap.c */
+/* Function to handle DTrace traps during probes. See powerpc/powerpc/trap.c */
 int
 dtrace_trap(struct trapframe *frame, u_int type)
 {
@@ -196,34 +280,34 @@ dtrace_trap(struct trapframe *frame, u_i
                 * All the rest will be handled in the usual way.
                 */
                switch (type) {
-               /* Page fault. */
-               case EXC_DSI:
-               case EXC_DSE:
-                       /* Flag a bad address. */
-                       cpu_core[curcpu].cpuc_dtrace_flags |= 
CPU_DTRACE_BADADDR;
-                       cpu_core[curcpu].cpuc_dtrace_illval = 
frame->cpu.aim.dar;
-
-                       /*
-                        * Offset the instruction pointer to the instruction
-                        * following the one causing the fault.
-                        */
-                       frame->srr0 += sizeof(int);
-                       return (1);
-               case EXC_ISI:
-               case EXC_ISE:
-                       /* Flag a bad address. */
-                       cpu_core[curcpu].cpuc_dtrace_flags |= 
CPU_DTRACE_BADADDR;
-                       cpu_core[curcpu].cpuc_dtrace_illval = frame->srr0;
-
-                       /*
-                        * Offset the instruction pointer to the instruction
-                        * following the one causing the fault.
-                        */
-                       frame->srr0 += sizeof(int);
-                       return (1);
-               default:
-                       /* Handle all other traps in the usual way. */
-                       break;
+                       /* Page fault. */
+                       case EXC_DSI:
+                       case EXC_DSE:
+                               /* Flag a bad address. */
+                               cpu_core[curcpu].cpuc_dtrace_flags |= 
CPU_DTRACE_BADADDR;
+                               cpu_core[curcpu].cpuc_dtrace_illval = 
frame->cpu.aim.dar;
+
+                               /*
+                                * Offset the instruction pointer to the 
instruction
+                                * following the one causing the fault.
+                                */
+                               frame->srr0 += sizeof(int);
+                               return (1);
+                       case EXC_ISI:
+                       case EXC_ISE:
+                               /* Flag a bad address. */
+                               cpu_core[curcpu].cpuc_dtrace_flags |= 
CPU_DTRACE_BADADDR;
+                               cpu_core[curcpu].cpuc_dtrace_illval = 
frame->srr0;
+
+                               /*
+                                * Offset the instruction pointer to the 
instruction
+                                * following the one causing the fault.
+                                */
+                               frame->srr0 += sizeof(int);
+                               return (1);
+                       default:
+                               /* Handle all other traps in the usual way. */
+                               break;
                }
        }
 
@@ -237,28 +321,29 @@ dtrace_probe_error(dtrace_state_t *state
 {
 
        dtrace_probe(dtrace_probeid_error, (uint64_t)(uintptr_t)state,
-           (uintptr_t)epid,
-           (uintptr_t)which, (uintptr_t)fault, (uintptr_t)fltoffs);
+                       (uintptr_t)epid,
+                       (uintptr_t)which, (uintptr_t)fault, (uintptr_t)fltoffs);
 }
 
 static int
 dtrace_invop_start(struct trapframe *frame)
 {
        switch (dtrace_invop(frame->srr0, (uintptr_t *)frame, 
frame->fixreg[3])) {
-       case DTRACE_INVOP_JUMP:
-               break;
-       case DTRACE_INVOP_BCTR:
-               frame->srr0 = frame->ctr;
-               break;
-       case DTRACE_INVOP_BLR:
-               frame->srr0 = frame->lr;
-               break;
-       case DTRACE_INVOP_MFLR_R0:
-               frame->fixreg[0] = frame->lr ;
-               break;
-       default:
-               return (-1);
-               break;
+               case DTRACE_INVOP_JUMP:
+                       break;
+               case DTRACE_INVOP_BCTR:
+                       frame->srr0 = frame->ctr;
+                       break;
+               case DTRACE_INVOP_BLR:
+                       frame->srr0 = frame->lr;
+                       break;
+               case DTRACE_INVOP_MFLR_R0:
+                       frame->fixreg[0] = frame->lr;
+                       frame->srr0 = frame->srr0 + 4;
+                       break;
+               default:
+                       return (-1);
+                       break;
        }
 
        return (0);

Modified: head/sys/cddl/dev/fbt/fbt_powerpc.c
==============================================================================
--- head/sys/cddl/dev/fbt/fbt_powerpc.c Sat Aug 31 16:21:13 2013        
(r255098)
+++ head/sys/cddl/dev/fbt/fbt_powerpc.c Sat Aug 31 16:30:20 2013        
(r255099)
@@ -57,6 +57,7 @@
 #include <sys/sysproto.h>
 #include <sys/uio.h>
 #include <sys/unistd.h>
+#include <machine/md_var.h>
 #include <machine/stdarg.h>
 
 #include <sys/dtrace.h>
@@ -172,7 +173,11 @@ fbt_invop(uintptr_t addr, uintptr_t *sta
                                        tmp = fbt->fbtp_savedval & FBT_BR_MASK;
                                        /* Sign extend. */
                                        if (tmp & 0x02000000)
-                                               tmp |= 0xFC000000;
+#ifdef __powerpc64__
+                                               tmp |= 0xfffffffffc000000ULL;
+#else
+                                               tmp |= 0xfc000000UL;
+#endif
                                        frame->srr0 += tmp;
                                }
                                cpu->cpu_dtrace_caller = 0;
@@ -193,9 +198,12 @@ fbt_provide_module_function(linker_file_
        const char *name = symval->name;
        fbt_probe_t *fbt, *retfbt;
        int j;
-       int size;
        u_int32_t *instr, *limit;
 
+       /* PowerPC64 uses '.' prefixes on symbol names, ignore it. */
+       if (name[0] == '.')
+               name++;
+
        if (strncmp(name, "dtrace_", 7) == 0 &&
            strncmp(name, "dtrace_safe_", 12) != 0) {
                /*
@@ -210,8 +218,6 @@ fbt_provide_module_function(linker_file_
        if (name[0] == '_' && name[1] == '_')
                return (0);
 
-       size = symval->size;
-
        instr = (u_int32_t *) symval->value;
        limit = (u_int32_t *) symval->value + symval->size;
 
@@ -219,7 +225,7 @@ fbt_provide_module_function(linker_file_
                if (*instr == FBT_MFLR_R0)
                        break;
 
-       if (*instr != FBT_MFLR_R0);
+       if (*instr != FBT_MFLR_R0)
                return (0);
 
        fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
@@ -264,9 +270,6 @@ again:
                }
        }
 
-       if (*instr == FBT_MFLR_R0)
-               return (0);
-
        if (*instr != FBT_MTLR_R0) {
                instr++;
                goto again;
@@ -291,7 +294,7 @@ again:
 
        if (retfbt == NULL) {
                fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
-                   name, FBT_RETURN, 3, fbt);
+                   name, FBT_RETURN, 5, fbt);
        } else {
                retfbt->fbtp_next = fbt;
                fbt->fbtp_id = retfbt->fbtp_id;
@@ -317,7 +320,7 @@ again:
 
        lf->fbt_nentries++;
 
-       instr += size;
+       instr += 4;
        goto again;
 }
 
@@ -434,6 +437,7 @@ fbt_enable(void *arg, dtrace_id_t id, vo
 
        for (; fbt != NULL; fbt = fbt->fbtp_next) {
                *fbt->fbtp_patchpoint = fbt->fbtp_patchval;
+               __syncicache(fbt->fbtp_patchpoint, 4);
        }
 }
 
@@ -449,8 +453,10 @@ fbt_disable(void *arg, dtrace_id_t id, v
        if ((ctl->loadcnt != fbt->fbtp_loadcnt))
                return;
 
-       for (; fbt != NULL; fbt = fbt->fbtp_next)
+       for (; fbt != NULL; fbt = fbt->fbtp_next) {
                *fbt->fbtp_patchpoint = fbt->fbtp_savedval;
+               __syncicache(fbt->fbtp_patchpoint, 4);
+       }
 }
 
 static void
@@ -464,8 +470,10 @@ fbt_suspend(void *arg, dtrace_id_t id, v
        if ((ctl->loadcnt != fbt->fbtp_loadcnt))
                return;
 
-       for (; fbt != NULL; fbt = fbt->fbtp_next)
+       for (; fbt != NULL; fbt = fbt->fbtp_next) {
                *fbt->fbtp_patchpoint = fbt->fbtp_savedval;
+               __syncicache(fbt->fbtp_patchpoint, 4);
+       }
 }
 
 static void
@@ -479,15 +487,16 @@ fbt_resume(void *arg, dtrace_id_t id, vo
        if ((ctl->loadcnt != fbt->fbtp_loadcnt))
                return;
 
-       for (; fbt != NULL; fbt = fbt->fbtp_next)
+       for (; fbt != NULL; fbt = fbt->fbtp_next) {
                *fbt->fbtp_patchpoint = fbt->fbtp_patchval;
+               __syncicache(fbt->fbtp_patchpoint, 4);
+       }
 }
 
 static int
 fbt_ctfoff_init(modctl_t *lf, linker_ctf_t *lc)
 {
        const Elf_Sym *symp = lc->symtab;;
-       const char *name;
        const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;
        const uint8_t *ctfdata = lc->ctftab + sizeof(ctf_header_t);
        int i;
@@ -519,11 +528,6 @@ fbt_ctfoff_init(modctl_t *lf, linker_ctf
                        continue;
                }
 
-               if (symp->st_name < lc->strcnt)
-                       name = lc->strtab + symp->st_name;
-               else
-                       name = "(?)";
-
                switch (ELF_ST_TYPE(symp->st_info)) {
                case STT_OBJECT:
                        if (objtoff >= hp->cth_funcoff ||
@@ -690,6 +694,8 @@ fbt_typoff_init(linker_ctf_t *lc)
                pop[kind]++;
        }
 
+       /* account for a sentinel value below */
+       ctf_typemax++;
        *lc->typlenp = ctf_typemax;
 
        if ((xp = malloc(sizeof(uint32_t) * ctf_typemax, M_LINKER, M_ZERO | 
M_WAITOK)) == NULL)
@@ -1171,6 +1177,11 @@ fbt_getargdesc(void *arg __unused, dtrac
        uint32_t offset;
        ushort_t info, kind, n;
 
+       if (fbt->fbtp_roffset != 0 && desc->dtargd_ndx == 0) {
+               (void) strcpy(desc->dtargd_native, "int");
+               return;
+       }
+
        desc->dtargd_ndx = DTRACE_ARGNONE;
 
        /* Get a pointer to the CTF data and it's length. */
@@ -1221,12 +1232,19 @@ fbt_getargdesc(void *arg __unused, dtrac
                return;
        }
 
-       /* Check if the requested argument doesn't exist. */
-       if (ndx >= n)
-               return;
+       if (fbt->fbtp_roffset != 0) {
+               /* Only return type is available for args[1] in return probe. */
+               if (ndx > 1)
+                       return;
+               ASSERT(ndx == 1);
+       } else {
+               /* Check if the requested argument doesn't exist. */
+               if (ndx >= n)
+                       return;
 
-       /* Skip the return type and arguments up to the one requested. */
-       dp += ndx + 1;
+               /* Skip the return type and arguments up to the one requested. 
*/
+               dp += ndx + 1;
+       }
 
        if (fbt_type_name(&lc, *dp, desc->dtargd_native, 
sizeof(desc->dtargd_native)) > 0)
                desc->dtargd_ndx = ndx;
@@ -1234,6 +1252,15 @@ fbt_getargdesc(void *arg __unused, dtrac
        return;
 }
 
+static int
+fbt_linker_file_cb(linker_file_t lf, void *arg)
+{
+
+       fbt_provide_module(arg, lf);
+
+       return (0);
+}
+
 static void
 fbt_load(void *dummy)
 {
@@ -1257,6 +1284,9 @@ fbt_load(void *dummy)
        if (dtrace_register("fbt", &fbt_attr, DTRACE_PRIV_USER,
            NULL, &fbt_pops, NULL, &fbt_id) != 0)
                return;
+
+       /* Create probes for the kernel and already-loaded modules. */
+       linker_file_foreach(fbt_linker_file_cb, NULL);
 }
 
 
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to