Path: blob/main/sys/cddl/contrib/opensolaris/uts/powerpc/dtrace/fasttrap_isa.c
96395 views
/*1* CDDL HEADER START2*3* The contents of this file are subject to the terms of the4* Common Development and Distribution License (the "License").5* You may not use this file except in compliance with the License.6*7* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE8* or http://www.opensolaris.org/os/licensing.9* See the License for the specific language governing permissions10* and limitations under the License.11*12* When distributing Covered Code, include this CDDL HEADER in each13* file and include the License file at usr/src/OPENSOLARIS.LICENSE.14* If applicable, add the following below this CDDL HEADER, with the15* fields enclosed by brackets "[]" replaced with your own identifying16* information: Portions Copyright [yyyy] [name of copyright owner]17*18* CDDL HEADER END19*/20/* Portions Copyright 2013 Justin Hibbits */21/*22* Copyright 2007 Sun Microsystems, Inc. All rights reserved.23* Use is subject to license terms.24*/2526#include <sys/fasttrap_isa.h>27#include <sys/fasttrap_impl.h>28#include <sys/dtrace.h>29#include <sys/dtrace_impl.h>30#include <cddl/dev/dtrace/dtrace_cddl.h>31#include <sys/proc.h>32#include <sys/types.h>33#include <sys/uio.h>34#include <sys/ptrace.h>35#include <sys/rmlock.h>36#include <sys/sysent.h>3738#define OP(x) ((x) >> 26)39#define OPX(x) (((x) >> 2) & 0x3FF)40#define OP_BO(x) (((x) & 0x03E00000) >> 21)41#define OP_BI(x) (((x) & 0x001F0000) >> 16)42#define OP_RS(x) (((x) & 0x03E00000) >> 21)43#define OP_RA(x) (((x) & 0x001F0000) >> 16)44#define OP_RB(x) (((x) & 0x0000F100) >> 11)4546int47fasttrap_tracepoint_install(proc_t *p, fasttrap_tracepoint_t *tp)48{49fasttrap_instr_t instr = FASTTRAP_INSTR;5051if (uwrite(p, &instr, 4, tp->ftt_pc) != 0)52return (-1);5354return (0);55}5657int58fasttrap_tracepoint_remove(proc_t *p, fasttrap_tracepoint_t *tp)59{60uint32_t instr;6162/*63* Distinguish between read or write failures and a changed64* instruction.65*/66if (uread(p, &instr, 4, tp->ftt_pc) != 0)67return (0);68if (instr != FASTTRAP_INSTR)69return (0);70if (uwrite(p, &tp->ftt_instr, 4, tp->ftt_pc) != 0)71return (-1);7273return (0);74}7576int77fasttrap_tracepoint_init(proc_t *p, fasttrap_tracepoint_t *tp, uintptr_t pc,78fasttrap_probe_type_t type)79{80uint32_t instr;81//int32_t disp;8283/*84* Read the instruction at the given address out of the process's85* address space. We don't have to worry about a debugger86* changing this instruction before we overwrite it with our trap87* instruction since P_PR_LOCK is set.88*/89if (uread(p, &instr, 4, pc) != 0)90return (-1);9192/*93* Decode the instruction to fill in the probe flags. We can have94* the process execute most instructions on its own using a pc/npc95* trick, but pc-relative control transfer present a problem since96* we're relocating the instruction. We emulate these instructions97* in the kernel. We assume a default type and over-write that as98* needed.99*100* pc-relative instructions must be emulated for correctness;101* other instructions (which represent a large set of commonly traced102* instructions) are emulated or otherwise optimized for performance.103*/104tp->ftt_type = FASTTRAP_T_COMMON;105tp->ftt_instr = instr;106107switch (OP(instr)) {108/* The following are invalid for trapping (invalid opcodes, tw/twi). */109case 0:110case 1:111case 2:112case 4:113case 5:114case 6:115case 30:116case 39:117case 58:118case 62:119case 3: /* twi */120return (-1);121case 31: /* tw */122if (OPX(instr) == 4)123return (-1);124else if (OPX(instr) == 444 && OP_RS(instr) == OP_RA(instr) &&125OP_RS(instr) == OP_RB(instr))126tp->ftt_type = FASTTRAP_T_NOP;127break;128case 16:129tp->ftt_type = FASTTRAP_T_BC;130tp->ftt_dest = instr & 0x0000FFFC; /* Extract target address */131if (instr & 0x00008000)132tp->ftt_dest |= 0xFFFF0000;133/* Use as offset if not absolute address. */134if (!(instr & 0x02))135tp->ftt_dest += pc;136tp->ftt_bo = OP_BO(instr);137tp->ftt_bi = OP_BI(instr);138break;139case 18:140tp->ftt_type = FASTTRAP_T_B;141tp->ftt_dest = instr & 0x03FFFFFC; /* Extract target address */142if (instr & 0x02000000)143tp->ftt_dest |= 0xFC000000;144/* Use as offset if not absolute address. */145if (!(instr & 0x02))146tp->ftt_dest += pc;147break;148case 19:149switch (OPX(instr)) {150case 528: /* bcctr */151tp->ftt_type = FASTTRAP_T_BCTR;152tp->ftt_bo = OP_BO(instr);153tp->ftt_bi = OP_BI(instr);154break;155case 16: /* bclr */156tp->ftt_type = FASTTRAP_T_BCTR;157tp->ftt_bo = OP_BO(instr);158tp->ftt_bi = OP_BI(instr);159break;160};161break;162case 24:163if (OP_RS(instr) == OP_RA(instr) &&164(instr & 0x0000FFFF) == 0)165tp->ftt_type = FASTTRAP_T_NOP;166break;167};168169/*170* We don't know how this tracepoint is going to be used, but in case171* it's used as part of a function return probe, we need to indicate172* whether it's always a return site or only potentially a return173* site. If it's part of a return probe, it's always going to be a174* return from that function if it's a restore instruction or if175* the previous instruction was a return. If we could reliably176* distinguish jump tables from return sites, this wouldn't be177* necessary.178*/179#if 0180if (tp->ftt_type != FASTTRAP_T_RESTORE &&181(uread(p, &instr, 4, pc - sizeof (instr)) != 0 ||182!(OP(instr) == 2 && OP3(instr) == OP3_RETURN)))183tp->ftt_flags |= FASTTRAP_F_RETMAYBE;184#endif185186return (0);187}188189static uint64_t190fasttrap_anarg(struct reg *rp, int argno)191{192uint64_t value;193proc_t *p = curproc;194195/* The first 8 arguments are in registers. */196if (argno < 8)197return rp->fixreg[argno + 3];198199/* Arguments on stack start after SP+LR (2 register slots). */200if (SV_PROC_FLAG(p, SV_ILP32)) {201DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);202value = dtrace_fuword32((void *)(rp->fixreg[1] + 8 +203((argno - 8) * sizeof(uint32_t))));204DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT | CPU_DTRACE_BADADDR);205} else {206DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);207value = dtrace_fuword64((void *)(rp->fixreg[1] + 48 +208((argno - 8) * sizeof(uint64_t))));209DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT | CPU_DTRACE_BADADDR);210}211return value;212}213214uint64_t215fasttrap_pid_getarg(void *arg, dtrace_id_t id, void *parg, int argno,216int aframes)217{218struct reg r;219220fill_regs(curthread, &r);221222return (fasttrap_anarg(&r, argno));223}224225uint64_t226fasttrap_usdt_getarg(void *arg, dtrace_id_t id, void *parg, int argno,227int aframes)228{229struct reg r;230231fill_regs(curthread, &r);232233return (fasttrap_anarg(&r, argno));234}235236static void237fasttrap_usdt_args(fasttrap_probe_t *probe, struct reg *rp, int argc,238uintptr_t *argv)239{240int i, x, cap = MIN(argc, probe->ftp_nargs);241242for (i = 0; i < cap; i++) {243x = probe->ftp_argmap[i];244245if (x < 8)246argv[i] = rp->fixreg[x];247else248#ifdef __powerpc64__249if (SV_PROC_FLAG(curproc, SV_ILP32))250#endif251argv[i] = fuword32((void *)(rp->fixreg[1] + 8 +252(x * sizeof(uint32_t))));253#ifdef __powerpc64__254else255argv[i] = fuword64((void *)(rp->fixreg[1] + 48 +256(x * sizeof(uint64_t))));257#endif258}259260for (; i < argc; i++) {261argv[i] = 0;262}263}264265static void266fasttrap_return_common(struct reg *rp, uintptr_t pc, pid_t pid,267uintptr_t new_pc)268{269struct rm_priotracker tracker;270fasttrap_tracepoint_t *tp;271fasttrap_bucket_t *bucket;272fasttrap_id_t *id;273274rm_rlock(&fasttrap_tp_lock, &tracker);275bucket = &fasttrap_tpoints.fth_table[FASTTRAP_TPOINTS_INDEX(pid, pc)];276277for (tp = bucket->ftb_data; tp != NULL; tp = tp->ftt_next) {278if (pid == tp->ftt_pid && pc == tp->ftt_pc &&279tp->ftt_proc->ftpc_acount != 0)280break;281}282283/*284* Don't sweat it if we can't find the tracepoint again; unlike285* when we're in fasttrap_pid_probe(), finding the tracepoint here286* is not essential to the correct execution of the process.287*/288if (tp == NULL) {289rm_runlock(&fasttrap_tp_lock, &tracker);290return;291}292293for (id = tp->ftt_retids; id != NULL; id = id->fti_next) {294/*295* If there's a branch that could act as a return site, we296* need to trace it, and check here if the program counter is297* external to the function.298*/299/* Skip function-local branches. */300if ((new_pc - id->fti_probe->ftp_faddr) < id->fti_probe->ftp_fsize)301continue;302303dtrace_probe(id->fti_probe->ftp_id,304pc - id->fti_probe->ftp_faddr,305rp->fixreg[3], rp->fixreg[4], 0, 0);306}307rm_runlock(&fasttrap_tp_lock, &tracker);308}309310311static int312fasttrap_branch_taken(int bo, int bi, struct reg *regs)313{314int crzero = 0;315316/* Branch always? */317if ((bo & 0x14) == 0x14)318return 1;319320/* Handle decrementing ctr */321if (!(bo & 0x04)) {322--regs->ctr;323crzero = (regs->ctr == 0);324if (bo & 0x10) {325return (!(crzero ^ (bo >> 1)));326}327}328329return (crzero | (((regs->cr >> (31 - bi)) ^ (bo >> 3)) ^ 1));330}331332333int334fasttrap_pid_probe(struct trapframe *frame)335{336struct reg reg, *rp;337struct rm_priotracker tracker;338proc_t *p = curproc;339uintptr_t pc;340uintptr_t new_pc = 0;341fasttrap_bucket_t *bucket;342fasttrap_tracepoint_t *tp, tp_local;343pid_t pid;344dtrace_icookie_t cookie;345uint_t is_enabled = 0;346347fill_regs(curthread, ®);348rp = ®349pc = rp->pc;350351/*352* It's possible that a user (in a veritable orgy of bad planning)353* could redirect this thread's flow of control before it reached the354* return probe fasttrap. In this case we need to kill the process355* since it's in a unrecoverable state.356*/357if (curthread->t_dtrace_step) {358ASSERT(curthread->t_dtrace_on);359fasttrap_sigtrap(p, curthread, pc);360return (0);361}362363/*364* Clear all user tracing flags.365*/366curthread->t_dtrace_ft = 0;367curthread->t_dtrace_pc = 0;368curthread->t_dtrace_npc = 0;369curthread->t_dtrace_scrpc = 0;370curthread->t_dtrace_astpc = 0;371372rm_rlock(&fasttrap_tp_lock, &tracker);373pid = p->p_pid;374bucket = &fasttrap_tpoints.fth_table[FASTTRAP_TPOINTS_INDEX(pid, pc)];375376/*377* Lookup the tracepoint that the process just hit.378*/379for (tp = bucket->ftb_data; tp != NULL; tp = tp->ftt_next) {380if (pid == tp->ftt_pid && pc == tp->ftt_pc &&381tp->ftt_proc->ftpc_acount != 0)382break;383}384385/*386* If we couldn't find a matching tracepoint, either a tracepoint has387* been inserted without using the pid<pid> ioctl interface (see388* fasttrap_ioctl), or somehow we have mislaid this tracepoint.389*/390if (tp == NULL) {391rm_runlock(&fasttrap_tp_lock, &tracker);392return (-1);393}394395if (tp->ftt_ids != NULL) {396fasttrap_id_t *id;397398for (id = tp->ftt_ids; id != NULL; id = id->fti_next) {399fasttrap_probe_t *probe = id->fti_probe;400401if (id->fti_ptype == DTFTP_ENTRY) {402/*403* We note that this was an entry404* probe to help ustack() find the405* first caller.406*/407cookie = dtrace_interrupt_disable();408DTRACE_CPUFLAG_SET(CPU_DTRACE_ENTRY);409dtrace_probe(probe->ftp_id, rp->fixreg[3],410rp->fixreg[4], rp->fixreg[5], rp->fixreg[6],411rp->fixreg[7]);412DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_ENTRY);413dtrace_interrupt_enable(cookie);414} else if (id->fti_ptype == DTFTP_IS_ENABLED) {415/*416* Note that in this case, we don't417* call dtrace_probe() since it's only418* an artificial probe meant to change419* the flow of control so that it420* encounters the true probe.421*/422is_enabled = 1;423} else if (probe->ftp_argmap == NULL) {424dtrace_probe(probe->ftp_id, rp->fixreg[3],425rp->fixreg[4], rp->fixreg[5], rp->fixreg[6],426rp->fixreg[7]);427} else {428uintptr_t t[5];429430fasttrap_usdt_args(probe, rp,431sizeof (t) / sizeof (t[0]), t);432433dtrace_probe(probe->ftp_id, t[0], t[1],434t[2], t[3], t[4]);435}436}437}438439/*440* We're about to do a bunch of work so we cache a local copy of441* the tracepoint to emulate the instruction, and then find the442* tracepoint again later if we need to light up any return probes.443*/444tp_local = *tp;445rm_runlock(&fasttrap_tp_lock, &tracker);446tp = &tp_local;447448/*449* If there's an is-enabled probe connected to this tracepoint it450* means that there was a 'xor r3, r3, r3'451* instruction that was placed there by DTrace when the binary was452* linked. As this probe is, in fact, enabled, we need to stuff 1453* into R3. Accordingly, we can bypass all the instruction454* emulation logic since we know the inevitable result. It's possible455* that a user could construct a scenario where the 'is-enabled'456* probe was on some other instruction, but that would be a rather457* exotic way to shoot oneself in the foot.458*/459if (is_enabled) {460rp->fixreg[3] = 1;461new_pc = rp->pc + 4;462goto done;463}464465466switch (tp->ftt_type) {467case FASTTRAP_T_NOP:468new_pc = rp->pc + 4;469break;470case FASTTRAP_T_BC:471if (!fasttrap_branch_taken(tp->ftt_bo, tp->ftt_bi, rp))472break;473/* FALLTHROUGH */474case FASTTRAP_T_B:475if (tp->ftt_instr & 0x01)476rp->lr = rp->pc + 4;477new_pc = tp->ftt_dest;478break;479case FASTTRAP_T_BLR:480case FASTTRAP_T_BCTR:481if (!fasttrap_branch_taken(tp->ftt_bo, tp->ftt_bi, rp))482break;483/* FALLTHROUGH */484if (tp->ftt_type == FASTTRAP_T_BCTR)485new_pc = rp->ctr;486else487new_pc = rp->lr;488if (tp->ftt_instr & 0x01)489rp->lr = rp->pc + 4;490break;491case FASTTRAP_T_COMMON:492curthread->t_dtrace_pc = pc;493curthread->t_dtrace_npc = pc + 4;494curthread->t_dtrace_on = 1;495new_pc = pc;496break;497};498done:499/*500* If there were no return probes when we first found the tracepoint,501* we should feel no obligation to honor any return probes that were502* subsequently enabled -- they'll just have to wait until the next503* time around.504*/505if (tp->ftt_retids != NULL) {506/*507* We need to wait until the results of the instruction are508* apparent before invoking any return probes. If this509* instruction was emulated we can just call510* fasttrap_return_common(); if it needs to be executed, we511* need to wait until the user thread returns to the kernel.512*/513if (tp->ftt_type != FASTTRAP_T_COMMON) {514fasttrap_return_common(rp, pc, pid, new_pc);515} else {516ASSERT(curthread->t_dtrace_ret != 0);517ASSERT(curthread->t_dtrace_pc == pc);518ASSERT(curthread->t_dtrace_scrpc != 0);519ASSERT(new_pc == curthread->t_dtrace_astpc);520}521}522523rp->pc = new_pc;524set_regs(curthread, rp);525526return (0);527}528529int530fasttrap_return_probe(struct trapframe *tf)531{532struct reg reg, *rp;533proc_t *p = curproc;534uintptr_t pc = curthread->t_dtrace_pc;535uintptr_t npc = curthread->t_dtrace_npc;536537curthread->t_dtrace_pc = 0;538curthread->t_dtrace_npc = 0;539curthread->t_dtrace_scrpc = 0;540curthread->t_dtrace_astpc = 0;541542fill_regs(curthread, ®);543rp = ®544545/*546* We set rp->pc to the address of the traced instruction so547* that it appears to dtrace_probe() that we're on the original548* instruction.549*/550rp->pc = pc;551552fasttrap_return_common(rp, pc, p->p_pid, npc);553554return (0);555}556557558