hwpmc_mips.c revision 232846
1/*-
2 * Copyright (c) 2010, George V. Neville-Neil <gnn@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: head/sys/dev/hwpmc/hwpmc_mips.c 232846 2012-03-12 01:19:41Z gonzo $");
30
31#include <sys/param.h>
32#include <sys/pmc.h>
33#include <sys/systm.h>
34
35#include <machine/pmc_mdep.h>
36#include <machine/md_var.h>
37#include <machine/mips_opcode.h>
38#include <machine/vmparam.h>
39
40#if defined(__mips_n64)
41#	define	MIPS_IS_VALID_KERNELADDR(reg)	((((reg) & 3) == 0) && \
42					((vm_offset_t)(reg) >= MIPS_XKPHYS_START))
43#else
44#	define	MIPS_IS_VALID_KERNELADDR(reg)	((((reg) & 3) == 0) && \
45					((vm_offset_t)(reg) >= MIPS_KSEG0_START))
46#endif
47
48/*
49 * We need some reasonable default to prevent backtrace code
50 * from wandering too far
51 */
52#define	MAX_FUNCTION_SIZE 0x10000
53#define	MAX_PROLOGUE_SIZE 0x100
54
55static int
56pmc_next_frame(register_t *pc, register_t *sp)
57{
58	InstFmt i;
59	uintptr_t va;
60	uint32_t instr, mask;
61	int more, stksize;
62	register_t ra = 0;
63
64	/* Jump here after a nonstandard (interrupt handler) frame */
65	stksize = 0;
66
67	/* check for bad SP: could foul up next frame */
68	if (!MIPS_IS_VALID_KERNELADDR(*sp)) {
69		goto error;
70	}
71
72	/* check for bad PC */
73	if (!MIPS_IS_VALID_KERNELADDR(*pc)) {
74		goto error;
75	}
76
77	/*
78	 * Find the beginning of the current subroutine by scanning
79	 * backwards from the current PC for the end of the previous
80	 * subroutine.
81	 */
82	va = *pc - sizeof(int);
83	while (1) {
84		instr = *((uint32_t *)va);
85
86		/* [d]addiu sp,sp,-X */
87		if (((instr & 0xffff8000) == 0x27bd8000)
88		    || ((instr & 0xffff8000) == 0x67bd8000))
89			break;
90
91		/* jr	ra */
92		if (instr == 0x03e00008) {
93			/* skip over branch-delay slot instruction */
94			va += 2 * sizeof(int);
95			break;
96		}
97
98		va -= sizeof(int);
99	}
100
101	/* skip over nulls which might separate .o files */
102	while ((instr = *((uint32_t *)va)) == 0)
103		va += sizeof(int);
104
105	/* scan forwards to find stack size and any saved registers */
106	stksize = 0;
107	more = 3;
108	mask = 0;
109	for (; more; va += sizeof(int),
110	    more = (more == 3) ? 3 : more - 1) {
111		/* stop if hit our current position */
112		if (va >= *pc)
113			break;
114		instr = *((uint32_t *)va);
115		i.word = instr;
116		switch (i.JType.op) {
117		case OP_SPECIAL:
118			switch (i.RType.func) {
119			case OP_JR:
120			case OP_JALR:
121				more = 2;	/* stop after next instruction */
122				break;
123
124			case OP_SYSCALL:
125			case OP_BREAK:
126				more = 1;	/* stop now */
127			};
128			break;
129
130		case OP_BCOND:
131		case OP_J:
132		case OP_JAL:
133		case OP_BEQ:
134		case OP_BNE:
135		case OP_BLEZ:
136		case OP_BGTZ:
137			more = 2;	/* stop after next instruction */
138			break;
139
140		case OP_COP0:
141		case OP_COP1:
142		case OP_COP2:
143		case OP_COP3:
144			switch (i.RType.rs) {
145			case OP_BCx:
146			case OP_BCy:
147				more = 2;	/* stop after next instruction */
148			};
149			break;
150
151		case OP_SW:
152		case OP_SD:
153			/* look for saved registers on the stack */
154			if (i.IType.rs != 29)
155				break;
156			/* only restore the first one */
157			if (mask & (1 << i.IType.rt))
158				break;
159			mask |= (1 << i.IType.rt);
160			if (i.IType.rt == 31)
161				ra = *((register_t *)(*sp + (short)i.IType.imm));
162			break;
163
164		case OP_ADDI:
165		case OP_ADDIU:
166		case OP_DADDI:
167		case OP_DADDIU:
168			/* look for stack pointer adjustment */
169			if (i.IType.rs != 29 || i.IType.rt != 29)
170				break;
171			stksize = -((short)i.IType.imm);
172		}
173	}
174
175	if (!MIPS_IS_VALID_KERNELADDR(ra))
176		return (-1);
177
178	*pc = ra;
179	*sp += stksize;
180
181	return (0);
182
183error:
184	return (-1);
185}
186
187static int
188pmc_next_uframe(register_t *pc, register_t *sp, register_t *ra)
189{
190	int offset, registers_on_stack;
191	uint32_t opcode, mask;
192	register_t function_start;
193	int stksize;
194	InstFmt i;
195
196	registers_on_stack = 0;
197	mask = 0;
198	function_start = 0;
199	offset = 0;
200	stksize = 0;
201
202	while (offset < MAX_FUNCTION_SIZE) {
203		opcode = fuword32((void *)(*pc - offset));
204
205		/* [d]addiu sp, sp, -X*/
206		if (((opcode & 0xffff8000) == 0x27bd8000)
207		    || ((opcode & 0xffff8000) == 0x67bd8000)) {
208			function_start = *pc - offset;
209			registers_on_stack = 1;
210			break;
211		}
212
213		/* lui gp, X */
214		if ((opcode & 0xffff8000) == 0x3c1c0000) {
215			/*
216			 * Function might start with this instruction
217			 * Keep an eye on "jr ra" and sp correction
218			 * with positive value further on
219			 */
220			function_start = *pc - offset;
221		}
222
223		if (function_start) {
224			/*
225			 * Stop looking further. Possible end of
226			 * function instruction: it means there is no
227			 * stack modifications, sp is unchanged
228			 */
229
230			/* [d]addiu sp,sp,X */
231			if (((opcode & 0xffff8000) == 0x27bd0000)
232			    || ((opcode & 0xffff8000) == 0x67bd0000))
233				break;
234
235			if (opcode == 0x03e00008)
236				break;
237		}
238
239		offset += sizeof(int);
240	}
241
242	if (!function_start)
243		return (-1);
244
245	if (registers_on_stack) {
246		offset = 0;
247		while ((offset < MAX_PROLOGUE_SIZE)
248		    && ((function_start + offset) < *pc)) {
249			i.word = fuword32((void *)(function_start + offset));
250			switch (i.JType.op) {
251			case OP_SW:
252				/* look for saved registers on the stack */
253				if (i.IType.rs != 29)
254					break;
255				/* only restore the first one */
256				if (mask & (1 << i.IType.rt))
257					break;
258				mask |= (1 << i.IType.rt);
259				if (i.IType.rt == 31)
260					*ra = fuword32((void *)(*sp + (short)i.IType.imm));
261				break;
262
263#if defined(__mips_n64)
264			case OP_SD:
265				/* look for saved registers on the stack */
266				if (i.IType.rs != 29)
267					break;
268				/* only restore the first one */
269				if (mask & (1 << i.IType.rt))
270					break;
271				mask |= (1 << i.IType.rt);
272				/* ra */
273				if (i.IType.rt == 31)
274					*ra = fuword64((void *)(*sp + (short)i.IType.imm));
275			break;
276#endif
277
278			case OP_ADDI:
279			case OP_ADDIU:
280			case OP_DADDI:
281			case OP_DADDIU:
282				/* look for stack pointer adjustment */
283				if (i.IType.rs != 29 || i.IType.rt != 29)
284					break;
285				stksize = -((short)i.IType.imm);
286			}
287
288			offset += sizeof(int);
289		}
290	}
291
292	/*
293	 * We reached the end of backtrace
294	 */
295	if (*pc == *ra)
296		return (-1);
297
298	*pc = *ra;
299	*sp += stksize;
300
301	return (0);
302}
303
304struct pmc_mdep *
305pmc_md_initialize()
306{
307  /*	if (cpu_class == CPU_CLASS_MIPS24K)*/
308		return pmc_mips24k_initialize();
309		/*	else
310			return NULL;*/
311}
312
313void
314pmc_md_finalize(struct pmc_mdep *md)
315{
316  /*	if (cpu_class == CPU_CLASS_MIPS24K) */
317		pmc_mips24k_finalize(md);
318		/*	else
319		KASSERT(0, ("[mips,%d] Unknown CPU Class 0x%x", __LINE__,
320		cpu_class));*/
321}
322
323int
324pmc_save_kernel_callchain(uintptr_t *cc, int nframes,
325    struct trapframe *tf)
326{
327	register_t pc, ra, sp;
328	int frames = 0;
329
330	pc = (uint64_t)tf->pc;
331	sp = (uint64_t)tf->sp;
332	ra = (uint64_t)tf->ra;
333
334	/*
335	 * Unwind, and unwind, and unwind
336	 */
337	while (1) {
338		cc[frames++] = pc;
339		if (frames >= nframes)
340			break;
341
342		if (pmc_next_frame(&pc, &sp) < 0)
343			break;
344	}
345
346	return (frames);
347}
348
349int
350pmc_save_user_callchain(uintptr_t *cc, int nframes,
351    struct trapframe *tf)
352{
353	register_t pc, ra, sp;
354	int frames = 0;
355
356	pc = (uint64_t)tf->pc;
357	sp = (uint64_t)tf->sp;
358	ra = (uint64_t)tf->ra;
359
360	/*
361	 * Unwind, and unwind, and unwind
362	 */
363	while (1) {
364		cc[frames++] = pc;
365		if (frames >= nframes)
366			break;
367
368		if (pmc_next_uframe(&pc, &sp, &ra) < 0)
369			break;
370	}
371
372	return (frames);
373}
374