1210198Srpaulo/*
2210198Srpaulo * CDDL HEADER START
3210198Srpaulo *
4210198Srpaulo * The contents of this file are subject to the terms of the
5210198Srpaulo * Common Development and Distribution License, Version 1.0 only
6210198Srpaulo * (the "License").  You may not use this file except in compliance
7210198Srpaulo * with the License.
8210198Srpaulo *
9210198Srpaulo * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10210198Srpaulo * or http://www.opensolaris.org/os/licensing.
11210198Srpaulo * See the License for the specific language governing permissions
12210198Srpaulo * and limitations under the License.
13210198Srpaulo *
14210198Srpaulo * When distributing Covered Code, include this CDDL HEADER in each
15210198Srpaulo * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16210198Srpaulo * If applicable, add the following below this CDDL HEADER, with the
17210198Srpaulo * fields enclosed by brackets "[]" replaced with your own identifying
18210198Srpaulo * information: Portions Copyright [yyyy] [name of copyright owner]
19210198Srpaulo *
20210198Srpaulo * CDDL HEADER END
21210198Srpaulo */
22210198Srpaulo/*
23210198Srpaulo * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24210198Srpaulo * Use is subject to license terms.
25210198Srpaulo */
26210198Srpaulo
27210198Srpaulo#pragma ident	"%Z%%M%	%I%	%E% SMI"
28210198Srpaulo
29210198Srpaulo#include <stdlib.h>
30210198Srpaulo#include <assert.h>
31210198Srpaulo#include <errno.h>
32210198Srpaulo#include <string.h>
33210198Srpaulo#include <libgen.h>
34210198Srpaulo
35210198Srpaulo#include <dt_impl.h>
36210198Srpaulo#include <dt_pid.h>
37210198Srpaulo
38210198Srpaulo#define	OP(x)		((x) >> 30)
39210198Srpaulo#define	OP2(x)		(((x) >> 22) & 0x07)
40210198Srpaulo#define	COND(x)		(((x) >> 25) & 0x0f)
41210198Srpaulo#define	A(x)		(((x) >> 29) & 0x01)
42210198Srpaulo
43210198Srpaulo#define	OP_BRANCH	0
44210198Srpaulo
45210198Srpaulo#define	OP2_BPcc	0x1
46210198Srpaulo#define	OP2_Bicc	0x2
47210198Srpaulo#define	OP2_BPr		0x3
48210198Srpaulo#define	OP2_FBPfcc	0x5
49210198Srpaulo#define	OP2_FBfcc	0x6
50210198Srpaulo
51210198Srpaulo/*ARGSUSED*/
52210198Srpauloint
53210198Srpaulodt_pid_create_entry_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp,
54210198Srpaulo    fasttrap_probe_spec_t *ftp, const GElf_Sym *symp)
55210198Srpaulo{
56210198Srpaulo	ftp->ftps_type = DTFTP_ENTRY;
57210198Srpaulo	ftp->ftps_pc = (uintptr_t)symp->st_value;
58210198Srpaulo	ftp->ftps_size = (size_t)symp->st_size;
59210198Srpaulo	ftp->ftps_noffs = 1;
60210198Srpaulo	ftp->ftps_offs[0] = 0;
61210198Srpaulo
62210198Srpaulo	if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) {
63210198Srpaulo		dt_dprintf("fasttrap probe creation ioctl failed: %s\n",
64210198Srpaulo		    strerror(errno));
65210198Srpaulo		return (dt_set_errno(dtp, errno));
66210198Srpaulo	}
67210198Srpaulo
68210198Srpaulo	return (1);
69210198Srpaulo}
70210198Srpaulo
71210198Srpauloint
72210198Srpaulodt_pid_create_return_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp,
73210198Srpaulo    fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, uint64_t *stret)
74210198Srpaulo{
75210198Srpaulo
76210198Srpaulo	uint32_t *text;
77210198Srpaulo	int i;
78210198Srpaulo	int srdepth = 0;
79210198Srpaulo
80210198Srpaulo	if ((text = malloc(symp->st_size + 4)) == NULL) {
81210198Srpaulo		dt_dprintf("mr sparkle: malloc() failed\n");
82210198Srpaulo		return (DT_PROC_ERR);
83210198Srpaulo	}
84210198Srpaulo
85210198Srpaulo	if (Pread(P, text, symp->st_size, symp->st_value) != symp->st_size) {
86210198Srpaulo		dt_dprintf("mr sparkle: Pread() failed\n");
87210198Srpaulo		free(text);
88210198Srpaulo		return (DT_PROC_ERR);
89210198Srpaulo	}
90210198Srpaulo
91210198Srpaulo	/*
92210198Srpaulo	 * Leave a dummy instruction in the last slot to simplify edge
93210198Srpaulo	 * conditions.
94210198Srpaulo	 */
95210198Srpaulo	text[symp->st_size / 4] = 0;
96210198Srpaulo
97210198Srpaulo	ftp->ftps_type = DTFTP_RETURN;
98210198Srpaulo	ftp->ftps_pc = symp->st_value;
99210198Srpaulo	ftp->ftps_size = symp->st_size;
100210198Srpaulo	ftp->ftps_noffs = 0;
101210198Srpaulo
102210198Srpaulo	for (i = 0; i < symp->st_size / 4; i++) {
103210198Srpaulo		/*
104210198Srpaulo		 * If we encounter an existing tracepoint, query the
105210198Srpaulo		 * kernel to find out the instruction that was
106210198Srpaulo		 * replaced at this spot.
107210198Srpaulo		 */
108210198Srpaulo		while (text[i] == FASTTRAP_INSTR) {
109210198Srpaulo			fasttrap_instr_query_t instr;
110210198Srpaulo
111210198Srpaulo			instr.ftiq_pid = Pstatus(P)->pr_pid;
112210198Srpaulo			instr.ftiq_pc = symp->st_value + i * 4;
113210198Srpaulo
114210198Srpaulo			if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_GETINSTR,
115210198Srpaulo			    &instr) != 0) {
116210198Srpaulo
117210198Srpaulo				if (errno == ESRCH || errno == ENOENT) {
118210198Srpaulo					if (Pread(P, &text[i], 4,
119210198Srpaulo					    instr.ftiq_pc) != 4) {
120210198Srpaulo						dt_dprintf("mr sparkle: "
121210198Srpaulo						    "Pread() failed\n");
122210198Srpaulo						free(text);
123210198Srpaulo						return (DT_PROC_ERR);
124210198Srpaulo					}
125210198Srpaulo					continue;
126210198Srpaulo				}
127210198Srpaulo
128210198Srpaulo				free(text);
129210198Srpaulo				dt_dprintf("mr sparkle: getinstr query "
130210198Srpaulo				    "failed: %s\n", strerror(errno));
131210198Srpaulo				return (DT_PROC_ERR);
132210198Srpaulo			}
133210198Srpaulo
134210198Srpaulo			text[i] = instr.ftiq_instr;
135210198Srpaulo			break;
136210198Srpaulo		}
137210198Srpaulo
138210198Srpaulo		/* save */
139210198Srpaulo		if ((text[i] & 0xc1f80000) == 0x81e00000) {
140210198Srpaulo			srdepth++;
141210198Srpaulo			continue;
142210198Srpaulo		}
143210198Srpaulo
144210198Srpaulo		/* restore */
145210198Srpaulo		if ((text[i] & 0xc1f80000) == 0x81e80000) {
146210198Srpaulo			srdepth--;
147210198Srpaulo			continue;
148210198Srpaulo		}
149210198Srpaulo
150210198Srpaulo		if (srdepth > 0) {
151210198Srpaulo			/* ret */
152210198Srpaulo			if (text[i] == 0x81c7e008)
153210198Srpaulo				goto is_ret;
154210198Srpaulo
155210198Srpaulo			/* return */
156210198Srpaulo			if (text[i] == 0x81cfe008)
157210198Srpaulo				goto is_ret;
158210198Srpaulo
159210198Srpaulo			/* call or jmpl w/ restore in the slot */
160210198Srpaulo			if (((text[i] & 0xc0000000) == 0x40000000 ||
161210198Srpaulo			    (text[i] & 0xc1f80000) == 0x81c00000) &&
162210198Srpaulo			    (text[i + 1] & 0xc1f80000) == 0x81e80000)
163210198Srpaulo				goto is_ret;
164210198Srpaulo
165210198Srpaulo			/* call to one of the stret routines */
166210198Srpaulo			if ((text[i] & 0xc0000000) == 0x40000000) {
167210198Srpaulo				int32_t	disp = text[i] << 2;
168210198Srpaulo				uint64_t dest = ftp->ftps_pc + i * 4 + disp;
169210198Srpaulo
170210198Srpaulo				dt_dprintf("dest = %llx\n", (u_longlong_t)dest);
171210198Srpaulo
172210198Srpaulo				if (dest == stret[0] || dest == stret[1] ||
173210198Srpaulo				    dest == stret[2] || dest == stret[3])
174210198Srpaulo					goto is_ret;
175210198Srpaulo			}
176210198Srpaulo		} else {
177210198Srpaulo			/* external call */
178210198Srpaulo			if ((text[i] & 0xc0000000) == 0x40000000) {
179210198Srpaulo				int32_t dst = text[i] << 2;
180210198Srpaulo
181210198Srpaulo				dst += i * 4;
182210198Srpaulo
183210198Srpaulo				if ((uintptr_t)dst >= (uintptr_t)symp->st_size)
184210198Srpaulo					goto is_ret;
185210198Srpaulo			}
186210198Srpaulo
187210198Srpaulo			/* jmpl into %g0 -- this includes the retl pseudo op */
188210198Srpaulo			if ((text[i] & 0xfff80000) == 0x81c00000)
189210198Srpaulo				goto is_ret;
190210198Srpaulo
191210198Srpaulo			/* external branch -- possible return site */
192210198Srpaulo			if (OP(text[i]) == OP_BRANCH) {
193210198Srpaulo				int32_t dst;
194210198Srpaulo				int baa;
195210198Srpaulo
196210198Srpaulo				switch (OP2(text[i])) {
197210198Srpaulo				case OP2_BPcc:
198210198Srpaulo					dst = text[i] & 0x7ffff;
199210198Srpaulo					dst <<= 13;
200210198Srpaulo					dst >>= 11;
201210198Srpaulo
202210198Srpaulo					baa = COND(text[i]) == 8 && A(text[i]);
203210198Srpaulo					break;
204210198Srpaulo				case OP2_Bicc:
205210198Srpaulo					dst = text[i] & 0x3fffff;
206210198Srpaulo					dst <<= 10;
207210198Srpaulo					dst >>= 8;
208210198Srpaulo
209210198Srpaulo					baa = COND(text[i]) == 8 && A(text[i]);
210210198Srpaulo					break;
211210198Srpaulo				case OP2_BPr:
212210198Srpaulo					dst = (((text[i]) >> 6) & 0xc000) |
213210198Srpaulo					    ((text[i]) & 0x3fff);
214210198Srpaulo					dst <<= 16;
215210198Srpaulo					dst >>= 14;
216210198Srpaulo
217210198Srpaulo					baa = 0;
218210198Srpaulo					break;
219210198Srpaulo				case OP2_FBPfcc:
220210198Srpaulo					dst = text[i] & 0x7ffff;
221210198Srpaulo					dst <<= 13;
222210198Srpaulo					dst >>= 11;
223210198Srpaulo
224210198Srpaulo					baa = COND(text[i]) == 8 && A(text[i]);
225210198Srpaulo					break;
226210198Srpaulo				case OP2_FBfcc:
227210198Srpaulo					dst = text[i] & 0x3fffff;
228210198Srpaulo					dst <<= 10;
229210198Srpaulo					dst >>= 8;
230210198Srpaulo
231210198Srpaulo					baa = COND(text[i]) == 8 && A(text[i]);
232210198Srpaulo					break;
233210198Srpaulo				default:
234210198Srpaulo					continue;
235210198Srpaulo				}
236210198Srpaulo
237210198Srpaulo				dst += i * 4;
238210198Srpaulo
239210198Srpaulo				/*
240210198Srpaulo				 * Interpret branches outside of the function's
241210198Srpaulo				 * bounds as potential return sites. If the
242210198Srpaulo				 * branch is a ba,a don't skip the instruction
243210198Srpaulo				 * in the delay slot.
244210198Srpaulo				 */
245210198Srpaulo				if ((uintptr_t)dst >=
246210198Srpaulo				    (uintptr_t)symp->st_size) {
247210198Srpaulo					if (baa)
248210198Srpaulo						goto is_ret_baa;
249210198Srpaulo					else
250210198Srpaulo						goto is_ret;
251210198Srpaulo				}
252210198Srpaulo			}
253210198Srpaulo		}
254210198Srpaulo
255210198Srpaulo		continue;
256210198Srpaulois_ret:
257210198Srpaulo		i++;
258210198Srpaulois_ret_baa:
259210198Srpaulo		dt_dprintf("return at offset %x\n", i * 4);
260210198Srpaulo		ftp->ftps_offs[ftp->ftps_noffs++] = i * 4;
261210198Srpaulo	}
262210198Srpaulo
263210198Srpaulo	free(text);
264210198Srpaulo	if (ftp->ftps_noffs > 0) {
265210198Srpaulo		if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) {
266210198Srpaulo			dt_dprintf("fasttrap probe creation ioctl failed: %s\n",
267210198Srpaulo			    strerror(errno));
268210198Srpaulo			return (dt_set_errno(dtp, errno));
269210198Srpaulo		}
270210198Srpaulo	}
271210198Srpaulo
272210198Srpaulo
273210198Srpaulo	return (ftp->ftps_noffs);
274210198Srpaulo}
275210198Srpaulo
276210198Srpaulo/*ARGSUSED*/
277210198Srpauloint
278210198Srpaulodt_pid_create_offset_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp,
279210198Srpaulo    fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, ulong_t off)
280210198Srpaulo{
281210198Srpaulo	if (off & 0x3)
282210198Srpaulo		return (DT_PROC_ALIGN);
283210198Srpaulo
284210198Srpaulo	ftp->ftps_type = DTFTP_OFFSETS;
285210198Srpaulo	ftp->ftps_pc = (uintptr_t)symp->st_value;
286210198Srpaulo	ftp->ftps_size = (size_t)symp->st_size;
287210198Srpaulo	ftp->ftps_noffs = 1;
288210198Srpaulo	ftp->ftps_offs[0] = off;
289210198Srpaulo
290210198Srpaulo	if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) {
291210198Srpaulo		dt_dprintf("fasttrap probe creation ioctl failed: %s\n",
292210198Srpaulo		    strerror(errno));
293210198Srpaulo		return (dt_set_errno(dtp, errno));
294210198Srpaulo	}
295210198Srpaulo
296210198Srpaulo	return (1);
297210198Srpaulo}
298210198Srpaulo
299210198Srpaulo/*ARGSUSED*/
300210198Srpauloint
301210198Srpaulodt_pid_create_glob_offset_probes(struct ps_prochandle *P, dtrace_hdl_t *dtp,
302210198Srpaulo    fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, const char *pattern)
303210198Srpaulo{
304210198Srpaulo	ulong_t i;
305210198Srpaulo
306210198Srpaulo	ftp->ftps_type = DTFTP_OFFSETS;
307210198Srpaulo	ftp->ftps_pc = (uintptr_t)symp->st_value;
308210198Srpaulo	ftp->ftps_size = (size_t)symp->st_size;
309210198Srpaulo	ftp->ftps_noffs = 0;
310210198Srpaulo
311210198Srpaulo	/*
312210198Srpaulo	 * If we're matching against everything, just iterate through each
313210198Srpaulo	 * instruction in the function, otherwise look for matching offset
314210198Srpaulo	 * names by constructing the string and comparing it against the
315210198Srpaulo	 * pattern.
316210198Srpaulo	 */
317210198Srpaulo	if (strcmp("*", pattern) == 0) {
318210198Srpaulo		for (i = 0; i < symp->st_size; i += 4) {
319210198Srpaulo			ftp->ftps_offs[ftp->ftps_noffs++] = i;
320210198Srpaulo		}
321210198Srpaulo	} else {
322210198Srpaulo		char name[sizeof (i) * 2 + 1];
323210198Srpaulo
324210198Srpaulo		for (i = 0; i < symp->st_size; i += 4) {
325210198Srpaulo			(void) sprintf(name, "%lx", i);
326210198Srpaulo			if (gmatch(name, pattern))
327210198Srpaulo				ftp->ftps_offs[ftp->ftps_noffs++] = i;
328210198Srpaulo		}
329210198Srpaulo	}
330210198Srpaulo
331210198Srpaulo	if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) {
332210198Srpaulo		dt_dprintf("fasttrap probe creation ioctl failed: %s\n",
333210198Srpaulo		    strerror(errno));
334210198Srpaulo		return (dt_set_errno(dtp, errno));
335210198Srpaulo	}
336210198Srpaulo
337210198Srpaulo	return (ftp->ftps_noffs);
338210198Srpaulo}
339