From owner-svn-src-all@FreeBSD.ORG Tue Oct 15 15:00:31 2013 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTP id A6B2F598; Tue, 15 Oct 2013 15:00:31 +0000 (UTC) (envelope-from jhibbits@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id 879412BC6; Tue, 15 Oct 2013 15:00:31 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.7/8.14.7) with ESMTP id r9FF0VqC081433; Tue, 15 Oct 2013 15:00:31 GMT (envelope-from jhibbits@svn.freebsd.org) Received: (from jhibbits@localhost) by svn.freebsd.org (8.14.7/8.14.5/Submit) id r9FF0UhN081419; Tue, 15 Oct 2013 15:00:30 GMT (envelope-from jhibbits@svn.freebsd.org) Message-Id: <201310151500.r9FF0UhN081419@svn.freebsd.org> From: Justin Hibbits Date: Tue, 15 Oct 2013 15:00:30 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r256543 - in head: cddl/contrib/opensolaris/lib/libdtrace/common cddl/contrib/opensolaris/lib/libdtrace/powerpc sys/cddl/contrib/opensolaris/uts/powerpc/dtrace sys/cddl/contrib/opensola... X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 15 Oct 2013 15:00:31 -0000 Author: jhibbits Date: Tue Oct 15 15:00:29 2013 New Revision: 256543 URL: http://svnweb.freebsd.org/changeset/base/256543 Log: Add fasttrap for PowerPC. This is the last piece of the dtrace/ppc puzzle. It's incomplete, it doesn't contain full instruction emulation, but it should be sufficient for most cases. MFC after: 1 month Modified: head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c head/cddl/contrib/opensolaris/lib/libdtrace/powerpc/dt_isadep.c head/sys/cddl/contrib/opensolaris/uts/powerpc/dtrace/fasttrap_isa.c head/sys/cddl/contrib/opensolaris/uts/powerpc/sys/fasttrap_isa.h head/sys/modules/dtrace/Makefile head/sys/modules/dtrace/fasttrap/Makefile head/sys/powerpc/aim/trap.c Modified: head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c ============================================================================== --- head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c Tue Oct 15 14:52:44 2013 (r256542) +++ head/cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c Tue Oct 15 15:00:29 2013 (r256543) @@ -242,8 +242,14 @@ printf("%s:%s(%d): DOODAD\n",__FUNCTION_ /* XXX */ printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); #elif defined(__powerpc__) -/* XXX */ -printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); + /* + * Add 4 bytes to hit the low half of this 64-bit + * big-endian address. + */ + rel->r_offset = s->dofs_offset + + dofr[j].dofr_offset + 4; + rel->r_info = ELF32_R_INFO(count + dep->de_global, + R_PPC_REL32); #elif defined(__sparc) /* * Add 4 bytes to hit the low half of this 64-bit @@ -423,7 +429,10 @@ prepare_elf64(dtrace_hdl_t *dtp, const d #elif defined(__mips__) /* XXX */ #elif defined(__powerpc__) -/* XXX */ + rel->r_offset = s->dofs_offset + + dofr[j].dofr_offset; + rel->r_info = ELF64_R_INFO(count + dep->de_global, + R_PPC64_REL64); #elif defined(__i386) || defined(__amd64) rel->r_offset = s->dofs_offset + dofr[j].dofr_offset; @@ -824,12 +833,84 @@ printf("%s:%s(%d): DOODAD\n",__FUNCTION_ return (0); } #elif defined(__powerpc__) +/* The sentinel is 'xor r3,r3,r3'. */ +#define DT_OP_XOR_R3 0x7c631a78 + +#define DT_OP_NOP 0x60000000 +#define DT_OP_BLR 0x4e800020 + +/* This captures all forms of branching to address. */ +#define DT_IS_BRANCH(inst) ((inst & 0xfc000000) == 0x48000000) +#define DT_IS_BL(inst) (DT_IS_BRANCH(inst) && (inst & 0x01)) + /* XXX */ static int dt_modtext(dtrace_hdl_t *dtp, char *p, int isenabled, GElf_Rela *rela, uint32_t *off) { -printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); + uint32_t *ip; + + if ((rela->r_offset & (sizeof (uint32_t) - 1)) != 0) + return (-1); + + /*LINTED*/ + ip = (uint32_t *)(p + rela->r_offset); + + /* + * We only know about some specific relocation types. + */ + if (GELF_R_TYPE(rela->r_info) != R_PPC_REL24 && + GELF_R_TYPE(rela->r_info) != R_PPC_PLTREL24) + return (-1); + + /* + * We may have already processed this object file in an earlier linker + * invocation. Check to see if the present instruction sequence matches + * the one we would install below. + */ + if (isenabled) { + if (ip[0] == DT_OP_XOR_R3) { + (*off) += sizeof (ip[0]); + return (0); + } + } else { + if (ip[0] == DT_OP_NOP) { + (*off) += sizeof (ip[0]); + return (0); + } + } + + /* + * We only expect branch to address instructions. + */ + if (!DT_IS_BRANCH(ip[0])) { + dt_dprintf("found %x instead of a branch instruction at %llx\n", + ip[0], (u_longlong_t)rela->r_offset); + return (-1); + } + + if (isenabled) { + /* + * It would necessarily indicate incorrect usage if an is- + * enabled probe were tail-called so flag that as an error. + * It's also potentially (very) tricky to handle gracefully, + * but could be done if this were a desired use scenario. + */ + if (!DT_IS_BL(ip[0])) { + dt_dprintf("tail call to is-enabled probe at %llx\n", + (u_longlong_t)rela->r_offset); + return (-1); + } + + ip[0] = DT_OP_XOR_R3; + (*off) += sizeof (ip[0]); + } else { + if (DT_IS_BL(ip[0])) + ip[0] = DT_OP_NOP; + else + ip[0] = DT_OP_BLR; + } + return (0); } Modified: head/cddl/contrib/opensolaris/lib/libdtrace/powerpc/dt_isadep.c ============================================================================== --- head/cddl/contrib/opensolaris/lib/libdtrace/powerpc/dt_isadep.c Tue Oct 15 14:52:44 2013 (r256542) +++ head/cddl/contrib/opensolaris/lib/libdtrace/powerpc/dt_isadep.c Tue Oct 15 15:00:29 2013 (r256543) @@ -35,14 +35,26 @@ #include #include +#include + /*ARGSUSED*/ int dt_pid_create_entry_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp, fasttrap_probe_spec_t *ftp, const GElf_Sym *symp) { + ftp->ftps_type = DTFTP_ENTRY; + ftp->ftps_pc = (uintptr_t)symp->st_value; + ftp->ftps_size = (size_t)symp->st_size; + ftp->ftps_noffs = 1; + ftp->ftps_offs[0] = 0; + + if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { + dt_dprintf("fasttrap probe creation ioctl failed: %s\n", + strerror(errno)); + return (dt_set_errno(dtp, errno)); + } - dt_dprintf("%s: unimplemented\n", __func__); - return (DT_PROC_ERR); + return (1); } int @@ -50,8 +62,74 @@ dt_pid_create_return_probe(struct ps_pro fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, uint64_t *stret) { - dt_dprintf("%s: unimplemented\n", __func__); - return (DT_PROC_ERR); + uintptr_t temp; + uint32_t *text; + int i; + int srdepth = 0; + + if ((text = malloc(symp->st_size + 4)) == NULL) { + dt_dprintf("mr sparkle: malloc() failed\n"); + return (DT_PROC_ERR); + } + + if (Pread(P, text, symp->st_size, symp->st_value) != symp->st_size) { + dt_dprintf("mr sparkle: Pread() failed\n"); + free(text); + return (DT_PROC_ERR); + } + + /* + * Leave a dummy instruction in the last slot to simplify edge + * conditions. + */ + text[symp->st_size / 4] = 0; + + ftp->ftps_type = DTFTP_RETURN; + ftp->ftps_pc = symp->st_value; + ftp->ftps_size = symp->st_size; + ftp->ftps_noffs = 0; + + for (i = 0; i < symp->st_size / 4; i++) { + + if ((text[i] & 0xfc000001) != 0x48000000 && + text[i] != 0x4e800020) + continue; + + /* + * Check for a jump within this function. If it's outside this + * function then it's a tail-call, so a return point. + */ + if ((text[i] & 0xfc000000) == 0x48000000) { + temp = (text[i] & 0x03fffffc); + /* Bit 30 denotes an absolute address. */ + if (!(text[i] & 0x02)) { + temp += symp->st_value + i * 4; + } + else { + /* Sign extend the absolute address. */ + if (temp & 0x02000000) { + temp |= (UINTPTR_MAX - 0x03ffffff); + } + } + if (temp >= symp->st_value && + temp <= (symp->st_value + symp->st_size)) + continue; + } + dt_dprintf("return at offset %x\n", i * 4); + ftp->ftps_offs[ftp->ftps_noffs++] = i * 4; + } + + free(text); + if (ftp->ftps_noffs > 0) { + if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { + dt_dprintf("fasttrap probe creation ioctl failed: %s\n", + strerror(errno)); + return (dt_set_errno(dtp, errno)); + } + } + + + return (ftp->ftps_noffs); } /*ARGSUSED*/ @@ -59,9 +137,22 @@ int dt_pid_create_offset_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp, fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, ulong_t off) { + if (off & 0x3) + return (DT_PROC_ALIGN); - dt_dprintf("%s: unimplemented\n", __func__); - return (DT_PROC_ERR); + ftp->ftps_type = DTFTP_OFFSETS; + ftp->ftps_pc = (uintptr_t)symp->st_value; + ftp->ftps_size = (size_t)symp->st_size; + ftp->ftps_noffs = 1; + ftp->ftps_offs[0] = off; + + if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { + dt_dprintf("fasttrap probe creation ioctl failed: %s\n", + strerror(errno)); + return (dt_set_errno(dtp, errno)); + } + + return (1); } /*ARGSUSED*/ @@ -69,7 +160,38 @@ int dt_pid_create_glob_offset_probes(struct ps_prochandle *P, dtrace_hdl_t *dtp, fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, const char *pattern) { + ulong_t i; + + ftp->ftps_type = DTFTP_OFFSETS; + ftp->ftps_pc = (uintptr_t)symp->st_value; + ftp->ftps_size = (size_t)symp->st_size; + ftp->ftps_noffs = 0; + + /* + * If we're matching against everything, just iterate through each + * instruction in the function, otherwise look for matching offset + * names by constructing the string and comparing it against the + * pattern. + */ + if (strcmp("*", pattern) == 0) { + for (i = 0; i < symp->st_size; i += 4) { + ftp->ftps_offs[ftp->ftps_noffs++] = i; + } + } else { + char name[sizeof (i) * 2 + 1]; + + for (i = 0; i < symp->st_size; i += 4) { + (void) sprintf(name, "%lx", i); + if (gmatch(name, pattern)) + ftp->ftps_offs[ftp->ftps_noffs++] = i; + } + } + + if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { + dt_dprintf("fasttrap probe creation ioctl failed: %s\n", + strerror(errno)); + return (dt_set_errno(dtp, errno)); + } - dt_dprintf("%s: unimplemented\n", __func__); - return (DT_PROC_ERR); + return (ftp->ftps_noffs); } Modified: head/sys/cddl/contrib/opensolaris/uts/powerpc/dtrace/fasttrap_isa.c ============================================================================== --- head/sys/cddl/contrib/opensolaris/uts/powerpc/dtrace/fasttrap_isa.c Tue Oct 15 14:52:44 2013 (r256542) +++ head/sys/cddl/contrib/opensolaris/uts/powerpc/dtrace/fasttrap_isa.c Tue Oct 15 15:00:29 2013 (r256543) @@ -18,13 +18,560 @@ * * CDDL HEADER END */ - +/* Portions Copyright 2013 Justin Hibbits */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OP(x) ((x) >> 26) +#define OPX(x) (((x) >> 2) & 0x3FF) +#define OP_BO(x) (((x) & 0x03E00000) >> 21) +#define OP_BI(x) (((x) & 0x001F0000) >> 16) +#define OP_RS(x) (((x) & 0x03E00000) >> 21) +#define OP_RA(x) (((x) & 0x001F0000) >> 16) +#define OP_RB(x) (((x) & 0x0000F100) >> 11) + + +static int +proc_ops(int op, proc_t *p, void *kaddr, off_t uaddr, size_t len) +{ + struct iovec iov; + struct uio uio; + + iov.iov_base = kaddr; + iov.iov_len = len; + uio.uio_offset = uaddr; + uio.uio_iov = &iov; + uio.uio_resid = len; + uio.uio_iovcnt = 1; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_td = curthread; + uio.uio_rw = op; + PHOLD(p); + if (proc_rwmem(p, &uio) < 0) { + PRELE(p); + return (-1); + } + PRELE(p); + + return (0); +} + +static int +uread(proc_t *p, void *kaddr, size_t len, uintptr_t uaddr) +{ + + return (proc_ops(UIO_READ, p, kaddr, uaddr, len)); +} + +static int +uwrite(proc_t *p, void *kaddr, size_t len, uintptr_t uaddr) +{ + + return (proc_ops(UIO_WRITE, p, kaddr, uaddr, len)); +} + +int +fasttrap_tracepoint_install(proc_t *p, fasttrap_tracepoint_t *tp) +{ + fasttrap_instr_t instr = FASTTRAP_INSTR; + + if (uwrite(p, &instr, 4, tp->ftt_pc) != 0) + return (-1); + + return (0); +} + +int +fasttrap_tracepoint_remove(proc_t *p, fasttrap_tracepoint_t *tp) +{ + uint32_t instr; + + /* + * Distinguish between read or write failures and a changed + * instruction. + */ + if (uread(p, &instr, 4, tp->ftt_pc) != 0) + return (0); + if (instr != FASTTRAP_INSTR) + return (0); + if (uwrite(p, &tp->ftt_instr, 4, tp->ftt_pc) != 0) + return (-1); + + return (0); +} + +int +fasttrap_tracepoint_init(proc_t *p, fasttrap_tracepoint_t *tp, uintptr_t pc, + fasttrap_probe_type_t type) +{ + uint32_t instr; + //int32_t disp; + + /* + * Read the instruction at the given address out of the process's + * address space. We don't have to worry about a debugger + * changing this instruction before we overwrite it with our trap + * instruction since P_PR_LOCK is set. + */ + if (uread(p, &instr, 4, pc) != 0) + return (-1); + + /* + * Decode the instruction to fill in the probe flags. We can have + * the process execute most instructions on its own using a pc/npc + * trick, but pc-relative control transfer present a problem since + * we're relocating the instruction. We emulate these instructions + * in the kernel. We assume a default type and over-write that as + * needed. + * + * pc-relative instructions must be emulated for correctness; + * other instructions (which represent a large set of commonly traced + * instructions) are emulated or otherwise optimized for performance. + */ + tp->ftt_type = FASTTRAP_T_COMMON; + tp->ftt_instr = instr; + + switch (OP(instr)) { + /* The following are invalid for trapping (invalid opcodes, tw/twi). */ + case 0: + case 1: + case 2: + case 4: + case 5: + case 6: + case 30: + case 39: + case 58: + case 62: + case 3: /* twi */ + return (-1); + case 31: /* tw */ + if (OPX(instr) == 4) + return (-1); + else if (OPX(instr) == 444 && OP_RS(instr) == OP_RA(instr) && + OP_RS(instr) == OP_RB(instr)) + tp->ftt_type = FASTTRAP_T_NOP; + break; + case 16: + tp->ftt_type = FASTTRAP_T_BC; + tp->ftt_dest = instr & 0x0000FFFC; /* Extract target address */ + if (instr & 0x00008000) + tp->ftt_dest |= 0xFFFF0000; + /* Use as offset if not absolute address. */ + if (!(instr & 0x02)) + tp->ftt_dest += pc; + tp->ftt_bo = OP_BO(instr); + tp->ftt_bi = OP_BI(instr); + break; + case 18: + tp->ftt_type = FASTTRAP_T_B; + tp->ftt_dest = instr & 0x03FFFFFC; /* Extract target address */ + if (instr & 0x02000000) + tp->ftt_dest |= 0xFC000000; + /* Use as offset if not absolute address. */ + if (!(instr & 0x02)) + tp->ftt_dest += pc; + break; + case 19: + switch (OPX(instr)) { + case 528: /* bcctr */ + tp->ftt_type = FASTTRAP_T_BCTR; + tp->ftt_bo = OP_BO(instr); + tp->ftt_bi = OP_BI(instr); + break; + case 16: /* bclr */ + tp->ftt_type = FASTTRAP_T_BCTR; + tp->ftt_bo = OP_BO(instr); + tp->ftt_bi = OP_BI(instr); + break; + }; + break; + case 24: + if (OP_RS(instr) == OP_RA(instr) && + (instr & 0x0000FFFF) == 0) + tp->ftt_type = FASTTRAP_T_NOP; + break; + }; + + /* + * We don't know how this tracepoint is going to be used, but in case + * it's used as part of a function return probe, we need to indicate + * whether it's always a return site or only potentially a return + * site. If it's part of a return probe, it's always going to be a + * return from that function if it's a restore instruction or if + * the previous instruction was a return. If we could reliably + * distinguish jump tables from return sites, this wouldn't be + * necessary. + */ +#if 0 + if (tp->ftt_type != FASTTRAP_T_RESTORE && + (uread(p, &instr, 4, pc - sizeof (instr)) != 0 || + !(OP(instr) == 2 && OP3(instr) == OP3_RETURN))) + tp->ftt_flags |= FASTTRAP_F_RETMAYBE; +#endif + + return (0); +} + +static uint64_t +fasttrap_anarg(struct reg *rp, int argno) +{ + uint64_t value; + proc_t *p = curproc; + + /* The first 8 arguments are in registers. */ + if (argno < 8) + return rp->fixreg[argno + 3]; + + /* Arguments on stack start after SP+LR (2 register slots). */ + if (SV_PROC_FLAG(p, SV_ILP32)) { + DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); + value = dtrace_fuword32((void *)(rp->fixreg[1] + 8 + + ((argno - 8) * sizeof(uint32_t)))); + DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT | CPU_DTRACE_BADADDR); + } else { + DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); + value = dtrace_fuword64((void *)(rp->fixreg[1] + 16 + + ((argno - 8) * sizeof(uint32_t)))); + DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT | CPU_DTRACE_BADADDR); + } + return value; +} + +uint64_t +fasttrap_pid_getarg(void *arg, dtrace_id_t id, void *parg, int argno, + int aframes) +{ + struct reg r; + + fill_regs(curthread, &r); + + return (fasttrap_anarg(&r, argno)); +} + +uint64_t +fasttrap_usdt_getarg(void *arg, dtrace_id_t id, void *parg, int argno, + int aframes) +{ + struct reg r; + + fill_regs(curthread, &r); + + return (fasttrap_anarg(&r, argno)); +} + +static void +fasttrap_usdt_args(fasttrap_probe_t *probe, struct reg *rp, int argc, + uintptr_t *argv) +{ + int i, x, cap = MIN(argc, probe->ftp_nargs); + + for (i = 0; i < cap; i++) { + x = probe->ftp_argmap[i]; + + if (x < 8) + argv[i] = rp->fixreg[x]; + else + if (SV_PROC_FLAG(curproc, SV_ILP32)) + argv[i] = fuword32((void *)(rp->fixreg[1] + 8 + + (x * sizeof(uint32_t)))); + else + argv[i] = fuword32((void *)(rp->fixreg[1] + 16 + + (x * sizeof(uint64_t)))); + } + + for (; i < argc; i++) { + argv[i] = 0; + } +} + +static void +fasttrap_return_common(struct reg *rp, uintptr_t pc, pid_t pid, + uintptr_t new_pc) +{ + fasttrap_tracepoint_t *tp; + fasttrap_bucket_t *bucket; + fasttrap_id_t *id; + + bucket = &fasttrap_tpoints.fth_table[FASTTRAP_TPOINTS_INDEX(pid, pc)]; + + for (tp = bucket->ftb_data; tp != NULL; tp = tp->ftt_next) { + if (pid == tp->ftt_pid && pc == tp->ftt_pc && + tp->ftt_proc->ftpc_acount != 0) + break; + } + + /* + * Don't sweat it if we can't find the tracepoint again; unlike + * when we're in fasttrap_pid_probe(), finding the tracepoint here + * is not essential to the correct execution of the process. + */ + if (tp == NULL) { + return; + } + + for (id = tp->ftt_retids; id != NULL; id = id->fti_next) { + /* + * If there's a branch that could act as a return site, we + * need to trace it, and check here if the program counter is + * external to the function. + */ + /* Skip function-local branches. */ + if ((new_pc - id->fti_probe->ftp_faddr) < id->fti_probe->ftp_fsize) + continue; + + dtrace_probe(id->fti_probe->ftp_id, + pc - id->fti_probe->ftp_faddr, + rp->fixreg[3], rp->fixreg[4], 0, 0); + } +} + + +static int +fasttrap_branch_taken(int bo, int bi, struct reg *regs) +{ + int crzero = 0; + + /* Branch always? */ + if ((bo & 0x14) == 0x14) + return 1; + + /* Handle decrementing ctr */ + if (!(bo & 0x04)) { + --regs->ctr; + crzero = (regs->ctr == 0); + if (bo & 0x10) { + return (!(crzero ^ (bo >> 1))); + } + } + + return (crzero | (((regs->cr >> (31 - bi)) ^ (bo >> 3)) ^ 1)); +} + + +int +fasttrap_pid_probe(struct reg *rp) +{ + proc_t *p = curproc; + uintptr_t pc = rp->pc; + uintptr_t new_pc = 0; + fasttrap_bucket_t *bucket; + fasttrap_tracepoint_t *tp, tp_local; + pid_t pid; + dtrace_icookie_t cookie; + uint_t is_enabled = 0; + + /* + * It's possible that a user (in a veritable orgy of bad planning) + * could redirect this thread's flow of control before it reached the + * return probe fasttrap. In this case we need to kill the process + * since it's in a unrecoverable state. + */ + if (curthread->t_dtrace_step) { + ASSERT(curthread->t_dtrace_on); + fasttrap_sigtrap(p, curthread, pc); + return (0); + } + + /* + * Clear all user tracing flags. + */ + curthread->t_dtrace_ft = 0; + curthread->t_dtrace_pc = 0; + curthread->t_dtrace_npc = 0; + curthread->t_dtrace_scrpc = 0; + curthread->t_dtrace_astpc = 0; + + + PROC_LOCK(p); + pid = p->p_pid; + bucket = &fasttrap_tpoints.fth_table[FASTTRAP_TPOINTS_INDEX(pid, pc)]; + + /* + * Lookup the tracepoint that the process just hit. + */ + for (tp = bucket->ftb_data; tp != NULL; tp = tp->ftt_next) { + if (pid == tp->ftt_pid && pc == tp->ftt_pc && + tp->ftt_proc->ftpc_acount != 0) + break; + } + + /* + * If we couldn't find a matching tracepoint, either a tracepoint has + * been inserted without using the pid ioctl interface (see + * fasttrap_ioctl), or somehow we have mislaid this tracepoint. + */ + if (tp == NULL) { + PROC_UNLOCK(p); + return (-1); + } + + if (tp->ftt_ids != NULL) { + fasttrap_id_t *id; + + for (id = tp->ftt_ids; id != NULL; id = id->fti_next) { + fasttrap_probe_t *probe = id->fti_probe; + + if (id->fti_ptype == DTFTP_ENTRY) { + /* + * We note that this was an entry + * probe to help ustack() find the + * first caller. + */ + cookie = dtrace_interrupt_disable(); + DTRACE_CPUFLAG_SET(CPU_DTRACE_ENTRY); + dtrace_probe(probe->ftp_id, rp->fixreg[3], + rp->fixreg[4], rp->fixreg[5], rp->fixreg[6], + rp->fixreg[7]); + DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_ENTRY); + dtrace_interrupt_enable(cookie); + } else if (id->fti_ptype == DTFTP_IS_ENABLED) { + /* + * Note that in this case, we don't + * call dtrace_probe() since it's only + * an artificial probe meant to change + * the flow of control so that it + * encounters the true probe. + */ + is_enabled = 1; + } else if (probe->ftp_argmap == NULL) { + dtrace_probe(probe->ftp_id, rp->fixreg[3], + rp->fixreg[4], rp->fixreg[5], rp->fixreg[6], + rp->fixreg[7]); + } else { + uintptr_t t[5]; + + fasttrap_usdt_args(probe, rp, + sizeof (t) / sizeof (t[0]), t); + + dtrace_probe(probe->ftp_id, t[0], t[1], + t[2], t[3], t[4]); + } + } + } + + /* + * We're about to do a bunch of work so we cache a local copy of + * the tracepoint to emulate the instruction, and then find the + * tracepoint again later if we need to light up any return probes. + */ + tp_local = *tp; + PROC_UNLOCK(p); + tp = &tp_local; + + /* + * If there's an is-enabled probe connected to this tracepoint it + * means that there was a 'xor r3, r3, r3' + * instruction that was placed there by DTrace when the binary was + * linked. As this probe is, in fact, enabled, we need to stuff 1 + * into R3. Accordingly, we can bypass all the instruction + * emulation logic since we know the inevitable result. It's possible + * that a user could construct a scenario where the 'is-enabled' + * probe was on some other instruction, but that would be a rather + * exotic way to shoot oneself in the foot. + */ + if (is_enabled) { + rp->fixreg[3] = 1; + new_pc = rp->pc + 4; + goto done; + } + + + switch (tp->ftt_type) { + case FASTTRAP_T_NOP: + new_pc = rp->pc + 4; + break; + case FASTTRAP_T_BC: + if (!fasttrap_branch_taken(tp->ftt_bo, tp->ftt_bi, rp)) + break; + /* FALLTHROUGH */ + case FASTTRAP_T_B: + if (tp->ftt_instr & 0x01) + rp->lr = rp->pc + 4; + new_pc = tp->ftt_dest; + break; + case FASTTRAP_T_BLR: + case FASTTRAP_T_BCTR: + if (!fasttrap_branch_taken(tp->ftt_bo, tp->ftt_bi, rp)) + break; + /* FALLTHROUGH */ + if (tp->ftt_type == FASTTRAP_T_BCTR) + new_pc = rp->ctr; + else + new_pc = rp->lr; + if (tp->ftt_instr & 0x01) + rp->lr = rp->pc + 4; + break; + case FASTTRAP_T_COMMON: + break; + }; +done: + /* + * If there were no return probes when we first found the tracepoint, + * we should feel no obligation to honor any return probes that were + * subsequently enabled -- they'll just have to wait until the next + * time around. + */ + if (tp->ftt_retids != NULL) { + /* + * We need to wait until the results of the instruction are + * apparent before invoking any return probes. If this + * instruction was emulated we can just call + * fasttrap_return_common(); if it needs to be executed, we + * need to wait until the user thread returns to the kernel. + */ + if (tp->ftt_type != FASTTRAP_T_COMMON) { + fasttrap_return_common(rp, pc, pid, new_pc); + } else { + ASSERT(curthread->t_dtrace_ret != 0); + ASSERT(curthread->t_dtrace_pc == pc); + ASSERT(curthread->t_dtrace_scrpc != 0); + ASSERT(new_pc == curthread->t_dtrace_astpc); + } + } + + rp->pc = new_pc; + set_regs(curthread, rp); + + return (0); +} + +int +fasttrap_return_probe(struct reg *rp) +{ + proc_t *p = curproc; + uintptr_t pc = curthread->t_dtrace_pc; + uintptr_t npc = curthread->t_dtrace_npc; + + curthread->t_dtrace_pc = 0; + curthread->t_dtrace_npc = 0; + curthread->t_dtrace_scrpc = 0; + curthread->t_dtrace_astpc = 0; + + /* + * We set rp->pc to the address of the traced instruction so + * that it appears to dtrace_probe() that we're on the original + * instruction, and so that the user can't easily detect our + * complex web of lies. dtrace_return_probe() (our caller) + * will correctly set %pc after we return. + */ + rp->pc = pc; + + fasttrap_return_common(rp, pc, p->p_pid, npc); + + return (0); +} -/* - * XXX: Placeholder for PowerPC fasttrap code - */ Modified: head/sys/cddl/contrib/opensolaris/uts/powerpc/sys/fasttrap_isa.h ============================================================================== --- head/sys/cddl/contrib/opensolaris/uts/powerpc/sys/fasttrap_isa.h Tue Oct 15 14:52:44 2013 (r256542) +++ head/sys/cddl/contrib/opensolaris/uts/powerpc/sys/fasttrap_isa.h Tue Oct 15 15:00:29 2013 (r256543) @@ -19,6 +19,7 @@ * * CDDL HEADER END */ +/* Portions Copyright 2013 Justin Hibbits */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. @@ -34,13 +35,39 @@ #ifdef __cplusplus extern "C" { #endif -/* - * XXXDTRACE: placehodler for PowerPC fasttrap stuff - */ -typedef uint32_t fasttrap_instr_t; #define FASTTRAP_SUNWDTRACE_SIZE 64 -#define FASTTRAP_INSTR 0x0FFFDDDD +#define FASTTRAP_INSTR 0x0FFFDDDD + +typedef uint32_t fasttrap_instr_t; + +typedef struct fasttrap_machtp_t { + fasttrap_instr_t ftmt_instr; /* original instruction */ + uintptr_t ftmt_dest; /* branch target */ + uint8_t ftmt_type; /* emulation type */ + uint8_t ftmt_flags; /* emulation flags */ + uint8_t ftmt_bo; /* BO field */ + uint8_t ftmt_bi; /* BI field (CR bit) */ +} fasttrap_machtp_t; + +#define ftt_instr ftt_mtp.ftmt_instr +#define ftt_dest ftt_mtp.ftmt_dest +#define ftt_type ftt_mtp.ftmt_type +#define ftt_flags ftt_mtp.ftmt_flags +#define ftt_bo ftt_mtp.ftmt_bo +#define ftt_bi ftt_mtp.ftmt_bi + +#define FASTTRAP_T_COMMON 0x00 +#define FASTTRAP_T_B 0x01 +#define FASTTRAP_T_BC 0x02 +#define FASTTRAP_T_BLR 0x03 +#define FASTTRAP_T_BCTR 0x04 +#define FASTTRAP_T_NOP 0x05 + +#define FASTTRAP_AFRAMES 3 +#define FASTTRAP_RETURN_AFRAMES 4 +#define FASTTRAP_ENTRY_AFRAMES 3 +#define FASTTRAP_OFFSET_AFRAMES 3 #ifdef __cplusplus } Modified: head/sys/modules/dtrace/Makefile ============================================================================== --- head/sys/modules/dtrace/Makefile Tue Oct 15 14:52:44 2013 (r256542) +++ head/sys/modules/dtrace/Makefile Tue Oct 15 15:00:29 2013 (r256543) @@ -20,7 +20,7 @@ SUBDIR= dtmalloc \ SUBDIR+= fasttrap fbt systrace_linux32 .endif .if ${MACHINE_CPUARCH} == "powerpc" -SUBDIR+= fbt +SUBDIR+= fbt fasttrap .endif .if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_ARCH} == "powerpc64" SUBDIR+= systrace_freebsd32 Modified: head/sys/modules/dtrace/fasttrap/Makefile ============================================================================== --- head/sys/modules/dtrace/fasttrap/Makefile Tue Oct 15 14:52:44 2013 (r256542) +++ head/sys/modules/dtrace/fasttrap/Makefile Tue Oct 15 15:00:29 2013 (r256543) @@ -13,6 +13,9 @@ CFLAGS+= -I${.CURDIR}/../../../cddl/comp .if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" CFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/uts/intel .PATH: ${.CURDIR}/../../../cddl/contrib/opensolaris/uts/intel/dtrace +.elif ${MACHINE_CPUARCH} == "powerpc" +CFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/uts/powerpc +.PATH: ${.CURDIR}/../../../cddl/contrib/opensolaris/uts/powerpc/dtrace .endif CFLAGS+= -DSMP Modified: head/sys/powerpc/aim/trap.c ============================================================================== --- head/sys/powerpc/aim/trap.c Tue Oct 15 14:52:44 2013 (r256542) +++ head/sys/powerpc/aim/trap.c Tue Oct 15 15:00:29 2013 (r256543) @@ -175,6 +175,9 @@ trap(struct trapframe *frame) { struct thread *td; struct proc *p; +#ifdef KDTRACE_HOOKS + uint32_t inst; +#endif int sig, type, user; u_int ucode; ksiginfo_t ksi; @@ -279,9 +282,18 @@ trap(struct trapframe *frame) case EXC_PGM: /* Identify the trap reason */ - if (frame->srr1 & EXC_PGM_TRAP) + if (frame->srr1 & EXC_PGM_TRAP) { +#ifdef KDTRACE_HOOKS + inst = fuword32((const void *)frame->srr0); + if (inst == 0x0FFFDDDD && dtrace_pid_probe_ptr != NULL) { + struct reg regs; + fill_regs(td, ®s); + (*dtrace_pid_probe_ptr)(®s); + break; + } +#endif sig = SIGTRAP; - else if (ppc_instr_emulate(frame) == 0) + } else if (ppc_instr_emulate(frame) == 0) frame->srr0 += 4; else sig = SIGILL;