xref: /freebsd/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c (revision 82283cad12a417abfb1469d899b2d7cfb1d38f77)
1c7570492SJustin Hibbits /*
2c7570492SJustin Hibbits  * CDDL HEADER START
3c7570492SJustin Hibbits  *
4c7570492SJustin Hibbits  * The contents of this file are subject to the terms of the
5c7570492SJustin Hibbits  * Common Development and Distribution License, Version 1.0 only
6c7570492SJustin Hibbits  * (the "License").  You may not use this file except in compliance
7c7570492SJustin Hibbits  * with the License.
8c7570492SJustin Hibbits  *
9c7570492SJustin Hibbits  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10c7570492SJustin Hibbits  * or http://www.opensolaris.org/os/licensing.
11c7570492SJustin Hibbits  * See the License for the specific language governing permissions
12c7570492SJustin Hibbits  * and limitations under the License.
13c7570492SJustin Hibbits  *
14c7570492SJustin Hibbits  * When distributing Covered Code, include this CDDL HEADER in each
15c7570492SJustin Hibbits  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16c7570492SJustin Hibbits  * If applicable, add the following below this CDDL HEADER, with the
17c7570492SJustin Hibbits  * fields enclosed by brackets "[]" replaced with your own identifying
18c7570492SJustin Hibbits  * information: Portions Copyright [yyyy] [name of copyright owner]
19c7570492SJustin Hibbits  *
20c7570492SJustin Hibbits  * CDDL HEADER END
21c7570492SJustin Hibbits  *
227e7a9efdSJustin Hibbits  * Portions Copyright 2012,2013 Justin Hibbits <jhibbits@freebsd.org>
23c7570492SJustin Hibbits  */
24c7570492SJustin Hibbits /*
25c7570492SJustin Hibbits  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
26c7570492SJustin Hibbits  * Use is subject to license terms.
27c7570492SJustin Hibbits  */
28c7570492SJustin Hibbits #include <sys/cdefs.h>
29c7570492SJustin Hibbits 
30c7570492SJustin Hibbits #include <sys/param.h>
31c7570492SJustin Hibbits #include <sys/systm.h>
32*82283cadSMark Johnston #include <sys/dtrace_impl.h>
33c7570492SJustin Hibbits #include <sys/kernel.h>
34c7570492SJustin Hibbits #include <sys/stack.h>
35c7570492SJustin Hibbits #include <sys/sysent.h>
36c7570492SJustin Hibbits #include <sys/pcpu.h>
37c7570492SJustin Hibbits 
38c7570492SJustin Hibbits #include <machine/frame.h>
39c7570492SJustin Hibbits #include <machine/md_var.h>
40675cad71SJustin Hibbits #include <machine/psl.h>
41*82283cadSMark Johnston #include <machine/reg.h>
42c7570492SJustin Hibbits #include <machine/stack.h>
43c7570492SJustin Hibbits 
44c7570492SJustin Hibbits #include <vm/vm.h>
45c7570492SJustin Hibbits #include <vm/vm_param.h>
46c7570492SJustin Hibbits #include <vm/pmap.h>
47c7570492SJustin Hibbits 
48c7570492SJustin Hibbits #include "regset.h"
49c7570492SJustin Hibbits 
50c7570492SJustin Hibbits /* Offset to the LR Save word (ppc32) */
51c7570492SJustin Hibbits #define RETURN_OFFSET	4
52594ce9adSJustin Hibbits /* Offset to LR Save word (ppc64).  CR Save area sits between back chain and LR */
53594ce9adSJustin Hibbits #define RETURN_OFFSET64	16
54c7570492SJustin Hibbits 
55e40a5cd3SJustin Hibbits #ifdef __powerpc64__
56e40a5cd3SJustin Hibbits #define OFFSET 4 /* Account for the TOC reload slot */
57675cad71SJustin Hibbits #define	FRAME_OFFSET	48
58e40a5cd3SJustin Hibbits #else
59e40a5cd3SJustin Hibbits #define OFFSET 0
60675cad71SJustin Hibbits #define	FRAME_OFFSET	8
61e40a5cd3SJustin Hibbits #endif
62e40a5cd3SJustin Hibbits 
63d69b94baSJustin Hibbits #define INKERNEL(x)	(((x) <= VM_MAX_KERNEL_ADDRESS && \
64d69b94baSJustin Hibbits 		(x) >= VM_MIN_KERNEL_ADDRESS) || \
65d69b94baSJustin Hibbits 		(PMAP_HAS_DMAP && (x) >= DMAP_BASE_ADDRESS && \
66d69b94baSJustin Hibbits 		 (x) <= DMAP_MAX_ADDRESS))
67c7570492SJustin Hibbits 
68e40a5cd3SJustin Hibbits static __inline int
69675cad71SJustin Hibbits dtrace_sp_inkernel(uintptr_t sp)
70e40a5cd3SJustin Hibbits {
71675cad71SJustin Hibbits 	struct trapframe *frame;
72e40a5cd3SJustin Hibbits 	vm_offset_t callpc;
73e40a5cd3SJustin Hibbits 
74d69b94baSJustin Hibbits 	/* Not within the kernel, or not aligned. */
75d69b94baSJustin Hibbits 	if (!INKERNEL(sp) || (sp & 0xf) != 0)
76d69b94baSJustin Hibbits 		return (0);
77e40a5cd3SJustin Hibbits #ifdef __powerpc64__
78e40a5cd3SJustin Hibbits 	callpc = *(vm_offset_t *)(sp + RETURN_OFFSET64);
79e40a5cd3SJustin Hibbits #else
80e40a5cd3SJustin Hibbits 	callpc = *(vm_offset_t *)(sp + RETURN_OFFSET);
81e40a5cd3SJustin Hibbits #endif
82e40a5cd3SJustin Hibbits 	if ((callpc & 3) || (callpc < 0x100))
83e40a5cd3SJustin Hibbits 		return (0);
84e40a5cd3SJustin Hibbits 
85e40a5cd3SJustin Hibbits 	/*
86e40a5cd3SJustin Hibbits 	 * trapexit() and asttrapexit() are sentinels
87e40a5cd3SJustin Hibbits 	 * for kernel stack tracing.
88e40a5cd3SJustin Hibbits 	 */
89675cad71SJustin Hibbits 	if (callpc + OFFSET == (vm_offset_t) &trapexit ||
90675cad71SJustin Hibbits 	    callpc + OFFSET == (vm_offset_t) &asttrapexit) {
91675cad71SJustin Hibbits 		frame = (struct trapframe *)(sp + FRAME_OFFSET);
92675cad71SJustin Hibbits 
93675cad71SJustin Hibbits 		return ((frame->srr1 & PSL_PR) == 0);
94675cad71SJustin Hibbits 	}
95e40a5cd3SJustin Hibbits 
96e40a5cd3SJustin Hibbits 	return (1);
97e40a5cd3SJustin Hibbits }
98e40a5cd3SJustin Hibbits 
99e9aae349SJustin Hibbits static __inline void
1003e1155adSJustin Hibbits dtrace_next_sp_pc(uintptr_t sp, uintptr_t *nsp, uintptr_t *pc, uintptr_t *lr)
101e40a5cd3SJustin Hibbits {
102e40a5cd3SJustin Hibbits 	vm_offset_t callpc;
103675cad71SJustin Hibbits 	struct trapframe *frame;
104e40a5cd3SJustin Hibbits 
1053e1155adSJustin Hibbits 	if (lr != 0 && *lr != 0)
1063e1155adSJustin Hibbits 		callpc = *lr;
1073e1155adSJustin Hibbits 	else
108e40a5cd3SJustin Hibbits #ifdef __powerpc64__
109e40a5cd3SJustin Hibbits 		callpc = *(vm_offset_t *)(sp + RETURN_OFFSET64);
110e40a5cd3SJustin Hibbits #else
111e40a5cd3SJustin Hibbits 		callpc = *(vm_offset_t *)(sp + RETURN_OFFSET);
112e40a5cd3SJustin Hibbits #endif
113e40a5cd3SJustin Hibbits 
114e40a5cd3SJustin Hibbits 	/*
115e40a5cd3SJustin Hibbits 	 * trapexit() and asttrapexit() are sentinels
116e40a5cd3SJustin Hibbits 	 * for kernel stack tracing.
117e40a5cd3SJustin Hibbits 	 */
118e40a5cd3SJustin Hibbits 	if ((callpc + OFFSET == (vm_offset_t) &trapexit ||
119675cad71SJustin Hibbits 	    callpc + OFFSET == (vm_offset_t) &asttrapexit)) {
120e40a5cd3SJustin Hibbits 		/* Access the trap frame */
121675cad71SJustin Hibbits 		frame = (struct trapframe *)(sp + FRAME_OFFSET);
122e9aae349SJustin Hibbits 
123e9aae349SJustin Hibbits 		if (nsp != NULL)
124e9aae349SJustin Hibbits 			*nsp = frame->fixreg[1];
125e9aae349SJustin Hibbits 		if (pc != NULL)
126e9aae349SJustin Hibbits 			*pc = frame->srr0;
1273e1155adSJustin Hibbits 		if (lr != NULL)
1283e1155adSJustin Hibbits 			*lr = frame->lr;
129d69b94baSJustin Hibbits 		return;
130675cad71SJustin Hibbits 	}
131e40a5cd3SJustin Hibbits 
132e9aae349SJustin Hibbits 	if (nsp != NULL)
133e9aae349SJustin Hibbits 		*nsp = *(uintptr_t *)sp;
134e9aae349SJustin Hibbits 	if (pc != NULL)
135e9aae349SJustin Hibbits 		*pc = callpc;
1363e1155adSJustin Hibbits 	/* lr is only valid for trap frames */
1373e1155adSJustin Hibbits 	if (lr != NULL)
1383e1155adSJustin Hibbits 		*lr = 0;
139e40a5cd3SJustin Hibbits }
140e40a5cd3SJustin Hibbits 
141c7570492SJustin Hibbits void
142c7570492SJustin Hibbits dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
143c7570492SJustin Hibbits     uint32_t *intrpc)
144c7570492SJustin Hibbits {
145c7570492SJustin Hibbits 	int depth = 0;
1463e1155adSJustin Hibbits 	uintptr_t osp, sp, lr = 0;
147c7570492SJustin Hibbits 	vm_offset_t callpc;
148c7570492SJustin Hibbits 	pc_t caller = (pc_t) solaris_cpu[curcpu].cpu_dtrace_caller;
149c7570492SJustin Hibbits 
150e40a5cd3SJustin Hibbits 	osp = PAGE_SIZE;
151c7570492SJustin Hibbits 	if (intrpc != 0)
152c7570492SJustin Hibbits 		pcstack[depth++] = (pc_t) intrpc;
153c7570492SJustin Hibbits 
154c7570492SJustin Hibbits 	aframes++;
155c7570492SJustin Hibbits 
156d69b94baSJustin Hibbits 	sp = (uintptr_t)__builtin_frame_address(0);
157c7570492SJustin Hibbits 
158c7570492SJustin Hibbits 	while (depth < pcstack_limit) {
159e40a5cd3SJustin Hibbits 		if (sp <= osp)
160c7570492SJustin Hibbits 			break;
161c7570492SJustin Hibbits 
162675cad71SJustin Hibbits 		if (!dtrace_sp_inkernel(sp))
163c7570492SJustin Hibbits 			break;
164e9aae349SJustin Hibbits 		osp = sp;
1653e1155adSJustin Hibbits 		dtrace_next_sp_pc(osp, &sp, &callpc, &lr);
166c7570492SJustin Hibbits 
167c7570492SJustin Hibbits 		if (aframes > 0) {
168c7570492SJustin Hibbits 			aframes--;
169c7570492SJustin Hibbits 			if ((aframes == 0) && (caller != 0)) {
170c7570492SJustin Hibbits 				pcstack[depth++] = caller;
171c7570492SJustin Hibbits 			}
172c7570492SJustin Hibbits 		}
173c7570492SJustin Hibbits 		else {
174c7570492SJustin Hibbits 			pcstack[depth++] = callpc;
175c7570492SJustin Hibbits 		}
176c7570492SJustin Hibbits 	}
177c7570492SJustin Hibbits 
178c7570492SJustin Hibbits 	for (; depth < pcstack_limit; depth++) {
179c7570492SJustin Hibbits 		pcstack[depth] = 0;
180c7570492SJustin Hibbits 	}
181c7570492SJustin Hibbits }
182c7570492SJustin Hibbits 
183c7570492SJustin Hibbits static int
184c7570492SJustin Hibbits dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc,
185c7570492SJustin Hibbits     uintptr_t sp)
186c7570492SJustin Hibbits {
187c7570492SJustin Hibbits 	proc_t *p = curproc;
188c7570492SJustin Hibbits 	int ret = 0;
189c7570492SJustin Hibbits 
190c7570492SJustin Hibbits 	ASSERT(pcstack == NULL || pcstack_limit > 0);
191c7570492SJustin Hibbits 
192c7570492SJustin Hibbits 	while (pc != 0) {
193c7570492SJustin Hibbits 		ret++;
194c7570492SJustin Hibbits 		if (pcstack != NULL) {
195c7570492SJustin Hibbits 			*pcstack++ = (uint64_t)pc;
196c7570492SJustin Hibbits 			pcstack_limit--;
197c7570492SJustin Hibbits 			if (pcstack_limit <= 0)
198c7570492SJustin Hibbits 				break;
199c7570492SJustin Hibbits 		}
200c7570492SJustin Hibbits 
201c7570492SJustin Hibbits 		if (sp == 0)
202c7570492SJustin Hibbits 			break;
203c7570492SJustin Hibbits 
204c7570492SJustin Hibbits 		if (SV_PROC_FLAG(p, SV_ILP32)) {
205c7570492SJustin Hibbits 			pc = dtrace_fuword32((void *)(sp + RETURN_OFFSET));
206c7570492SJustin Hibbits 			sp = dtrace_fuword32((void *)sp);
207c7570492SJustin Hibbits 		}
208c7570492SJustin Hibbits 		else {
209c7570492SJustin Hibbits 			pc = dtrace_fuword64((void *)(sp + RETURN_OFFSET64));
210c7570492SJustin Hibbits 			sp = dtrace_fuword64((void *)sp);
211c7570492SJustin Hibbits 		}
212c7570492SJustin Hibbits 	}
213c7570492SJustin Hibbits 
214c7570492SJustin Hibbits 	return (ret);
215c7570492SJustin Hibbits }
216c7570492SJustin Hibbits 
217c7570492SJustin Hibbits void
218c7570492SJustin Hibbits dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit)
219c7570492SJustin Hibbits {
220c7570492SJustin Hibbits 	proc_t *p = curproc;
221c7570492SJustin Hibbits 	struct trapframe *tf;
222c7570492SJustin Hibbits 	uintptr_t pc, sp;
223c7570492SJustin Hibbits 	volatile uint16_t *flags =
224c7570492SJustin Hibbits 	    (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
225c7570492SJustin Hibbits 	int n;
226c7570492SJustin Hibbits 
227c7570492SJustin Hibbits 	if (*flags & CPU_DTRACE_FAULT)
228c7570492SJustin Hibbits 		return;
229c7570492SJustin Hibbits 
230c7570492SJustin Hibbits 	if (pcstack_limit <= 0)
231c7570492SJustin Hibbits 		return;
232c7570492SJustin Hibbits 
233c7570492SJustin Hibbits 	/*
234c7570492SJustin Hibbits 	 * If there's no user context we still need to zero the stack.
235c7570492SJustin Hibbits 	 */
236c7570492SJustin Hibbits 	if (p == NULL || (tf = curthread->td_frame) == NULL)
237c7570492SJustin Hibbits 		goto zero;
238c7570492SJustin Hibbits 
239c7570492SJustin Hibbits 	*pcstack++ = (uint64_t)p->p_pid;
240c7570492SJustin Hibbits 	pcstack_limit--;
241c7570492SJustin Hibbits 
242c7570492SJustin Hibbits 	if (pcstack_limit <= 0)
243c7570492SJustin Hibbits 		return;
244c7570492SJustin Hibbits 
245c7570492SJustin Hibbits 	pc = tf->srr0;
246c7570492SJustin Hibbits 	sp = tf->fixreg[1];
247c7570492SJustin Hibbits 
248c7570492SJustin Hibbits 	if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
249c7570492SJustin Hibbits 		/*
250c7570492SJustin Hibbits 		 * In an entry probe.  The frame pointer has not yet been
251c7570492SJustin Hibbits 		 * pushed (that happens in the function prologue).  The
252c7570492SJustin Hibbits 		 * best approach is to add the current pc as a missing top
253c7570492SJustin Hibbits 		 * of stack and back the pc up to the caller, which is stored
254c7570492SJustin Hibbits 		 * at the current stack pointer address since the call
255c7570492SJustin Hibbits 		 * instruction puts it there right before the branch.
256c7570492SJustin Hibbits 		 */
257c7570492SJustin Hibbits 
258c7570492SJustin Hibbits 		*pcstack++ = (uint64_t)pc;
259c7570492SJustin Hibbits 		pcstack_limit--;
260c7570492SJustin Hibbits 		if (pcstack_limit <= 0)
261c7570492SJustin Hibbits 			return;
262c7570492SJustin Hibbits 
263c7570492SJustin Hibbits 		pc = tf->lr;
264c7570492SJustin Hibbits 	}
265c7570492SJustin Hibbits 
266c7570492SJustin Hibbits 	n = dtrace_getustack_common(pcstack, pcstack_limit, pc, sp);
267c7570492SJustin Hibbits 	ASSERT(n >= 0);
268c7570492SJustin Hibbits 	ASSERT(n <= pcstack_limit);
269c7570492SJustin Hibbits 
270c7570492SJustin Hibbits 	pcstack += n;
271c7570492SJustin Hibbits 	pcstack_limit -= n;
272c7570492SJustin Hibbits 
273c7570492SJustin Hibbits zero:
274c7570492SJustin Hibbits 	while (pcstack_limit-- > 0)
275c7570492SJustin Hibbits 		*pcstack++ = 0;
276c7570492SJustin Hibbits }
277c7570492SJustin Hibbits 
278c7570492SJustin Hibbits int
279c7570492SJustin Hibbits dtrace_getustackdepth(void)
280c7570492SJustin Hibbits {
281c7570492SJustin Hibbits 	proc_t *p = curproc;
282c7570492SJustin Hibbits 	struct trapframe *tf;
283c7570492SJustin Hibbits 	uintptr_t pc, sp;
284c7570492SJustin Hibbits 	int n = 0;
285c7570492SJustin Hibbits 
286c7570492SJustin Hibbits 	if (p == NULL || (tf = curthread->td_frame) == NULL)
287c7570492SJustin Hibbits 		return (0);
288c7570492SJustin Hibbits 
289c7570492SJustin Hibbits 	if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_FAULT))
290c7570492SJustin Hibbits 		return (-1);
291c7570492SJustin Hibbits 
292c7570492SJustin Hibbits 	pc = tf->srr0;
293c7570492SJustin Hibbits 	sp = tf->fixreg[1];
294c7570492SJustin Hibbits 
295c7570492SJustin Hibbits 	if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
296c7570492SJustin Hibbits 		/*
297c7570492SJustin Hibbits 		 * In an entry probe.  The frame pointer has not yet been
298c7570492SJustin Hibbits 		 * pushed (that happens in the function prologue).  The
299c7570492SJustin Hibbits 		 * best approach is to add the current pc as a missing top
300c7570492SJustin Hibbits 		 * of stack and back the pc up to the caller, which is stored
301c7570492SJustin Hibbits 		 * at the current stack pointer address since the call
302c7570492SJustin Hibbits 		 * instruction puts it there right before the branch.
303c7570492SJustin Hibbits 		 */
304c7570492SJustin Hibbits 
305c7570492SJustin Hibbits 		if (SV_PROC_FLAG(p, SV_ILP32)) {
306c7570492SJustin Hibbits 			pc = dtrace_fuword32((void *) sp);
307c7570492SJustin Hibbits 		}
308c7570492SJustin Hibbits 		else
309c7570492SJustin Hibbits 			pc = dtrace_fuword64((void *) sp);
310c7570492SJustin Hibbits 		n++;
311c7570492SJustin Hibbits 	}
312c7570492SJustin Hibbits 
313c7570492SJustin Hibbits 	n += dtrace_getustack_common(NULL, 0, pc, sp);
314c7570492SJustin Hibbits 
315c7570492SJustin Hibbits 	return (n);
316c7570492SJustin Hibbits }
317c7570492SJustin Hibbits 
318c7570492SJustin Hibbits void
319c7570492SJustin Hibbits dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit)
320c7570492SJustin Hibbits {
321c7570492SJustin Hibbits 	proc_t *p = curproc;
322c7570492SJustin Hibbits 	struct trapframe *tf;
323c7570492SJustin Hibbits 	uintptr_t pc, sp;
324c7570492SJustin Hibbits 	volatile uint16_t *flags =
325c7570492SJustin Hibbits 	    (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
326c7570492SJustin Hibbits #ifdef notyet	/* XXX signal stack */
327c7570492SJustin Hibbits 	uintptr_t oldcontext;
328c7570492SJustin Hibbits 	size_t s1, s2;
329c7570492SJustin Hibbits #endif
330c7570492SJustin Hibbits 
331c7570492SJustin Hibbits 	if (*flags & CPU_DTRACE_FAULT)
332c7570492SJustin Hibbits 		return;
333c7570492SJustin Hibbits 
334c7570492SJustin Hibbits 	if (pcstack_limit <= 0)
335c7570492SJustin Hibbits 		return;
336c7570492SJustin Hibbits 
337c7570492SJustin Hibbits 	/*
338c7570492SJustin Hibbits 	 * If there's no user context we still need to zero the stack.
339c7570492SJustin Hibbits 	 */
340c7570492SJustin Hibbits 	if (p == NULL || (tf = curthread->td_frame) == NULL)
341c7570492SJustin Hibbits 		goto zero;
342c7570492SJustin Hibbits 
343c7570492SJustin Hibbits 	*pcstack++ = (uint64_t)p->p_pid;
344c7570492SJustin Hibbits 	pcstack_limit--;
345c7570492SJustin Hibbits 
346c7570492SJustin Hibbits 	if (pcstack_limit <= 0)
347c7570492SJustin Hibbits 		return;
348c7570492SJustin Hibbits 
349c7570492SJustin Hibbits 	pc = tf->srr0;
350c7570492SJustin Hibbits 	sp = tf->fixreg[1];
351c7570492SJustin Hibbits 
352c7570492SJustin Hibbits #ifdef notyet /* XXX signal stack */
353c7570492SJustin Hibbits 	oldcontext = lwp->lwp_oldcontext;
354c7570492SJustin Hibbits 	s1 = sizeof (struct xframe) + 2 * sizeof (long);
355c7570492SJustin Hibbits 	s2 = s1 + sizeof (siginfo_t);
356c7570492SJustin Hibbits #endif
357c7570492SJustin Hibbits 
358c7570492SJustin Hibbits 	if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
359c7570492SJustin Hibbits 		*pcstack++ = (uint64_t)pc;
360c7570492SJustin Hibbits 		*fpstack++ = 0;
361c7570492SJustin Hibbits 		pcstack_limit--;
362c7570492SJustin Hibbits 		if (pcstack_limit <= 0)
363c7570492SJustin Hibbits 			return;
364c7570492SJustin Hibbits 
365c7570492SJustin Hibbits 		if (SV_PROC_FLAG(p, SV_ILP32)) {
366c7570492SJustin Hibbits 			pc = dtrace_fuword32((void *)sp);
367c7570492SJustin Hibbits 		}
368c7570492SJustin Hibbits 		else {
369c7570492SJustin Hibbits 			pc = dtrace_fuword64((void *)sp);
370c7570492SJustin Hibbits 		}
371c7570492SJustin Hibbits 	}
372c7570492SJustin Hibbits 
373c7570492SJustin Hibbits 	while (pc != 0) {
374c7570492SJustin Hibbits 		*pcstack++ = (uint64_t)pc;
375c7570492SJustin Hibbits 		*fpstack++ = sp;
376c7570492SJustin Hibbits 		pcstack_limit--;
377c7570492SJustin Hibbits 		if (pcstack_limit <= 0)
378c7570492SJustin Hibbits 			break;
379c7570492SJustin Hibbits 
380c7570492SJustin Hibbits 		if (sp == 0)
381c7570492SJustin Hibbits 			break;
382c7570492SJustin Hibbits 
383c7570492SJustin Hibbits #ifdef notyet /* XXX signal stack */
384c7570492SJustin Hibbits 		if (oldcontext == sp + s1 || oldcontext == sp + s2) {
385c7570492SJustin Hibbits 			ucontext_t *ucp = (ucontext_t *)oldcontext;
386c7570492SJustin Hibbits 			greg_t *gregs = ucp->uc_mcontext.gregs;
387c7570492SJustin Hibbits 
388c7570492SJustin Hibbits 			sp = dtrace_fulword(&gregs[REG_FP]);
389c7570492SJustin Hibbits 			pc = dtrace_fulword(&gregs[REG_PC]);
390c7570492SJustin Hibbits 
391c7570492SJustin Hibbits 			oldcontext = dtrace_fulword(&ucp->uc_link);
392c7570492SJustin Hibbits 		} else
393c7570492SJustin Hibbits #endif /* XXX */
394c7570492SJustin Hibbits 		{
395c7570492SJustin Hibbits 			if (SV_PROC_FLAG(p, SV_ILP32)) {
396c7570492SJustin Hibbits 				pc = dtrace_fuword32((void *)(sp + RETURN_OFFSET));
397c7570492SJustin Hibbits 				sp = dtrace_fuword32((void *)sp);
398c7570492SJustin Hibbits 			}
399c7570492SJustin Hibbits 			else {
400c7570492SJustin Hibbits 				pc = dtrace_fuword64((void *)(sp + RETURN_OFFSET64));
401c7570492SJustin Hibbits 				sp = dtrace_fuword64((void *)sp);
402c7570492SJustin Hibbits 			}
403c7570492SJustin Hibbits 		}
404c7570492SJustin Hibbits 
405c7570492SJustin Hibbits 		/*
406c7570492SJustin Hibbits 		 * This is totally bogus:  if we faulted, we're going to clear
407c7570492SJustin Hibbits 		 * the fault and break.  This is to deal with the apparently
408c7570492SJustin Hibbits 		 * broken Java stacks on x86.
409c7570492SJustin Hibbits 		 */
410c7570492SJustin Hibbits 		if (*flags & CPU_DTRACE_FAULT) {
411c7570492SJustin Hibbits 			*flags &= ~CPU_DTRACE_FAULT;
412c7570492SJustin Hibbits 			break;
413c7570492SJustin Hibbits 		}
414c7570492SJustin Hibbits 	}
415c7570492SJustin Hibbits 
416c7570492SJustin Hibbits zero:
417c7570492SJustin Hibbits 	while (pcstack_limit-- > 0)
418c7570492SJustin Hibbits 		*pcstack++ = 0;
419c7570492SJustin Hibbits }
420c7570492SJustin Hibbits 
421c7570492SJustin Hibbits /*ARGSUSED*/
422c7570492SJustin Hibbits uint64_t
423c7570492SJustin Hibbits dtrace_getarg(int arg, int aframes)
424c7570492SJustin Hibbits {
425f0bd82a1SJustin Hibbits 	uintptr_t val;
426d69b94baSJustin Hibbits 	uintptr_t *fp = (uintptr_t *)__builtin_frame_address(0);
427f0bd82a1SJustin Hibbits 	uintptr_t *stack;
428f0bd82a1SJustin Hibbits 	int i;
429f0bd82a1SJustin Hibbits 
430f0bd82a1SJustin Hibbits 	/*
431f0bd82a1SJustin Hibbits 	 * A total of 8 arguments are passed via registers; any argument with
432f0bd82a1SJustin Hibbits 	 * index of 7 or lower is therefore in a register.
433f0bd82a1SJustin Hibbits 	 */
434f0bd82a1SJustin Hibbits 	int inreg = 7;
435f0bd82a1SJustin Hibbits 
436f0bd82a1SJustin Hibbits 	for (i = 1; i <= aframes; i++) {
437f0bd82a1SJustin Hibbits 		fp = (uintptr_t *)*fp;
438f0bd82a1SJustin Hibbits 
439f0bd82a1SJustin Hibbits 		/*
440d69b94baSJustin Hibbits 		 * On ppc32 trapexit() is the immediately following label.  On
441d69b94baSJustin Hibbits 		 * ppc64 AIM trapexit() follows a nop.
442f0bd82a1SJustin Hibbits 		 */
443e40a5cd3SJustin Hibbits #ifdef __powerpc64__
444e40a5cd3SJustin Hibbits 		if ((long)(fp[2]) + 4 == (long)trapexit) {
445e40a5cd3SJustin Hibbits #else
446e40a5cd3SJustin Hibbits 		if ((long)(fp[1]) == (long)trapexit) {
447e40a5cd3SJustin Hibbits #endif
448f0bd82a1SJustin Hibbits 			/*
449f0bd82a1SJustin Hibbits 			 * In the case of powerpc, we will use the pointer to the regs
450f0bd82a1SJustin Hibbits 			 * structure that was pushed when we took the trap.  To get this
451f0bd82a1SJustin Hibbits 			 * structure, we must increment beyond the frame structure.  If the
452f0bd82a1SJustin Hibbits 			 * argument that we're seeking is passed on the stack, we'll pull
453f0bd82a1SJustin Hibbits 			 * the true stack pointer out of the saved registers and decrement
454f0bd82a1SJustin Hibbits 			 * our argument by the number of arguments passed in registers; if
455f0bd82a1SJustin Hibbits 			 * the argument we're seeking is passed in regsiters, we can just
456f0bd82a1SJustin Hibbits 			 * load it directly.
457f0bd82a1SJustin Hibbits 			 */
458f0bd82a1SJustin Hibbits #ifdef __powerpc64__
459f0bd82a1SJustin Hibbits 			struct reg *rp = (struct reg *)((uintptr_t)fp[0] + 48);
460f0bd82a1SJustin Hibbits #else
461f0bd82a1SJustin Hibbits 			struct reg *rp = (struct reg *)((uintptr_t)fp[0] + 8);
462f0bd82a1SJustin Hibbits #endif
463f0bd82a1SJustin Hibbits 
464f0bd82a1SJustin Hibbits 			if (arg <= inreg) {
465f0bd82a1SJustin Hibbits 				stack = &rp->fixreg[3];
466f0bd82a1SJustin Hibbits 			} else {
467f0bd82a1SJustin Hibbits 				stack = (uintptr_t *)(rp->fixreg[1]);
468f0bd82a1SJustin Hibbits 				arg -= inreg;
469f0bd82a1SJustin Hibbits 			}
470f0bd82a1SJustin Hibbits 			goto load;
471f0bd82a1SJustin Hibbits 		}
472f0bd82a1SJustin Hibbits 
473f0bd82a1SJustin Hibbits 	}
474f0bd82a1SJustin Hibbits 
475f0bd82a1SJustin Hibbits 	/*
476f0bd82a1SJustin Hibbits 	 * We know that we did not come through a trap to get into
477f0bd82a1SJustin Hibbits 	 * dtrace_probe() -- the provider simply called dtrace_probe()
478f0bd82a1SJustin Hibbits 	 * directly.  As this is the case, we need to shift the argument
479f0bd82a1SJustin Hibbits 	 * that we're looking for:  the probe ID is the first argument to
480f0bd82a1SJustin Hibbits 	 * dtrace_probe(), so the argument n will actually be found where
481f0bd82a1SJustin Hibbits 	 * one would expect to find argument (n + 1).
482f0bd82a1SJustin Hibbits 	 */
483f0bd82a1SJustin Hibbits 	arg++;
484f0bd82a1SJustin Hibbits 
485f0bd82a1SJustin Hibbits 	if (arg <= inreg) {
486f0bd82a1SJustin Hibbits 		/*
487f0bd82a1SJustin Hibbits 		 * This shouldn't happen.  If the argument is passed in a
488f0bd82a1SJustin Hibbits 		 * register then it should have been, well, passed in a
489f0bd82a1SJustin Hibbits 		 * register...
490f0bd82a1SJustin Hibbits 		 */
491f0bd82a1SJustin Hibbits 		DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
492c7570492SJustin Hibbits 		return (0);
493c7570492SJustin Hibbits 	}
494c7570492SJustin Hibbits 
495f0bd82a1SJustin Hibbits 	arg -= (inreg + 1);
496f0bd82a1SJustin Hibbits 	stack = fp + 2;
497c7570492SJustin Hibbits 
498f0bd82a1SJustin Hibbits load:
499f0bd82a1SJustin Hibbits 	DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
500f0bd82a1SJustin Hibbits 	val = stack[arg];
501f0bd82a1SJustin Hibbits 	DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
502c7570492SJustin Hibbits 
503f0bd82a1SJustin Hibbits 	return (val);
504c7570492SJustin Hibbits }
505c7570492SJustin Hibbits 
506c7570492SJustin Hibbits int
507c7570492SJustin Hibbits dtrace_getstackdepth(int aframes)
508c7570492SJustin Hibbits {
509c7570492SJustin Hibbits 	int depth = 0;
510e40a5cd3SJustin Hibbits 	uintptr_t osp, sp;
511e40a5cd3SJustin Hibbits 	vm_offset_t callpc;
512c7570492SJustin Hibbits 
513e40a5cd3SJustin Hibbits 	osp = PAGE_SIZE;
514d69b94baSJustin Hibbits 	sp = (uintptr_t)__builtin_frame_address(0);
515c7570492SJustin Hibbits 	for(;;) {
516e40a5cd3SJustin Hibbits 		if (sp <= osp)
517c7570492SJustin Hibbits 			break;
518e40a5cd3SJustin Hibbits 
519675cad71SJustin Hibbits 		if (!dtrace_sp_inkernel(sp))
520c7570492SJustin Hibbits 			break;
521e40a5cd3SJustin Hibbits 
522c7570492SJustin Hibbits 		depth++;
523e40a5cd3SJustin Hibbits 		osp = sp;
5243e1155adSJustin Hibbits 		dtrace_next_sp_pc(sp, &sp, NULL, NULL);
525c7570492SJustin Hibbits 	}
526c7570492SJustin Hibbits 	if (depth < aframes)
527e40a5cd3SJustin Hibbits 		return (0);
528e40a5cd3SJustin Hibbits 
529d69b94baSJustin Hibbits 	return (depth - aframes);
530c7570492SJustin Hibbits }
531c7570492SJustin Hibbits 
532c7570492SJustin Hibbits ulong_t
53398ab9802SChristos Margiolis dtrace_getreg(struct trapframe *frame, uint_t reg)
534c7570492SJustin Hibbits {
535c7570492SJustin Hibbits 	if (reg < 32)
53698ab9802SChristos Margiolis 		return (frame->fixreg[reg]);
537c7570492SJustin Hibbits 
538c7570492SJustin Hibbits 	switch (reg) {
5397f0df9acSJustin Hibbits 	case 32:
54098ab9802SChristos Margiolis 		return (frame->lr);
5417f0df9acSJustin Hibbits 	case 33:
54298ab9802SChristos Margiolis 		return (frame->cr);
5437f0df9acSJustin Hibbits 	case 34:
54498ab9802SChristos Margiolis 		return (frame->xer);
5457f0df9acSJustin Hibbits 	case 35:
54698ab9802SChristos Margiolis 		return (frame->ctr);
5477f0df9acSJustin Hibbits 	case 36:
54898ab9802SChristos Margiolis 		return (frame->srr0);
5497f0df9acSJustin Hibbits 	case 37:
55098ab9802SChristos Margiolis 		return (frame->srr1);
5517f0df9acSJustin Hibbits 	case 38:
55298ab9802SChristos Margiolis 		return (frame->exc);
553c7570492SJustin Hibbits 	default:
554c7570492SJustin Hibbits 		DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
555c7570492SJustin Hibbits 		return (0);
556c7570492SJustin Hibbits 	}
557c7570492SJustin Hibbits }
558c7570492SJustin Hibbits 
559c7570492SJustin Hibbits static int
560c7570492SJustin Hibbits dtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size)
561c7570492SJustin Hibbits {
562c7570492SJustin Hibbits 	ASSERT(INKERNEL(kaddr) && kaddr + size >= kaddr);
563c7570492SJustin Hibbits 
564c7570492SJustin Hibbits 	if (uaddr + size > VM_MAXUSER_ADDRESS || uaddr + size < uaddr) {
565c7570492SJustin Hibbits 		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
566c7570492SJustin Hibbits 		cpu_core[curcpu].cpuc_dtrace_illval = uaddr;
567c7570492SJustin Hibbits 		return (0);
568c7570492SJustin Hibbits 	}
569c7570492SJustin Hibbits 
570c7570492SJustin Hibbits 	return (1);
571c7570492SJustin Hibbits }
572c7570492SJustin Hibbits 
573c7570492SJustin Hibbits void
574c7570492SJustin Hibbits dtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size,
575c7570492SJustin Hibbits     volatile uint16_t *flags)
576c7570492SJustin Hibbits {
577c7570492SJustin Hibbits 	if (dtrace_copycheck(uaddr, kaddr, size))
5787e7a9efdSJustin Hibbits 		if (copyin((const void *)uaddr, (void *)kaddr, size)) {
5797e7a9efdSJustin Hibbits 			DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
5807e7a9efdSJustin Hibbits 			cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
5817e7a9efdSJustin Hibbits 		}
582c7570492SJustin Hibbits }
583c7570492SJustin Hibbits 
584c7570492SJustin Hibbits void
585c7570492SJustin Hibbits dtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size,
586c7570492SJustin Hibbits     volatile uint16_t *flags)
587c7570492SJustin Hibbits {
5887e7a9efdSJustin Hibbits 	if (dtrace_copycheck(uaddr, kaddr, size)) {
5897e7a9efdSJustin Hibbits 		if (copyout((const void *)kaddr, (void *)uaddr, size)) {
5907e7a9efdSJustin Hibbits 			DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
5917e7a9efdSJustin Hibbits 			cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
5927e7a9efdSJustin Hibbits 		}
5937e7a9efdSJustin Hibbits 	}
594c7570492SJustin Hibbits }
595c7570492SJustin Hibbits 
596c7570492SJustin Hibbits void
597c7570492SJustin Hibbits dtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size,
598c7570492SJustin Hibbits     volatile uint16_t *flags)
599c7570492SJustin Hibbits {
6007e7a9efdSJustin Hibbits 	size_t actual;
6017e7a9efdSJustin Hibbits 	int    error;
6027e7a9efdSJustin Hibbits 
6037e7a9efdSJustin Hibbits 	if (dtrace_copycheck(uaddr, kaddr, size)) {
6047e7a9efdSJustin Hibbits 		error = copyinstr((const void *)uaddr, (void *)kaddr,
6057e7a9efdSJustin Hibbits 		    size, &actual);
6067e7a9efdSJustin Hibbits 
6077e7a9efdSJustin Hibbits 		/* ENAMETOOLONG is not a fault condition. */
6087e7a9efdSJustin Hibbits 		if (error && error != ENAMETOOLONG) {
6097e7a9efdSJustin Hibbits 			DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
6107e7a9efdSJustin Hibbits 			cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
6117e7a9efdSJustin Hibbits 		}
6127e7a9efdSJustin Hibbits 	}
613c7570492SJustin Hibbits }
614c7570492SJustin Hibbits 
6157e7a9efdSJustin Hibbits /*
6167e7a9efdSJustin Hibbits  * The bulk of this function could be replaced to match dtrace_copyinstr()
6177e7a9efdSJustin Hibbits  * if we ever implement a copyoutstr().
6187e7a9efdSJustin Hibbits  */
619c7570492SJustin Hibbits void
620c7570492SJustin Hibbits dtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size,
621c7570492SJustin Hibbits     volatile uint16_t *flags)
622c7570492SJustin Hibbits {
6237e7a9efdSJustin Hibbits 	size_t len;
6247e7a9efdSJustin Hibbits 
6257e7a9efdSJustin Hibbits 	if (dtrace_copycheck(uaddr, kaddr, size)) {
6267e7a9efdSJustin Hibbits 		len = strlen((const char *)kaddr);
6277e7a9efdSJustin Hibbits 		if (len > size)
6287e7a9efdSJustin Hibbits 			len = size;
6297e7a9efdSJustin Hibbits 
6307e7a9efdSJustin Hibbits 		if (copyout((const void *)kaddr, (void *)uaddr, len)) {
6317e7a9efdSJustin Hibbits 			DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
6327e7a9efdSJustin Hibbits 			cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
6337e7a9efdSJustin Hibbits 		}
6347e7a9efdSJustin Hibbits 	}
635c7570492SJustin Hibbits }
636c7570492SJustin Hibbits 
637c7570492SJustin Hibbits uint8_t
638c7570492SJustin Hibbits dtrace_fuword8(void *uaddr)
639c7570492SJustin Hibbits {
640c7570492SJustin Hibbits 	if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
641c7570492SJustin Hibbits 		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
642c7570492SJustin Hibbits 		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
643c7570492SJustin Hibbits 		return (0);
644c7570492SJustin Hibbits 	}
6457e7a9efdSJustin Hibbits 	return (fubyte(uaddr));
646c7570492SJustin Hibbits }
647c7570492SJustin Hibbits 
648c7570492SJustin Hibbits uint16_t
649c7570492SJustin Hibbits dtrace_fuword16(void *uaddr)
650c7570492SJustin Hibbits {
6517e7a9efdSJustin Hibbits 	uint16_t ret = 0;
6527e7a9efdSJustin Hibbits 
6537e7a9efdSJustin Hibbits 	if (dtrace_copycheck((uintptr_t)uaddr, (uintptr_t)&ret, sizeof(ret))) {
6547e7a9efdSJustin Hibbits 		if (copyin((const void *)uaddr, (void *)&ret, sizeof(ret))) {
655c7570492SJustin Hibbits 			DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
656c7570492SJustin Hibbits 			cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
657c7570492SJustin Hibbits 		}
6587e7a9efdSJustin Hibbits 	}
6597e7a9efdSJustin Hibbits 	return ret;
660c7570492SJustin Hibbits }
661c7570492SJustin Hibbits 
662c7570492SJustin Hibbits uint32_t
663c7570492SJustin Hibbits dtrace_fuword32(void *uaddr)
664c7570492SJustin Hibbits {
665c7570492SJustin Hibbits 	if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
666c7570492SJustin Hibbits 		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
667c7570492SJustin Hibbits 		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
668c7570492SJustin Hibbits 		return (0);
669c7570492SJustin Hibbits 	}
6707e7a9efdSJustin Hibbits 	return (fuword32(uaddr));
671c7570492SJustin Hibbits }
672c7570492SJustin Hibbits 
673c7570492SJustin Hibbits uint64_t
674c7570492SJustin Hibbits dtrace_fuword64(void *uaddr)
675c7570492SJustin Hibbits {
6767e7a9efdSJustin Hibbits 	uint64_t ret = 0;
6777e7a9efdSJustin Hibbits 
6787e7a9efdSJustin Hibbits 	if (dtrace_copycheck((uintptr_t)uaddr, (uintptr_t)&ret, sizeof(ret))) {
6797e7a9efdSJustin Hibbits 		if (copyin((const void *)uaddr, (void *)&ret, sizeof(ret))) {
680c7570492SJustin Hibbits 			DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
681c7570492SJustin Hibbits 			cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
682c7570492SJustin Hibbits 		}
6837e7a9efdSJustin Hibbits 	}
6847e7a9efdSJustin Hibbits 	return ret;
685c7570492SJustin Hibbits }
68680a5635cSJustin Hibbits 
68780a5635cSJustin Hibbits uintptr_t
68880a5635cSJustin Hibbits dtrace_fulword(void *uaddr)
68980a5635cSJustin Hibbits {
69080a5635cSJustin Hibbits 	uintptr_t ret = 0;
69180a5635cSJustin Hibbits 
69280a5635cSJustin Hibbits 	if (dtrace_copycheck((uintptr_t)uaddr, (uintptr_t)&ret, sizeof(ret))) {
69380a5635cSJustin Hibbits 		if (copyin((const void *)uaddr, (void *)&ret, sizeof(ret))) {
69480a5635cSJustin Hibbits 			DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
69580a5635cSJustin Hibbits 			cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
69680a5635cSJustin Hibbits 		}
69780a5635cSJustin Hibbits 	}
69880a5635cSJustin Hibbits 	return ret;
69980a5635cSJustin Hibbits }
700