dtrace_isa.c revision 233525
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 233525 2012-03-26 21:47:06Z 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
490233525Sgonzo#if defined(__mips_o32)
491233525Sgonzo	/*
492233525Sgonzo	 * For MIPS32 fill out arguments 5..8 from the stack
493233525Sgonzo	 */
494233525Sgonzo	for (arg = 4; arg < 8; arg++) {
495233525Sgonzo		addr = (vm_offset_t)(*sp + arg*sizeof(register_t));
496233525Sgonzo		if (args)
497233525Sgonzo			args[arg] = kdbpeekd((int *)addr);
498233525Sgonzo		if (valid_args)
499233525Sgonzo			valid_args[arg] = 1;
500233525Sgonzo	}
501233525Sgonzo#endif
502233525Sgonzo
503233409Sgonzo	return (0);
504233409Sgonzoerror:
505233409Sgonzo	return (-1);
506233409Sgonzo}
507233409Sgonzo
508233409Sgonzostatic int
509233409Sgonzodtrace_next_uframe(register_t *pc, register_t *sp, register_t *ra)
510233409Sgonzo{
511233409Sgonzo	int offset, registers_on_stack;
512233409Sgonzo	uint32_t opcode, mask;
513233409Sgonzo	register_t function_start;
514233409Sgonzo	int stksize;
515233409Sgonzo	InstFmt i;
516233409Sgonzo
517233525Sgonzo	volatile uint16_t *flags =
518233525Sgonzo	    (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
519233525Sgonzo
520233409Sgonzo	registers_on_stack = 0;
521233409Sgonzo	mask = 0;
522233409Sgonzo	function_start = 0;
523233409Sgonzo	offset = 0;
524233409Sgonzo	stksize = 0;
525233409Sgonzo
526233409Sgonzo	while (offset < MAX_FUNCTION_SIZE) {
527233409Sgonzo		opcode = dtrace_fuword32((void *)(vm_offset_t)(*pc - offset));
528233409Sgonzo
529233525Sgonzo		if (*flags & CPU_DTRACE_FAULT)
530233525Sgonzo			goto fault;
531233525Sgonzo
532233409Sgonzo		/* [d]addiu sp, sp, -X*/
533233409Sgonzo		if (((opcode & 0xffff8000) == 0x27bd8000)
534233409Sgonzo		    || ((opcode & 0xffff8000) == 0x67bd8000)) {
535233409Sgonzo			function_start = *pc - offset;
536233409Sgonzo			registers_on_stack = 1;
537233409Sgonzo			break;
538233409Sgonzo		}
539233409Sgonzo
540233409Sgonzo		/* lui gp, X */
541233409Sgonzo		if ((opcode & 0xffff8000) == 0x3c1c0000) {
542233409Sgonzo			/*
543233409Sgonzo			 * Function might start with this instruction
544233409Sgonzo			 * Keep an eye on "jr ra" and sp correction
545233409Sgonzo			 * with positive value further on
546233409Sgonzo			 */
547233409Sgonzo			function_start = *pc - offset;
548233409Sgonzo		}
549233409Sgonzo
550233409Sgonzo		if (function_start) {
551233409Sgonzo			/*
552233409Sgonzo			 * Stop looking further. Possible end of
553233409Sgonzo			 * function instruction: it means there is no
554233409Sgonzo			 * stack modifications, sp is unchanged
555233409Sgonzo			 */
556233409Sgonzo
557233409Sgonzo			/* [d]addiu sp,sp,X */
558233409Sgonzo			if (((opcode & 0xffff8000) == 0x27bd0000)
559233409Sgonzo			    || ((opcode & 0xffff8000) == 0x67bd0000))
560233409Sgonzo				break;
561233409Sgonzo
562233409Sgonzo			if (opcode == 0x03e00008)
563233409Sgonzo				break;
564233409Sgonzo		}
565233409Sgonzo
566233409Sgonzo		offset += sizeof(int);
567233409Sgonzo	}
568233409Sgonzo
569233409Sgonzo	if (!function_start)
570233409Sgonzo		return (-1);
571233409Sgonzo
572233409Sgonzo	if (registers_on_stack) {
573233409Sgonzo		offset = 0;
574233409Sgonzo		while ((offset < MAX_PROLOGUE_SIZE)
575233409Sgonzo		    && ((function_start + offset) < *pc)) {
576233409Sgonzo			i.word =
577233409Sgonzo			    dtrace_fuword32((void *)(vm_offset_t)(function_start + offset));
578233409Sgonzo			switch (i.JType.op) {
579233409Sgonzo			case OP_SW:
580233409Sgonzo				/* look for saved registers on the stack */
581233409Sgonzo				if (i.IType.rs != 29)
582233409Sgonzo					break;
583233409Sgonzo				/* only restore the first one */
584233409Sgonzo				if (mask & (1 << i.IType.rt))
585233409Sgonzo					break;
586233409Sgonzo				mask |= (1 << i.IType.rt);
587233409Sgonzo				if (i.IType.rt == 31)
588233409Sgonzo					*ra = dtrace_fuword32((void *)(vm_offset_t)(*sp + (short)i.IType.imm));
589233409Sgonzo				break;
590233409Sgonzo
591233409Sgonzo			case OP_SD:
592233409Sgonzo				/* look for saved registers on the stack */
593233409Sgonzo				if (i.IType.rs != 29)
594233409Sgonzo					break;
595233409Sgonzo				/* only restore the first one */
596233409Sgonzo				if (mask & (1 << i.IType.rt))
597233409Sgonzo					break;
598233409Sgonzo				mask |= (1 << i.IType.rt);
599233409Sgonzo				/* ra */
600233409Sgonzo				if (i.IType.rt == 31)
601233409Sgonzo					*ra = dtrace_fuword64((void *)(vm_offset_t)(*sp + (short)i.IType.imm));
602233409Sgonzo			break;
603233409Sgonzo
604233409Sgonzo			case OP_ADDI:
605233409Sgonzo			case OP_ADDIU:
606233409Sgonzo			case OP_DADDI:
607233409Sgonzo			case OP_DADDIU:
608233409Sgonzo				/* look for stack pointer adjustment */
609233409Sgonzo				if (i.IType.rs != 29 || i.IType.rt != 29)
610233409Sgonzo					break;
611233409Sgonzo				stksize = -((short)i.IType.imm);
612233409Sgonzo			}
613233409Sgonzo
614233409Sgonzo			offset += sizeof(int);
615233525Sgonzo
616233525Sgonzo			if (*flags & CPU_DTRACE_FAULT)
617233525Sgonzo				goto fault;
618233409Sgonzo		}
619233409Sgonzo	}
620233409Sgonzo
621233409Sgonzo	/*
622233409Sgonzo	 * We reached the end of backtrace
623233409Sgonzo	 */
624233409Sgonzo	if (*pc == *ra)
625233409Sgonzo		return (-1);
626233409Sgonzo
627233409Sgonzo	*pc = *ra;
628233409Sgonzo	*sp += stksize;
629233409Sgonzo
630233409Sgonzo	return (0);
631233525Sgonzofault:
632233525Sgonzo	/*
633233525Sgonzo	 * We just got lost in backtrace, no big deal
634233525Sgonzo	 */
635233525Sgonzo	*flags &= ~CPU_DTRACE_FAULT;
636233525Sgonzo	return (-1);
637233409Sgonzo}
638233409Sgonzo
639233409Sgonzostatic int
640233409Sgonzodtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size)
641233409Sgonzo{
642233409Sgonzo
643233409Sgonzo	if (uaddr + size > VM_MAXUSER_ADDRESS || uaddr + size < uaddr) {
644233409Sgonzo		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
645233409Sgonzo		cpu_core[curcpu].cpuc_dtrace_illval = uaddr;
646233409Sgonzo		return (0);
647233409Sgonzo	}
648233409Sgonzo
649233409Sgonzo	return (1);
650233409Sgonzo}
651233409Sgonzo
652233409Sgonzovoid
653233409Sgonzodtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size,
654233409Sgonzo    volatile uint16_t *flags)
655233409Sgonzo{
656233409Sgonzo	if (dtrace_copycheck(uaddr, kaddr, size))
657233409Sgonzo		dtrace_copy(uaddr, kaddr, size);
658233409Sgonzo}
659233409Sgonzo
660233409Sgonzovoid
661233409Sgonzodtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size,
662233409Sgonzo    volatile uint16_t *flags)
663233409Sgonzo{
664233409Sgonzo	if (dtrace_copycheck(uaddr, kaddr, size))
665233409Sgonzo		dtrace_copy(kaddr, uaddr, size);
666233409Sgonzo}
667233409Sgonzo
668233409Sgonzovoid
669233409Sgonzodtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size,
670233409Sgonzo    volatile uint16_t *flags)
671233409Sgonzo{
672233409Sgonzo	if (dtrace_copycheck(uaddr, kaddr, size))
673233409Sgonzo		dtrace_copystr(uaddr, kaddr, size, flags);
674233409Sgonzo}
675233409Sgonzo
676233409Sgonzovoid
677233409Sgonzodtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size,
678233409Sgonzo    volatile uint16_t *flags)
679233409Sgonzo{
680233409Sgonzo	if (dtrace_copycheck(uaddr, kaddr, size))
681233409Sgonzo		dtrace_copystr(kaddr, uaddr, size, flags);
682233409Sgonzo}
683233409Sgonzo
684233409Sgonzouint8_t
685233409Sgonzodtrace_fuword8(void *uaddr)
686233409Sgonzo{
687233409Sgonzo	if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
688233409Sgonzo		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
689233409Sgonzo		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
690233409Sgonzo		return (0);
691233409Sgonzo	}
692233409Sgonzo	return (dtrace_fuword8_nocheck(uaddr));
693233409Sgonzo}
694233409Sgonzo
695233409Sgonzouint16_t
696233409Sgonzodtrace_fuword16(void *uaddr)
697233409Sgonzo{
698233409Sgonzo	if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
699233409Sgonzo		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
700233409Sgonzo		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
701233409Sgonzo		return (0);
702233409Sgonzo	}
703233409Sgonzo	return (dtrace_fuword16_nocheck(uaddr));
704233409Sgonzo}
705233409Sgonzo
706233409Sgonzouint32_t
707233409Sgonzodtrace_fuword32(void *uaddr)
708233409Sgonzo{
709233409Sgonzo	if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
710233409Sgonzo		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
711233409Sgonzo		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
712233409Sgonzo		return (0);
713233409Sgonzo	}
714233409Sgonzo	return (dtrace_fuword32_nocheck(uaddr));
715233409Sgonzo}
716233409Sgonzo
717233409Sgonzouint64_t
718233409Sgonzodtrace_fuword64(void *uaddr)
719233409Sgonzo{
720233409Sgonzo	if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
721233409Sgonzo		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
722233409Sgonzo		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
723233409Sgonzo		return (0);
724233409Sgonzo	}
725233409Sgonzo	return (dtrace_fuword64_nocheck(uaddr));
726233409Sgonzo}
727