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