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