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