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: releng/11.0/sys/cddl/dev/dtrace/mips/dtrace_isa.c 295882 2016-02-22 09:08:04Z skra $
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 <ddb/db_sym.h>
48233409Sgonzo#include <ddb/ddb.h>
49233409Sgonzo#include <sys/kdb.h>
50233409Sgonzo
51233409Sgonzo#include "regset.h"
52233409Sgonzo
53233409Sgonzo#ifdef __mips_n64
54233409Sgonzo#define	MIPS_IS_VALID_KERNELADDR(reg)	((((reg) & 3) == 0) && \
55233409Sgonzo					((vm_offset_t)(reg) >= MIPS_XKPHYS_START))
56233409Sgonzo#else
57233409Sgonzo#define	MIPS_IS_VALID_KERNELADDR(reg)	((((reg) & 3) == 0) && \
58233409Sgonzo					((vm_offset_t)(reg) >= MIPS_KSEG0_START))
59233409Sgonzo#endif
60233409Sgonzo
61233409Sgonzo
62233409Sgonzo
63233409Sgonzo/*
64233409Sgonzo * Wee need some reasonable default to prevent backtrace code
65233409Sgonzo * from wandering too far
66233409Sgonzo */
67233409Sgonzo#define	MAX_FUNCTION_SIZE 0x10000
68233409Sgonzo#define	MAX_PROLOGUE_SIZE 0x100
69233409Sgonzo
70233409Sgonzouint8_t dtrace_fuword8_nocheck(void *);
71233409Sgonzouint16_t dtrace_fuword16_nocheck(void *);
72233409Sgonzouint32_t dtrace_fuword32_nocheck(void *);
73233409Sgonzouint64_t dtrace_fuword64_nocheck(void *);
74233409Sgonzo
75233409Sgonzostatic int dtrace_next_frame(register_t *pc, register_t *sp, register_t *args, int *valid_args);
76233409Sgonzostatic int dtrace_next_uframe(register_t *pc, register_t *sp, register_t *ra);
77233409Sgonzo
78233409Sgonzovoid
79233409Sgonzodtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
80233409Sgonzo    uint32_t *intrpc)
81233409Sgonzo{
82233409Sgonzo	int depth = 0;
83233409Sgonzo	vm_offset_t callpc;
84233409Sgonzo	pc_t caller = (pc_t) solaris_cpu[curcpu].cpu_dtrace_caller;
85233409Sgonzo	register_t sp, ra, pc;
86233409Sgonzo
87233409Sgonzo	if (intrpc != 0)
88233409Sgonzo		pcstack[depth++] = (pc_t) intrpc;
89233409Sgonzo
90233409Sgonzo	aframes++;
91233409Sgonzo
92233409Sgonzo	sp = (register_t)(intptr_t)__builtin_frame_address(0);
93233409Sgonzo	ra = (register_t)(intptr_t)__builtin_return_address(0);
94233409Sgonzo
95233409Sgonzo       	__asm __volatile(
96233409Sgonzo		"jal 99f\n"
97233409Sgonzo		"nop\n"
98233409Sgonzo		"99:\n"
99233409Sgonzo		"move %0, $31\n" /* get ra */
100233409Sgonzo		"move $31, %1\n" /* restore ra */
101233409Sgonzo		: "=r" (pc)
102233409Sgonzo		: "r" (ra));
103233409Sgonzo
104233409Sgonzo	while (depth < pcstack_limit) {
105233409Sgonzo
106233409Sgonzo		callpc = pc;
107233409Sgonzo
108233409Sgonzo		if (aframes > 0) {
109233409Sgonzo			aframes--;
110233409Sgonzo			if ((aframes == 0) && (caller != 0)) {
111233409Sgonzo				pcstack[depth++] = caller;
112233409Sgonzo			}
113233409Sgonzo		}
114233409Sgonzo		else {
115233409Sgonzo			pcstack[depth++] = callpc;
116233409Sgonzo		}
117233409Sgonzo
118233409Sgonzo		if (dtrace_next_frame(&pc, &sp, NULL, NULL) < 0)
119233409Sgonzo			break;
120233409Sgonzo	}
121233409Sgonzo
122233409Sgonzo	for (; depth < pcstack_limit; depth++) {
123233409Sgonzo		pcstack[depth] = 0;
124233409Sgonzo	}
125233409Sgonzo}
126233409Sgonzo
127233409Sgonzovoid
128233409Sgonzodtrace_getupcstack(uint64_t *pcstack, int pcstack_limit)
129233409Sgonzo{
130233409Sgonzo	proc_t *p = curproc;
131233409Sgonzo	struct trapframe *tf;
132233409Sgonzo	register_t sp, ra, pc;
133233409Sgonzo	volatile uint16_t *flags =
134233409Sgonzo	    (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
135233409Sgonzo
136233409Sgonzo	if (*flags & CPU_DTRACE_FAULT)
137233409Sgonzo		return;
138233409Sgonzo
139233409Sgonzo	if (pcstack_limit <= 0)
140233409Sgonzo		return;
141233409Sgonzo
142233409Sgonzo	/*
143233409Sgonzo	 * If there's no user context we still need to zero the stack.
144233409Sgonzo	 */
145233409Sgonzo	if (p == NULL || (tf = curthread->td_frame) == NULL)
146233409Sgonzo		goto zero;
147233409Sgonzo
148233409Sgonzo	*pcstack++ = (uint64_t)p->p_pid;
149233409Sgonzo	pcstack_limit--;
150233409Sgonzo
151233409Sgonzo	if (pcstack_limit <= 0)
152233409Sgonzo		return;
153233409Sgonzo
154233409Sgonzo	pc = (uint64_t)tf->pc;
155233409Sgonzo	sp = (uint64_t)tf->sp;
156233409Sgonzo	ra = (uint64_t)tf->ra;
157233409Sgonzo	*pcstack++ = (uint64_t)tf->pc;
158233409Sgonzo
159233409Sgonzo	/*
160233409Sgonzo	 * Unwind, and unwind, and unwind
161233409Sgonzo	 */
162233409Sgonzo	while (1) {
163233409Sgonzo		if (dtrace_next_uframe(&pc, &sp, &ra) < 0)
164233409Sgonzo			break;
165233409Sgonzo
166233409Sgonzo		*pcstack++ = pc;
167233409Sgonzo		pcstack_limit--;
168233409Sgonzo
169233409Sgonzo		if (pcstack_limit <= 0)
170233409Sgonzo			break;
171233409Sgonzo	}
172233409Sgonzo
173233409Sgonzozero:
174233409Sgonzo	while (pcstack_limit-- > 0)
175233409Sgonzo		*pcstack++ = 0;
176233409Sgonzo}
177233409Sgonzo
178233409Sgonzoint
179233409Sgonzodtrace_getustackdepth(void)
180233409Sgonzo{
181233409Sgonzo	int n = 0;
182233409Sgonzo	proc_t *p = curproc;
183233409Sgonzo	struct trapframe *tf;
184233409Sgonzo	register_t sp, ra, pc;
185233409Sgonzo	volatile uint16_t *flags =
186233409Sgonzo	    (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
187233409Sgonzo
188233409Sgonzo	if (*flags & CPU_DTRACE_FAULT)
189233409Sgonzo		return (0);
190233409Sgonzo
191233409Sgonzo	if (p == NULL || (tf = curthread->td_frame) == NULL)
192233409Sgonzo		return (0);
193233409Sgonzo
194233409Sgonzo	pc = (uint64_t)tf->pc;
195233409Sgonzo	sp = (uint64_t)tf->sp;
196233409Sgonzo	ra = (uint64_t)tf->ra;
197233409Sgonzo	n++;
198233409Sgonzo
199233409Sgonzo	/*
200233409Sgonzo	 * Unwind, and unwind, and unwind
201233409Sgonzo	 */
202233409Sgonzo	while (1) {
203233409Sgonzo		if (dtrace_next_uframe(&pc, &sp, &ra) < 0)
204233409Sgonzo			break;
205233409Sgonzo		n++;
206233409Sgonzo	}
207233409Sgonzo
208233409Sgonzo	return (n);
209233409Sgonzo}
210233409Sgonzo
211233409Sgonzovoid
212233409Sgonzodtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit)
213233409Sgonzo{
214233409Sgonzo	printf("IMPLEMENT ME: %s\n", __func__);
215233409Sgonzo}
216233409Sgonzo
217233409Sgonzo/*ARGSUSED*/
218233409Sgonzouint64_t
219233409Sgonzodtrace_getarg(int arg, int aframes)
220233409Sgonzo{
221233409Sgonzo	int i;
222233409Sgonzo	register_t sp, ra, pc;
223233409Sgonzo	/* XXX: Fix this ugly code */
224233409Sgonzo	register_t args[8];
225233409Sgonzo	int valid[8];
226233409Sgonzo
227233409Sgonzo	sp = (register_t)(intptr_t)__builtin_frame_address(0);
228233409Sgonzo	ra = (register_t)(intptr_t)__builtin_return_address(0);
229233409Sgonzo
230233409Sgonzo       	__asm __volatile(
231233409Sgonzo		"jal 99f\n"
232233409Sgonzo		"nop\n"
233233409Sgonzo		"99:\n"
234233409Sgonzo		"move %0, $31\n" /* get ra */
235233409Sgonzo		"move $31, %1\n" /* restore ra */
236233409Sgonzo		: "=r" (pc)
237233409Sgonzo		: "r" (ra));
238233409Sgonzo
239233409Sgonzo	for (i = 0; i <= aframes + 1; i++) {
240233409Sgonzo		if (dtrace_next_frame(&pc, &sp, args, valid) < 0) {
241233409Sgonzo			printf("%s: stack ends at frame #%d\n", __func__, i);
242233409Sgonzo			return (0);
243233409Sgonzo		}
244233409Sgonzo	}
245233409Sgonzo
246233409Sgonzo	if (arg < 8) {
247233409Sgonzo		if (valid[arg])
248233409Sgonzo			return (args[arg]);
249233409Sgonzo		else
250233409Sgonzo			printf("%s: request arg%d is not valid\n", __func__, arg);
251233409Sgonzo	}
252233409Sgonzo
253233409Sgonzo	return (0);
254233409Sgonzo}
255233409Sgonzo
256233409Sgonzoint
257233409Sgonzodtrace_getstackdepth(int aframes)
258233409Sgonzo{
259233409Sgonzo	register_t sp, ra, pc;
260233409Sgonzo	int depth = 0;
261233409Sgonzo
262233409Sgonzo	sp = (register_t)(intptr_t)__builtin_frame_address(0);
263233409Sgonzo	ra = (register_t)(intptr_t)__builtin_return_address(0);
264233409Sgonzo
265233409Sgonzo       	__asm __volatile(
266233409Sgonzo		"jal 99f\n"
267233409Sgonzo		"nop\n"
268233409Sgonzo		"99:\n"
269233409Sgonzo		"move %0, $31\n" /* get ra */
270233409Sgonzo		"move $31, %1\n" /* restore ra */
271233409Sgonzo		: "=r" (pc)
272233409Sgonzo		: "r" (ra));
273233409Sgonzo
274233409Sgonzo	for (;;) {
275233409Sgonzo		if (dtrace_next_frame(&pc, &sp, NULL, NULL) < 0)
276233409Sgonzo			break;
277233409Sgonzo		depth++;
278233409Sgonzo	}
279233409Sgonzo
280233409Sgonzo	if (depth < aframes)
281233409Sgonzo		return 0;
282233409Sgonzo	else
283233409Sgonzo		return depth - aframes;
284233409Sgonzo}
285233409Sgonzo
286233409Sgonzoulong_t
287233409Sgonzodtrace_getreg(struct trapframe *rp, uint_t reg)
288233409Sgonzo{
289233409Sgonzo
290233409Sgonzo	return (0);
291233409Sgonzo}
292233409Sgonzo
293233409Sgonzostatic int
294233409Sgonzodtrace_next_frame(register_t *pc, register_t *sp,
295233409Sgonzo	register_t *args, int *valid_args)
296233409Sgonzo{
297233409Sgonzo	InstFmt i;
298233409Sgonzo	/*
299233409Sgonzo	 * Arrays for a0..a3 registers and flags if content
300233409Sgonzo	 * of these registers is valid, e.g. obtained from the stack
301233409Sgonzo	 */
302233409Sgonzo	uintptr_t va;
303233409Sgonzo	unsigned instr, mask;
304233409Sgonzo	unsigned int frames = 0;
305233409Sgonzo	int more, stksize;
306233409Sgonzo	register_t ra = 0;
307233409Sgonzo	int arg, r;
308233409Sgonzo	vm_offset_t addr;
309233409Sgonzo
310233409Sgonzo	/*
311233409Sgonzo	 * Invalidate arguments values
312233409Sgonzo	 */
313233409Sgonzo	if (valid_args) {
314233409Sgonzo		for (r = 0; r < 8; r++)
315233409Sgonzo			valid_args[r] = 0;
316233409Sgonzo	}
317233409Sgonzo
318233409Sgonzo	/* Jump here after a nonstandard (interrupt handler) frame */
319233409Sgonzo	stksize = 0;
320233409Sgonzo	if (frames++ > 100) {
321233409Sgonzo		/* return breaks stackframe-size heuristics with gcc -O2 */
322233409Sgonzo		goto error;	/* XXX */
323233409Sgonzo	}
324233409Sgonzo
325233409Sgonzo	/* check for bad SP: could foul up next frame */
326233409Sgonzo	if (!MIPS_IS_VALID_KERNELADDR(*sp)) {
327233409Sgonzo		goto error;
328233409Sgonzo	}
329233409Sgonzo
330233409Sgonzo	/* check for bad PC */
331233409Sgonzo	if (!MIPS_IS_VALID_KERNELADDR(*pc)) {
332233409Sgonzo		goto error;
333233409Sgonzo	}
334233409Sgonzo
335233409Sgonzo	/*
336233409Sgonzo	 * Find the beginning of the current subroutine by scanning
337233409Sgonzo	 * backwards from the current PC for the end of the previous
338233409Sgonzo	 * subroutine.
339233409Sgonzo	 */
340233409Sgonzo	va = *pc - sizeof(int);
341233409Sgonzo	while (1) {
342233409Sgonzo		instr = kdbpeek((int *)va);
343233409Sgonzo
344233409Sgonzo		/* [d]addiu sp,sp,-X */
345233409Sgonzo		if (((instr & 0xffff8000) == 0x27bd8000)
346233409Sgonzo		    || ((instr & 0xffff8000) == 0x67bd8000))
347233409Sgonzo			break;
348233409Sgonzo
349233409Sgonzo		/* jr	ra */
350233409Sgonzo		if (instr == 0x03e00008) {
351233409Sgonzo			/* skip over branch-delay slot instruction */
352233409Sgonzo			va += 2 * sizeof(int);
353233409Sgonzo			break;
354233409Sgonzo		}
355233409Sgonzo
356233409Sgonzo		va -= sizeof(int);
357233409Sgonzo	}
358233409Sgonzo
359233409Sgonzo	/* skip over nulls which might separate .o files */
360233409Sgonzo	while ((instr = kdbpeek((int *)va)) == 0)
361233409Sgonzo		va += sizeof(int);
362233409Sgonzo
363233409Sgonzo	/* scan forwards to find stack size and any saved registers */
364233409Sgonzo	stksize = 0;
365233409Sgonzo	more = 3;
366233409Sgonzo	mask = 0;
367233409Sgonzo	for (; more; va += sizeof(int),
368233409Sgonzo	    more = (more == 3) ? 3 : more - 1) {
369233409Sgonzo		/* stop if hit our current position */
370233409Sgonzo		if (va >= *pc)
371233409Sgonzo			break;
372233409Sgonzo		instr = kdbpeek((int *)va);
373233409Sgonzo		i.word = instr;
374233409Sgonzo		switch (i.JType.op) {
375233409Sgonzo		case OP_SPECIAL:
376233409Sgonzo			switch (i.RType.func) {
377233409Sgonzo			case OP_JR:
378233409Sgonzo			case OP_JALR:
379233409Sgonzo				more = 2;	/* stop after next instruction */
380233409Sgonzo				break;
381233409Sgonzo
382233409Sgonzo			case OP_SYSCALL:
383233409Sgonzo			case OP_BREAK:
384233409Sgonzo				more = 1;	/* stop now */
385233409Sgonzo			};
386233409Sgonzo			break;
387233409Sgonzo
388233409Sgonzo		case OP_BCOND:
389233409Sgonzo		case OP_J:
390233409Sgonzo		case OP_JAL:
391233409Sgonzo		case OP_BEQ:
392233409Sgonzo		case OP_BNE:
393233409Sgonzo		case OP_BLEZ:
394233409Sgonzo		case OP_BGTZ:
395233409Sgonzo			more = 2;	/* stop after next instruction */
396233409Sgonzo			break;
397233409Sgonzo
398233409Sgonzo		case OP_COP0:
399233409Sgonzo		case OP_COP1:
400233409Sgonzo		case OP_COP2:
401233409Sgonzo		case OP_COP3:
402233409Sgonzo			switch (i.RType.rs) {
403233409Sgonzo			case OP_BCx:
404233409Sgonzo			case OP_BCy:
405233409Sgonzo				more = 2;	/* stop after next instruction */
406233409Sgonzo			};
407233409Sgonzo			break;
408233409Sgonzo
409233409Sgonzo		case OP_SW:
410233409Sgonzo			/* look for saved registers on the stack */
411233409Sgonzo			if (i.IType.rs != 29)
412233409Sgonzo				break;
413233409Sgonzo			/* only restore the first one */
414233409Sgonzo			if (mask & (1 << i.IType.rt))
415233409Sgonzo				break;
416233409Sgonzo			mask |= (1 << i.IType.rt);
417233409Sgonzo			addr = (vm_offset_t)(*sp + (short)i.IType.imm);
418233409Sgonzo			switch (i.IType.rt) {
419233409Sgonzo			case 4:/* a0 */
420233409Sgonzo			case 5:/* a1 */
421233409Sgonzo			case 6:/* a2 */
422233409Sgonzo			case 7:/* a3 */
423233409Sgonzo#if defined(__mips_n64) || defined(__mips_n32)
424233409Sgonzo			case 8:/* a4 */
425233409Sgonzo			case 9:/* a5 */
426233409Sgonzo			case 10:/* a6 */
427233409Sgonzo			case 11:/* a7 */
428233409Sgonzo#endif
429233409Sgonzo				arg = i.IType.rt - 4;
430233409Sgonzo				if (args)
431233409Sgonzo					args[arg] = kdbpeek((int*)addr);
432233409Sgonzo				if (valid_args)
433233409Sgonzo					valid_args[arg] = 1;
434233409Sgonzo				break;
435233409Sgonzo			case 31:	/* ra */
436233409Sgonzo				ra = kdbpeek((int *)addr);
437233409Sgonzo			}
438233409Sgonzo			break;
439233409Sgonzo
440233409Sgonzo		case OP_SD:
441233409Sgonzo			/* look for saved registers on the stack */
442233409Sgonzo			if (i.IType.rs != 29)
443233409Sgonzo				break;
444233409Sgonzo			/* only restore the first one */
445233409Sgonzo			if (mask & (1 << i.IType.rt))
446233409Sgonzo				break;
447233409Sgonzo			mask |= (1 << i.IType.rt);
448233409Sgonzo			addr = (vm_offset_t)(*sp + (short)i.IType.imm);
449233409Sgonzo			switch (i.IType.rt) {
450233409Sgonzo			case 4:/* a0 */
451233409Sgonzo			case 5:/* a1 */
452233409Sgonzo			case 6:/* a2 */
453233409Sgonzo			case 7:/* a3 */
454233409Sgonzo#if defined(__mips_n64) || defined(__mips_n32)
455233409Sgonzo			case 8:/* a4 */
456233409Sgonzo			case 9:/* a5 */
457233409Sgonzo			case 10:/* a6 */
458233409Sgonzo			case 11:/* a7 */
459233409Sgonzo#endif
460233409Sgonzo				arg = i.IType.rt - 4;
461233409Sgonzo				if (args)
462233409Sgonzo					args[arg] = kdbpeekd((int *)addr);
463233409Sgonzo				if (valid_args)
464233409Sgonzo					valid_args[arg] = 1;
465233409Sgonzo				break;
466233409Sgonzo
467233409Sgonzo			case 31:	/* ra */
468233409Sgonzo				ra = kdbpeekd((int *)addr);
469233409Sgonzo			}
470233409Sgonzo			break;
471233409Sgonzo
472233409Sgonzo		case OP_ADDI:
473233409Sgonzo		case OP_ADDIU:
474233409Sgonzo		case OP_DADDI:
475233409Sgonzo		case OP_DADDIU:
476233409Sgonzo			/* look for stack pointer adjustment */
477233409Sgonzo			if (i.IType.rs != 29 || i.IType.rt != 29)
478233409Sgonzo				break;
479233409Sgonzo			stksize = -((short)i.IType.imm);
480233409Sgonzo		}
481233409Sgonzo	}
482233409Sgonzo
483233409Sgonzo	if (!MIPS_IS_VALID_KERNELADDR(ra))
484233409Sgonzo		return (-1);
485233409Sgonzo
486233409Sgonzo	*pc = ra;
487233409Sgonzo	*sp += stksize;
488233409Sgonzo
489233525Sgonzo#if defined(__mips_o32)
490233525Sgonzo	/*
491233525Sgonzo	 * For MIPS32 fill out arguments 5..8 from the stack
492233525Sgonzo	 */
493233525Sgonzo	for (arg = 4; arg < 8; arg++) {
494233525Sgonzo		addr = (vm_offset_t)(*sp + arg*sizeof(register_t));
495233525Sgonzo		if (args)
496233525Sgonzo			args[arg] = kdbpeekd((int *)addr);
497233525Sgonzo		if (valid_args)
498233525Sgonzo			valid_args[arg] = 1;
499233525Sgonzo	}
500233525Sgonzo#endif
501233525Sgonzo
502233409Sgonzo	return (0);
503233409Sgonzoerror:
504233409Sgonzo	return (-1);
505233409Sgonzo}
506233409Sgonzo
507233409Sgonzostatic int
508233409Sgonzodtrace_next_uframe(register_t *pc, register_t *sp, register_t *ra)
509233409Sgonzo{
510233409Sgonzo	int offset, registers_on_stack;
511233409Sgonzo	uint32_t opcode, mask;
512233409Sgonzo	register_t function_start;
513233409Sgonzo	int stksize;
514233409Sgonzo	InstFmt i;
515233409Sgonzo
516233525Sgonzo	volatile uint16_t *flags =
517233525Sgonzo	    (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
518233525Sgonzo
519233409Sgonzo	registers_on_stack = 0;
520233409Sgonzo	mask = 0;
521233409Sgonzo	function_start = 0;
522233409Sgonzo	offset = 0;
523233409Sgonzo	stksize = 0;
524233409Sgonzo
525233409Sgonzo	while (offset < MAX_FUNCTION_SIZE) {
526233409Sgonzo		opcode = dtrace_fuword32((void *)(vm_offset_t)(*pc - offset));
527233409Sgonzo
528233525Sgonzo		if (*flags & CPU_DTRACE_FAULT)
529233525Sgonzo			goto fault;
530233525Sgonzo
531233409Sgonzo		/* [d]addiu sp, sp, -X*/
532233409Sgonzo		if (((opcode & 0xffff8000) == 0x27bd8000)
533233409Sgonzo		    || ((opcode & 0xffff8000) == 0x67bd8000)) {
534233409Sgonzo			function_start = *pc - offset;
535233409Sgonzo			registers_on_stack = 1;
536233409Sgonzo			break;
537233409Sgonzo		}
538233409Sgonzo
539233409Sgonzo		/* lui gp, X */
540233409Sgonzo		if ((opcode & 0xffff8000) == 0x3c1c0000) {
541233409Sgonzo			/*
542233409Sgonzo			 * Function might start with this instruction
543233409Sgonzo			 * Keep an eye on "jr ra" and sp correction
544233409Sgonzo			 * with positive value further on
545233409Sgonzo			 */
546233409Sgonzo			function_start = *pc - offset;
547233409Sgonzo		}
548233409Sgonzo
549233409Sgonzo		if (function_start) {
550233409Sgonzo			/*
551233409Sgonzo			 * Stop looking further. Possible end of
552233409Sgonzo			 * function instruction: it means there is no
553233409Sgonzo			 * stack modifications, sp is unchanged
554233409Sgonzo			 */
555233409Sgonzo
556233409Sgonzo			/* [d]addiu sp,sp,X */
557233409Sgonzo			if (((opcode & 0xffff8000) == 0x27bd0000)
558233409Sgonzo			    || ((opcode & 0xffff8000) == 0x67bd0000))
559233409Sgonzo				break;
560233409Sgonzo
561233409Sgonzo			if (opcode == 0x03e00008)
562233409Sgonzo				break;
563233409Sgonzo		}
564233409Sgonzo
565233409Sgonzo		offset += sizeof(int);
566233409Sgonzo	}
567233409Sgonzo
568233409Sgonzo	if (!function_start)
569233409Sgonzo		return (-1);
570233409Sgonzo
571233409Sgonzo	if (registers_on_stack) {
572233409Sgonzo		offset = 0;
573233409Sgonzo		while ((offset < MAX_PROLOGUE_SIZE)
574233409Sgonzo		    && ((function_start + offset) < *pc)) {
575233409Sgonzo			i.word =
576233409Sgonzo			    dtrace_fuword32((void *)(vm_offset_t)(function_start + offset));
577233409Sgonzo			switch (i.JType.op) {
578233409Sgonzo			case OP_SW:
579233409Sgonzo				/* look for saved registers on the stack */
580233409Sgonzo				if (i.IType.rs != 29)
581233409Sgonzo					break;
582233409Sgonzo				/* only restore the first one */
583233409Sgonzo				if (mask & (1 << i.IType.rt))
584233409Sgonzo					break;
585233409Sgonzo				mask |= (1 << i.IType.rt);
586233409Sgonzo				if (i.IType.rt == 31)
587233409Sgonzo					*ra = dtrace_fuword32((void *)(vm_offset_t)(*sp + (short)i.IType.imm));
588233409Sgonzo				break;
589233409Sgonzo
590233409Sgonzo			case OP_SD:
591233409Sgonzo				/* look for saved registers on the stack */
592233409Sgonzo				if (i.IType.rs != 29)
593233409Sgonzo					break;
594233409Sgonzo				/* only restore the first one */
595233409Sgonzo				if (mask & (1 << i.IType.rt))
596233409Sgonzo					break;
597233409Sgonzo				mask |= (1 << i.IType.rt);
598233409Sgonzo				/* ra */
599233409Sgonzo				if (i.IType.rt == 31)
600233409Sgonzo					*ra = dtrace_fuword64((void *)(vm_offset_t)(*sp + (short)i.IType.imm));
601233409Sgonzo			break;
602233409Sgonzo
603233409Sgonzo			case OP_ADDI:
604233409Sgonzo			case OP_ADDIU:
605233409Sgonzo			case OP_DADDI:
606233409Sgonzo			case OP_DADDIU:
607233409Sgonzo				/* look for stack pointer adjustment */
608233409Sgonzo				if (i.IType.rs != 29 || i.IType.rt != 29)
609233409Sgonzo					break;
610233409Sgonzo				stksize = -((short)i.IType.imm);
611233409Sgonzo			}
612233409Sgonzo
613233409Sgonzo			offset += sizeof(int);
614233525Sgonzo
615233525Sgonzo			if (*flags & CPU_DTRACE_FAULT)
616233525Sgonzo				goto fault;
617233409Sgonzo		}
618233409Sgonzo	}
619233409Sgonzo
620233409Sgonzo	/*
621233409Sgonzo	 * We reached the end of backtrace
622233409Sgonzo	 */
623233409Sgonzo	if (*pc == *ra)
624233409Sgonzo		return (-1);
625233409Sgonzo
626233409Sgonzo	*pc = *ra;
627233409Sgonzo	*sp += stksize;
628233409Sgonzo
629233409Sgonzo	return (0);
630233525Sgonzofault:
631233525Sgonzo	/*
632233525Sgonzo	 * We just got lost in backtrace, no big deal
633233525Sgonzo	 */
634233525Sgonzo	*flags &= ~CPU_DTRACE_FAULT;
635233525Sgonzo	return (-1);
636233409Sgonzo}
637233409Sgonzo
638233409Sgonzostatic int
639233409Sgonzodtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size)
640233409Sgonzo{
641233409Sgonzo
642233409Sgonzo	if (uaddr + size > VM_MAXUSER_ADDRESS || uaddr + size < uaddr) {
643233409Sgonzo		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
644233409Sgonzo		cpu_core[curcpu].cpuc_dtrace_illval = uaddr;
645233409Sgonzo		return (0);
646233409Sgonzo	}
647233409Sgonzo
648233409Sgonzo	return (1);
649233409Sgonzo}
650233409Sgonzo
651233409Sgonzovoid
652233409Sgonzodtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size,
653233409Sgonzo    volatile uint16_t *flags)
654233409Sgonzo{
655233409Sgonzo	if (dtrace_copycheck(uaddr, kaddr, size))
656233409Sgonzo		dtrace_copy(uaddr, kaddr, size);
657233409Sgonzo}
658233409Sgonzo
659233409Sgonzovoid
660233409Sgonzodtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size,
661233409Sgonzo    volatile uint16_t *flags)
662233409Sgonzo{
663233409Sgonzo	if (dtrace_copycheck(uaddr, kaddr, size))
664233409Sgonzo		dtrace_copy(kaddr, uaddr, size);
665233409Sgonzo}
666233409Sgonzo
667233409Sgonzovoid
668233409Sgonzodtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size,
669233409Sgonzo    volatile uint16_t *flags)
670233409Sgonzo{
671233409Sgonzo	if (dtrace_copycheck(uaddr, kaddr, size))
672233409Sgonzo		dtrace_copystr(uaddr, kaddr, size, flags);
673233409Sgonzo}
674233409Sgonzo
675233409Sgonzovoid
676233409Sgonzodtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size,
677233409Sgonzo    volatile uint16_t *flags)
678233409Sgonzo{
679233409Sgonzo	if (dtrace_copycheck(uaddr, kaddr, size))
680233409Sgonzo		dtrace_copystr(kaddr, uaddr, size, flags);
681233409Sgonzo}
682233409Sgonzo
683233409Sgonzouint8_t
684233409Sgonzodtrace_fuword8(void *uaddr)
685233409Sgonzo{
686233409Sgonzo	if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
687233409Sgonzo		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
688233409Sgonzo		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
689233409Sgonzo		return (0);
690233409Sgonzo	}
691233409Sgonzo	return (dtrace_fuword8_nocheck(uaddr));
692233409Sgonzo}
693233409Sgonzo
694233409Sgonzouint16_t
695233409Sgonzodtrace_fuword16(void *uaddr)
696233409Sgonzo{
697233409Sgonzo	if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
698233409Sgonzo		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
699233409Sgonzo		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
700233409Sgonzo		return (0);
701233409Sgonzo	}
702233409Sgonzo	return (dtrace_fuword16_nocheck(uaddr));
703233409Sgonzo}
704233409Sgonzo
705233409Sgonzouint32_t
706233409Sgonzodtrace_fuword32(void *uaddr)
707233409Sgonzo{
708233409Sgonzo	if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
709233409Sgonzo		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
710233409Sgonzo		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
711233409Sgonzo		return (0);
712233409Sgonzo	}
713233409Sgonzo	return (dtrace_fuword32_nocheck(uaddr));
714233409Sgonzo}
715233409Sgonzo
716233409Sgonzouint64_t
717233409Sgonzodtrace_fuword64(void *uaddr)
718233409Sgonzo{
719233409Sgonzo	if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
720233409Sgonzo		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
721233409Sgonzo		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
722233409Sgonzo		return (0);
723233409Sgonzo	}
724233409Sgonzo	return (dtrace_fuword64_nocheck(uaddr));
725233409Sgonzo}
726