dtrace_isa.c revision 233409
1233409Sgonzo/*
2233409Sgonzo * CDDL HEADER START
3233409Sgonzo *
4233409Sgonzo * The contents of this file are subject to the terms of the
5233409Sgonzo * Common Development and Distribution License, Version 1.0 only
6233409Sgonzo * (the "License").  You may not use this file except in compliance
7233409Sgonzo * with the License.
8233409Sgonzo *
9233409Sgonzo * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10233409Sgonzo * or http://www.opensolaris.org/os/licensing.
11233409Sgonzo * See the License for the specific language governing permissions
12233409Sgonzo * and limitations under the License.
13233409Sgonzo *
14233409Sgonzo * When distributing Covered Code, include this CDDL HEADER in each
15233409Sgonzo * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16233409Sgonzo * If applicable, add the following below this CDDL HEADER, with the
17233409Sgonzo * fields enclosed by brackets "[]" replaced with your own identifying
18233409Sgonzo * information: Portions Copyright [yyyy] [name of copyright owner]
19233409Sgonzo *
20233409Sgonzo * CDDL HEADER END
21233409Sgonzo *
22233409Sgonzo * $FreeBSD: head/sys/cddl/dev/dtrace/mips/dtrace_isa.c 233409 2012-03-24 05:14:37Z gonzo $
23233409Sgonzo */
24233409Sgonzo/*
25233409Sgonzo * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
26233409Sgonzo * Use is subject to license terms.
27233409Sgonzo */
28233409Sgonzo#include <sys/cdefs.h>
29233409Sgonzo
30233409Sgonzo#include <sys/param.h>
31233409Sgonzo#include <sys/systm.h>
32233409Sgonzo#include <sys/kernel.h>
33233409Sgonzo#include <sys/stack.h>
34233409Sgonzo#include <sys/pcpu.h>
35233409Sgonzo
36233409Sgonzo#include <machine/frame.h>
37233409Sgonzo#include <machine/md_var.h>
38233409Sgonzo#include <machine/reg.h>
39233409Sgonzo
40233409Sgonzo#include <vm/vm.h>
41233409Sgonzo#include <vm/vm_param.h>
42233409Sgonzo#include <vm/pmap.h>
43233409Sgonzo
44233409Sgonzo#include <machine/db_machdep.h>
45233409Sgonzo#include <machine/md_var.h>
46233409Sgonzo#include <machine/mips_opcode.h>
47233409Sgonzo#include <machine/vmparam.h>
48233409Sgonzo#include <ddb/db_sym.h>
49233409Sgonzo#include <ddb/ddb.h>
50233409Sgonzo#include <sys/kdb.h>
51233409Sgonzo
52233409Sgonzo#include "regset.h"
53233409Sgonzo
54233409Sgonzo#ifdef __mips_n64
55233409Sgonzo#define	MIPS_IS_VALID_KERNELADDR(reg)	((((reg) & 3) == 0) && \
56233409Sgonzo					((vm_offset_t)(reg) >= MIPS_XKPHYS_START))
57233409Sgonzo#else
58233409Sgonzo#define	MIPS_IS_VALID_KERNELADDR(reg)	((((reg) & 3) == 0) && \
59233409Sgonzo					((vm_offset_t)(reg) >= MIPS_KSEG0_START))
60233409Sgonzo#endif
61233409Sgonzo
62233409Sgonzo
63233409Sgonzo
64233409Sgonzo/*
65233409Sgonzo * Wee need some reasonable default to prevent backtrace code
66233409Sgonzo * from wandering too far
67233409Sgonzo */
68233409Sgonzo#define	MAX_FUNCTION_SIZE 0x10000
69233409Sgonzo#define	MAX_PROLOGUE_SIZE 0x100
70233409Sgonzo
71233409Sgonzouint8_t dtrace_fuword8_nocheck(void *);
72233409Sgonzouint16_t dtrace_fuword16_nocheck(void *);
73233409Sgonzouint32_t dtrace_fuword32_nocheck(void *);
74233409Sgonzouint64_t dtrace_fuword64_nocheck(void *);
75233409Sgonzo
76233409Sgonzostatic int dtrace_next_frame(register_t *pc, register_t *sp, register_t *args, int *valid_args);
77233409Sgonzostatic int dtrace_next_uframe(register_t *pc, register_t *sp, register_t *ra);
78233409Sgonzo
79233409Sgonzovoid
80233409Sgonzodtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
81233409Sgonzo    uint32_t *intrpc)
82233409Sgonzo{
83233409Sgonzo	int depth = 0;
84233409Sgonzo	vm_offset_t callpc;
85233409Sgonzo	pc_t caller = (pc_t) solaris_cpu[curcpu].cpu_dtrace_caller;
86233409Sgonzo	register_t sp, ra, pc;
87233409Sgonzo
88233409Sgonzo	if (intrpc != 0)
89233409Sgonzo		pcstack[depth++] = (pc_t) intrpc;
90233409Sgonzo
91233409Sgonzo	aframes++;
92233409Sgonzo
93233409Sgonzo	sp = (register_t)(intptr_t)__builtin_frame_address(0);
94233409Sgonzo	ra = (register_t)(intptr_t)__builtin_return_address(0);
95233409Sgonzo
96233409Sgonzo       	__asm __volatile(
97233409Sgonzo		"jal 99f\n"
98233409Sgonzo		"nop\n"
99233409Sgonzo		"99:\n"
100233409Sgonzo		"move %0, $31\n" /* get ra */
101233409Sgonzo		"move $31, %1\n" /* restore ra */
102233409Sgonzo		: "=r" (pc)
103233409Sgonzo		: "r" (ra));
104233409Sgonzo
105233409Sgonzo	while (depth < pcstack_limit) {
106233409Sgonzo
107233409Sgonzo		callpc = pc;
108233409Sgonzo
109233409Sgonzo		if (aframes > 0) {
110233409Sgonzo			aframes--;
111233409Sgonzo			if ((aframes == 0) && (caller != 0)) {
112233409Sgonzo				pcstack[depth++] = caller;
113233409Sgonzo			}
114233409Sgonzo		}
115233409Sgonzo		else {
116233409Sgonzo			pcstack[depth++] = callpc;
117233409Sgonzo		}
118233409Sgonzo
119233409Sgonzo		if (dtrace_next_frame(&pc, &sp, NULL, NULL) < 0)
120233409Sgonzo			break;
121233409Sgonzo	}
122233409Sgonzo
123233409Sgonzo	for (; depth < pcstack_limit; depth++) {
124233409Sgonzo		pcstack[depth] = 0;
125233409Sgonzo	}
126233409Sgonzo}
127233409Sgonzo
128233409Sgonzovoid
129233409Sgonzodtrace_getupcstack(uint64_t *pcstack, int pcstack_limit)
130233409Sgonzo{
131233409Sgonzo	proc_t *p = curproc;
132233409Sgonzo	struct trapframe *tf;
133233409Sgonzo	register_t sp, ra, pc;
134233409Sgonzo	volatile uint16_t *flags =
135233409Sgonzo	    (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
136233409Sgonzo
137233409Sgonzo	if (*flags & CPU_DTRACE_FAULT)
138233409Sgonzo		return;
139233409Sgonzo
140233409Sgonzo	if (pcstack_limit <= 0)
141233409Sgonzo		return;
142233409Sgonzo
143233409Sgonzo	/*
144233409Sgonzo	 * If there's no user context we still need to zero the stack.
145233409Sgonzo	 */
146233409Sgonzo	if (p == NULL || (tf = curthread->td_frame) == NULL)
147233409Sgonzo		goto zero;
148233409Sgonzo
149233409Sgonzo	*pcstack++ = (uint64_t)p->p_pid;
150233409Sgonzo	pcstack_limit--;
151233409Sgonzo
152233409Sgonzo	if (pcstack_limit <= 0)
153233409Sgonzo		return;
154233409Sgonzo
155233409Sgonzo	pc = (uint64_t)tf->pc;
156233409Sgonzo	sp = (uint64_t)tf->sp;
157233409Sgonzo	ra = (uint64_t)tf->ra;
158233409Sgonzo	*pcstack++ = (uint64_t)tf->pc;
159233409Sgonzo
160233409Sgonzo	/*
161233409Sgonzo	 * Unwind, and unwind, and unwind
162233409Sgonzo	 */
163233409Sgonzo	while (1) {
164233409Sgonzo		if (dtrace_next_uframe(&pc, &sp, &ra) < 0)
165233409Sgonzo			break;
166233409Sgonzo
167233409Sgonzo		*pcstack++ = pc;
168233409Sgonzo		pcstack_limit--;
169233409Sgonzo
170233409Sgonzo		if (pcstack_limit <= 0)
171233409Sgonzo			break;
172233409Sgonzo	}
173233409Sgonzo
174233409Sgonzozero:
175233409Sgonzo	while (pcstack_limit-- > 0)
176233409Sgonzo		*pcstack++ = 0;
177233409Sgonzo}
178233409Sgonzo
179233409Sgonzoint
180233409Sgonzodtrace_getustackdepth(void)
181233409Sgonzo{
182233409Sgonzo	int n = 0;
183233409Sgonzo	proc_t *p = curproc;
184233409Sgonzo	struct trapframe *tf;
185233409Sgonzo	register_t sp, ra, pc;
186233409Sgonzo	volatile uint16_t *flags =
187233409Sgonzo	    (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
188233409Sgonzo
189233409Sgonzo	if (*flags & CPU_DTRACE_FAULT)
190233409Sgonzo		return (0);
191233409Sgonzo
192233409Sgonzo	if (p == NULL || (tf = curthread->td_frame) == NULL)
193233409Sgonzo		return (0);
194233409Sgonzo
195233409Sgonzo	pc = (uint64_t)tf->pc;
196233409Sgonzo	sp = (uint64_t)tf->sp;
197233409Sgonzo	ra = (uint64_t)tf->ra;
198233409Sgonzo	n++;
199233409Sgonzo
200233409Sgonzo	/*
201233409Sgonzo	 * Unwind, and unwind, and unwind
202233409Sgonzo	 */
203233409Sgonzo	while (1) {
204233409Sgonzo		if (dtrace_next_uframe(&pc, &sp, &ra) < 0)
205233409Sgonzo			break;
206233409Sgonzo		n++;
207233409Sgonzo	}
208233409Sgonzo
209233409Sgonzo	return (n);
210233409Sgonzo}
211233409Sgonzo
212233409Sgonzovoid
213233409Sgonzodtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit)
214233409Sgonzo{
215233409Sgonzo	printf("IMPLEMENT ME: %s\n", __func__);
216233409Sgonzo}
217233409Sgonzo
218233409Sgonzo/*ARGSUSED*/
219233409Sgonzouint64_t
220233409Sgonzodtrace_getarg(int arg, int aframes)
221233409Sgonzo{
222233409Sgonzo	int i;
223233409Sgonzo	register_t sp, ra, pc;
224233409Sgonzo	/* XXX: Fix this ugly code */
225233409Sgonzo	register_t args[8];
226233409Sgonzo	int valid[8];
227233409Sgonzo
228233409Sgonzo	sp = (register_t)(intptr_t)__builtin_frame_address(0);
229233409Sgonzo	ra = (register_t)(intptr_t)__builtin_return_address(0);
230233409Sgonzo
231233409Sgonzo       	__asm __volatile(
232233409Sgonzo		"jal 99f\n"
233233409Sgonzo		"nop\n"
234233409Sgonzo		"99:\n"
235233409Sgonzo		"move %0, $31\n" /* get ra */
236233409Sgonzo		"move $31, %1\n" /* restore ra */
237233409Sgonzo		: "=r" (pc)
238233409Sgonzo		: "r" (ra));
239233409Sgonzo
240233409Sgonzo	for (i = 0; i <= aframes + 1; i++) {
241233409Sgonzo		if (dtrace_next_frame(&pc, &sp, args, valid) < 0) {
242233409Sgonzo			printf("%s: stack ends at frame #%d\n", __func__, i);
243233409Sgonzo			return (0);
244233409Sgonzo		}
245233409Sgonzo	}
246233409Sgonzo
247233409Sgonzo	if (arg < 8) {
248233409Sgonzo		if (valid[arg])
249233409Sgonzo			return (args[arg]);
250233409Sgonzo		else
251233409Sgonzo			printf("%s: request arg%d is not valid\n", __func__, arg);
252233409Sgonzo	}
253233409Sgonzo
254233409Sgonzo	return (0);
255233409Sgonzo}
256233409Sgonzo
257233409Sgonzoint
258233409Sgonzodtrace_getstackdepth(int aframes)
259233409Sgonzo{
260233409Sgonzo	register_t sp, ra, pc;
261233409Sgonzo	int depth = 0;
262233409Sgonzo
263233409Sgonzo	sp = (register_t)(intptr_t)__builtin_frame_address(0);
264233409Sgonzo	ra = (register_t)(intptr_t)__builtin_return_address(0);
265233409Sgonzo
266233409Sgonzo       	__asm __volatile(
267233409Sgonzo		"jal 99f\n"
268233409Sgonzo		"nop\n"
269233409Sgonzo		"99:\n"
270233409Sgonzo		"move %0, $31\n" /* get ra */
271233409Sgonzo		"move $31, %1\n" /* restore ra */
272233409Sgonzo		: "=r" (pc)
273233409Sgonzo		: "r" (ra));
274233409Sgonzo
275233409Sgonzo	for (;;) {
276233409Sgonzo		if (dtrace_next_frame(&pc, &sp, NULL, NULL) < 0)
277233409Sgonzo			break;
278233409Sgonzo		depth++;
279233409Sgonzo	}
280233409Sgonzo
281233409Sgonzo	if (depth < aframes)
282233409Sgonzo		return 0;
283233409Sgonzo	else
284233409Sgonzo		return depth - aframes;
285233409Sgonzo}
286233409Sgonzo
287233409Sgonzoulong_t
288233409Sgonzodtrace_getreg(struct trapframe *rp, uint_t reg)
289233409Sgonzo{
290233409Sgonzo
291233409Sgonzo	return (0);
292233409Sgonzo}
293233409Sgonzo
294233409Sgonzostatic int
295233409Sgonzodtrace_next_frame(register_t *pc, register_t *sp,
296233409Sgonzo	register_t *args, int *valid_args)
297233409Sgonzo{
298233409Sgonzo	InstFmt i;
299233409Sgonzo	/*
300233409Sgonzo	 * Arrays for a0..a3 registers and flags if content
301233409Sgonzo	 * of these registers is valid, e.g. obtained from the stack
302233409Sgonzo	 */
303233409Sgonzo	uintptr_t va;
304233409Sgonzo	unsigned instr, mask;
305233409Sgonzo	unsigned int frames = 0;
306233409Sgonzo	int more, stksize;
307233409Sgonzo	register_t ra = 0;
308233409Sgonzo	int arg, r;
309233409Sgonzo	vm_offset_t addr;
310233409Sgonzo
311233409Sgonzo	/*
312233409Sgonzo	 * Invalidate arguments values
313233409Sgonzo	 */
314233409Sgonzo	if (valid_args) {
315233409Sgonzo		for (r = 0; r < 8; r++)
316233409Sgonzo			valid_args[r] = 0;
317233409Sgonzo	}
318233409Sgonzo
319233409Sgonzo	/* Jump here after a nonstandard (interrupt handler) frame */
320233409Sgonzo	stksize = 0;
321233409Sgonzo	if (frames++ > 100) {
322233409Sgonzo		/* return breaks stackframe-size heuristics with gcc -O2 */
323233409Sgonzo		goto error;	/* XXX */
324233409Sgonzo	}
325233409Sgonzo
326233409Sgonzo	/* check for bad SP: could foul up next frame */
327233409Sgonzo	if (!MIPS_IS_VALID_KERNELADDR(*sp)) {
328233409Sgonzo		goto error;
329233409Sgonzo	}
330233409Sgonzo
331233409Sgonzo	/* check for bad PC */
332233409Sgonzo	if (!MIPS_IS_VALID_KERNELADDR(*pc)) {
333233409Sgonzo		goto error;
334233409Sgonzo	}
335233409Sgonzo
336233409Sgonzo	/*
337233409Sgonzo	 * Find the beginning of the current subroutine by scanning
338233409Sgonzo	 * backwards from the current PC for the end of the previous
339233409Sgonzo	 * subroutine.
340233409Sgonzo	 */
341233409Sgonzo	va = *pc - sizeof(int);
342233409Sgonzo	while (1) {
343233409Sgonzo		instr = kdbpeek((int *)va);
344233409Sgonzo
345233409Sgonzo		/* [d]addiu sp,sp,-X */
346233409Sgonzo		if (((instr & 0xffff8000) == 0x27bd8000)
347233409Sgonzo		    || ((instr & 0xffff8000) == 0x67bd8000))
348233409Sgonzo			break;
349233409Sgonzo
350233409Sgonzo		/* jr	ra */
351233409Sgonzo		if (instr == 0x03e00008) {
352233409Sgonzo			/* skip over branch-delay slot instruction */
353233409Sgonzo			va += 2 * sizeof(int);
354233409Sgonzo			break;
355233409Sgonzo		}
356233409Sgonzo
357233409Sgonzo		va -= sizeof(int);
358233409Sgonzo	}
359233409Sgonzo
360233409Sgonzo	/* skip over nulls which might separate .o files */
361233409Sgonzo	while ((instr = kdbpeek((int *)va)) == 0)
362233409Sgonzo		va += sizeof(int);
363233409Sgonzo
364233409Sgonzo	/* scan forwards to find stack size and any saved registers */
365233409Sgonzo	stksize = 0;
366233409Sgonzo	more = 3;
367233409Sgonzo	mask = 0;
368233409Sgonzo	for (; more; va += sizeof(int),
369233409Sgonzo	    more = (more == 3) ? 3 : more - 1) {
370233409Sgonzo		/* stop if hit our current position */
371233409Sgonzo		if (va >= *pc)
372233409Sgonzo			break;
373233409Sgonzo		instr = kdbpeek((int *)va);
374233409Sgonzo		i.word = instr;
375233409Sgonzo		switch (i.JType.op) {
376233409Sgonzo		case OP_SPECIAL:
377233409Sgonzo			switch (i.RType.func) {
378233409Sgonzo			case OP_JR:
379233409Sgonzo			case OP_JALR:
380233409Sgonzo				more = 2;	/* stop after next instruction */
381233409Sgonzo				break;
382233409Sgonzo
383233409Sgonzo			case OP_SYSCALL:
384233409Sgonzo			case OP_BREAK:
385233409Sgonzo				more = 1;	/* stop now */
386233409Sgonzo			};
387233409Sgonzo			break;
388233409Sgonzo
389233409Sgonzo		case OP_BCOND:
390233409Sgonzo		case OP_J:
391233409Sgonzo		case OP_JAL:
392233409Sgonzo		case OP_BEQ:
393233409Sgonzo		case OP_BNE:
394233409Sgonzo		case OP_BLEZ:
395233409Sgonzo		case OP_BGTZ:
396233409Sgonzo			more = 2;	/* stop after next instruction */
397233409Sgonzo			break;
398233409Sgonzo
399233409Sgonzo		case OP_COP0:
400233409Sgonzo		case OP_COP1:
401233409Sgonzo		case OP_COP2:
402233409Sgonzo		case OP_COP3:
403233409Sgonzo			switch (i.RType.rs) {
404233409Sgonzo			case OP_BCx:
405233409Sgonzo			case OP_BCy:
406233409Sgonzo				more = 2;	/* stop after next instruction */
407233409Sgonzo			};
408233409Sgonzo			break;
409233409Sgonzo
410233409Sgonzo		case OP_SW:
411233409Sgonzo			/* look for saved registers on the stack */
412233409Sgonzo			if (i.IType.rs != 29)
413233409Sgonzo				break;
414233409Sgonzo			/* only restore the first one */
415233409Sgonzo			if (mask & (1 << i.IType.rt))
416233409Sgonzo				break;
417233409Sgonzo			mask |= (1 << i.IType.rt);
418233409Sgonzo			addr = (vm_offset_t)(*sp + (short)i.IType.imm);
419233409Sgonzo			switch (i.IType.rt) {
420233409Sgonzo			case 4:/* a0 */
421233409Sgonzo			case 5:/* a1 */
422233409Sgonzo			case 6:/* a2 */
423233409Sgonzo			case 7:/* a3 */
424233409Sgonzo#if defined(__mips_n64) || defined(__mips_n32)
425233409Sgonzo			case 8:/* a4 */
426233409Sgonzo			case 9:/* a5 */
427233409Sgonzo			case 10:/* a6 */
428233409Sgonzo			case 11:/* a7 */
429233409Sgonzo#endif
430233409Sgonzo				arg = i.IType.rt - 4;
431233409Sgonzo				if (args)
432233409Sgonzo					args[arg] = kdbpeek((int*)addr);
433233409Sgonzo				if (valid_args)
434233409Sgonzo					valid_args[arg] = 1;
435233409Sgonzo				break;
436233409Sgonzo			case 31:	/* ra */
437233409Sgonzo				ra = kdbpeek((int *)addr);
438233409Sgonzo			}
439233409Sgonzo			break;
440233409Sgonzo
441233409Sgonzo		case OP_SD:
442233409Sgonzo			/* look for saved registers on the stack */
443233409Sgonzo			if (i.IType.rs != 29)
444233409Sgonzo				break;
445233409Sgonzo			/* only restore the first one */
446233409Sgonzo			if (mask & (1 << i.IType.rt))
447233409Sgonzo				break;
448233409Sgonzo			mask |= (1 << i.IType.rt);
449233409Sgonzo			addr = (vm_offset_t)(*sp + (short)i.IType.imm);
450233409Sgonzo			switch (i.IType.rt) {
451233409Sgonzo			case 4:/* a0 */
452233409Sgonzo			case 5:/* a1 */
453233409Sgonzo			case 6:/* a2 */
454233409Sgonzo			case 7:/* a3 */
455233409Sgonzo#if defined(__mips_n64) || defined(__mips_n32)
456233409Sgonzo			case 8:/* a4 */
457233409Sgonzo			case 9:/* a5 */
458233409Sgonzo			case 10:/* a6 */
459233409Sgonzo			case 11:/* a7 */
460233409Sgonzo#endif
461233409Sgonzo				arg = i.IType.rt - 4;
462233409Sgonzo				if (args)
463233409Sgonzo					args[arg] = kdbpeekd((int *)addr);
464233409Sgonzo				if (valid_args)
465233409Sgonzo					valid_args[arg] = 1;
466233409Sgonzo				break;
467233409Sgonzo
468233409Sgonzo			case 31:	/* ra */
469233409Sgonzo				ra = kdbpeekd((int *)addr);
470233409Sgonzo			}
471233409Sgonzo			break;
472233409Sgonzo
473233409Sgonzo		case OP_ADDI:
474233409Sgonzo		case OP_ADDIU:
475233409Sgonzo		case OP_DADDI:
476233409Sgonzo		case OP_DADDIU:
477233409Sgonzo			/* look for stack pointer adjustment */
478233409Sgonzo			if (i.IType.rs != 29 || i.IType.rt != 29)
479233409Sgonzo				break;
480233409Sgonzo			stksize = -((short)i.IType.imm);
481233409Sgonzo		}
482233409Sgonzo	}
483233409Sgonzo
484233409Sgonzo	if (!MIPS_IS_VALID_KERNELADDR(ra))
485233409Sgonzo		return (-1);
486233409Sgonzo
487233409Sgonzo	*pc = ra;
488233409Sgonzo	*sp += stksize;
489233409Sgonzo
490233409Sgonzo	return (0);
491233409Sgonzoerror:
492233409Sgonzo	return (-1);
493233409Sgonzo}
494233409Sgonzo
495233409Sgonzostatic int
496233409Sgonzodtrace_next_uframe(register_t *pc, register_t *sp, register_t *ra)
497233409Sgonzo{
498233409Sgonzo	int offset, registers_on_stack;
499233409Sgonzo	uint32_t opcode, mask;
500233409Sgonzo	register_t function_start;
501233409Sgonzo	int stksize;
502233409Sgonzo	InstFmt i;
503233409Sgonzo
504233409Sgonzo	registers_on_stack = 0;
505233409Sgonzo	mask = 0;
506233409Sgonzo	function_start = 0;
507233409Sgonzo	offset = 0;
508233409Sgonzo	stksize = 0;
509233409Sgonzo
510233409Sgonzo	while (offset < MAX_FUNCTION_SIZE) {
511233409Sgonzo		opcode = dtrace_fuword32((void *)(vm_offset_t)(*pc - offset));
512233409Sgonzo
513233409Sgonzo		/* [d]addiu sp, sp, -X*/
514233409Sgonzo		if (((opcode & 0xffff8000) == 0x27bd8000)
515233409Sgonzo		    || ((opcode & 0xffff8000) == 0x67bd8000)) {
516233409Sgonzo			function_start = *pc - offset;
517233409Sgonzo			registers_on_stack = 1;
518233409Sgonzo			break;
519233409Sgonzo		}
520233409Sgonzo
521233409Sgonzo		/* lui gp, X */
522233409Sgonzo		if ((opcode & 0xffff8000) == 0x3c1c0000) {
523233409Sgonzo			/*
524233409Sgonzo			 * Function might start with this instruction
525233409Sgonzo			 * Keep an eye on "jr ra" and sp correction
526233409Sgonzo			 * with positive value further on
527233409Sgonzo			 */
528233409Sgonzo			function_start = *pc - offset;
529233409Sgonzo		}
530233409Sgonzo
531233409Sgonzo		if (function_start) {
532233409Sgonzo			/*
533233409Sgonzo			 * Stop looking further. Possible end of
534233409Sgonzo			 * function instruction: it means there is no
535233409Sgonzo			 * stack modifications, sp is unchanged
536233409Sgonzo			 */
537233409Sgonzo
538233409Sgonzo			/* [d]addiu sp,sp,X */
539233409Sgonzo			if (((opcode & 0xffff8000) == 0x27bd0000)
540233409Sgonzo			    || ((opcode & 0xffff8000) == 0x67bd0000))
541233409Sgonzo				break;
542233409Sgonzo
543233409Sgonzo			if (opcode == 0x03e00008)
544233409Sgonzo				break;
545233409Sgonzo		}
546233409Sgonzo
547233409Sgonzo		offset += sizeof(int);
548233409Sgonzo	}
549233409Sgonzo
550233409Sgonzo	if (!function_start)
551233409Sgonzo		return (-1);
552233409Sgonzo
553233409Sgonzo	if (registers_on_stack) {
554233409Sgonzo		offset = 0;
555233409Sgonzo		while ((offset < MAX_PROLOGUE_SIZE)
556233409Sgonzo		    && ((function_start + offset) < *pc)) {
557233409Sgonzo			i.word =
558233409Sgonzo			    dtrace_fuword32((void *)(vm_offset_t)(function_start + offset));
559233409Sgonzo			switch (i.JType.op) {
560233409Sgonzo			case OP_SW:
561233409Sgonzo				/* look for saved registers on the stack */
562233409Sgonzo				if (i.IType.rs != 29)
563233409Sgonzo					break;
564233409Sgonzo				/* only restore the first one */
565233409Sgonzo				if (mask & (1 << i.IType.rt))
566233409Sgonzo					break;
567233409Sgonzo				mask |= (1 << i.IType.rt);
568233409Sgonzo				if (i.IType.rt == 31)
569233409Sgonzo					*ra = dtrace_fuword32((void *)(vm_offset_t)(*sp + (short)i.IType.imm));
570233409Sgonzo				break;
571233409Sgonzo
572233409Sgonzo			case OP_SD:
573233409Sgonzo				/* look for saved registers on the stack */
574233409Sgonzo				if (i.IType.rs != 29)
575233409Sgonzo					break;
576233409Sgonzo				/* only restore the first one */
577233409Sgonzo				if (mask & (1 << i.IType.rt))
578233409Sgonzo					break;
579233409Sgonzo				mask |= (1 << i.IType.rt);
580233409Sgonzo				/* ra */
581233409Sgonzo				if (i.IType.rt == 31)
582233409Sgonzo					*ra = dtrace_fuword64((void *)(vm_offset_t)(*sp + (short)i.IType.imm));
583233409Sgonzo			break;
584233409Sgonzo
585233409Sgonzo			case OP_ADDI:
586233409Sgonzo			case OP_ADDIU:
587233409Sgonzo			case OP_DADDI:
588233409Sgonzo			case OP_DADDIU:
589233409Sgonzo				/* look for stack pointer adjustment */
590233409Sgonzo				if (i.IType.rs != 29 || i.IType.rt != 29)
591233409Sgonzo					break;
592233409Sgonzo				stksize = -((short)i.IType.imm);
593233409Sgonzo			}
594233409Sgonzo
595233409Sgonzo			offset += sizeof(int);
596233409Sgonzo		}
597233409Sgonzo	}
598233409Sgonzo
599233409Sgonzo	/*
600233409Sgonzo	 * We reached the end of backtrace
601233409Sgonzo	 */
602233409Sgonzo	if (*pc == *ra)
603233409Sgonzo		return (-1);
604233409Sgonzo
605233409Sgonzo	*pc = *ra;
606233409Sgonzo	*sp += stksize;
607233409Sgonzo
608233409Sgonzo	return (0);
609233409Sgonzo}
610233409Sgonzo
611233409Sgonzostatic int
612233409Sgonzodtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size)
613233409Sgonzo{
614233409Sgonzo
615233409Sgonzo	if (uaddr + size > VM_MAXUSER_ADDRESS || uaddr + size < uaddr) {
616233409Sgonzo		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
617233409Sgonzo		cpu_core[curcpu].cpuc_dtrace_illval = uaddr;
618233409Sgonzo		return (0);
619233409Sgonzo	}
620233409Sgonzo
621233409Sgonzo	return (1);
622233409Sgonzo}
623233409Sgonzo
624233409Sgonzovoid
625233409Sgonzodtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size,
626233409Sgonzo    volatile uint16_t *flags)
627233409Sgonzo{
628233409Sgonzo	if (dtrace_copycheck(uaddr, kaddr, size))
629233409Sgonzo		dtrace_copy(uaddr, kaddr, size);
630233409Sgonzo}
631233409Sgonzo
632233409Sgonzovoid
633233409Sgonzodtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size,
634233409Sgonzo    volatile uint16_t *flags)
635233409Sgonzo{
636233409Sgonzo	if (dtrace_copycheck(uaddr, kaddr, size))
637233409Sgonzo		dtrace_copy(kaddr, uaddr, size);
638233409Sgonzo}
639233409Sgonzo
640233409Sgonzovoid
641233409Sgonzodtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size,
642233409Sgonzo    volatile uint16_t *flags)
643233409Sgonzo{
644233409Sgonzo	if (dtrace_copycheck(uaddr, kaddr, size))
645233409Sgonzo		dtrace_copystr(uaddr, kaddr, size, flags);
646233409Sgonzo}
647233409Sgonzo
648233409Sgonzovoid
649233409Sgonzodtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size,
650233409Sgonzo    volatile uint16_t *flags)
651233409Sgonzo{
652233409Sgonzo	if (dtrace_copycheck(uaddr, kaddr, size))
653233409Sgonzo		dtrace_copystr(kaddr, uaddr, size, flags);
654233409Sgonzo}
655233409Sgonzo
656233409Sgonzouint8_t
657233409Sgonzodtrace_fuword8(void *uaddr)
658233409Sgonzo{
659233409Sgonzo	if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
660233409Sgonzo		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
661233409Sgonzo		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
662233409Sgonzo		return (0);
663233409Sgonzo	}
664233409Sgonzo	return (dtrace_fuword8_nocheck(uaddr));
665233409Sgonzo}
666233409Sgonzo
667233409Sgonzouint16_t
668233409Sgonzodtrace_fuword16(void *uaddr)
669233409Sgonzo{
670233409Sgonzo	if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
671233409Sgonzo		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
672233409Sgonzo		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
673233409Sgonzo		return (0);
674233409Sgonzo	}
675233409Sgonzo	return (dtrace_fuword16_nocheck(uaddr));
676233409Sgonzo}
677233409Sgonzo
678233409Sgonzouint32_t
679233409Sgonzodtrace_fuword32(void *uaddr)
680233409Sgonzo{
681233409Sgonzo	if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
682233409Sgonzo		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
683233409Sgonzo		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
684233409Sgonzo		return (0);
685233409Sgonzo	}
686233409Sgonzo	return (dtrace_fuword32_nocheck(uaddr));
687233409Sgonzo}
688233409Sgonzo
689233409Sgonzouint64_t
690233409Sgonzodtrace_fuword64(void *uaddr)
691233409Sgonzo{
692233409Sgonzo	if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
693233409Sgonzo		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
694233409Sgonzo		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
695233409Sgonzo		return (0);
696233409Sgonzo	}
697233409Sgonzo	return (dtrace_fuword64_nocheck(uaddr));
698233409Sgonzo}
699