17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*ad4023c4Sdp * Common Development and Distribution License (the "License"). 6*ad4023c4Sdp * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*ad4023c4Sdp * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate #include <sys/errno.h> 297c478bd9Sstevel@tonic-gate #include <sys/stat.h> 307c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 317c478bd9Sstevel@tonic-gate #include <sys/conf.h> 327c478bd9Sstevel@tonic-gate #include <sys/systm.h> 337c478bd9Sstevel@tonic-gate #include <sys/ddi.h> 347c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 357c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h> 367c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 377c478bd9Sstevel@tonic-gate #include <sys/strsubr.h> 387c478bd9Sstevel@tonic-gate #include <sys/dtrace.h> 397c478bd9Sstevel@tonic-gate #include <sys/kobj.h> 407c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 417c478bd9Sstevel@tonic-gate #include <sys/atomic.h> 427c478bd9Sstevel@tonic-gate #include <vm/seg_kmem.h> 437c478bd9Sstevel@tonic-gate #include <sys/stack.h> 447c478bd9Sstevel@tonic-gate #include <sys/ctf_api.h> 457c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 467c478bd9Sstevel@tonic-gate 477c478bd9Sstevel@tonic-gate static dev_info_t *fbt_devi; 487c478bd9Sstevel@tonic-gate static dtrace_provider_id_t fbt_id; 497c478bd9Sstevel@tonic-gate static uintptr_t fbt_trampoline; 507c478bd9Sstevel@tonic-gate static caddr_t fbt_trampoline_window; 517c478bd9Sstevel@tonic-gate static size_t fbt_trampoline_size; 527c478bd9Sstevel@tonic-gate static int fbt_verbose = 0; 537c478bd9Sstevel@tonic-gate 547c478bd9Sstevel@tonic-gate /* 557c478bd9Sstevel@tonic-gate * Various interesting bean counters. 567c478bd9Sstevel@tonic-gate */ 577c478bd9Sstevel@tonic-gate static int fbt_entry; 587c478bd9Sstevel@tonic-gate static int fbt_ret; 597c478bd9Sstevel@tonic-gate static int fbt_retl; 607c478bd9Sstevel@tonic-gate static int fbt_retl_jmptab; 617c478bd9Sstevel@tonic-gate static int fbt_retl_twoinstr; 627c478bd9Sstevel@tonic-gate static int fbt_retl_tailcall; 637c478bd9Sstevel@tonic-gate static int fbt_retl_tailjmpl; 647c478bd9Sstevel@tonic-gate static int fbt_leaf_functions; 657c478bd9Sstevel@tonic-gate 667c478bd9Sstevel@tonic-gate extern char stubs_base[]; 677c478bd9Sstevel@tonic-gate extern char stubs_end[]; 687c478bd9Sstevel@tonic-gate 697c478bd9Sstevel@tonic-gate #define FBT_REG_G0 0 707c478bd9Sstevel@tonic-gate #define FBT_REG_G1 1 717c478bd9Sstevel@tonic-gate #define FBT_REG_O0 8 727c478bd9Sstevel@tonic-gate #define FBT_REG_O1 9 737c478bd9Sstevel@tonic-gate #define FBT_REG_O2 10 747c478bd9Sstevel@tonic-gate #define FBT_REG_O3 11 757c478bd9Sstevel@tonic-gate #define FBT_REG_O4 12 767c478bd9Sstevel@tonic-gate #define FBT_REG_O5 13 777c478bd9Sstevel@tonic-gate #define FBT_REG_O6 14 787c478bd9Sstevel@tonic-gate #define FBT_REG_O7 15 797c478bd9Sstevel@tonic-gate #define FBT_REG_I0 24 807c478bd9Sstevel@tonic-gate #define FBT_REG_I1 25 817c478bd9Sstevel@tonic-gate #define FBT_REG_I2 26 827c478bd9Sstevel@tonic-gate #define FBT_REG_I3 27 837c478bd9Sstevel@tonic-gate #define FBT_REG_I4 28 847c478bd9Sstevel@tonic-gate #define FBT_REG_I7 31 857c478bd9Sstevel@tonic-gate #define FBT_REG_L0 16 867c478bd9Sstevel@tonic-gate #define FBT_REG_L1 17 877c478bd9Sstevel@tonic-gate #define FBT_REG_L2 18 887c478bd9Sstevel@tonic-gate #define FBT_REG_L3 19 897c478bd9Sstevel@tonic-gate #define FBT_REG_PC 5 907c478bd9Sstevel@tonic-gate 917c478bd9Sstevel@tonic-gate #define FBT_REG_ISGLOBAL(r) ((r) < 8) 927c478bd9Sstevel@tonic-gate #define FBT_REG_ISOUTPUT(r) ((r) >= 8 && (r) < 16) 937c478bd9Sstevel@tonic-gate #define FBT_REG_ISLOCAL(r) ((r) >= 16 && (r) < 24) 947c478bd9Sstevel@tonic-gate #define FBT_REG_ISVOLATILE(r) \ 957c478bd9Sstevel@tonic-gate ((FBT_REG_ISGLOBAL(r) || FBT_REG_ISOUTPUT(r)) && (r) != FBT_REG_G0) 967c478bd9Sstevel@tonic-gate #define FBT_REG_NLOCALS 8 977c478bd9Sstevel@tonic-gate 987c478bd9Sstevel@tonic-gate #define FBT_REG_MARKLOCAL(locals, r) \ 997c478bd9Sstevel@tonic-gate if (FBT_REG_ISLOCAL(r)) \ 1007c478bd9Sstevel@tonic-gate (locals)[(r) - FBT_REG_L0] = 1; 1017c478bd9Sstevel@tonic-gate 1027c478bd9Sstevel@tonic-gate #define FBT_REG_INITLOCALS(local, locals) \ 1037c478bd9Sstevel@tonic-gate for ((local) = 0; (local) < FBT_REG_NLOCALS; (local)++) \ 1047c478bd9Sstevel@tonic-gate (locals)[(local)] = 0; \ 1057c478bd9Sstevel@tonic-gate (local) = FBT_REG_L0 1067c478bd9Sstevel@tonic-gate 1077c478bd9Sstevel@tonic-gate #define FBT_REG_ALLOCLOCAL(local, locals) \ 1087c478bd9Sstevel@tonic-gate while ((locals)[(local) - FBT_REG_L0]) \ 1097c478bd9Sstevel@tonic-gate (local)++; \ 1107c478bd9Sstevel@tonic-gate (locals)[(local) - FBT_REG_L0] = 1; 1117c478bd9Sstevel@tonic-gate 1127c478bd9Sstevel@tonic-gate #define FBT_OP_MASK 0xc0000000 1137c478bd9Sstevel@tonic-gate #define FBT_OP_SHIFT 30 1147c478bd9Sstevel@tonic-gate #define FBT_OP(val) ((val) & FBT_FMT1_MASK) 1157c478bd9Sstevel@tonic-gate 1167c478bd9Sstevel@tonic-gate #define FBT_SIMM13_MASK 0x1fff 1177c478bd9Sstevel@tonic-gate #define FBT_SIMM13_MAX ((int32_t)0xfff) 1187c478bd9Sstevel@tonic-gate #define FBT_IMM22_MASK 0x3fffff 1197c478bd9Sstevel@tonic-gate #define FBT_IMM22_SHIFT 10 1207c478bd9Sstevel@tonic-gate #define FBT_IMM10_MASK 0x3ff 1217c478bd9Sstevel@tonic-gate 1227c478bd9Sstevel@tonic-gate #define FBT_DISP30_MASK 0x3fffffff 1237c478bd9Sstevel@tonic-gate #define FBT_DISP30(from, to) \ 1247c478bd9Sstevel@tonic-gate (((uintptr_t)(to) - (uintptr_t)(from) >> 2) & FBT_DISP30_MASK) 1257c478bd9Sstevel@tonic-gate 1267c478bd9Sstevel@tonic-gate #define FBT_DISP22_MASK 0x3fffff 1277c478bd9Sstevel@tonic-gate #define FBT_DISP22(from, to) \ 1287c478bd9Sstevel@tonic-gate (((uintptr_t)(to) - (uintptr_t)(from) >> 2) & FBT_DISP22_MASK) 1297c478bd9Sstevel@tonic-gate 1307c478bd9Sstevel@tonic-gate #define FBT_DISP19_MASK 0x7ffff 1317c478bd9Sstevel@tonic-gate #define FBT_DISP19(from, to) \ 1327c478bd9Sstevel@tonic-gate (((uintptr_t)(to) - (uintptr_t)(from) >> 2) & FBT_DISP19_MASK) 1337c478bd9Sstevel@tonic-gate 1347c478bd9Sstevel@tonic-gate #define FBT_DISP16_HISHIFT 20 1357c478bd9Sstevel@tonic-gate #define FBT_DISP16_HIMASK (0x3 << FBT_DISP16_HISHIFT) 1367c478bd9Sstevel@tonic-gate #define FBT_DISP16_LOMASK (0x3fff) 1377c478bd9Sstevel@tonic-gate #define FBT_DISP16_MASK (FBT_DISP16_HIMASK | FBT_DISP16_LOMASK) 1387c478bd9Sstevel@tonic-gate #define FBT_DISP16(val) \ 1397c478bd9Sstevel@tonic-gate ((((val) & FBT_DISP16_HIMASK) >> 6) | ((val) & FBT_DISP16_LOMASK)) 1407c478bd9Sstevel@tonic-gate 1417c478bd9Sstevel@tonic-gate #define FBT_DISP14_MASK 0x3fff 1427c478bd9Sstevel@tonic-gate #define FBT_DISP14(from, to) \ 1437c478bd9Sstevel@tonic-gate (((uintptr_t)(to) - (uintptr_t)(from) >> 2) & FBT_DISP14_MASK) 1447c478bd9Sstevel@tonic-gate 1457c478bd9Sstevel@tonic-gate #define FBT_OP0 (((uint32_t)0) << FBT_OP_SHIFT) 1467c478bd9Sstevel@tonic-gate #define FBT_OP1 (((uint32_t)1) << FBT_OP_SHIFT) 1477c478bd9Sstevel@tonic-gate #define FBT_OP2 (((uint32_t)2) << FBT_OP_SHIFT) 1487c478bd9Sstevel@tonic-gate #define FBT_ILLTRAP 0 1497c478bd9Sstevel@tonic-gate 1507c478bd9Sstevel@tonic-gate #define FBT_ANNUL_SHIFT 29 1517c478bd9Sstevel@tonic-gate #define FBT_ANNUL (1 << FBT_ANNUL_SHIFT) 1527c478bd9Sstevel@tonic-gate 1537c478bd9Sstevel@tonic-gate #define FBT_FMT3_OP3_SHIFT 19 1547c478bd9Sstevel@tonic-gate #define FBT_FMT3_OP_MASK 0xc1f80000 1557c478bd9Sstevel@tonic-gate #define FBT_FMT3_OP(val) ((val) & FBT_FMT3_OP_MASK) 1567c478bd9Sstevel@tonic-gate 1577c478bd9Sstevel@tonic-gate #define FBT_FMT3_RD_SHIFT 25 1587c478bd9Sstevel@tonic-gate #define FBT_FMT3_RD_MASK (0x1f << FBT_FMT3_RD_SHIFT) 1597c478bd9Sstevel@tonic-gate #define FBT_FMT3_RD(val) \ 1607c478bd9Sstevel@tonic-gate (((val) & FBT_FMT3_RD_MASK) >> FBT_FMT3_RD_SHIFT) 1617c478bd9Sstevel@tonic-gate 1627c478bd9Sstevel@tonic-gate #define FBT_FMT3_RS1_SHIFT 14 1637c478bd9Sstevel@tonic-gate #define FBT_FMT3_RS1_MASK (0x1f << FBT_FMT3_RS1_SHIFT) 1647c478bd9Sstevel@tonic-gate #define FBT_FMT3_RS1(val) \ 1657c478bd9Sstevel@tonic-gate (((val) & FBT_FMT3_RS1_MASK) >> FBT_FMT3_RS1_SHIFT) 1667c478bd9Sstevel@tonic-gate #define FBT_FMT3_RS1_SET(val, rs1) \ 1677c478bd9Sstevel@tonic-gate (val) = ((val) & ~FBT_FMT3_RS1_MASK) | ((rs1) << FBT_FMT3_RS1_SHIFT) 1687c478bd9Sstevel@tonic-gate 1697c478bd9Sstevel@tonic-gate #define FBT_FMT3_RS2_SHIFT 0 1707c478bd9Sstevel@tonic-gate #define FBT_FMT3_RS2_MASK (0x1f << FBT_FMT3_RS2_SHIFT) 1717c478bd9Sstevel@tonic-gate #define FBT_FMT3_RS2(val) \ 1727c478bd9Sstevel@tonic-gate (((val) & FBT_FMT3_RS2_MASK) >> FBT_FMT3_RS2_SHIFT) 1737c478bd9Sstevel@tonic-gate #define FBT_FMT3_RS2_SET(val, rs2) \ 1747c478bd9Sstevel@tonic-gate (val) = ((val) & ~FBT_FMT3_RS2_MASK) | ((rs2) << FBT_FMT3_RS2_SHIFT) 1757c478bd9Sstevel@tonic-gate 1767c478bd9Sstevel@tonic-gate #define FBT_FMT3_IMM_SHIFT 13 1777c478bd9Sstevel@tonic-gate #define FBT_FMT3_IMM (1 << FBT_FMT3_IMM_SHIFT) 1787c478bd9Sstevel@tonic-gate #define FBT_FMT3_SIMM13_MASK FBT_SIMM13_MASK 1797c478bd9Sstevel@tonic-gate 1807c478bd9Sstevel@tonic-gate #define FBT_FMT3_ISIMM(val) ((val) & FBT_FMT3_IMM) 1817c478bd9Sstevel@tonic-gate #define FBT_FMT3_SIMM13(val) ((val) & FBT_FMT3_SIMM13_MASK) 1827c478bd9Sstevel@tonic-gate 1837c478bd9Sstevel@tonic-gate #define FBT_FMT2_OP2_SHIFT 22 1847c478bd9Sstevel@tonic-gate #define FBT_FMT2_OP2_MASK (0x7 << FBT_FMT2_OP2_SHIFT) 1857c478bd9Sstevel@tonic-gate #define FBT_FMT2_RD_SHIFT 25 1867c478bd9Sstevel@tonic-gate 1877c478bd9Sstevel@tonic-gate #define FBT_FMT1_OP(val) ((val) & FBT_OP_MASK) 1887c478bd9Sstevel@tonic-gate #define FBT_FMT1_DISP30(val) ((val) & FBT_DISP30_MASK) 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate #define FBT_FMT2_OP2_BPCC (0x01 << FBT_FMT2_OP2_SHIFT) 1917c478bd9Sstevel@tonic-gate #define FBT_FMT2_OP2_BCC (0x02 << FBT_FMT2_OP2_SHIFT) 1927c478bd9Sstevel@tonic-gate #define FBT_FMT2_OP2_BPR (0x03 << FBT_FMT2_OP2_SHIFT) 1937c478bd9Sstevel@tonic-gate #define FBT_FMT2_OP2_SETHI (0x04 << FBT_FMT2_OP2_SHIFT) 1947c478bd9Sstevel@tonic-gate 1957c478bd9Sstevel@tonic-gate #define FBT_FMT2_COND_SHIFT 25 1967c478bd9Sstevel@tonic-gate #define FBT_FMT2_COND_BA (0x8 << FBT_FMT2_COND_SHIFT) 1977c478bd9Sstevel@tonic-gate #define FBT_FMT2_COND_BL (0x3 << FBT_FMT2_COND_SHIFT) 1987c478bd9Sstevel@tonic-gate #define FBT_FMT2_COND_BGE (0xb << FBT_FMT2_COND_SHIFT) 1997c478bd9Sstevel@tonic-gate 2007c478bd9Sstevel@tonic-gate #define FBT_OP_RESTORE (FBT_OP2 | (0x3d << FBT_FMT3_OP3_SHIFT)) 2017c478bd9Sstevel@tonic-gate #define FBT_OP_SAVE (FBT_OP2 | (0x3c << FBT_FMT3_OP3_SHIFT)) 2027c478bd9Sstevel@tonic-gate #define FBT_OP_JMPL (FBT_OP2 | (0x38 << FBT_FMT3_OP3_SHIFT)) 2037c478bd9Sstevel@tonic-gate #define FBT_OP_RETURN (FBT_OP2 | (0x39 << FBT_FMT3_OP3_SHIFT)) 2047c478bd9Sstevel@tonic-gate #define FBT_OP_CALL FBT_OP1 2057c478bd9Sstevel@tonic-gate #define FBT_OP_SETHI (FBT_OP0 | FBT_FMT2_OP2_SETHI) 2067c478bd9Sstevel@tonic-gate #define FBT_OP_ADD (FBT_OP2 | (0x00 << FBT_FMT3_OP3_SHIFT)) 2077c478bd9Sstevel@tonic-gate #define FBT_OP_OR (FBT_OP2 | (0x02 << FBT_FMT3_OP3_SHIFT)) 2087c478bd9Sstevel@tonic-gate #define FBT_OP_SUB (FBT_OP2 | (0x04 << FBT_FMT3_OP3_SHIFT)) 2097c478bd9Sstevel@tonic-gate #define FBT_OP_CC (FBT_OP2 | (0x10 << FBT_FMT3_OP3_SHIFT)) 2107c478bd9Sstevel@tonic-gate #define FBT_OP_BA (FBT_OP0 | FBT_FMT2_OP2_BCC | FBT_FMT2_COND_BA) 2117c478bd9Sstevel@tonic-gate #define FBT_OP_BL (FBT_OP0 | FBT_FMT2_OP2_BCC | FBT_FMT2_COND_BL) 2127c478bd9Sstevel@tonic-gate #define FBT_OP_BGE (FBT_OP0 | FBT_FMT2_OP2_BCC | FBT_FMT2_COND_BGE) 2137c478bd9Sstevel@tonic-gate #define FBT_OP_BAPCC (FBT_OP0 | FBT_FMT2_OP2_BPCC | FBT_FMT2_COND_BA) 2147c478bd9Sstevel@tonic-gate #define FBT_OP_RD (FBT_OP2 | (0x28 << FBT_FMT3_OP3_SHIFT)) 2157c478bd9Sstevel@tonic-gate 2167c478bd9Sstevel@tonic-gate #define FBT_ORLO(rs, val, rd) \ 2177c478bd9Sstevel@tonic-gate (FBT_OP_OR | ((rs) << FBT_FMT3_RS1_SHIFT) | \ 2187c478bd9Sstevel@tonic-gate ((rd) << FBT_FMT3_RD_SHIFT) | FBT_FMT3_IMM | ((val) & FBT_IMM10_MASK)) 2197c478bd9Sstevel@tonic-gate 2207c478bd9Sstevel@tonic-gate #define FBT_ORSIMM13(rs, val, rd) \ 2217c478bd9Sstevel@tonic-gate (FBT_OP_OR | ((rs) << FBT_FMT3_RS1_SHIFT) | \ 2227c478bd9Sstevel@tonic-gate ((rd) << FBT_FMT3_RD_SHIFT) | FBT_FMT3_IMM | ((val) & FBT_SIMM13_MASK)) 2237c478bd9Sstevel@tonic-gate 2247c478bd9Sstevel@tonic-gate #define FBT_ADDSIMM13(rs, val, rd) \ 2257c478bd9Sstevel@tonic-gate (FBT_OP_ADD | ((rs) << FBT_FMT3_RS1_SHIFT) | \ 2267c478bd9Sstevel@tonic-gate ((rd) << FBT_FMT3_RD_SHIFT) | FBT_FMT3_IMM | ((val) & FBT_SIMM13_MASK)) 2277c478bd9Sstevel@tonic-gate 2287c478bd9Sstevel@tonic-gate #define FBT_ADD(rs1, rs2, rd) \ 2297c478bd9Sstevel@tonic-gate (FBT_OP_ADD | ((rs1) << FBT_FMT3_RS1_SHIFT) | \ 2307c478bd9Sstevel@tonic-gate ((rs2) << FBT_FMT3_RS2_SHIFT) | ((rd) << FBT_FMT3_RD_SHIFT)) 2317c478bd9Sstevel@tonic-gate 2327c478bd9Sstevel@tonic-gate #define FBT_CMP(rs1, rs2) \ 2337c478bd9Sstevel@tonic-gate (FBT_OP_SUB | FBT_OP_CC | ((rs1) << FBT_FMT3_RS1_SHIFT) | \ 2347c478bd9Sstevel@tonic-gate ((rs2) << FBT_FMT3_RS2_SHIFT) | (FBT_REG_G0 << FBT_FMT3_RD_SHIFT)) 2357c478bd9Sstevel@tonic-gate 2367c478bd9Sstevel@tonic-gate #define FBT_MOV(rs, rd) \ 2377c478bd9Sstevel@tonic-gate (FBT_OP_OR | (FBT_REG_G0 << FBT_FMT3_RS1_SHIFT) | \ 2387c478bd9Sstevel@tonic-gate ((rs) << FBT_FMT3_RS2_SHIFT) | ((rd) << FBT_FMT3_RD_SHIFT)) 2397c478bd9Sstevel@tonic-gate 2407c478bd9Sstevel@tonic-gate #define FBT_SETHI(val, reg) \ 2417c478bd9Sstevel@tonic-gate (FBT_OP_SETHI | (reg << FBT_FMT2_RD_SHIFT) | \ 2427c478bd9Sstevel@tonic-gate ((val >> FBT_IMM22_SHIFT) & FBT_IMM22_MASK)) 2437c478bd9Sstevel@tonic-gate 2447c478bd9Sstevel@tonic-gate #define FBT_CALL(orig, dest) (FBT_OP_CALL | FBT_DISP30(orig, dest)) 2457c478bd9Sstevel@tonic-gate 2467c478bd9Sstevel@tonic-gate #define FBT_RET \ 2477c478bd9Sstevel@tonic-gate (FBT_OP_JMPL | (FBT_REG_I7 << FBT_FMT3_RS1_SHIFT) | \ 2487c478bd9Sstevel@tonic-gate (FBT_REG_G0 << FBT_FMT3_RD_SHIFT) | FBT_FMT3_IMM | (sizeof (pc_t) << 1)) 2497c478bd9Sstevel@tonic-gate 2507c478bd9Sstevel@tonic-gate #define FBT_SAVEIMM(rd, val, rs1) \ 2517c478bd9Sstevel@tonic-gate (FBT_OP_SAVE | ((rs1) << FBT_FMT3_RS1_SHIFT) | \ 2527c478bd9Sstevel@tonic-gate ((rd) << FBT_FMT3_RD_SHIFT) | FBT_FMT3_IMM | ((val) & FBT_SIMM13_MASK)) 2537c478bd9Sstevel@tonic-gate 2547c478bd9Sstevel@tonic-gate #define FBT_RESTORE(rd, rs1, rs2) \ 2557c478bd9Sstevel@tonic-gate (FBT_OP_RESTORE | ((rs1) << FBT_FMT3_RS1_SHIFT) | \ 2567c478bd9Sstevel@tonic-gate ((rd) << FBT_FMT3_RD_SHIFT) | ((rs2) << FBT_FMT3_RS2_SHIFT)) 2577c478bd9Sstevel@tonic-gate 2587c478bd9Sstevel@tonic-gate #define FBT_RETURN(rs1, val) \ 2597c478bd9Sstevel@tonic-gate (FBT_OP_RETURN | ((rs1) << FBT_FMT3_RS1_SHIFT) | \ 2607c478bd9Sstevel@tonic-gate FBT_FMT3_IMM | ((val) & FBT_SIMM13_MASK)) 2617c478bd9Sstevel@tonic-gate 2627c478bd9Sstevel@tonic-gate #define FBT_BA(orig, dest) (FBT_OP_BA | FBT_DISP22(orig, dest)) 2637c478bd9Sstevel@tonic-gate #define FBT_BAA(orig, dest) (FBT_BA(orig, dest) | FBT_ANNUL) 2647c478bd9Sstevel@tonic-gate #define FBT_BL(orig, dest) (FBT_OP_BL | FBT_DISP22(orig, dest)) 2657c478bd9Sstevel@tonic-gate #define FBT_BGE(orig, dest) (FBT_OP_BGE | FBT_DISP22(orig, dest)) 2667c478bd9Sstevel@tonic-gate #define FBT_BDEST(va, instr) ((uintptr_t)(va) + \ 2677c478bd9Sstevel@tonic-gate (((int32_t)(((instr) & FBT_DISP22_MASK) << 10)) >> 8)) 2687c478bd9Sstevel@tonic-gate #define FBT_BPCCDEST(va, instr) ((uintptr_t)(va) + \ 2697c478bd9Sstevel@tonic-gate (((int32_t)(((instr) & FBT_DISP19_MASK) << 13)) >> 11)) 2707c478bd9Sstevel@tonic-gate #define FBT_BPRDEST(va, instr) ((uintptr_t)(va) + \ 2717c478bd9Sstevel@tonic-gate (((int32_t)((FBT_DISP16(instr)) << 16)) >> 14)) 2727c478bd9Sstevel@tonic-gate 2737c478bd9Sstevel@tonic-gate /* 2747c478bd9Sstevel@tonic-gate * We're only going to treat a save as safe if (a) both rs1 and rd are 2757c478bd9Sstevel@tonic-gate * %sp and (b) if the instruction has a simm, the value isn't 0. 2767c478bd9Sstevel@tonic-gate */ 2777c478bd9Sstevel@tonic-gate #define FBT_IS_SAVE(instr) \ 2787c478bd9Sstevel@tonic-gate (FBT_FMT3_OP(instr) == FBT_OP_SAVE && \ 2797c478bd9Sstevel@tonic-gate FBT_FMT3_RD(instr) == FBT_REG_O6 && \ 2807c478bd9Sstevel@tonic-gate FBT_FMT3_RS1(instr) == FBT_REG_O6 && \ 2817c478bd9Sstevel@tonic-gate !(FBT_FMT3_ISIMM(instr) && FBT_FMT3_SIMM13(instr) == 0)) 2827c478bd9Sstevel@tonic-gate 2837c478bd9Sstevel@tonic-gate #define FBT_IS_BA(instr) (((instr) & ~FBT_DISP22_MASK) == FBT_OP_BA) 2847c478bd9Sstevel@tonic-gate #define FBT_IS_BAPCC(instr) (((instr) & ~FBT_DISP22_MASK) == FBT_OP_BAPCC) 2857c478bd9Sstevel@tonic-gate 2867c478bd9Sstevel@tonic-gate #define FBT_IS_RDPC(instr) ((FBT_FMT3_OP(instr) == FBT_OP_RD) && \ 2877c478bd9Sstevel@tonic-gate (FBT_FMT3_RD(instr) == FBT_REG_PC)) 2887c478bd9Sstevel@tonic-gate 2897c478bd9Sstevel@tonic-gate #define FBT_IS_PCRELATIVE(instr) \ 2907c478bd9Sstevel@tonic-gate ((((instr) & FBT_OP_MASK) == FBT_OP0 && \ 2917c478bd9Sstevel@tonic-gate ((instr) & FBT_FMT2_OP2_MASK) != FBT_FMT2_OP2_SETHI) || \ 2927c478bd9Sstevel@tonic-gate ((instr) & FBT_OP_MASK) == FBT_OP1 || \ 2937c478bd9Sstevel@tonic-gate FBT_IS_RDPC(instr)) 2947c478bd9Sstevel@tonic-gate 2957c478bd9Sstevel@tonic-gate #define FBT_IS_CTI(instr) \ 2967c478bd9Sstevel@tonic-gate ((((instr) & FBT_OP_MASK) == FBT_OP0 && \ 2977c478bd9Sstevel@tonic-gate ((instr) & FBT_FMT2_OP2_MASK) != FBT_FMT2_OP2_SETHI) || \ 2987c478bd9Sstevel@tonic-gate ((instr) & FBT_OP_MASK) == FBT_OP1 || \ 2997c478bd9Sstevel@tonic-gate (FBT_FMT3_OP(instr) == FBT_OP_JMPL) || \ 3007c478bd9Sstevel@tonic-gate (FBT_FMT3_OP(instr) == FBT_OP_RETURN)) 3017c478bd9Sstevel@tonic-gate 3027c478bd9Sstevel@tonic-gate #define FBT_PROBENAME_ENTRY "entry" 3037c478bd9Sstevel@tonic-gate #define FBT_PROBENAME_RETURN "return" 3047c478bd9Sstevel@tonic-gate #define FBT_ESTIMATE_ID (UINT32_MAX) 3057c478bd9Sstevel@tonic-gate #define FBT_COUNTER(id, count) if ((id) != FBT_ESTIMATE_ID) (count)++ 3067c478bd9Sstevel@tonic-gate 3077c478bd9Sstevel@tonic-gate #define FBT_ENTENT_MAXSIZE (16 * sizeof (uint32_t)) 3087c478bd9Sstevel@tonic-gate #define FBT_RETENT_MAXSIZE (11 * sizeof (uint32_t)) 3097c478bd9Sstevel@tonic-gate #define FBT_RETLENT_MAXSIZE (23 * sizeof (uint32_t)) 3107c478bd9Sstevel@tonic-gate #define FBT_ENT_MAXSIZE \ 3117c478bd9Sstevel@tonic-gate MAX(MAX(FBT_ENTENT_MAXSIZE, FBT_RETENT_MAXSIZE), FBT_RETLENT_MAXSIZE) 3127c478bd9Sstevel@tonic-gate 3137c478bd9Sstevel@tonic-gate typedef struct fbt_probe { 3147c478bd9Sstevel@tonic-gate char *fbtp_name; 3157c478bd9Sstevel@tonic-gate dtrace_id_t fbtp_id; 3167c478bd9Sstevel@tonic-gate uintptr_t fbtp_addr; 3177c478bd9Sstevel@tonic-gate struct modctl *fbtp_ctl; 3187c478bd9Sstevel@tonic-gate int fbtp_loadcnt; 3197c478bd9Sstevel@tonic-gate int fbtp_symndx; 3207c478bd9Sstevel@tonic-gate int fbtp_primary; 3217c478bd9Sstevel@tonic-gate int fbtp_return; 3227c478bd9Sstevel@tonic-gate uint32_t *fbtp_patchpoint; 3237c478bd9Sstevel@tonic-gate uint32_t fbtp_patchval; 3247c478bd9Sstevel@tonic-gate uint32_t fbtp_savedval; 3257c478bd9Sstevel@tonic-gate struct fbt_probe *fbtp_next; 3267c478bd9Sstevel@tonic-gate } fbt_probe_t; 3277c478bd9Sstevel@tonic-gate 3287c478bd9Sstevel@tonic-gate typedef struct fbt_trampoline { 3297c478bd9Sstevel@tonic-gate uintptr_t fbtt_va; 3307c478bd9Sstevel@tonic-gate uintptr_t fbtt_limit; 3317c478bd9Sstevel@tonic-gate uintptr_t fbtt_next; 3327c478bd9Sstevel@tonic-gate } fbt_trampoline_t; 3337c478bd9Sstevel@tonic-gate 3347c478bd9Sstevel@tonic-gate static caddr_t 3357c478bd9Sstevel@tonic-gate fbt_trampoline_map(uintptr_t tramp, size_t size) 3367c478bd9Sstevel@tonic-gate { 3377c478bd9Sstevel@tonic-gate uintptr_t offs; 3387c478bd9Sstevel@tonic-gate page_t **ppl; 3397c478bd9Sstevel@tonic-gate 3407c478bd9Sstevel@tonic-gate ASSERT(fbt_trampoline_window == NULL); 3417c478bd9Sstevel@tonic-gate ASSERT(fbt_trampoline_size == 0); 3427c478bd9Sstevel@tonic-gate ASSERT(fbt_trampoline == NULL); 3437c478bd9Sstevel@tonic-gate 3447c478bd9Sstevel@tonic-gate size += tramp & PAGEOFFSET; 3457c478bd9Sstevel@tonic-gate fbt_trampoline = tramp & PAGEMASK; 3467c478bd9Sstevel@tonic-gate fbt_trampoline_size = (size + PAGESIZE - 1) & PAGEMASK; 3477c478bd9Sstevel@tonic-gate fbt_trampoline_window = 3487c478bd9Sstevel@tonic-gate vmem_alloc(heap_arena, fbt_trampoline_size, VM_SLEEP); 3497c478bd9Sstevel@tonic-gate 3507c478bd9Sstevel@tonic-gate (void) as_pagelock(&kas, &ppl, (caddr_t)fbt_trampoline, 3517c478bd9Sstevel@tonic-gate fbt_trampoline_size, S_WRITE); 3527c478bd9Sstevel@tonic-gate 3537c478bd9Sstevel@tonic-gate for (offs = 0; offs < fbt_trampoline_size; offs += PAGESIZE) { 3547c478bd9Sstevel@tonic-gate hat_devload(kas.a_hat, fbt_trampoline_window + offs, PAGESIZE, 3557c478bd9Sstevel@tonic-gate hat_getpfnum(kas.a_hat, (caddr_t)fbt_trampoline + offs), 3567c478bd9Sstevel@tonic-gate PROT_READ | PROT_WRITE, 3577c478bd9Sstevel@tonic-gate HAT_LOAD_LOCK | HAT_LOAD_NOCONSIST); 3587c478bd9Sstevel@tonic-gate } 3597c478bd9Sstevel@tonic-gate 3607c478bd9Sstevel@tonic-gate as_pageunlock(&kas, ppl, (caddr_t)fbt_trampoline, fbt_trampoline_size, 3617c478bd9Sstevel@tonic-gate S_WRITE); 3627c478bd9Sstevel@tonic-gate 3637c478bd9Sstevel@tonic-gate return (fbt_trampoline_window + (tramp & PAGEOFFSET)); 3647c478bd9Sstevel@tonic-gate } 3657c478bd9Sstevel@tonic-gate 3667c478bd9Sstevel@tonic-gate static void 3677c478bd9Sstevel@tonic-gate fbt_trampoline_unmap() 3687c478bd9Sstevel@tonic-gate { 3697c478bd9Sstevel@tonic-gate ASSERT(fbt_trampoline_window != NULL); 3707c478bd9Sstevel@tonic-gate ASSERT(fbt_trampoline_size != 0); 3717c478bd9Sstevel@tonic-gate ASSERT(fbt_trampoline != NULL); 3727c478bd9Sstevel@tonic-gate 3737c478bd9Sstevel@tonic-gate membar_enter(); 3747c478bd9Sstevel@tonic-gate sync_icache((caddr_t)fbt_trampoline, fbt_trampoline_size); 3757c478bd9Sstevel@tonic-gate sync_icache(fbt_trampoline_window, fbt_trampoline_size); 3767c478bd9Sstevel@tonic-gate 3777c478bd9Sstevel@tonic-gate hat_unload(kas.a_hat, fbt_trampoline_window, fbt_trampoline_size, 3787c478bd9Sstevel@tonic-gate HAT_UNLOAD_UNLOCK); 3797c478bd9Sstevel@tonic-gate 3807c478bd9Sstevel@tonic-gate vmem_free(heap_arena, fbt_trampoline_window, fbt_trampoline_size); 3817c478bd9Sstevel@tonic-gate 3827c478bd9Sstevel@tonic-gate fbt_trampoline_window = NULL; 3837c478bd9Sstevel@tonic-gate fbt_trampoline = NULL; 3847c478bd9Sstevel@tonic-gate fbt_trampoline_size = 0; 3857c478bd9Sstevel@tonic-gate } 3867c478bd9Sstevel@tonic-gate 3877c478bd9Sstevel@tonic-gate static uintptr_t 3887c478bd9Sstevel@tonic-gate fbt_patch_entry(uint32_t *instr, uint32_t id, fbt_trampoline_t *tramp, 3897c478bd9Sstevel@tonic-gate int nargs) 3907c478bd9Sstevel@tonic-gate { 3917c478bd9Sstevel@tonic-gate uint32_t *tinstr = (uint32_t *)tramp->fbtt_next; 3927c478bd9Sstevel@tonic-gate uint32_t first = *instr; 3937c478bd9Sstevel@tonic-gate uintptr_t va = tramp->fbtt_va; 3947c478bd9Sstevel@tonic-gate uintptr_t base = tramp->fbtt_next; 3957c478bd9Sstevel@tonic-gate 3967c478bd9Sstevel@tonic-gate if (tramp->fbtt_next + FBT_ENTENT_MAXSIZE > tramp->fbtt_limit) { 3977c478bd9Sstevel@tonic-gate /* 3987c478bd9Sstevel@tonic-gate * There isn't sufficient room for this entry; return failure. 3997c478bd9Sstevel@tonic-gate */ 4007c478bd9Sstevel@tonic-gate return (0); 4017c478bd9Sstevel@tonic-gate } 4027c478bd9Sstevel@tonic-gate 4037c478bd9Sstevel@tonic-gate FBT_COUNTER(id, fbt_entry); 4047c478bd9Sstevel@tonic-gate 4057c478bd9Sstevel@tonic-gate if (FBT_IS_SAVE(first)) { 4067c478bd9Sstevel@tonic-gate *tinstr++ = first; 4077c478bd9Sstevel@tonic-gate } else { 4087c478bd9Sstevel@tonic-gate *tinstr++ = FBT_SAVEIMM(FBT_REG_O6, -SA(MINFRAME), FBT_REG_O6); 4097c478bd9Sstevel@tonic-gate } 4107c478bd9Sstevel@tonic-gate 4117c478bd9Sstevel@tonic-gate if (id > (uint32_t)FBT_SIMM13_MAX) { 4127c478bd9Sstevel@tonic-gate *tinstr++ = FBT_SETHI(id, FBT_REG_O0); 4137c478bd9Sstevel@tonic-gate *tinstr++ = FBT_ORLO(FBT_REG_O0, id, FBT_REG_O0); 4147c478bd9Sstevel@tonic-gate } else { 4157c478bd9Sstevel@tonic-gate *tinstr++ = FBT_ORSIMM13(FBT_REG_G0, id, FBT_REG_O0); 4167c478bd9Sstevel@tonic-gate } 4177c478bd9Sstevel@tonic-gate 4187c478bd9Sstevel@tonic-gate if (nargs >= 1) 4197c478bd9Sstevel@tonic-gate *tinstr++ = FBT_MOV(FBT_REG_I0, FBT_REG_O1); 4207c478bd9Sstevel@tonic-gate 4217c478bd9Sstevel@tonic-gate if (nargs >= 2) 4227c478bd9Sstevel@tonic-gate *tinstr++ = FBT_MOV(FBT_REG_I1, FBT_REG_O2); 4237c478bd9Sstevel@tonic-gate 4247c478bd9Sstevel@tonic-gate if (nargs >= 3) 4257c478bd9Sstevel@tonic-gate *tinstr++ = FBT_MOV(FBT_REG_I2, FBT_REG_O3); 4267c478bd9Sstevel@tonic-gate 4277c478bd9Sstevel@tonic-gate if (nargs >= 4) 4287c478bd9Sstevel@tonic-gate *tinstr++ = FBT_MOV(FBT_REG_I3, FBT_REG_O4); 4297c478bd9Sstevel@tonic-gate 4307c478bd9Sstevel@tonic-gate if (nargs >= 5) 4317c478bd9Sstevel@tonic-gate *tinstr++ = FBT_MOV(FBT_REG_I4, FBT_REG_O5); 4327c478bd9Sstevel@tonic-gate 4337c478bd9Sstevel@tonic-gate if (FBT_IS_SAVE(first)) { 4347c478bd9Sstevel@tonic-gate uintptr_t ret = (uintptr_t)instr - sizeof (uint32_t); 4357c478bd9Sstevel@tonic-gate 4367c478bd9Sstevel@tonic-gate *tinstr++ = FBT_SETHI(ret, FBT_REG_G1); 4377c478bd9Sstevel@tonic-gate *tinstr = FBT_CALL((uintptr_t)tinstr - base + va, dtrace_probe); 4387c478bd9Sstevel@tonic-gate tinstr++; 4397c478bd9Sstevel@tonic-gate *tinstr++ = FBT_ORLO(FBT_REG_G1, ret, FBT_REG_O7); 4407c478bd9Sstevel@tonic-gate } else { 4417c478bd9Sstevel@tonic-gate uintptr_t slot = *--tinstr; 4427c478bd9Sstevel@tonic-gate uintptr_t ret = (uintptr_t)instr + sizeof (uint32_t); 4437c478bd9Sstevel@tonic-gate uint32_t delay = first; 4447c478bd9Sstevel@tonic-gate 4457c478bd9Sstevel@tonic-gate *tinstr = FBT_CALL((uintptr_t)tinstr - base + va, dtrace_probe); 4467c478bd9Sstevel@tonic-gate tinstr++; 4477c478bd9Sstevel@tonic-gate *tinstr++ = slot; 4487c478bd9Sstevel@tonic-gate *tinstr++ = FBT_RESTORE(FBT_REG_G0, FBT_REG_G0, FBT_REG_G0); 4497c478bd9Sstevel@tonic-gate 4507c478bd9Sstevel@tonic-gate if (FBT_IS_BA(first) || FBT_IS_BAPCC(first)) { 4517c478bd9Sstevel@tonic-gate /* 4527c478bd9Sstevel@tonic-gate * This is a special case: we are instrumenting a 4537c478bd9Sstevel@tonic-gate * a non-annulled branch-always (or variant). We'll 4547c478bd9Sstevel@tonic-gate * return directly to the destination of the branch, 4557c478bd9Sstevel@tonic-gate * copying the instruction in the delay slot here, 4567c478bd9Sstevel@tonic-gate * and then executing it in the slot of a ba. 4577c478bd9Sstevel@tonic-gate */ 4587c478bd9Sstevel@tonic-gate if (FBT_IS_BA(first)) { 4597c478bd9Sstevel@tonic-gate ret = FBT_BDEST(instr, *instr); 4607c478bd9Sstevel@tonic-gate } else { 4617c478bd9Sstevel@tonic-gate ret = FBT_BPCCDEST(instr, *instr); 4627c478bd9Sstevel@tonic-gate } 4637c478bd9Sstevel@tonic-gate 4647c478bd9Sstevel@tonic-gate delay = *(instr + 1); 4657c478bd9Sstevel@tonic-gate } 4667c478bd9Sstevel@tonic-gate 4677c478bd9Sstevel@tonic-gate if ((first & FBT_OP_MASK) != FBT_OP0 || 4687c478bd9Sstevel@tonic-gate (first & FBT_FMT2_OP2_MASK) != FBT_FMT2_OP2_BPR) { 4697c478bd9Sstevel@tonic-gate *tinstr = FBT_BA((uintptr_t)tinstr - base + va, ret); 4707c478bd9Sstevel@tonic-gate tinstr++; 4717c478bd9Sstevel@tonic-gate *tinstr++ = delay; 4727c478bd9Sstevel@tonic-gate } else { 4737c478bd9Sstevel@tonic-gate /* 4747c478bd9Sstevel@tonic-gate * If this is a branch-on-register, we have a little 4757c478bd9Sstevel@tonic-gate * more work to do: because the displacement is only 4767c478bd9Sstevel@tonic-gate * sixteen bits, we're going to thunk the branch into 4777c478bd9Sstevel@tonic-gate * the trampoline, and then ba,a to the appropriate 4787c478bd9Sstevel@tonic-gate * destination in the branch targets. That is, we're 4797c478bd9Sstevel@tonic-gate * constructing this sequence in the trampoline: 4807c478bd9Sstevel@tonic-gate * 4817c478bd9Sstevel@tonic-gate * br[cc] %[rs], 1f 4827c478bd9Sstevel@tonic-gate * <delay-instruction> 4837c478bd9Sstevel@tonic-gate * ba,a <not-taken-destination> 4847c478bd9Sstevel@tonic-gate * 1: ba,a <taken-destination> 4857c478bd9Sstevel@tonic-gate * 4867c478bd9Sstevel@tonic-gate */ 4877c478bd9Sstevel@tonic-gate uintptr_t targ = FBT_BPRDEST(instr, first); 4887c478bd9Sstevel@tonic-gate 4897c478bd9Sstevel@tonic-gate *tinstr = first & ~(FBT_DISP16_MASK); 4907c478bd9Sstevel@tonic-gate *tinstr |= FBT_DISP14(tinstr, &tinstr[3]); 4917c478bd9Sstevel@tonic-gate tinstr++; 4927c478bd9Sstevel@tonic-gate *tinstr++ = *(instr + 1); 4937c478bd9Sstevel@tonic-gate *tinstr = FBT_BAA((uintptr_t)tinstr - base + va, 4947c478bd9Sstevel@tonic-gate ret + sizeof (uint32_t)); 4957c478bd9Sstevel@tonic-gate tinstr++; 4967c478bd9Sstevel@tonic-gate *tinstr = FBT_BAA((uintptr_t)tinstr - base + va, targ); 4977c478bd9Sstevel@tonic-gate tinstr++; 4987c478bd9Sstevel@tonic-gate } 4997c478bd9Sstevel@tonic-gate } 5007c478bd9Sstevel@tonic-gate 5017c478bd9Sstevel@tonic-gate tramp->fbtt_va += (uintptr_t)tinstr - tramp->fbtt_next; 5027c478bd9Sstevel@tonic-gate tramp->fbtt_next = (uintptr_t)tinstr; 5037c478bd9Sstevel@tonic-gate 5047c478bd9Sstevel@tonic-gate return (1); 5057c478bd9Sstevel@tonic-gate } 5067c478bd9Sstevel@tonic-gate 5077c478bd9Sstevel@tonic-gate /* 5087c478bd9Sstevel@tonic-gate * We are patching control-transfer/restore couplets. There are three 5097c478bd9Sstevel@tonic-gate * variants of couplet: 5107c478bd9Sstevel@tonic-gate * 5117c478bd9Sstevel@tonic-gate * (a) return rs1 + imm 5127c478bd9Sstevel@tonic-gate * delay 5137c478bd9Sstevel@tonic-gate * 5147c478bd9Sstevel@tonic-gate * (b) jmpl rs1 + (rs2 | offset), rd 5157c478bd9Sstevel@tonic-gate * restore rs1, rs2 | imm, rd 5167c478bd9Sstevel@tonic-gate * 5177c478bd9Sstevel@tonic-gate * (c) call displacement 5187c478bd9Sstevel@tonic-gate * restore rs1, rs2 | imm, rd 5197c478bd9Sstevel@tonic-gate * 5207c478bd9Sstevel@tonic-gate * If rs1 in (a) is anything other than %i7, or imm is anything other than 8, 5217c478bd9Sstevel@tonic-gate * or delay is a DCTI, we fail. If rd from the jmpl in (b) is something other 5227c478bd9Sstevel@tonic-gate * than %g0 (a ret or a tail-call through a function pointer) or %o7 (a call 5237c478bd9Sstevel@tonic-gate * through a register), we fail. 5247c478bd9Sstevel@tonic-gate * 5257c478bd9Sstevel@tonic-gate * Note that rs1 and rs2 in the restore instructions in (b) and (c) are 5267c478bd9Sstevel@tonic-gate * potentially outputs and/or globals. Because these registers cannot be 5277c478bd9Sstevel@tonic-gate * relied upon across the call to dtrace_probe(), we move rs1 into an unused 5287c478bd9Sstevel@tonic-gate * local, ls0, and rs2 into an unused local, ls1, and restructure the restore 5297c478bd9Sstevel@tonic-gate * to be: 5307c478bd9Sstevel@tonic-gate * 5317c478bd9Sstevel@tonic-gate * restore ls0, ls1, rd 5327c478bd9Sstevel@tonic-gate * 5337c478bd9Sstevel@tonic-gate * Likewise, rs1 and rs2 in the jmpl of case (b) may be outputs and/or globals. 5347c478bd9Sstevel@tonic-gate * If the jmpl uses outputs or globals, we restructure it to be: 5357c478bd9Sstevel@tonic-gate * 5367c478bd9Sstevel@tonic-gate * jmpl ls2 + (ls3 | offset), (%g0 | %o7) 5377c478bd9Sstevel@tonic-gate * 5387c478bd9Sstevel@tonic-gate */ 5397c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 5407c478bd9Sstevel@tonic-gate static int 5417c478bd9Sstevel@tonic-gate fbt_canpatch_return(uint32_t *instr, int offset, const char *name) 5427c478bd9Sstevel@tonic-gate { 5437c478bd9Sstevel@tonic-gate int rd; 5447c478bd9Sstevel@tonic-gate 5457c478bd9Sstevel@tonic-gate if (FBT_FMT3_OP(*instr) == FBT_OP_RETURN) { 5467c478bd9Sstevel@tonic-gate uint32_t delay = *(instr + 1); 5477c478bd9Sstevel@tonic-gate 5487c478bd9Sstevel@tonic-gate if (*instr != FBT_RETURN(FBT_REG_I7, 8)) { 5497c478bd9Sstevel@tonic-gate /* 5507c478bd9Sstevel@tonic-gate * It's unclear if we should warn about this or not. 5517c478bd9Sstevel@tonic-gate * We really wouldn't expect the compiler to generate 5527c478bd9Sstevel@tonic-gate * return instructions with something other than %i7 5537c478bd9Sstevel@tonic-gate * as rs1 and 8 as the simm13 -- it would just be 5547c478bd9Sstevel@tonic-gate * mean-spirited. That said, such a construct isn't 5557c478bd9Sstevel@tonic-gate * necessarily incorrect. Sill, we err on the side of 5567c478bd9Sstevel@tonic-gate * caution and warn about it... 5577c478bd9Sstevel@tonic-gate */ 5587c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "cannot instrument return of %s at " 5597c478bd9Sstevel@tonic-gate "%p: non-canonical return instruction", name, 5607c478bd9Sstevel@tonic-gate (void *)instr); 5617c478bd9Sstevel@tonic-gate return (0); 5627c478bd9Sstevel@tonic-gate } 5637c478bd9Sstevel@tonic-gate 5647c478bd9Sstevel@tonic-gate if (FBT_IS_CTI(delay)) { 5657c478bd9Sstevel@tonic-gate /* 5667c478bd9Sstevel@tonic-gate * This is even weirder -- a DCTI coupled with a 5677c478bd9Sstevel@tonic-gate * return instruction. Similar constructs are used to 5687c478bd9Sstevel@tonic-gate * return from utraps, but these typically have the 5697c478bd9Sstevel@tonic-gate * return in the slot -- and we wouldn't expect to see 5707c478bd9Sstevel@tonic-gate * it in the kernel regardless. At any rate, we don't 5717c478bd9Sstevel@tonic-gate * want to try to instrument this construct, whatever 5727c478bd9Sstevel@tonic-gate * it may be. 5737c478bd9Sstevel@tonic-gate */ 5747c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "cannot instrument return of %s at " 5757c478bd9Sstevel@tonic-gate "%p: CTI in delay slot of return instruction", 5767c478bd9Sstevel@tonic-gate name, (void *)instr); 5777c478bd9Sstevel@tonic-gate return (0); 5787c478bd9Sstevel@tonic-gate } 5797c478bd9Sstevel@tonic-gate 5807c478bd9Sstevel@tonic-gate if (FBT_IS_PCRELATIVE(delay)) { 5817c478bd9Sstevel@tonic-gate /* 5827c478bd9Sstevel@tonic-gate * This is also very weird, but might be correct code 5837c478bd9Sstevel@tonic-gate * if the function is (for example) returning the 5847c478bd9Sstevel@tonic-gate * address of the delay instruction of the return as 5857c478bd9Sstevel@tonic-gate * its return value (e.g. "rd %pc, %o0" in the slot). 5867c478bd9Sstevel@tonic-gate * Perhaps correct, but still too weird to not warn 5877c478bd9Sstevel@tonic-gate * about it... 5887c478bd9Sstevel@tonic-gate */ 5897c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "cannot instrument return of %s at " 5907c478bd9Sstevel@tonic-gate "%p: PC-relative instruction in delay slot of " 5917c478bd9Sstevel@tonic-gate "return instruction", name, (void *)instr); 5927c478bd9Sstevel@tonic-gate return (0); 5937c478bd9Sstevel@tonic-gate } 5947c478bd9Sstevel@tonic-gate 5957c478bd9Sstevel@tonic-gate return (1); 5967c478bd9Sstevel@tonic-gate } 5977c478bd9Sstevel@tonic-gate 5987c478bd9Sstevel@tonic-gate if (FBT_FMT3_OP(*(instr + 1)) != FBT_OP_RESTORE) 5997c478bd9Sstevel@tonic-gate return (0); 6007c478bd9Sstevel@tonic-gate 6017c478bd9Sstevel@tonic-gate if (FBT_FMT1_OP(*instr) == FBT_OP_CALL) 6027c478bd9Sstevel@tonic-gate return (1); 6037c478bd9Sstevel@tonic-gate 6047c478bd9Sstevel@tonic-gate if (FBT_FMT3_OP(*instr) != FBT_OP_JMPL) 6057c478bd9Sstevel@tonic-gate return (0); 6067c478bd9Sstevel@tonic-gate 6077c478bd9Sstevel@tonic-gate rd = FBT_FMT3_RD(*instr); 6087c478bd9Sstevel@tonic-gate 6097c478bd9Sstevel@tonic-gate if (rd == FBT_REG_I7 || rd == FBT_REG_O7 || rd == FBT_REG_G0) 6107c478bd9Sstevel@tonic-gate return (1); 6117c478bd9Sstevel@tonic-gate 6127c478bd9Sstevel@tonic-gate /* 6137c478bd9Sstevel@tonic-gate * We have encountered a jmpl that is storing the calling %pc in 6147c478bd9Sstevel@tonic-gate * some register besides %i7, %o7 or %g0. This is strange; emit 6157c478bd9Sstevel@tonic-gate * a warning and fail. 6167c478bd9Sstevel@tonic-gate */ 6177c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "cannot instrument return of %s at %p: unexpected " 6187c478bd9Sstevel@tonic-gate "jmpl destination register", name, (void *)instr); 6197c478bd9Sstevel@tonic-gate return (0); 6207c478bd9Sstevel@tonic-gate } 6217c478bd9Sstevel@tonic-gate 6227c478bd9Sstevel@tonic-gate static int 6237c478bd9Sstevel@tonic-gate fbt_canpatch_retl(uint32_t *instr, int offset, const char *name) 6247c478bd9Sstevel@tonic-gate { 6257c478bd9Sstevel@tonic-gate if (FBT_FMT1_OP(*instr) == FBT_OP_CALL || 6267c478bd9Sstevel@tonic-gate (FBT_FMT3_OP(*instr) == FBT_OP_JMPL && 6277c478bd9Sstevel@tonic-gate FBT_FMT3_RD(*instr) == FBT_REG_O7)) { 6287c478bd9Sstevel@tonic-gate /* 6297c478bd9Sstevel@tonic-gate * If this is a call (or a jmpl that links into %o7), we can 6307c478bd9Sstevel@tonic-gate * patch it iff the next instruction uses %o7 as a destination 6317c478bd9Sstevel@tonic-gate * register. Because there is an ABI responsibility to 6327c478bd9Sstevel@tonic-gate * restore %o7 to the value before the call/jmpl, we don't 6337c478bd9Sstevel@tonic-gate * particularly care how this routine is managing to restore 6347c478bd9Sstevel@tonic-gate * it (mov, add, ld or divx for all we care). If it doesn't 6357c478bd9Sstevel@tonic-gate * seem to be restoring it at all, however, we'll refuse 6367c478bd9Sstevel@tonic-gate * to patch it. 6377c478bd9Sstevel@tonic-gate */ 6387c478bd9Sstevel@tonic-gate uint32_t delay = *(instr + 1); 6397c478bd9Sstevel@tonic-gate uint32_t op, rd; 6407c478bd9Sstevel@tonic-gate 6417c478bd9Sstevel@tonic-gate op = FBT_FMT1_OP(delay); 6427c478bd9Sstevel@tonic-gate rd = FBT_FMT3_RD(delay); 6437c478bd9Sstevel@tonic-gate 6447c478bd9Sstevel@tonic-gate if (op != FBT_OP2 || rd != FBT_REG_O7) { 6457c478bd9Sstevel@tonic-gate /* 6467c478bd9Sstevel@tonic-gate * This is odd. Before we assume that we're looking 6477c478bd9Sstevel@tonic-gate * at something bizarre (and warn accordingly), we'll 6487c478bd9Sstevel@tonic-gate * check to see if it's obviously a jump table entry. 6497c478bd9Sstevel@tonic-gate */ 6507c478bd9Sstevel@tonic-gate if (*instr < (uintptr_t)instr && 6517c478bd9Sstevel@tonic-gate *instr >= (uintptr_t)instr - offset) 6527c478bd9Sstevel@tonic-gate return (0); 6537c478bd9Sstevel@tonic-gate 6547c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "cannot instrument return of %s at " 6557c478bd9Sstevel@tonic-gate "%p: leaf jmpl/call delay isn't restoring %%o7", 6567c478bd9Sstevel@tonic-gate name, (void *)instr); 6577c478bd9Sstevel@tonic-gate return (0); 6587c478bd9Sstevel@tonic-gate } 6597c478bd9Sstevel@tonic-gate 6607c478bd9Sstevel@tonic-gate return (1); 6617c478bd9Sstevel@tonic-gate } 6627c478bd9Sstevel@tonic-gate 6637c478bd9Sstevel@tonic-gate if (offset == sizeof (uint32_t)) { 6647c478bd9Sstevel@tonic-gate /* 6657c478bd9Sstevel@tonic-gate * If this is the second instruction in the function, we're 6667c478bd9Sstevel@tonic-gate * going to allow it to be patched if the first instruction 6677c478bd9Sstevel@tonic-gate * is a patchable return-from-leaf instruction. 6687c478bd9Sstevel@tonic-gate */ 6697c478bd9Sstevel@tonic-gate if (fbt_canpatch_retl(instr - 1, 0, name)) 6707c478bd9Sstevel@tonic-gate return (1); 6717c478bd9Sstevel@tonic-gate } 6727c478bd9Sstevel@tonic-gate 6737c478bd9Sstevel@tonic-gate if (FBT_FMT3_OP(*instr) != FBT_OP_JMPL) 6747c478bd9Sstevel@tonic-gate return (0); 6757c478bd9Sstevel@tonic-gate 6767c478bd9Sstevel@tonic-gate if (FBT_FMT3_RD(*instr) != FBT_REG_G0) 6777c478bd9Sstevel@tonic-gate return (0); 6787c478bd9Sstevel@tonic-gate 6797c478bd9Sstevel@tonic-gate return (1); 6807c478bd9Sstevel@tonic-gate } 6817c478bd9Sstevel@tonic-gate 6827c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 6837c478bd9Sstevel@tonic-gate static uint32_t 6847c478bd9Sstevel@tonic-gate fbt_patch_return(uint32_t *instr, uint32_t *funcbase, uint32_t *funclim, 6857c478bd9Sstevel@tonic-gate int offset, uint32_t id, fbt_trampoline_t *tramp, const char *name) 6867c478bd9Sstevel@tonic-gate { 6877c478bd9Sstevel@tonic-gate uint32_t *tinstr = (uint32_t *)tramp->fbtt_next; 6887c478bd9Sstevel@tonic-gate uint32_t cti = *instr, restore = *(instr + 1), rs1, dest; 6897c478bd9Sstevel@tonic-gate uintptr_t va = tramp->fbtt_va; 6907c478bd9Sstevel@tonic-gate uintptr_t base = tramp->fbtt_next; 6917c478bd9Sstevel@tonic-gate uint32_t locals[FBT_REG_NLOCALS], local; 6927c478bd9Sstevel@tonic-gate 6937c478bd9Sstevel@tonic-gate if (tramp->fbtt_next + FBT_RETENT_MAXSIZE > tramp->fbtt_limit) { 6947c478bd9Sstevel@tonic-gate /* 6957c478bd9Sstevel@tonic-gate * There isn't sufficient room for this entry; return failure. 6967c478bd9Sstevel@tonic-gate */ 6977c478bd9Sstevel@tonic-gate return (FBT_ILLTRAP); 6987c478bd9Sstevel@tonic-gate } 6997c478bd9Sstevel@tonic-gate 7007c478bd9Sstevel@tonic-gate FBT_COUNTER(id, fbt_ret); 7017c478bd9Sstevel@tonic-gate 7027c478bd9Sstevel@tonic-gate if (FBT_FMT3_OP(*instr) == FBT_OP_RETURN) { 7037c478bd9Sstevel@tonic-gate /* 7047c478bd9Sstevel@tonic-gate * To handle the case of the return instruction, we'll emit a 7057c478bd9Sstevel@tonic-gate * restore, followed by the instruction in the slot (which 7067c478bd9Sstevel@tonic-gate * we'll transplant here), and then another save. While it 7077c478bd9Sstevel@tonic-gate * may seem intellectually unsatisfying to emit the additional 7087c478bd9Sstevel@tonic-gate * restore/save couplet, one can take solace in the fact that 7097c478bd9Sstevel@tonic-gate * we don't do this if the instruction in the return delay 7107c478bd9Sstevel@tonic-gate * slot is a nop -- which it is nearly 90% of the time with 7117c478bd9Sstevel@tonic-gate * gcc. (And besides, this couplet can't induce unnecessary 7127c478bd9Sstevel@tonic-gate * spill/fill traps; rewriting the delay instruction to be 7137c478bd9Sstevel@tonic-gate * in terms of the current window hardly seems worth the 7147c478bd9Sstevel@tonic-gate * trouble -- let alone the risk.) 7157c478bd9Sstevel@tonic-gate */ 7167c478bd9Sstevel@tonic-gate uint32_t delay = *(instr + 1); 7177c478bd9Sstevel@tonic-gate ASSERT(*instr == FBT_RETURN(FBT_REG_I7, 8)); 7187c478bd9Sstevel@tonic-gate 7197c478bd9Sstevel@tonic-gate cti = FBT_RET; 7207c478bd9Sstevel@tonic-gate restore = FBT_RESTORE(FBT_REG_G0, FBT_REG_G0, FBT_REG_G0); 7217c478bd9Sstevel@tonic-gate 7227c478bd9Sstevel@tonic-gate if (delay != FBT_SETHI(0, FBT_REG_G0)) { 7237c478bd9Sstevel@tonic-gate *tinstr++ = restore; 7247c478bd9Sstevel@tonic-gate *tinstr++ = delay; 7257c478bd9Sstevel@tonic-gate *tinstr++ = FBT_SAVEIMM(FBT_REG_O6, 7267c478bd9Sstevel@tonic-gate -SA(MINFRAME), FBT_REG_O6); 7277c478bd9Sstevel@tonic-gate } 7287c478bd9Sstevel@tonic-gate } 7297c478bd9Sstevel@tonic-gate 7307c478bd9Sstevel@tonic-gate FBT_REG_INITLOCALS(local, locals); 7317c478bd9Sstevel@tonic-gate 7327c478bd9Sstevel@tonic-gate /* 7337c478bd9Sstevel@tonic-gate * Mark the locals used in the jmpl. 7347c478bd9Sstevel@tonic-gate */ 7357c478bd9Sstevel@tonic-gate if (FBT_FMT3_OP(cti) == FBT_OP_JMPL) { 7367c478bd9Sstevel@tonic-gate uint32_t rs1 = FBT_FMT3_RS1(cti); 7377c478bd9Sstevel@tonic-gate FBT_REG_MARKLOCAL(locals, rs1); 7387c478bd9Sstevel@tonic-gate 7397c478bd9Sstevel@tonic-gate if (!FBT_FMT3_ISIMM(cti)) { 7407c478bd9Sstevel@tonic-gate uint32_t rs2 = FBT_FMT3_RS2(cti); 7417c478bd9Sstevel@tonic-gate FBT_REG_MARKLOCAL(locals, rs2); 7427c478bd9Sstevel@tonic-gate } 7437c478bd9Sstevel@tonic-gate } 7447c478bd9Sstevel@tonic-gate 7457c478bd9Sstevel@tonic-gate /* 7467c478bd9Sstevel@tonic-gate * And mark the locals used in the restore. 7477c478bd9Sstevel@tonic-gate */ 7487c478bd9Sstevel@tonic-gate rs1 = FBT_FMT3_RS1(restore); 7497c478bd9Sstevel@tonic-gate FBT_REG_MARKLOCAL(locals, rs1); 7507c478bd9Sstevel@tonic-gate 7517c478bd9Sstevel@tonic-gate if (!FBT_FMT3_ISIMM(restore)) { 7527c478bd9Sstevel@tonic-gate uint32_t rs2 = FBT_FMT3_RS2(restore); 7537c478bd9Sstevel@tonic-gate FBT_REG_MARKLOCAL(locals, rs2); 7547c478bd9Sstevel@tonic-gate } 7557c478bd9Sstevel@tonic-gate 7567c478bd9Sstevel@tonic-gate if (FBT_FMT3_OP(cti) == FBT_OP_JMPL) { 7577c478bd9Sstevel@tonic-gate uint32_t rs1 = FBT_FMT3_RS1(cti); 7587c478bd9Sstevel@tonic-gate 7597c478bd9Sstevel@tonic-gate if (FBT_REG_ISVOLATILE(rs1)) { 7607c478bd9Sstevel@tonic-gate FBT_REG_ALLOCLOCAL(local, locals); 7617c478bd9Sstevel@tonic-gate FBT_FMT3_RS1_SET(cti, local); 7627c478bd9Sstevel@tonic-gate *tinstr++ = FBT_MOV(rs1, local); 7637c478bd9Sstevel@tonic-gate } 7647c478bd9Sstevel@tonic-gate 7657c478bd9Sstevel@tonic-gate if (!FBT_FMT3_ISIMM(cti)) { 7667c478bd9Sstevel@tonic-gate uint32_t rs2 = FBT_FMT3_RS2(cti); 7677c478bd9Sstevel@tonic-gate 7687c478bd9Sstevel@tonic-gate if (FBT_REG_ISVOLATILE(rs2)) { 7697c478bd9Sstevel@tonic-gate FBT_REG_ALLOCLOCAL(local, locals); 7707c478bd9Sstevel@tonic-gate FBT_FMT3_RS2_SET(cti, local); 7717c478bd9Sstevel@tonic-gate *tinstr++ = FBT_MOV(rs2, local); 7727c478bd9Sstevel@tonic-gate } 7737c478bd9Sstevel@tonic-gate } 7747c478bd9Sstevel@tonic-gate } 7757c478bd9Sstevel@tonic-gate 7767c478bd9Sstevel@tonic-gate rs1 = FBT_FMT3_RS1(restore); 7777c478bd9Sstevel@tonic-gate 7787c478bd9Sstevel@tonic-gate if (FBT_REG_ISVOLATILE(rs1)) { 7797c478bd9Sstevel@tonic-gate FBT_REG_ALLOCLOCAL(local, locals); 7807c478bd9Sstevel@tonic-gate FBT_FMT3_RS1_SET(restore, local); 7817c478bd9Sstevel@tonic-gate *tinstr++ = FBT_MOV(rs1, local); 7827c478bd9Sstevel@tonic-gate } 7837c478bd9Sstevel@tonic-gate 7847c478bd9Sstevel@tonic-gate if (!FBT_FMT3_ISIMM(restore)) { 7857c478bd9Sstevel@tonic-gate uint32_t rs2 = FBT_FMT3_RS2(restore); 7867c478bd9Sstevel@tonic-gate 7877c478bd9Sstevel@tonic-gate if (FBT_REG_ISVOLATILE(rs2)) { 7887c478bd9Sstevel@tonic-gate FBT_REG_ALLOCLOCAL(local, locals); 7897c478bd9Sstevel@tonic-gate FBT_FMT3_RS2_SET(restore, local); 7907c478bd9Sstevel@tonic-gate *tinstr++ = FBT_MOV(rs2, local); 7917c478bd9Sstevel@tonic-gate } 7927c478bd9Sstevel@tonic-gate } 7937c478bd9Sstevel@tonic-gate 7947c478bd9Sstevel@tonic-gate if (id > (uint32_t)FBT_SIMM13_MAX) { 7957c478bd9Sstevel@tonic-gate *tinstr++ = FBT_SETHI(id, FBT_REG_O0); 7967c478bd9Sstevel@tonic-gate *tinstr++ = FBT_ORLO(FBT_REG_O0, id, FBT_REG_O0); 7977c478bd9Sstevel@tonic-gate } else { 7987c478bd9Sstevel@tonic-gate *tinstr++ = FBT_ORSIMM13(FBT_REG_G0, id, FBT_REG_O0); 7997c478bd9Sstevel@tonic-gate } 8007c478bd9Sstevel@tonic-gate 8017c478bd9Sstevel@tonic-gate if (offset > (uint32_t)FBT_SIMM13_MAX) { 8027c478bd9Sstevel@tonic-gate *tinstr++ = FBT_SETHI(offset, FBT_REG_O1); 8037c478bd9Sstevel@tonic-gate *tinstr++ = FBT_ORLO(FBT_REG_O1, offset, FBT_REG_O1); 8047c478bd9Sstevel@tonic-gate } else { 8057c478bd9Sstevel@tonic-gate *tinstr++ = FBT_ORSIMM13(FBT_REG_G0, offset, FBT_REG_O1); 8067c478bd9Sstevel@tonic-gate } 8077c478bd9Sstevel@tonic-gate 8087c478bd9Sstevel@tonic-gate *tinstr = FBT_CALL((uintptr_t)tinstr - base + va, dtrace_probe); 8097c478bd9Sstevel@tonic-gate tinstr++; 8107c478bd9Sstevel@tonic-gate 8117c478bd9Sstevel@tonic-gate if (FBT_FMT3_RD(restore) == FBT_REG_O0) { 8127c478bd9Sstevel@tonic-gate /* 8137c478bd9Sstevel@tonic-gate * If the destination register of the restore is %o0, we 8147c478bd9Sstevel@tonic-gate * need to perform the implied calculation to derive the 8157c478bd9Sstevel@tonic-gate * return value. 8167c478bd9Sstevel@tonic-gate */ 8177c478bd9Sstevel@tonic-gate uint32_t add = (restore & ~FBT_FMT3_OP_MASK) | FBT_OP_ADD; 8187c478bd9Sstevel@tonic-gate add &= ~FBT_FMT3_RD_MASK; 8197c478bd9Sstevel@tonic-gate *tinstr++ = add | (FBT_REG_O2 << FBT_FMT3_RD_SHIFT); 8207c478bd9Sstevel@tonic-gate } else { 8217c478bd9Sstevel@tonic-gate *tinstr++ = FBT_MOV(FBT_REG_I0, FBT_REG_O2); 8227c478bd9Sstevel@tonic-gate } 8237c478bd9Sstevel@tonic-gate 8247c478bd9Sstevel@tonic-gate /* 8257c478bd9Sstevel@tonic-gate * If the control transfer instruction is %pc-relative (i.e. a 8267c478bd9Sstevel@tonic-gate * call), we need to reset it appropriately. 8277c478bd9Sstevel@tonic-gate */ 8287c478bd9Sstevel@tonic-gate if (FBT_FMT1_OP(cti) == FBT_OP_CALL) { 8297c478bd9Sstevel@tonic-gate dest = (uintptr_t)instr + (FBT_FMT1_DISP30(cti) << 2); 8307c478bd9Sstevel@tonic-gate *tinstr = FBT_CALL((uintptr_t)tinstr - base + va, dest); 8317c478bd9Sstevel@tonic-gate tinstr++; 8327c478bd9Sstevel@tonic-gate } else { 8337c478bd9Sstevel@tonic-gate *tinstr++ = cti; 8347c478bd9Sstevel@tonic-gate } 8357c478bd9Sstevel@tonic-gate 8367c478bd9Sstevel@tonic-gate *tinstr++ = restore; 8377c478bd9Sstevel@tonic-gate tramp->fbtt_va += (uintptr_t)tinstr - tramp->fbtt_next; 8387c478bd9Sstevel@tonic-gate tramp->fbtt_next = (uintptr_t)tinstr; 8397c478bd9Sstevel@tonic-gate 8407c478bd9Sstevel@tonic-gate return (FBT_BAA(instr, va)); 8417c478bd9Sstevel@tonic-gate } 8427c478bd9Sstevel@tonic-gate 8437c478bd9Sstevel@tonic-gate static uint32_t 8447c478bd9Sstevel@tonic-gate fbt_patch_retl(uint32_t *instr, uint32_t *funcbase, uint32_t *funclim, 8457c478bd9Sstevel@tonic-gate int offset, uint32_t id, fbt_trampoline_t *tramp, const char *name) 8467c478bd9Sstevel@tonic-gate { 8477c478bd9Sstevel@tonic-gate uint32_t *tinstr = (uint32_t *)tramp->fbtt_next; 8487c478bd9Sstevel@tonic-gate uintptr_t va = tramp->fbtt_va; 8497c478bd9Sstevel@tonic-gate uintptr_t base = tramp->fbtt_next; 8507c478bd9Sstevel@tonic-gate uint32_t cti = *instr, dest; 8517c478bd9Sstevel@tonic-gate int annul = 0; 8527c478bd9Sstevel@tonic-gate 8537c478bd9Sstevel@tonic-gate FBT_COUNTER(id, fbt_retl); 8547c478bd9Sstevel@tonic-gate 8557c478bd9Sstevel@tonic-gate if (tramp->fbtt_next + FBT_RETLENT_MAXSIZE > tramp->fbtt_limit) { 8567c478bd9Sstevel@tonic-gate /* 8577c478bd9Sstevel@tonic-gate * There isn't sufficient room for this entry; return failure. 8587c478bd9Sstevel@tonic-gate */ 8597c478bd9Sstevel@tonic-gate return (FBT_ILLTRAP); 8607c478bd9Sstevel@tonic-gate } 8617c478bd9Sstevel@tonic-gate 8627c478bd9Sstevel@tonic-gate if (offset == sizeof (uint32_t) && 8637c478bd9Sstevel@tonic-gate fbt_canpatch_retl(instr - 1, 0, name)) { 8647c478bd9Sstevel@tonic-gate *tinstr++ = *instr; 8657c478bd9Sstevel@tonic-gate annul = 1; 8667c478bd9Sstevel@tonic-gate FBT_COUNTER(id, fbt_retl_twoinstr); 8677c478bd9Sstevel@tonic-gate } else { 8687c478bd9Sstevel@tonic-gate if (FBT_FMT3_OP(cti) == FBT_OP_JMPL && 8697c478bd9Sstevel@tonic-gate FBT_FMT3_RD(cti) != FBT_REG_O7 && 8707c478bd9Sstevel@tonic-gate FBT_FMT3_RS1(cti) != FBT_REG_O7) { 8717c478bd9Sstevel@tonic-gate annul = 1; 8727c478bd9Sstevel@tonic-gate *tinstr++ = *(instr + 1); 8737c478bd9Sstevel@tonic-gate } 8747c478bd9Sstevel@tonic-gate } 8757c478bd9Sstevel@tonic-gate 8767c478bd9Sstevel@tonic-gate *tinstr++ = FBT_SAVEIMM(FBT_REG_O6, -SA(MINFRAME), FBT_REG_O6); 8777c478bd9Sstevel@tonic-gate 8787c478bd9Sstevel@tonic-gate if (FBT_FMT3_OP(cti) == FBT_OP_JMPL) { 8797c478bd9Sstevel@tonic-gate uint32_t rs1, rs2, o2i = FBT_REG_I0 - FBT_REG_O0; 8807c478bd9Sstevel@tonic-gate 8817c478bd9Sstevel@tonic-gate /* 8827c478bd9Sstevel@tonic-gate * If we have a jmpl and it's in terms of output registers, we 8837c478bd9Sstevel@tonic-gate * need to rewrite it to be in terms of the corresponding input 8847c478bd9Sstevel@tonic-gate * registers. If it's in terms of the globals, we'll rewrite 8857c478bd9Sstevel@tonic-gate * it to be in terms of locals. 8867c478bd9Sstevel@tonic-gate */ 8877c478bd9Sstevel@tonic-gate rs1 = FBT_FMT3_RS1(cti); 8887c478bd9Sstevel@tonic-gate 8897c478bd9Sstevel@tonic-gate if (FBT_REG_ISOUTPUT(rs1)) 8907c478bd9Sstevel@tonic-gate rs1 += o2i; 8917c478bd9Sstevel@tonic-gate 8927c478bd9Sstevel@tonic-gate if (FBT_REG_ISGLOBAL(rs1)) { 8937c478bd9Sstevel@tonic-gate *tinstr++ = FBT_MOV(rs1, FBT_REG_L0); 8947c478bd9Sstevel@tonic-gate rs1 = FBT_REG_L0; 8957c478bd9Sstevel@tonic-gate } 8967c478bd9Sstevel@tonic-gate 8977c478bd9Sstevel@tonic-gate FBT_FMT3_RS1_SET(cti, rs1); 8987c478bd9Sstevel@tonic-gate 8997c478bd9Sstevel@tonic-gate if (!FBT_FMT3_ISIMM(cti)) { 9007c478bd9Sstevel@tonic-gate rs2 = FBT_FMT3_RS2(cti); 9017c478bd9Sstevel@tonic-gate 9027c478bd9Sstevel@tonic-gate if (FBT_REG_ISOUTPUT(rs2)) 9037c478bd9Sstevel@tonic-gate rs2 += o2i; 9047c478bd9Sstevel@tonic-gate 9057c478bd9Sstevel@tonic-gate if (FBT_REG_ISGLOBAL(rs2)) { 9067c478bd9Sstevel@tonic-gate *tinstr++ = FBT_MOV(rs2, FBT_REG_L1); 9077c478bd9Sstevel@tonic-gate rs2 = FBT_REG_L1; 9087c478bd9Sstevel@tonic-gate } 9097c478bd9Sstevel@tonic-gate 9107c478bd9Sstevel@tonic-gate FBT_FMT3_RS2_SET(cti, rs2); 9117c478bd9Sstevel@tonic-gate } 9127c478bd9Sstevel@tonic-gate 9137c478bd9Sstevel@tonic-gate /* 9147c478bd9Sstevel@tonic-gate * Now we need to check the rd and source register for the jmpl; 9157c478bd9Sstevel@tonic-gate * If neither rd nor the source register is %o7, then we might 9167c478bd9Sstevel@tonic-gate * have a jmp that is actually part of a jump table. We need 9177c478bd9Sstevel@tonic-gate * to generate the code to compare it to the base and limit of 9187c478bd9Sstevel@tonic-gate * the function. 9197c478bd9Sstevel@tonic-gate */ 9207c478bd9Sstevel@tonic-gate if (FBT_FMT3_RD(cti) != FBT_REG_O7 && rs1 != FBT_REG_I7) { 9217c478bd9Sstevel@tonic-gate uintptr_t base = (uintptr_t)funcbase; 9227c478bd9Sstevel@tonic-gate uintptr_t limit = (uintptr_t)funclim; 9237c478bd9Sstevel@tonic-gate 9247c478bd9Sstevel@tonic-gate FBT_COUNTER(id, fbt_retl_jmptab); 9257c478bd9Sstevel@tonic-gate 9267c478bd9Sstevel@tonic-gate if (FBT_FMT3_ISIMM(cti)) { 9277c478bd9Sstevel@tonic-gate *tinstr++ = FBT_ADDSIMM13(rs1, 9287c478bd9Sstevel@tonic-gate FBT_FMT3_SIMM13(cti), FBT_REG_L2); 9297c478bd9Sstevel@tonic-gate } else { 9307c478bd9Sstevel@tonic-gate *tinstr++ = FBT_ADD(rs1, rs2, FBT_REG_L2); 9317c478bd9Sstevel@tonic-gate } 9327c478bd9Sstevel@tonic-gate 9337c478bd9Sstevel@tonic-gate *tinstr++ = FBT_SETHI(base, FBT_REG_L3); 9347c478bd9Sstevel@tonic-gate *tinstr++ = FBT_ORLO(FBT_REG_L3, base, FBT_REG_L3); 9357c478bd9Sstevel@tonic-gate *tinstr++ = FBT_CMP(FBT_REG_L2, FBT_REG_L3); 9367c478bd9Sstevel@tonic-gate *tinstr++ = FBT_BL(0, 8 * sizeof (uint32_t)); 9377c478bd9Sstevel@tonic-gate *tinstr++ = FBT_SETHI(limit, FBT_REG_L3); 9387c478bd9Sstevel@tonic-gate *tinstr++ = FBT_ORLO(FBT_REG_L3, limit, FBT_REG_L3); 9397c478bd9Sstevel@tonic-gate *tinstr++ = FBT_CMP(FBT_REG_L2, FBT_REG_L3); 9407c478bd9Sstevel@tonic-gate *tinstr++ = FBT_BGE(0, 4 * sizeof (uint32_t)); 9417c478bd9Sstevel@tonic-gate *tinstr++ = FBT_SETHI(0, FBT_REG_G0); 9427c478bd9Sstevel@tonic-gate *tinstr++ = cti; 9437c478bd9Sstevel@tonic-gate *tinstr++ = FBT_RESTORE(FBT_REG_G0, 9447c478bd9Sstevel@tonic-gate FBT_REG_G0, FBT_REG_G0); 9457c478bd9Sstevel@tonic-gate } 9467c478bd9Sstevel@tonic-gate } 9477c478bd9Sstevel@tonic-gate 9487c478bd9Sstevel@tonic-gate if (id > (uint32_t)FBT_SIMM13_MAX) { 9497c478bd9Sstevel@tonic-gate *tinstr++ = FBT_SETHI(id, FBT_REG_O0); 9507c478bd9Sstevel@tonic-gate *tinstr++ = FBT_ORLO(FBT_REG_O0, id, FBT_REG_O0); 9517c478bd9Sstevel@tonic-gate } else { 9527c478bd9Sstevel@tonic-gate *tinstr++ = FBT_ORSIMM13(FBT_REG_G0, id, FBT_REG_O0); 9537c478bd9Sstevel@tonic-gate } 9547c478bd9Sstevel@tonic-gate 9557c478bd9Sstevel@tonic-gate if (offset > (uint32_t)FBT_SIMM13_MAX) { 9567c478bd9Sstevel@tonic-gate *tinstr++ = FBT_SETHI(offset, FBT_REG_O1); 9577c478bd9Sstevel@tonic-gate *tinstr++ = FBT_ORLO(FBT_REG_O1, offset, FBT_REG_O1); 9587c478bd9Sstevel@tonic-gate } else { 9597c478bd9Sstevel@tonic-gate *tinstr++ = FBT_ORSIMM13(FBT_REG_G0, offset, FBT_REG_O1); 9607c478bd9Sstevel@tonic-gate } 9617c478bd9Sstevel@tonic-gate 9627c478bd9Sstevel@tonic-gate *tinstr = FBT_CALL((uintptr_t)tinstr - base + va, dtrace_probe); 9637c478bd9Sstevel@tonic-gate tinstr++; 9647c478bd9Sstevel@tonic-gate *tinstr++ = FBT_MOV(FBT_REG_I0, FBT_REG_O2); 9657c478bd9Sstevel@tonic-gate 9667c478bd9Sstevel@tonic-gate /* 9677c478bd9Sstevel@tonic-gate * If the control transfer instruction is %pc-relative (i.e. a 9687c478bd9Sstevel@tonic-gate * call), we need to reset it appropriately. 9697c478bd9Sstevel@tonic-gate */ 9707c478bd9Sstevel@tonic-gate if (FBT_FMT1_OP(cti) == FBT_OP_CALL) { 9717c478bd9Sstevel@tonic-gate FBT_COUNTER(id, fbt_retl_tailcall); 9727c478bd9Sstevel@tonic-gate dest = (uintptr_t)instr + (FBT_FMT1_DISP30(cti) << 2); 9737c478bd9Sstevel@tonic-gate *tinstr = FBT_CALL((uintptr_t)tinstr - base + va, dest); 9747c478bd9Sstevel@tonic-gate tinstr++; 9757c478bd9Sstevel@tonic-gate annul = 1; 9767c478bd9Sstevel@tonic-gate } else { 9777c478bd9Sstevel@tonic-gate if (FBT_FMT3_OP(cti) == FBT_OP_JMPL) { 9787c478bd9Sstevel@tonic-gate *tinstr++ = cti; 9797c478bd9Sstevel@tonic-gate 9807c478bd9Sstevel@tonic-gate if (FBT_FMT3_RD(cti) == FBT_REG_O7) { 9817c478bd9Sstevel@tonic-gate FBT_COUNTER(id, fbt_retl_tailjmpl); 9827c478bd9Sstevel@tonic-gate annul = 1; 9837c478bd9Sstevel@tonic-gate } 9847c478bd9Sstevel@tonic-gate } else { 9857c478bd9Sstevel@tonic-gate *tinstr++ = FBT_RET; 9867c478bd9Sstevel@tonic-gate } 9877c478bd9Sstevel@tonic-gate } 9887c478bd9Sstevel@tonic-gate 9897c478bd9Sstevel@tonic-gate *tinstr++ = FBT_RESTORE(FBT_REG_G0, FBT_REG_G0, FBT_REG_G0); 9907c478bd9Sstevel@tonic-gate 9917c478bd9Sstevel@tonic-gate tramp->fbtt_va += (uintptr_t)tinstr - tramp->fbtt_next; 9927c478bd9Sstevel@tonic-gate tramp->fbtt_next = (uintptr_t)tinstr; 9937c478bd9Sstevel@tonic-gate 9947c478bd9Sstevel@tonic-gate return (annul ? FBT_BAA(instr, va) : FBT_BA(instr, va)); 9957c478bd9Sstevel@tonic-gate } 9967c478bd9Sstevel@tonic-gate 9977c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 9987c478bd9Sstevel@tonic-gate static void 9997c478bd9Sstevel@tonic-gate fbt_provide_module(void *arg, struct modctl *ctl) 10007c478bd9Sstevel@tonic-gate { 10017c478bd9Sstevel@tonic-gate struct module *mp = ctl->mod_mp; 10027c478bd9Sstevel@tonic-gate char *modname = ctl->mod_modname; 10037c478bd9Sstevel@tonic-gate char *str = mp->strings; 10047c478bd9Sstevel@tonic-gate int nsyms = mp->nsyms; 10057c478bd9Sstevel@tonic-gate Shdr *symhdr = mp->symhdr; 10067c478bd9Sstevel@tonic-gate size_t symsize; 10077c478bd9Sstevel@tonic-gate char *name; 10087c478bd9Sstevel@tonic-gate int i; 10097c478bd9Sstevel@tonic-gate fbt_probe_t *fbt, *retfbt; 10107c478bd9Sstevel@tonic-gate fbt_trampoline_t tramp; 10117c478bd9Sstevel@tonic-gate uintptr_t offset; 10127c478bd9Sstevel@tonic-gate int primary = 0; 10137c478bd9Sstevel@tonic-gate ctf_file_t *fp = NULL; 10147c478bd9Sstevel@tonic-gate int error; 10157c478bd9Sstevel@tonic-gate int estimate = 1; 10167c478bd9Sstevel@tonic-gate uint32_t faketramp[50]; 10177c478bd9Sstevel@tonic-gate size_t fbt_size = 0; 10187c478bd9Sstevel@tonic-gate 10197c478bd9Sstevel@tonic-gate /* 10207c478bd9Sstevel@tonic-gate * Employees of dtrace and their families are ineligible. Void 10217c478bd9Sstevel@tonic-gate * where prohibited. 10227c478bd9Sstevel@tonic-gate */ 10237c478bd9Sstevel@tonic-gate if (strcmp(modname, "dtrace") == 0) 10247c478bd9Sstevel@tonic-gate return; 10257c478bd9Sstevel@tonic-gate 10267c478bd9Sstevel@tonic-gate if (ctl->mod_requisites != NULL) { 10277c478bd9Sstevel@tonic-gate struct modctl_list *list; 10287c478bd9Sstevel@tonic-gate 10297c478bd9Sstevel@tonic-gate list = (struct modctl_list *)ctl->mod_requisites; 10307c478bd9Sstevel@tonic-gate 10317c478bd9Sstevel@tonic-gate for (; list != NULL; list = list->modl_next) { 10327c478bd9Sstevel@tonic-gate if (strcmp(list->modl_modp->mod_modname, "dtrace") == 0) 10337c478bd9Sstevel@tonic-gate return; 10347c478bd9Sstevel@tonic-gate } 10357c478bd9Sstevel@tonic-gate } 10367c478bd9Sstevel@tonic-gate 10377c478bd9Sstevel@tonic-gate /* 10387c478bd9Sstevel@tonic-gate * KMDB is ineligible for instrumentation -- it may execute in 10397c478bd9Sstevel@tonic-gate * any context, including probe context. 10407c478bd9Sstevel@tonic-gate */ 10417c478bd9Sstevel@tonic-gate if (strcmp(modname, "kmdbmod") == 0) 10427c478bd9Sstevel@tonic-gate return; 10437c478bd9Sstevel@tonic-gate 10447c478bd9Sstevel@tonic-gate if (str == NULL || symhdr == NULL || symhdr->sh_addr == NULL) { 10457c478bd9Sstevel@tonic-gate /* 10467c478bd9Sstevel@tonic-gate * If this module doesn't (yet) have its string or symbol 10477c478bd9Sstevel@tonic-gate * table allocated, clear out. 10487c478bd9Sstevel@tonic-gate */ 10497c478bd9Sstevel@tonic-gate return; 10507c478bd9Sstevel@tonic-gate } 10517c478bd9Sstevel@tonic-gate 10527c478bd9Sstevel@tonic-gate symsize = symhdr->sh_entsize; 10537c478bd9Sstevel@tonic-gate 10547c478bd9Sstevel@tonic-gate if (mp->fbt_nentries) { 10557c478bd9Sstevel@tonic-gate /* 10567c478bd9Sstevel@tonic-gate * This module has some FBT entries allocated; we're afraid 10577c478bd9Sstevel@tonic-gate * to screw with it. 10587c478bd9Sstevel@tonic-gate */ 10597c478bd9Sstevel@tonic-gate return; 10607c478bd9Sstevel@tonic-gate } 10617c478bd9Sstevel@tonic-gate 10627c478bd9Sstevel@tonic-gate if (mp->fbt_tab != NULL) 10637c478bd9Sstevel@tonic-gate estimate = 0; 10647c478bd9Sstevel@tonic-gate 10657c478bd9Sstevel@tonic-gate /* 10667c478bd9Sstevel@tonic-gate * This is a hack for unix/genunix/krtld. 10677c478bd9Sstevel@tonic-gate */ 10687c478bd9Sstevel@tonic-gate primary = vmem_contains(heap_arena, (void *)ctl, 10697c478bd9Sstevel@tonic-gate sizeof (struct modctl)) == 0; 10707c478bd9Sstevel@tonic-gate kobj_textwin_alloc(mp); 10717c478bd9Sstevel@tonic-gate 10727c478bd9Sstevel@tonic-gate /* 10737c478bd9Sstevel@tonic-gate * Open the CTF data for the module. We'll use this to determine the 10747c478bd9Sstevel@tonic-gate * functions that can be instrumented. Note that this call can fail, 10757c478bd9Sstevel@tonic-gate * in which case we'll use heuristics to determine the functions that 10767c478bd9Sstevel@tonic-gate * can be instrumented. (But in particular, leaf functions will not be 10777c478bd9Sstevel@tonic-gate * instrumented.) 10787c478bd9Sstevel@tonic-gate */ 10797c478bd9Sstevel@tonic-gate fp = ctf_modopen(mp, &error); 10807c478bd9Sstevel@tonic-gate 10817c478bd9Sstevel@tonic-gate forreal: 10827c478bd9Sstevel@tonic-gate if (!estimate) { 10837c478bd9Sstevel@tonic-gate tramp.fbtt_next = 10847c478bd9Sstevel@tonic-gate (uintptr_t)fbt_trampoline_map((uintptr_t)mp->fbt_tab, 10857c478bd9Sstevel@tonic-gate mp->fbt_size); 10867c478bd9Sstevel@tonic-gate tramp.fbtt_limit = tramp.fbtt_next + mp->fbt_size; 10877c478bd9Sstevel@tonic-gate tramp.fbtt_va = (uintptr_t)mp->fbt_tab; 10887c478bd9Sstevel@tonic-gate } 10897c478bd9Sstevel@tonic-gate 10907c478bd9Sstevel@tonic-gate for (i = 1; i < nsyms; i++) { 10917c478bd9Sstevel@tonic-gate ctf_funcinfo_t f; 10927c478bd9Sstevel@tonic-gate uint32_t *instr, *base, *limit; 10937c478bd9Sstevel@tonic-gate Sym *sym = (Sym *)(symhdr->sh_addr + i * symsize); 10947c478bd9Sstevel@tonic-gate int have_ctf = 0, is_leaf = 0, nargs, cti = 0; 10957c478bd9Sstevel@tonic-gate int (*canpatch)(uint32_t *, int, const char *); 10967c478bd9Sstevel@tonic-gate uint32_t (*patch)(uint32_t *, uint32_t *, uint32_t *, int, 10977c478bd9Sstevel@tonic-gate uint32_t, fbt_trampoline_t *, const char *); 10987c478bd9Sstevel@tonic-gate 10997c478bd9Sstevel@tonic-gate if (ELF_ST_TYPE(sym->st_info) != STT_FUNC) 11007c478bd9Sstevel@tonic-gate continue; 11017c478bd9Sstevel@tonic-gate 11027c478bd9Sstevel@tonic-gate /* 11037c478bd9Sstevel@tonic-gate * Weak symbols are not candidates. This could be made to 11047c478bd9Sstevel@tonic-gate * work (where weak functions and their underlying function 11057c478bd9Sstevel@tonic-gate * appear as two disjoint probes), but it's not simple. 11067c478bd9Sstevel@tonic-gate */ 11077c478bd9Sstevel@tonic-gate if (ELF_ST_BIND(sym->st_info) == STB_WEAK) 11087c478bd9Sstevel@tonic-gate continue; 11097c478bd9Sstevel@tonic-gate 11107c478bd9Sstevel@tonic-gate name = str + sym->st_name; 11117c478bd9Sstevel@tonic-gate 11127c478bd9Sstevel@tonic-gate if (strstr(name, "dtrace_") == name && 11137c478bd9Sstevel@tonic-gate strstr(name, "dtrace_safe_") != name) { 11147c478bd9Sstevel@tonic-gate /* 11157c478bd9Sstevel@tonic-gate * Anything beginning with "dtrace_" may be called 11167c478bd9Sstevel@tonic-gate * from probe context unless it explitly indicates 11177c478bd9Sstevel@tonic-gate * that it won't be called from probe context by 11187c478bd9Sstevel@tonic-gate * using the prefix "dtrace_safe_". 11197c478bd9Sstevel@tonic-gate */ 11207c478bd9Sstevel@tonic-gate continue; 11217c478bd9Sstevel@tonic-gate } 11227c478bd9Sstevel@tonic-gate 1123a1b5e537Sbmc if (strstr(name, "kdi_") == name || 1124a1b5e537Sbmc strstr(name, "_kdi_") != NULL) { 11257c478bd9Sstevel@tonic-gate /* 1126a1b5e537Sbmc * Any function name beginning with "kdi_" or 1127a1b5e537Sbmc * containing the string "_kdi_" is a part of the 11287c478bd9Sstevel@tonic-gate * kernel debugger interface and may be called in 11297c478bd9Sstevel@tonic-gate * arbitrary context -- including probe context. 11307c478bd9Sstevel@tonic-gate */ 11317c478bd9Sstevel@tonic-gate continue; 11327c478bd9Sstevel@tonic-gate } 11337c478bd9Sstevel@tonic-gate 11347c478bd9Sstevel@tonic-gate if (strstr(name, "__relocatable") != NULL) { 11357c478bd9Sstevel@tonic-gate /* 11367c478bd9Sstevel@tonic-gate * Anything with the string "__relocatable" anywhere 11377c478bd9Sstevel@tonic-gate * in the function name is considered to be a function 11387c478bd9Sstevel@tonic-gate * that may be manually relocated before execution. 11397c478bd9Sstevel@tonic-gate * Because FBT uses a PC-relative technique for 11407c478bd9Sstevel@tonic-gate * instrumentation, these functions cannot safely 11417c478bd9Sstevel@tonic-gate * be instrumented by us. 11427c478bd9Sstevel@tonic-gate */ 11437c478bd9Sstevel@tonic-gate continue; 11447c478bd9Sstevel@tonic-gate } 11457c478bd9Sstevel@tonic-gate 11467c478bd9Sstevel@tonic-gate if (strstr(name, "ip_ocsum") == name) { 11477c478bd9Sstevel@tonic-gate /* 11487c478bd9Sstevel@tonic-gate * The ip_ocsum_* family of routines are all ABI 11497c478bd9Sstevel@tonic-gate * violators. (They expect incoming arguments in the 11507c478bd9Sstevel@tonic-gate * globals!) Break the ABI? No soup for you! 11517c478bd9Sstevel@tonic-gate */ 11527c478bd9Sstevel@tonic-gate continue; 11537c478bd9Sstevel@tonic-gate } 11547c478bd9Sstevel@tonic-gate 11557c478bd9Sstevel@tonic-gate /* 11567c478bd9Sstevel@tonic-gate * We want to scan the function for one (and only one) save. 11577c478bd9Sstevel@tonic-gate * Any more indicates that something fancy is going on. 11587c478bd9Sstevel@tonic-gate */ 11597c478bd9Sstevel@tonic-gate base = (uint32_t *)sym->st_value; 11607c478bd9Sstevel@tonic-gate limit = (uint32_t *)(sym->st_value + sym->st_size); 11617c478bd9Sstevel@tonic-gate 11627c478bd9Sstevel@tonic-gate /* 11637c478bd9Sstevel@tonic-gate * We don't want to interpose on the module stubs. 11647c478bd9Sstevel@tonic-gate */ 11657c478bd9Sstevel@tonic-gate if (base >= (uint32_t *)stubs_base && 11667c478bd9Sstevel@tonic-gate base <= (uint32_t *)stubs_end) 11677c478bd9Sstevel@tonic-gate continue; 11687c478bd9Sstevel@tonic-gate 11697c478bd9Sstevel@tonic-gate /* 11707c478bd9Sstevel@tonic-gate * We can't safely trace a zero-length function... 11717c478bd9Sstevel@tonic-gate */ 11727c478bd9Sstevel@tonic-gate if (base == limit) 11737c478bd9Sstevel@tonic-gate continue; 11747c478bd9Sstevel@tonic-gate 11757c478bd9Sstevel@tonic-gate /* 11767c478bd9Sstevel@tonic-gate * Due to 4524008, _init and _fini may have a bloated st_size. 11777c478bd9Sstevel@tonic-gate * While this bug was fixed quite some time ago, old drivers 11787c478bd9Sstevel@tonic-gate * may be lurking. We need to develop a better solution to 11797c478bd9Sstevel@tonic-gate * this problem, such that correct _init and _fini functions 11807c478bd9Sstevel@tonic-gate * (the vast majority) may be correctly traced. One solution 11817c478bd9Sstevel@tonic-gate * may be to scan through the entire symbol table to see if 11827c478bd9Sstevel@tonic-gate * any symbol overlaps with _init. If none does, set a bit in 11837c478bd9Sstevel@tonic-gate * the module structure that this module has correct _init and 11847c478bd9Sstevel@tonic-gate * _fini sizes. This will cause some pain the first time a 11857c478bd9Sstevel@tonic-gate * module is scanned, but at least it would be O(N) instead of 11867c478bd9Sstevel@tonic-gate * O(N log N)... 11877c478bd9Sstevel@tonic-gate */ 11887c478bd9Sstevel@tonic-gate if (strcmp(name, "_init") == 0) 11897c478bd9Sstevel@tonic-gate continue; 11907c478bd9Sstevel@tonic-gate 11917c478bd9Sstevel@tonic-gate if (strcmp(name, "_fini") == 0) 11927c478bd9Sstevel@tonic-gate continue; 11937c478bd9Sstevel@tonic-gate 11947c478bd9Sstevel@tonic-gate instr = base; 11957c478bd9Sstevel@tonic-gate 11967c478bd9Sstevel@tonic-gate /* 11977c478bd9Sstevel@tonic-gate * While we try hard to only trace safe functions (that is, 11987c478bd9Sstevel@tonic-gate * functions at TL=0), one unsafe function manages to otherwise 11997c478bd9Sstevel@tonic-gate * appear safe: prom_trap(). We could discover prom_trap() 12007c478bd9Sstevel@tonic-gate * if we added an additional rule: in order to trace a 12017c478bd9Sstevel@tonic-gate * function, we must either (a) discover a restore or (b) 12027c478bd9Sstevel@tonic-gate * determine that the function does not have any unlinked 12037c478bd9Sstevel@tonic-gate * control transfers to another function (i.e., the function 12047c478bd9Sstevel@tonic-gate * never returns). Unfortunately, as of this writing, one 12057c478bd9Sstevel@tonic-gate * legitimate function (resume_from_zombie()) transfers 12067c478bd9Sstevel@tonic-gate * control to a different function (_resume_from_idle()) 12077c478bd9Sstevel@tonic-gate * without executing a restore. Barring a rule to figure out 12087c478bd9Sstevel@tonic-gate * that resume_from_zombie() is safe while prom_trap() is not, 12097c478bd9Sstevel@tonic-gate * we resort to hard-coding prom_trap() here. 12107c478bd9Sstevel@tonic-gate */ 12117c478bd9Sstevel@tonic-gate if (strcmp(name, "prom_trap") == 0) 12127c478bd9Sstevel@tonic-gate continue; 12137c478bd9Sstevel@tonic-gate 12147c478bd9Sstevel@tonic-gate if (fp != NULL && ctf_func_info(fp, i, &f) != CTF_ERR) { 12157c478bd9Sstevel@tonic-gate nargs = f.ctc_argc; 12167c478bd9Sstevel@tonic-gate have_ctf = 1; 12177c478bd9Sstevel@tonic-gate } else { 12187c478bd9Sstevel@tonic-gate nargs = 32; 12197c478bd9Sstevel@tonic-gate } 12207c478bd9Sstevel@tonic-gate 12217c478bd9Sstevel@tonic-gate /* 12227c478bd9Sstevel@tonic-gate * If the first instruction of the function is a branch and 12237c478bd9Sstevel@tonic-gate * it's not a branch-always-not-annulled, we're going to refuse 12247c478bd9Sstevel@tonic-gate * to patch it. 12257c478bd9Sstevel@tonic-gate */ 12267c478bd9Sstevel@tonic-gate if ((*instr & FBT_OP_MASK) == FBT_OP0 && 12277c478bd9Sstevel@tonic-gate (*instr & FBT_FMT2_OP2_MASK) != FBT_FMT2_OP2_SETHI && 12287c478bd9Sstevel@tonic-gate (*instr & FBT_FMT2_OP2_MASK) != FBT_FMT2_OP2_BPR) { 12297c478bd9Sstevel@tonic-gate if (!FBT_IS_BA(*instr) && !FBT_IS_BAPCC(*instr)) { 12307c478bd9Sstevel@tonic-gate if (have_ctf) { 12317c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "cannot instrument %s:" 12327c478bd9Sstevel@tonic-gate " begins with non-ba, " 12337c478bd9Sstevel@tonic-gate "non-br CTI", name); 12347c478bd9Sstevel@tonic-gate } 12357c478bd9Sstevel@tonic-gate continue; 12367c478bd9Sstevel@tonic-gate } 12377c478bd9Sstevel@tonic-gate } 12387c478bd9Sstevel@tonic-gate 12397c478bd9Sstevel@tonic-gate while (!FBT_IS_SAVE(*instr)) { 12407c478bd9Sstevel@tonic-gate /* 12417c478bd9Sstevel@tonic-gate * Before we assume that this is a leaf routine, check 12427c478bd9Sstevel@tonic-gate * forward in the basic block for a save. 12437c478bd9Sstevel@tonic-gate */ 12447c478bd9Sstevel@tonic-gate int op = *instr & FBT_OP_MASK; 12457c478bd9Sstevel@tonic-gate int op2 = *instr & FBT_FMT2_OP2_MASK; 12467c478bd9Sstevel@tonic-gate 12477c478bd9Sstevel@tonic-gate if (op == FBT_OP0 && op2 != FBT_FMT2_OP2_SETHI) { 12487c478bd9Sstevel@tonic-gate /* 12497c478bd9Sstevel@tonic-gate * This is a CTI. If we see a subsequent 12507c478bd9Sstevel@tonic-gate * save, we will refuse to process this 12517c478bd9Sstevel@tonic-gate * routine unless both of the following are 12527c478bd9Sstevel@tonic-gate * true: 12537c478bd9Sstevel@tonic-gate * 12547c478bd9Sstevel@tonic-gate * (a) The branch is not annulled 12557c478bd9Sstevel@tonic-gate * 12567c478bd9Sstevel@tonic-gate * (b) The subsequent save is in the delay 12577c478bd9Sstevel@tonic-gate * slot of the branch 12587c478bd9Sstevel@tonic-gate */ 12597c478bd9Sstevel@tonic-gate if ((*instr & FBT_ANNUL) || 12607c478bd9Sstevel@tonic-gate !FBT_IS_SAVE(*(instr + 1))) { 12617c478bd9Sstevel@tonic-gate cti = 1; 12627c478bd9Sstevel@tonic-gate } else { 12637c478bd9Sstevel@tonic-gate instr++; 12647c478bd9Sstevel@tonic-gate break; 12657c478bd9Sstevel@tonic-gate } 12667c478bd9Sstevel@tonic-gate } 12677c478bd9Sstevel@tonic-gate 12687c478bd9Sstevel@tonic-gate if (op == FBT_OP1) 12697c478bd9Sstevel@tonic-gate cti = 1; 12707c478bd9Sstevel@tonic-gate 12717c478bd9Sstevel@tonic-gate if (++instr == limit) 12727c478bd9Sstevel@tonic-gate break; 12737c478bd9Sstevel@tonic-gate } 12747c478bd9Sstevel@tonic-gate 12757c478bd9Sstevel@tonic-gate if (instr < limit && cti) { 12767c478bd9Sstevel@tonic-gate /* 12777c478bd9Sstevel@tonic-gate * If we found a CTI before the save, we need to not 12787c478bd9Sstevel@tonic-gate * do anything. But if we have CTF information, this 12797c478bd9Sstevel@tonic-gate * is weird enough that it merits a message. 12807c478bd9Sstevel@tonic-gate */ 12817c478bd9Sstevel@tonic-gate if (!have_ctf) 12827c478bd9Sstevel@tonic-gate continue; 12837c478bd9Sstevel@tonic-gate 12847c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "cannot instrument %s: " 12857c478bd9Sstevel@tonic-gate "save not in first basic block", name); 12867c478bd9Sstevel@tonic-gate continue; 12877c478bd9Sstevel@tonic-gate } 12887c478bd9Sstevel@tonic-gate 12897c478bd9Sstevel@tonic-gate if (instr == limit) { 12907c478bd9Sstevel@tonic-gate if (!have_ctf) 12917c478bd9Sstevel@tonic-gate continue; 12927c478bd9Sstevel@tonic-gate is_leaf = 1; 12937c478bd9Sstevel@tonic-gate 12947c478bd9Sstevel@tonic-gate if (!estimate) 12957c478bd9Sstevel@tonic-gate fbt_leaf_functions++; 12967c478bd9Sstevel@tonic-gate 12977c478bd9Sstevel@tonic-gate canpatch = fbt_canpatch_retl; 12987c478bd9Sstevel@tonic-gate patch = fbt_patch_retl; 12997c478bd9Sstevel@tonic-gate } else { 13007c478bd9Sstevel@tonic-gate canpatch = fbt_canpatch_return; 13017c478bd9Sstevel@tonic-gate patch = fbt_patch_return; 13027c478bd9Sstevel@tonic-gate } 13037c478bd9Sstevel@tonic-gate 13047c478bd9Sstevel@tonic-gate if (!have_ctf && !is_leaf) { 13057c478bd9Sstevel@tonic-gate /* 13067c478bd9Sstevel@tonic-gate * Before we assume that this isn't something tricky, 13077c478bd9Sstevel@tonic-gate * look for other saves. If we find them, there are 13087c478bd9Sstevel@tonic-gate * multiple entry points here (or something), and we'll 13097c478bd9Sstevel@tonic-gate * leave it alone. 13107c478bd9Sstevel@tonic-gate */ 13117c478bd9Sstevel@tonic-gate while (++instr < limit) { 13127c478bd9Sstevel@tonic-gate if (FBT_IS_SAVE(*instr)) 13137c478bd9Sstevel@tonic-gate break; 13147c478bd9Sstevel@tonic-gate } 13157c478bd9Sstevel@tonic-gate 13167c478bd9Sstevel@tonic-gate if (instr != limit) 13177c478bd9Sstevel@tonic-gate continue; 13187c478bd9Sstevel@tonic-gate } 13197c478bd9Sstevel@tonic-gate 13207c478bd9Sstevel@tonic-gate instr = base; 13217c478bd9Sstevel@tonic-gate 13227c478bd9Sstevel@tonic-gate if (FBT_IS_CTI(*instr)) { 13237c478bd9Sstevel@tonic-gate /* 13247c478bd9Sstevel@tonic-gate * If we have a CTI, we want to be sure that we don't 13257c478bd9Sstevel@tonic-gate * have a CTI or a PC-relative instruction in the 13267c478bd9Sstevel@tonic-gate * delay slot -- we want to be able to thunk the 13277c478bd9Sstevel@tonic-gate * instruction into the trampoline without worrying 13287c478bd9Sstevel@tonic-gate * about either DCTIs or relocations. It would be 13297c478bd9Sstevel@tonic-gate * very odd for the compiler to generate this kind of 13307c478bd9Sstevel@tonic-gate * code, so we warn about it if we have CTF 13317c478bd9Sstevel@tonic-gate * information. 13327c478bd9Sstevel@tonic-gate */ 13337c478bd9Sstevel@tonic-gate if (FBT_IS_CTI(*(instr + 1))) { 13347c478bd9Sstevel@tonic-gate if (!have_ctf) 13357c478bd9Sstevel@tonic-gate continue; 13367c478bd9Sstevel@tonic-gate 13377c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "cannot instrument %s: " 13387c478bd9Sstevel@tonic-gate "CTI in delay slot of first instruction", 13397c478bd9Sstevel@tonic-gate name); 13407c478bd9Sstevel@tonic-gate continue; 13417c478bd9Sstevel@tonic-gate } 13427c478bd9Sstevel@tonic-gate 13437c478bd9Sstevel@tonic-gate if (FBT_IS_PCRELATIVE(*(instr + 1))) { 13447c478bd9Sstevel@tonic-gate if (!have_ctf) 13457c478bd9Sstevel@tonic-gate continue; 13467c478bd9Sstevel@tonic-gate 13477c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "cannot instrument %s: " 13487c478bd9Sstevel@tonic-gate "PC-relative instruction in delay slot of" 13497c478bd9Sstevel@tonic-gate " first instruction", name); 13507c478bd9Sstevel@tonic-gate continue; 13517c478bd9Sstevel@tonic-gate } 13527c478bd9Sstevel@tonic-gate } 13537c478bd9Sstevel@tonic-gate 13547c478bd9Sstevel@tonic-gate if (estimate) { 13557c478bd9Sstevel@tonic-gate tramp.fbtt_next = (uintptr_t)faketramp; 13567c478bd9Sstevel@tonic-gate tramp.fbtt_limit = tramp.fbtt_next + sizeof (faketramp); 13577c478bd9Sstevel@tonic-gate (void) fbt_patch_entry(instr, FBT_ESTIMATE_ID, 13587c478bd9Sstevel@tonic-gate &tramp, nargs); 13597c478bd9Sstevel@tonic-gate fbt_size += tramp.fbtt_next - (uintptr_t)faketramp; 13607c478bd9Sstevel@tonic-gate } else { 13617c478bd9Sstevel@tonic-gate fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP); 13627c478bd9Sstevel@tonic-gate fbt->fbtp_name = name; 13637c478bd9Sstevel@tonic-gate fbt->fbtp_ctl = ctl; 13647c478bd9Sstevel@tonic-gate fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 13657c478bd9Sstevel@tonic-gate name, FBT_PROBENAME_ENTRY, 1, fbt); 13667c478bd9Sstevel@tonic-gate fbt->fbtp_patchval = FBT_BAA(instr, tramp.fbtt_va); 13677c478bd9Sstevel@tonic-gate 13687c478bd9Sstevel@tonic-gate if (!fbt_patch_entry(instr, fbt->fbtp_id, 13697c478bd9Sstevel@tonic-gate &tramp, nargs)) { 13707c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "unexpectedly short FBT table " 13717c478bd9Sstevel@tonic-gate "in module %s (sym %d of %d)", modname, 13727c478bd9Sstevel@tonic-gate i, nsyms); 13737c478bd9Sstevel@tonic-gate break; 13747c478bd9Sstevel@tonic-gate } 13757c478bd9Sstevel@tonic-gate 13767c478bd9Sstevel@tonic-gate fbt->fbtp_patchpoint = 13777c478bd9Sstevel@tonic-gate (uint32_t *)((uintptr_t)mp->textwin + 13787c478bd9Sstevel@tonic-gate ((uintptr_t)instr - (uintptr_t)mp->text)); 13797c478bd9Sstevel@tonic-gate fbt->fbtp_savedval = *instr; 13807c478bd9Sstevel@tonic-gate 13817c478bd9Sstevel@tonic-gate fbt->fbtp_loadcnt = ctl->mod_loadcnt; 13827c478bd9Sstevel@tonic-gate fbt->fbtp_primary = primary; 13837c478bd9Sstevel@tonic-gate fbt->fbtp_symndx = i; 13847c478bd9Sstevel@tonic-gate mp->fbt_nentries++; 13857c478bd9Sstevel@tonic-gate } 13867c478bd9Sstevel@tonic-gate 13877c478bd9Sstevel@tonic-gate retfbt = NULL; 13887c478bd9Sstevel@tonic-gate again: 13897c478bd9Sstevel@tonic-gate if (++instr == limit) 13907c478bd9Sstevel@tonic-gate continue; 13917c478bd9Sstevel@tonic-gate 13927c478bd9Sstevel@tonic-gate offset = (uintptr_t)instr - (uintptr_t)base; 13937c478bd9Sstevel@tonic-gate 13947c478bd9Sstevel@tonic-gate if (!(*canpatch)(instr, offset, name)) 13957c478bd9Sstevel@tonic-gate goto again; 13967c478bd9Sstevel@tonic-gate 13977c478bd9Sstevel@tonic-gate if (estimate) { 13987c478bd9Sstevel@tonic-gate tramp.fbtt_next = (uintptr_t)faketramp; 13997c478bd9Sstevel@tonic-gate tramp.fbtt_limit = tramp.fbtt_next + sizeof (faketramp); 14007c478bd9Sstevel@tonic-gate (void) (*patch)(instr, base, limit, 14017c478bd9Sstevel@tonic-gate offset, FBT_ESTIMATE_ID, &tramp, name); 14027c478bd9Sstevel@tonic-gate fbt_size += tramp.fbtt_next - (uintptr_t)faketramp; 14037c478bd9Sstevel@tonic-gate 14047c478bd9Sstevel@tonic-gate goto again; 14057c478bd9Sstevel@tonic-gate } 14067c478bd9Sstevel@tonic-gate 14077c478bd9Sstevel@tonic-gate fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP); 14087c478bd9Sstevel@tonic-gate fbt->fbtp_name = name; 14097c478bd9Sstevel@tonic-gate fbt->fbtp_ctl = ctl; 14107c478bd9Sstevel@tonic-gate 14117c478bd9Sstevel@tonic-gate if (retfbt == NULL) { 14127c478bd9Sstevel@tonic-gate fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, 14137c478bd9Sstevel@tonic-gate name, FBT_PROBENAME_RETURN, 1, fbt); 14147c478bd9Sstevel@tonic-gate } else { 14157c478bd9Sstevel@tonic-gate retfbt->fbtp_next = fbt; 14167c478bd9Sstevel@tonic-gate fbt->fbtp_id = retfbt->fbtp_id; 14177c478bd9Sstevel@tonic-gate } 14187c478bd9Sstevel@tonic-gate 14197c478bd9Sstevel@tonic-gate fbt->fbtp_return = 1; 14207c478bd9Sstevel@tonic-gate retfbt = fbt; 14217c478bd9Sstevel@tonic-gate 14227c478bd9Sstevel@tonic-gate if ((fbt->fbtp_patchval = (*patch)(instr, base, limit, offset, 14237c478bd9Sstevel@tonic-gate fbt->fbtp_id, &tramp, name)) == FBT_ILLTRAP) { 14247c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "unexpectedly short FBT table " 14257c478bd9Sstevel@tonic-gate "in module %s (sym %d of %d)", modname, i, nsyms); 14267c478bd9Sstevel@tonic-gate break; 14277c478bd9Sstevel@tonic-gate } 14287c478bd9Sstevel@tonic-gate 14297c478bd9Sstevel@tonic-gate fbt->fbtp_patchpoint = (uint32_t *)((uintptr_t)mp->textwin + 14307c478bd9Sstevel@tonic-gate ((uintptr_t)instr - (uintptr_t)mp->text)); 14317c478bd9Sstevel@tonic-gate fbt->fbtp_savedval = *instr; 14327c478bd9Sstevel@tonic-gate fbt->fbtp_loadcnt = ctl->mod_loadcnt; 14337c478bd9Sstevel@tonic-gate fbt->fbtp_primary = primary; 14347c478bd9Sstevel@tonic-gate fbt->fbtp_symndx = i; 14357c478bd9Sstevel@tonic-gate mp->fbt_nentries++; 14367c478bd9Sstevel@tonic-gate 14377c478bd9Sstevel@tonic-gate goto again; 14387c478bd9Sstevel@tonic-gate } 14397c478bd9Sstevel@tonic-gate 14407c478bd9Sstevel@tonic-gate if (estimate) { 14417c478bd9Sstevel@tonic-gate /* 14427c478bd9Sstevel@tonic-gate * Slosh on another entry's worth... 14437c478bd9Sstevel@tonic-gate */ 14447c478bd9Sstevel@tonic-gate fbt_size += FBT_ENT_MAXSIZE; 14457c478bd9Sstevel@tonic-gate mp->fbt_size = fbt_size; 14467c478bd9Sstevel@tonic-gate mp->fbt_tab = kobj_texthole_alloc(mp->text, fbt_size); 14477c478bd9Sstevel@tonic-gate 14487c478bd9Sstevel@tonic-gate if (mp->fbt_tab == NULL) { 14497c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "couldn't allocate FBT table " 14507c478bd9Sstevel@tonic-gate "for module %s", modname); 14517c478bd9Sstevel@tonic-gate } else { 14527c478bd9Sstevel@tonic-gate estimate = 0; 14537c478bd9Sstevel@tonic-gate goto forreal; 14547c478bd9Sstevel@tonic-gate } 14557c478bd9Sstevel@tonic-gate } else { 14567c478bd9Sstevel@tonic-gate fbt_trampoline_unmap(); 14577c478bd9Sstevel@tonic-gate } 14587c478bd9Sstevel@tonic-gate 14597c478bd9Sstevel@tonic-gate error: 14607c478bd9Sstevel@tonic-gate if (fp != NULL) 14617c478bd9Sstevel@tonic-gate ctf_close(fp); 14627c478bd9Sstevel@tonic-gate } 14637c478bd9Sstevel@tonic-gate 14647c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 14657c478bd9Sstevel@tonic-gate static void 14667c478bd9Sstevel@tonic-gate fbt_destroy(void *arg, dtrace_id_t id, void *parg) 14677c478bd9Sstevel@tonic-gate { 14687c478bd9Sstevel@tonic-gate fbt_probe_t *fbt = parg, *next; 14697c478bd9Sstevel@tonic-gate struct modctl *ctl = fbt->fbtp_ctl; 14707c478bd9Sstevel@tonic-gate 14717c478bd9Sstevel@tonic-gate do { 14727c478bd9Sstevel@tonic-gate if (ctl != NULL && ctl->mod_loadcnt == fbt->fbtp_loadcnt) { 14737c478bd9Sstevel@tonic-gate if ((ctl->mod_loadcnt == fbt->fbtp_loadcnt && 14747c478bd9Sstevel@tonic-gate ctl->mod_loaded) || fbt->fbtp_primary) { 14757c478bd9Sstevel@tonic-gate ((struct module *) 14767c478bd9Sstevel@tonic-gate (ctl->mod_mp))->fbt_nentries--; 14777c478bd9Sstevel@tonic-gate } 14787c478bd9Sstevel@tonic-gate } 14797c478bd9Sstevel@tonic-gate 14807c478bd9Sstevel@tonic-gate next = fbt->fbtp_next; 14817c478bd9Sstevel@tonic-gate kmem_free(fbt, sizeof (fbt_probe_t)); 14827c478bd9Sstevel@tonic-gate fbt = next; 14837c478bd9Sstevel@tonic-gate } while (fbt != NULL); 14847c478bd9Sstevel@tonic-gate } 14857c478bd9Sstevel@tonic-gate 14867c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 14877c478bd9Sstevel@tonic-gate static void 14887c478bd9Sstevel@tonic-gate fbt_enable(void *arg, dtrace_id_t id, void *parg) 14897c478bd9Sstevel@tonic-gate { 14907c478bd9Sstevel@tonic-gate fbt_probe_t *fbt = parg, *f; 14917c478bd9Sstevel@tonic-gate struct modctl *ctl = fbt->fbtp_ctl; 14927c478bd9Sstevel@tonic-gate 14937c478bd9Sstevel@tonic-gate ctl->mod_nenabled++; 14947c478bd9Sstevel@tonic-gate 14957c478bd9Sstevel@tonic-gate for (f = fbt; f != NULL; f = f->fbtp_next) { 14967c478bd9Sstevel@tonic-gate if (f->fbtp_patchpoint == NULL) { 14977c478bd9Sstevel@tonic-gate /* 14987c478bd9Sstevel@tonic-gate * Due to a shortened FBT table, this entry was never 14997c478bd9Sstevel@tonic-gate * completed; refuse to enable it. 15007c478bd9Sstevel@tonic-gate */ 15017c478bd9Sstevel@tonic-gate if (fbt_verbose) { 15027c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "fbt is failing for probe %s " 15037c478bd9Sstevel@tonic-gate "(short FBT table in %s)", 15047c478bd9Sstevel@tonic-gate fbt->fbtp_name, ctl->mod_modname); 15057c478bd9Sstevel@tonic-gate } 15067c478bd9Sstevel@tonic-gate 15077c478bd9Sstevel@tonic-gate return; 15087c478bd9Sstevel@tonic-gate } 15097c478bd9Sstevel@tonic-gate } 15107c478bd9Sstevel@tonic-gate 15117c478bd9Sstevel@tonic-gate /* 15127c478bd9Sstevel@tonic-gate * If this module has disappeared since we discovered its probes, 15137c478bd9Sstevel@tonic-gate * refuse to enable it. 15147c478bd9Sstevel@tonic-gate */ 15157c478bd9Sstevel@tonic-gate if (!fbt->fbtp_primary && !ctl->mod_loaded) { 15167c478bd9Sstevel@tonic-gate if (fbt_verbose) { 15177c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "fbt is failing for probe %s " 15187c478bd9Sstevel@tonic-gate "(module %s unloaded)", 15197c478bd9Sstevel@tonic-gate fbt->fbtp_name, ctl->mod_modname); 15207c478bd9Sstevel@tonic-gate } 15217c478bd9Sstevel@tonic-gate 15227c478bd9Sstevel@tonic-gate return; 15237c478bd9Sstevel@tonic-gate } 15247c478bd9Sstevel@tonic-gate 15257c478bd9Sstevel@tonic-gate /* 15267c478bd9Sstevel@tonic-gate * Now check that our modctl has the expected load count. If it 15277c478bd9Sstevel@tonic-gate * doesn't, this module must have been unloaded and reloaded -- and 15287c478bd9Sstevel@tonic-gate * we're not going to touch it. 15297c478bd9Sstevel@tonic-gate */ 15307c478bd9Sstevel@tonic-gate if (ctl->mod_loadcnt != fbt->fbtp_loadcnt) { 15317c478bd9Sstevel@tonic-gate if (fbt_verbose) { 15327c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "fbt is failing for probe %s " 15337c478bd9Sstevel@tonic-gate "(module %s reloaded)", 15347c478bd9Sstevel@tonic-gate fbt->fbtp_name, ctl->mod_modname); 15357c478bd9Sstevel@tonic-gate } 15367c478bd9Sstevel@tonic-gate 15377c478bd9Sstevel@tonic-gate return; 15387c478bd9Sstevel@tonic-gate } 15397c478bd9Sstevel@tonic-gate 15407c478bd9Sstevel@tonic-gate for (; fbt != NULL; fbt = fbt->fbtp_next) 15417c478bd9Sstevel@tonic-gate *fbt->fbtp_patchpoint = fbt->fbtp_patchval; 15427c478bd9Sstevel@tonic-gate } 15437c478bd9Sstevel@tonic-gate 15447c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 15457c478bd9Sstevel@tonic-gate static void 15467c478bd9Sstevel@tonic-gate fbt_disable(void *arg, dtrace_id_t id, void *parg) 15477c478bd9Sstevel@tonic-gate { 15487c478bd9Sstevel@tonic-gate fbt_probe_t *fbt = parg, *f; 15497c478bd9Sstevel@tonic-gate struct modctl *ctl = fbt->fbtp_ctl; 15507c478bd9Sstevel@tonic-gate 15517c478bd9Sstevel@tonic-gate ASSERT(ctl->mod_nenabled > 0); 15527c478bd9Sstevel@tonic-gate ctl->mod_nenabled--; 15537c478bd9Sstevel@tonic-gate 15547c478bd9Sstevel@tonic-gate for (f = fbt; f != NULL; f = f->fbtp_next) { 15557c478bd9Sstevel@tonic-gate if (f->fbtp_patchpoint == NULL) 15567c478bd9Sstevel@tonic-gate return; 15577c478bd9Sstevel@tonic-gate } 15587c478bd9Sstevel@tonic-gate 15597c478bd9Sstevel@tonic-gate if ((!fbt->fbtp_primary && !ctl->mod_loaded) || 15607c478bd9Sstevel@tonic-gate (ctl->mod_loadcnt != fbt->fbtp_loadcnt)) 15617c478bd9Sstevel@tonic-gate return; 15627c478bd9Sstevel@tonic-gate 15637c478bd9Sstevel@tonic-gate for (; fbt != NULL; fbt = fbt->fbtp_next) 15647c478bd9Sstevel@tonic-gate *fbt->fbtp_patchpoint = fbt->fbtp_savedval; 15657c478bd9Sstevel@tonic-gate } 15667c478bd9Sstevel@tonic-gate 15677c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 15687c478bd9Sstevel@tonic-gate static void 15697c478bd9Sstevel@tonic-gate fbt_suspend(void *arg, dtrace_id_t id, void *parg) 15707c478bd9Sstevel@tonic-gate { 15717c478bd9Sstevel@tonic-gate fbt_probe_t *fbt = parg; 15727c478bd9Sstevel@tonic-gate struct modctl *ctl = fbt->fbtp_ctl; 15737c478bd9Sstevel@tonic-gate 15747c478bd9Sstevel@tonic-gate if (!fbt->fbtp_primary && !ctl->mod_loaded) 15757c478bd9Sstevel@tonic-gate return; 15767c478bd9Sstevel@tonic-gate 15777c478bd9Sstevel@tonic-gate if (ctl->mod_loadcnt != fbt->fbtp_loadcnt) 15787c478bd9Sstevel@tonic-gate return; 15797c478bd9Sstevel@tonic-gate 15807c478bd9Sstevel@tonic-gate ASSERT(ctl->mod_nenabled > 0); 15817c478bd9Sstevel@tonic-gate 15827c478bd9Sstevel@tonic-gate for (; fbt != NULL; fbt = fbt->fbtp_next) 15837c478bd9Sstevel@tonic-gate *fbt->fbtp_patchpoint = fbt->fbtp_savedval; 15847c478bd9Sstevel@tonic-gate } 15857c478bd9Sstevel@tonic-gate 15867c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 15877c478bd9Sstevel@tonic-gate static void 15887c478bd9Sstevel@tonic-gate fbt_resume(void *arg, dtrace_id_t id, void *parg) 15897c478bd9Sstevel@tonic-gate { 15907c478bd9Sstevel@tonic-gate fbt_probe_t *fbt = parg; 15917c478bd9Sstevel@tonic-gate struct modctl *ctl = fbt->fbtp_ctl; 15927c478bd9Sstevel@tonic-gate 15937c478bd9Sstevel@tonic-gate if (!fbt->fbtp_primary && !ctl->mod_loaded) 15947c478bd9Sstevel@tonic-gate return; 15957c478bd9Sstevel@tonic-gate 15967c478bd9Sstevel@tonic-gate if (ctl->mod_loadcnt != fbt->fbtp_loadcnt) 15977c478bd9Sstevel@tonic-gate return; 15987c478bd9Sstevel@tonic-gate 15997c478bd9Sstevel@tonic-gate ASSERT(ctl->mod_nenabled > 0); 16007c478bd9Sstevel@tonic-gate 16017c478bd9Sstevel@tonic-gate for (; fbt != NULL; fbt = fbt->fbtp_next) 16027c478bd9Sstevel@tonic-gate *fbt->fbtp_patchpoint = fbt->fbtp_patchval; 16037c478bd9Sstevel@tonic-gate } 16047c478bd9Sstevel@tonic-gate 16057c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 16067c478bd9Sstevel@tonic-gate static void 16077c478bd9Sstevel@tonic-gate fbt_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc) 16087c478bd9Sstevel@tonic-gate { 16097c478bd9Sstevel@tonic-gate fbt_probe_t *fbt = parg; 16107c478bd9Sstevel@tonic-gate struct modctl *ctl = fbt->fbtp_ctl; 16117c478bd9Sstevel@tonic-gate struct module *mp = ctl->mod_mp; 16127c478bd9Sstevel@tonic-gate ctf_file_t *fp = NULL, *pfp; 16137c478bd9Sstevel@tonic-gate ctf_funcinfo_t f; 16147c478bd9Sstevel@tonic-gate int error; 16157c478bd9Sstevel@tonic-gate ctf_id_t argv[32], type; 16167c478bd9Sstevel@tonic-gate int argc = sizeof (argv) / sizeof (ctf_id_t); 16177c478bd9Sstevel@tonic-gate const char *parent; 16187c478bd9Sstevel@tonic-gate 16197c478bd9Sstevel@tonic-gate if (!ctl->mod_loaded || (ctl->mod_loadcnt != fbt->fbtp_loadcnt)) 16207c478bd9Sstevel@tonic-gate goto err; 16217c478bd9Sstevel@tonic-gate 16227c478bd9Sstevel@tonic-gate if (fbt->fbtp_return && desc->dtargd_ndx == 0) { 16237c478bd9Sstevel@tonic-gate (void) strcpy(desc->dtargd_native, "int"); 16247c478bd9Sstevel@tonic-gate return; 16257c478bd9Sstevel@tonic-gate } 16267c478bd9Sstevel@tonic-gate 16277c478bd9Sstevel@tonic-gate if ((fp = ctf_modopen(mp, &error)) == NULL) { 16287c478bd9Sstevel@tonic-gate /* 16297c478bd9Sstevel@tonic-gate * We have no CTF information for this module -- and therefore 16307c478bd9Sstevel@tonic-gate * no args[] information. 16317c478bd9Sstevel@tonic-gate */ 16327c478bd9Sstevel@tonic-gate goto err; 16337c478bd9Sstevel@tonic-gate } 16347c478bd9Sstevel@tonic-gate 16357c478bd9Sstevel@tonic-gate /* 16367c478bd9Sstevel@tonic-gate * If we have a parent container, we must manually import it. 16377c478bd9Sstevel@tonic-gate */ 16387c478bd9Sstevel@tonic-gate if ((parent = ctf_parent_name(fp)) != NULL) { 16397c478bd9Sstevel@tonic-gate struct modctl *mod; 16407c478bd9Sstevel@tonic-gate 16417c478bd9Sstevel@tonic-gate /* 16427c478bd9Sstevel@tonic-gate * We must iterate over all modules to find the module that 16437c478bd9Sstevel@tonic-gate * is our parent. 16447c478bd9Sstevel@tonic-gate */ 16457c478bd9Sstevel@tonic-gate for (mod = &modules; mod != NULL; mod = mod->mod_next) { 16467c478bd9Sstevel@tonic-gate if (strcmp(mod->mod_filename, parent) == 0) 16477c478bd9Sstevel@tonic-gate break; 16487c478bd9Sstevel@tonic-gate } 16497c478bd9Sstevel@tonic-gate 16507c478bd9Sstevel@tonic-gate if (mod == NULL) 16517c478bd9Sstevel@tonic-gate goto err; 16527c478bd9Sstevel@tonic-gate 16537c478bd9Sstevel@tonic-gate if ((pfp = ctf_modopen(mod->mod_mp, &error)) == NULL) 16547c478bd9Sstevel@tonic-gate goto err; 16557c478bd9Sstevel@tonic-gate 16567c478bd9Sstevel@tonic-gate if (ctf_import(fp, pfp) != 0) { 16577c478bd9Sstevel@tonic-gate ctf_close(pfp); 16587c478bd9Sstevel@tonic-gate goto err; 16597c478bd9Sstevel@tonic-gate } 16607c478bd9Sstevel@tonic-gate 16617c478bd9Sstevel@tonic-gate ctf_close(pfp); 16627c478bd9Sstevel@tonic-gate } 16637c478bd9Sstevel@tonic-gate 16647c478bd9Sstevel@tonic-gate if (ctf_func_info(fp, fbt->fbtp_symndx, &f) == CTF_ERR) 16657c478bd9Sstevel@tonic-gate goto err; 16667c478bd9Sstevel@tonic-gate 16677c478bd9Sstevel@tonic-gate if (fbt->fbtp_return) { 16687c478bd9Sstevel@tonic-gate if (desc->dtargd_ndx > 1) 16697c478bd9Sstevel@tonic-gate goto err; 16707c478bd9Sstevel@tonic-gate 16717c478bd9Sstevel@tonic-gate ASSERT(desc->dtargd_ndx == 1); 16727c478bd9Sstevel@tonic-gate type = f.ctc_return; 16737c478bd9Sstevel@tonic-gate } else { 16747c478bd9Sstevel@tonic-gate if (desc->dtargd_ndx + 1 > f.ctc_argc) 16757c478bd9Sstevel@tonic-gate goto err; 16767c478bd9Sstevel@tonic-gate 16777c478bd9Sstevel@tonic-gate if (ctf_func_args(fp, fbt->fbtp_symndx, argc, argv) == CTF_ERR) 16787c478bd9Sstevel@tonic-gate goto err; 16797c478bd9Sstevel@tonic-gate 16807c478bd9Sstevel@tonic-gate type = argv[desc->dtargd_ndx]; 16817c478bd9Sstevel@tonic-gate } 16827c478bd9Sstevel@tonic-gate 16837c478bd9Sstevel@tonic-gate if (ctf_type_name(fp, type, desc->dtargd_native, 16847c478bd9Sstevel@tonic-gate DTRACE_ARGTYPELEN) != NULL) { 16857c478bd9Sstevel@tonic-gate ctf_close(fp); 16867c478bd9Sstevel@tonic-gate return; 16877c478bd9Sstevel@tonic-gate } 16887c478bd9Sstevel@tonic-gate err: 16897c478bd9Sstevel@tonic-gate if (fp != NULL) 16907c478bd9Sstevel@tonic-gate ctf_close(fp); 16917c478bd9Sstevel@tonic-gate 16927c478bd9Sstevel@tonic-gate desc->dtargd_ndx = DTRACE_ARGNONE; 16937c478bd9Sstevel@tonic-gate } 16947c478bd9Sstevel@tonic-gate 16957c478bd9Sstevel@tonic-gate static dtrace_pattr_t fbt_attr = { 16967c478bd9Sstevel@tonic-gate { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA }, 16977c478bd9Sstevel@tonic-gate { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, 16987c478bd9Sstevel@tonic-gate { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, 16997c478bd9Sstevel@tonic-gate { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA }, 17007c478bd9Sstevel@tonic-gate { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA }, 17017c478bd9Sstevel@tonic-gate }; 17027c478bd9Sstevel@tonic-gate 17037c478bd9Sstevel@tonic-gate static dtrace_pops_t fbt_pops = { 17047c478bd9Sstevel@tonic-gate NULL, 17057c478bd9Sstevel@tonic-gate fbt_provide_module, 17067c478bd9Sstevel@tonic-gate fbt_enable, 17077c478bd9Sstevel@tonic-gate fbt_disable, 17087c478bd9Sstevel@tonic-gate fbt_suspend, 17097c478bd9Sstevel@tonic-gate fbt_resume, 17107c478bd9Sstevel@tonic-gate fbt_getargdesc, 17117c478bd9Sstevel@tonic-gate NULL, 17127c478bd9Sstevel@tonic-gate NULL, 17137c478bd9Sstevel@tonic-gate fbt_destroy 17147c478bd9Sstevel@tonic-gate }; 17157c478bd9Sstevel@tonic-gate 17167c478bd9Sstevel@tonic-gate static int 17177c478bd9Sstevel@tonic-gate fbt_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 17187c478bd9Sstevel@tonic-gate { 17197c478bd9Sstevel@tonic-gate switch (cmd) { 17207c478bd9Sstevel@tonic-gate case DDI_ATTACH: 17217c478bd9Sstevel@tonic-gate break; 17227c478bd9Sstevel@tonic-gate case DDI_RESUME: 17237c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 17247c478bd9Sstevel@tonic-gate default: 17257c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 17267c478bd9Sstevel@tonic-gate } 17277c478bd9Sstevel@tonic-gate 17287c478bd9Sstevel@tonic-gate if (ddi_create_minor_node(devi, "fbt", S_IFCHR, 0, 17297c478bd9Sstevel@tonic-gate DDI_PSEUDO, NULL) == DDI_FAILURE || 1730*ad4023c4Sdp dtrace_register("fbt", &fbt_attr, DTRACE_PRIV_KERNEL, NULL, 17317c478bd9Sstevel@tonic-gate &fbt_pops, NULL, &fbt_id) != 0) { 17327c478bd9Sstevel@tonic-gate ddi_remove_minor_node(devi, NULL); 17337c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 17347c478bd9Sstevel@tonic-gate } 17357c478bd9Sstevel@tonic-gate 17367c478bd9Sstevel@tonic-gate ddi_report_dev(devi); 17377c478bd9Sstevel@tonic-gate fbt_devi = devi; 17387c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 17397c478bd9Sstevel@tonic-gate } 17407c478bd9Sstevel@tonic-gate 17417c478bd9Sstevel@tonic-gate static int 17427c478bd9Sstevel@tonic-gate fbt_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 17437c478bd9Sstevel@tonic-gate { 17447c478bd9Sstevel@tonic-gate switch (cmd) { 17457c478bd9Sstevel@tonic-gate case DDI_DETACH: 17467c478bd9Sstevel@tonic-gate break; 17477c478bd9Sstevel@tonic-gate case DDI_SUSPEND: 17487c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 17497c478bd9Sstevel@tonic-gate default: 17507c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 17517c478bd9Sstevel@tonic-gate } 17527c478bd9Sstevel@tonic-gate 17537c478bd9Sstevel@tonic-gate if (dtrace_unregister(fbt_id) != 0) 17547c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 17557c478bd9Sstevel@tonic-gate 17567c478bd9Sstevel@tonic-gate ddi_remove_minor_node(devi, NULL); 17577c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 17587c478bd9Sstevel@tonic-gate } 17597c478bd9Sstevel@tonic-gate 17607c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 17617c478bd9Sstevel@tonic-gate static int 17627c478bd9Sstevel@tonic-gate fbt_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 17637c478bd9Sstevel@tonic-gate { 17647c478bd9Sstevel@tonic-gate int error; 17657c478bd9Sstevel@tonic-gate 17667c478bd9Sstevel@tonic-gate switch (infocmd) { 17677c478bd9Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO: 17687c478bd9Sstevel@tonic-gate *result = (void *)fbt_devi; 17697c478bd9Sstevel@tonic-gate error = DDI_SUCCESS; 17707c478bd9Sstevel@tonic-gate break; 17717c478bd9Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE: 17727c478bd9Sstevel@tonic-gate *result = (void *)0; 17737c478bd9Sstevel@tonic-gate error = DDI_SUCCESS; 17747c478bd9Sstevel@tonic-gate break; 17757c478bd9Sstevel@tonic-gate default: 17767c478bd9Sstevel@tonic-gate error = DDI_FAILURE; 17777c478bd9Sstevel@tonic-gate } 17787c478bd9Sstevel@tonic-gate return (error); 17797c478bd9Sstevel@tonic-gate } 17807c478bd9Sstevel@tonic-gate 17817c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 17827c478bd9Sstevel@tonic-gate static int 17837c478bd9Sstevel@tonic-gate fbt_open(dev_t *devp, int flag, int otyp, cred_t *cred_p) 17847c478bd9Sstevel@tonic-gate { 17857c478bd9Sstevel@tonic-gate return (0); 17867c478bd9Sstevel@tonic-gate } 17877c478bd9Sstevel@tonic-gate 17887c478bd9Sstevel@tonic-gate static struct cb_ops fbt_cb_ops = { 17897c478bd9Sstevel@tonic-gate fbt_open, /* open */ 17907c478bd9Sstevel@tonic-gate nodev, /* close */ 17917c478bd9Sstevel@tonic-gate nulldev, /* strategy */ 17927c478bd9Sstevel@tonic-gate nulldev, /* print */ 17937c478bd9Sstevel@tonic-gate nodev, /* dump */ 17947c478bd9Sstevel@tonic-gate nodev, /* read */ 17957c478bd9Sstevel@tonic-gate nodev, /* write */ 17967c478bd9Sstevel@tonic-gate nodev, /* ioctl */ 17977c478bd9Sstevel@tonic-gate nodev, /* devmap */ 17987c478bd9Sstevel@tonic-gate nodev, /* mmap */ 17997c478bd9Sstevel@tonic-gate nodev, /* segmap */ 18007c478bd9Sstevel@tonic-gate nochpoll, /* poll */ 18017c478bd9Sstevel@tonic-gate ddi_prop_op, /* cb_prop_op */ 18027c478bd9Sstevel@tonic-gate 0, /* streamtab */ 18037c478bd9Sstevel@tonic-gate D_NEW | D_MP /* Driver compatibility flag */ 18047c478bd9Sstevel@tonic-gate }; 18057c478bd9Sstevel@tonic-gate 18067c478bd9Sstevel@tonic-gate static struct dev_ops fbt_ops = { 18077c478bd9Sstevel@tonic-gate DEVO_REV, /* devo_rev */ 18087c478bd9Sstevel@tonic-gate 0, /* refcnt */ 18097c478bd9Sstevel@tonic-gate fbt_info, /* get_dev_info */ 18107c478bd9Sstevel@tonic-gate nulldev, /* identify */ 18117c478bd9Sstevel@tonic-gate nulldev, /* probe */ 18127c478bd9Sstevel@tonic-gate fbt_attach, /* attach */ 18137c478bd9Sstevel@tonic-gate fbt_detach, /* detach */ 18147c478bd9Sstevel@tonic-gate nodev, /* reset */ 18157c478bd9Sstevel@tonic-gate &fbt_cb_ops, /* driver operations */ 18167c478bd9Sstevel@tonic-gate NULL, /* bus operations */ 18177c478bd9Sstevel@tonic-gate nodev /* dev power */ 18187c478bd9Sstevel@tonic-gate }; 18197c478bd9Sstevel@tonic-gate 18207c478bd9Sstevel@tonic-gate /* 18217c478bd9Sstevel@tonic-gate * Module linkage information for the kernel. 18227c478bd9Sstevel@tonic-gate */ 18237c478bd9Sstevel@tonic-gate static struct modldrv modldrv = { 18247c478bd9Sstevel@tonic-gate &mod_driverops, /* module type (this is a pseudo driver) */ 18257c478bd9Sstevel@tonic-gate "Function Boundary Tracing", /* name of module */ 18267c478bd9Sstevel@tonic-gate &fbt_ops, /* driver ops */ 18277c478bd9Sstevel@tonic-gate }; 18287c478bd9Sstevel@tonic-gate 18297c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = { 18307c478bd9Sstevel@tonic-gate MODREV_1, 18317c478bd9Sstevel@tonic-gate (void *)&modldrv, 18327c478bd9Sstevel@tonic-gate NULL 18337c478bd9Sstevel@tonic-gate }; 18347c478bd9Sstevel@tonic-gate 18357c478bd9Sstevel@tonic-gate int 18367c478bd9Sstevel@tonic-gate _init(void) 18377c478bd9Sstevel@tonic-gate { 18387c478bd9Sstevel@tonic-gate return (mod_install(&modlinkage)); 18397c478bd9Sstevel@tonic-gate } 18407c478bd9Sstevel@tonic-gate 18417c478bd9Sstevel@tonic-gate int 18427c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop) 18437c478bd9Sstevel@tonic-gate { 18447c478bd9Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 18457c478bd9Sstevel@tonic-gate } 18467c478bd9Sstevel@tonic-gate 18477c478bd9Sstevel@tonic-gate int 18487c478bd9Sstevel@tonic-gate _fini(void) 18497c478bd9Sstevel@tonic-gate { 18507c478bd9Sstevel@tonic-gate return (mod_remove(&modlinkage)); 18517c478bd9Sstevel@tonic-gate } 1852