hwpmc_mips.c revision 232846
1204635Sgnn/*-
2204635Sgnn * Copyright (c) 2010, George V. Neville-Neil <gnn@freebsd.org>
3204635Sgnn * All rights reserved.
4204635Sgnn *
5204635Sgnn * Redistribution and use in source and binary forms, with or without
6204635Sgnn * modification, are permitted provided that the following conditions
7204635Sgnn * are met:
8204635Sgnn * 1. Redistributions of source code must retain the above copyright
9204635Sgnn *    notice, this list of conditions and the following disclaimer.
10204635Sgnn * 2. Redistributions in binary form must reproduce the above copyright
11204635Sgnn *    notice, this list of conditions and the following disclaimer in the
12204635Sgnn *    documentation and/or other materials provided with the distribution.
13204635Sgnn *
14204635Sgnn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15204635Sgnn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16204635Sgnn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17204635Sgnn * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18204635Sgnn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19204635Sgnn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20204635Sgnn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21204635Sgnn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22204635Sgnn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23204635Sgnn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24204635Sgnn * SUCH DAMAGE.
25204635Sgnn *
26204635Sgnn */
27204635Sgnn
28204635Sgnn#include <sys/cdefs.h>
29204635Sgnn__FBSDID("$FreeBSD: head/sys/dev/hwpmc/hwpmc_mips.c 232846 2012-03-12 01:19:41Z gonzo $");
30204635Sgnn
31204635Sgnn#include <sys/param.h>
32204635Sgnn#include <sys/pmc.h>
33204635Sgnn#include <sys/systm.h>
34204635Sgnn
35204635Sgnn#include <machine/pmc_mdep.h>
36204635Sgnn#include <machine/md_var.h>
37232846Sgonzo#include <machine/mips_opcode.h>
38232846Sgonzo#include <machine/vmparam.h>
39204635Sgnn
40232846Sgonzo#if defined(__mips_n64)
41232846Sgonzo#	define	MIPS_IS_VALID_KERNELADDR(reg)	((((reg) & 3) == 0) && \
42232846Sgonzo					((vm_offset_t)(reg) >= MIPS_XKPHYS_START))
43232846Sgonzo#else
44232846Sgonzo#	define	MIPS_IS_VALID_KERNELADDR(reg)	((((reg) & 3) == 0) && \
45232846Sgonzo					((vm_offset_t)(reg) >= MIPS_KSEG0_START))
46232846Sgonzo#endif
47232846Sgonzo
48232846Sgonzo/*
49232846Sgonzo * We need some reasonable default to prevent backtrace code
50232846Sgonzo * from wandering too far
51232846Sgonzo */
52232846Sgonzo#define	MAX_FUNCTION_SIZE 0x10000
53232846Sgonzo#define	MAX_PROLOGUE_SIZE 0x100
54232846Sgonzo
55232846Sgonzostatic int
56232846Sgonzopmc_next_frame(register_t *pc, register_t *sp)
57232846Sgonzo{
58232846Sgonzo	InstFmt i;
59232846Sgonzo	uintptr_t va;
60232846Sgonzo	uint32_t instr, mask;
61232846Sgonzo	int more, stksize;
62232846Sgonzo	register_t ra = 0;
63232846Sgonzo
64232846Sgonzo	/* Jump here after a nonstandard (interrupt handler) frame */
65232846Sgonzo	stksize = 0;
66232846Sgonzo
67232846Sgonzo	/* check for bad SP: could foul up next frame */
68232846Sgonzo	if (!MIPS_IS_VALID_KERNELADDR(*sp)) {
69232846Sgonzo		goto error;
70232846Sgonzo	}
71232846Sgonzo
72232846Sgonzo	/* check for bad PC */
73232846Sgonzo	if (!MIPS_IS_VALID_KERNELADDR(*pc)) {
74232846Sgonzo		goto error;
75232846Sgonzo	}
76232846Sgonzo
77232846Sgonzo	/*
78232846Sgonzo	 * Find the beginning of the current subroutine by scanning
79232846Sgonzo	 * backwards from the current PC for the end of the previous
80232846Sgonzo	 * subroutine.
81232846Sgonzo	 */
82232846Sgonzo	va = *pc - sizeof(int);
83232846Sgonzo	while (1) {
84232846Sgonzo		instr = *((uint32_t *)va);
85232846Sgonzo
86232846Sgonzo		/* [d]addiu sp,sp,-X */
87232846Sgonzo		if (((instr & 0xffff8000) == 0x27bd8000)
88232846Sgonzo		    || ((instr & 0xffff8000) == 0x67bd8000))
89232846Sgonzo			break;
90232846Sgonzo
91232846Sgonzo		/* jr	ra */
92232846Sgonzo		if (instr == 0x03e00008) {
93232846Sgonzo			/* skip over branch-delay slot instruction */
94232846Sgonzo			va += 2 * sizeof(int);
95232846Sgonzo			break;
96232846Sgonzo		}
97232846Sgonzo
98232846Sgonzo		va -= sizeof(int);
99232846Sgonzo	}
100232846Sgonzo
101232846Sgonzo	/* skip over nulls which might separate .o files */
102232846Sgonzo	while ((instr = *((uint32_t *)va)) == 0)
103232846Sgonzo		va += sizeof(int);
104232846Sgonzo
105232846Sgonzo	/* scan forwards to find stack size and any saved registers */
106232846Sgonzo	stksize = 0;
107232846Sgonzo	more = 3;
108232846Sgonzo	mask = 0;
109232846Sgonzo	for (; more; va += sizeof(int),
110232846Sgonzo	    more = (more == 3) ? 3 : more - 1) {
111232846Sgonzo		/* stop if hit our current position */
112232846Sgonzo		if (va >= *pc)
113232846Sgonzo			break;
114232846Sgonzo		instr = *((uint32_t *)va);
115232846Sgonzo		i.word = instr;
116232846Sgonzo		switch (i.JType.op) {
117232846Sgonzo		case OP_SPECIAL:
118232846Sgonzo			switch (i.RType.func) {
119232846Sgonzo			case OP_JR:
120232846Sgonzo			case OP_JALR:
121232846Sgonzo				more = 2;	/* stop after next instruction */
122232846Sgonzo				break;
123232846Sgonzo
124232846Sgonzo			case OP_SYSCALL:
125232846Sgonzo			case OP_BREAK:
126232846Sgonzo				more = 1;	/* stop now */
127232846Sgonzo			};
128232846Sgonzo			break;
129232846Sgonzo
130232846Sgonzo		case OP_BCOND:
131232846Sgonzo		case OP_J:
132232846Sgonzo		case OP_JAL:
133232846Sgonzo		case OP_BEQ:
134232846Sgonzo		case OP_BNE:
135232846Sgonzo		case OP_BLEZ:
136232846Sgonzo		case OP_BGTZ:
137232846Sgonzo			more = 2;	/* stop after next instruction */
138232846Sgonzo			break;
139232846Sgonzo
140232846Sgonzo		case OP_COP0:
141232846Sgonzo		case OP_COP1:
142232846Sgonzo		case OP_COP2:
143232846Sgonzo		case OP_COP3:
144232846Sgonzo			switch (i.RType.rs) {
145232846Sgonzo			case OP_BCx:
146232846Sgonzo			case OP_BCy:
147232846Sgonzo				more = 2;	/* stop after next instruction */
148232846Sgonzo			};
149232846Sgonzo			break;
150232846Sgonzo
151232846Sgonzo		case OP_SW:
152232846Sgonzo		case OP_SD:
153232846Sgonzo			/* look for saved registers on the stack */
154232846Sgonzo			if (i.IType.rs != 29)
155232846Sgonzo				break;
156232846Sgonzo			/* only restore the first one */
157232846Sgonzo			if (mask & (1 << i.IType.rt))
158232846Sgonzo				break;
159232846Sgonzo			mask |= (1 << i.IType.rt);
160232846Sgonzo			if (i.IType.rt == 31)
161232846Sgonzo				ra = *((register_t *)(*sp + (short)i.IType.imm));
162232846Sgonzo			break;
163232846Sgonzo
164232846Sgonzo		case OP_ADDI:
165232846Sgonzo		case OP_ADDIU:
166232846Sgonzo		case OP_DADDI:
167232846Sgonzo		case OP_DADDIU:
168232846Sgonzo			/* look for stack pointer adjustment */
169232846Sgonzo			if (i.IType.rs != 29 || i.IType.rt != 29)
170232846Sgonzo				break;
171232846Sgonzo			stksize = -((short)i.IType.imm);
172232846Sgonzo		}
173232846Sgonzo	}
174232846Sgonzo
175232846Sgonzo	if (!MIPS_IS_VALID_KERNELADDR(ra))
176232846Sgonzo		return (-1);
177232846Sgonzo
178232846Sgonzo	*pc = ra;
179232846Sgonzo	*sp += stksize;
180232846Sgonzo
181232846Sgonzo	return (0);
182232846Sgonzo
183232846Sgonzoerror:
184232846Sgonzo	return (-1);
185232846Sgonzo}
186232846Sgonzo
187232846Sgonzostatic int
188232846Sgonzopmc_next_uframe(register_t *pc, register_t *sp, register_t *ra)
189232846Sgonzo{
190232846Sgonzo	int offset, registers_on_stack;
191232846Sgonzo	uint32_t opcode, mask;
192232846Sgonzo	register_t function_start;
193232846Sgonzo	int stksize;
194232846Sgonzo	InstFmt i;
195232846Sgonzo
196232846Sgonzo	registers_on_stack = 0;
197232846Sgonzo	mask = 0;
198232846Sgonzo	function_start = 0;
199232846Sgonzo	offset = 0;
200232846Sgonzo	stksize = 0;
201232846Sgonzo
202232846Sgonzo	while (offset < MAX_FUNCTION_SIZE) {
203232846Sgonzo		opcode = fuword32((void *)(*pc - offset));
204232846Sgonzo
205232846Sgonzo		/* [d]addiu sp, sp, -X*/
206232846Sgonzo		if (((opcode & 0xffff8000) == 0x27bd8000)
207232846Sgonzo		    || ((opcode & 0xffff8000) == 0x67bd8000)) {
208232846Sgonzo			function_start = *pc - offset;
209232846Sgonzo			registers_on_stack = 1;
210232846Sgonzo			break;
211232846Sgonzo		}
212232846Sgonzo
213232846Sgonzo		/* lui gp, X */
214232846Sgonzo		if ((opcode & 0xffff8000) == 0x3c1c0000) {
215232846Sgonzo			/*
216232846Sgonzo			 * Function might start with this instruction
217232846Sgonzo			 * Keep an eye on "jr ra" and sp correction
218232846Sgonzo			 * with positive value further on
219232846Sgonzo			 */
220232846Sgonzo			function_start = *pc - offset;
221232846Sgonzo		}
222232846Sgonzo
223232846Sgonzo		if (function_start) {
224232846Sgonzo			/*
225232846Sgonzo			 * Stop looking further. Possible end of
226232846Sgonzo			 * function instruction: it means there is no
227232846Sgonzo			 * stack modifications, sp is unchanged
228232846Sgonzo			 */
229232846Sgonzo
230232846Sgonzo			/* [d]addiu sp,sp,X */
231232846Sgonzo			if (((opcode & 0xffff8000) == 0x27bd0000)
232232846Sgonzo			    || ((opcode & 0xffff8000) == 0x67bd0000))
233232846Sgonzo				break;
234232846Sgonzo
235232846Sgonzo			if (opcode == 0x03e00008)
236232846Sgonzo				break;
237232846Sgonzo		}
238232846Sgonzo
239232846Sgonzo		offset += sizeof(int);
240232846Sgonzo	}
241232846Sgonzo
242232846Sgonzo	if (!function_start)
243232846Sgonzo		return (-1);
244232846Sgonzo
245232846Sgonzo	if (registers_on_stack) {
246232846Sgonzo		offset = 0;
247232846Sgonzo		while ((offset < MAX_PROLOGUE_SIZE)
248232846Sgonzo		    && ((function_start + offset) < *pc)) {
249232846Sgonzo			i.word = fuword32((void *)(function_start + offset));
250232846Sgonzo			switch (i.JType.op) {
251232846Sgonzo			case OP_SW:
252232846Sgonzo				/* look for saved registers on the stack */
253232846Sgonzo				if (i.IType.rs != 29)
254232846Sgonzo					break;
255232846Sgonzo				/* only restore the first one */
256232846Sgonzo				if (mask & (1 << i.IType.rt))
257232846Sgonzo					break;
258232846Sgonzo				mask |= (1 << i.IType.rt);
259232846Sgonzo				if (i.IType.rt == 31)
260232846Sgonzo					*ra = fuword32((void *)(*sp + (short)i.IType.imm));
261232846Sgonzo				break;
262232846Sgonzo
263232846Sgonzo#if defined(__mips_n64)
264232846Sgonzo			case OP_SD:
265232846Sgonzo				/* look for saved registers on the stack */
266232846Sgonzo				if (i.IType.rs != 29)
267232846Sgonzo					break;
268232846Sgonzo				/* only restore the first one */
269232846Sgonzo				if (mask & (1 << i.IType.rt))
270232846Sgonzo					break;
271232846Sgonzo				mask |= (1 << i.IType.rt);
272232846Sgonzo				/* ra */
273232846Sgonzo				if (i.IType.rt == 31)
274232846Sgonzo					*ra = fuword64((void *)(*sp + (short)i.IType.imm));
275232846Sgonzo			break;
276232846Sgonzo#endif
277232846Sgonzo
278232846Sgonzo			case OP_ADDI:
279232846Sgonzo			case OP_ADDIU:
280232846Sgonzo			case OP_DADDI:
281232846Sgonzo			case OP_DADDIU:
282232846Sgonzo				/* look for stack pointer adjustment */
283232846Sgonzo				if (i.IType.rs != 29 || i.IType.rt != 29)
284232846Sgonzo					break;
285232846Sgonzo				stksize = -((short)i.IType.imm);
286232846Sgonzo			}
287232846Sgonzo
288232846Sgonzo			offset += sizeof(int);
289232846Sgonzo		}
290232846Sgonzo	}
291232846Sgonzo
292232846Sgonzo	/*
293232846Sgonzo	 * We reached the end of backtrace
294232846Sgonzo	 */
295232846Sgonzo	if (*pc == *ra)
296232846Sgonzo		return (-1);
297232846Sgonzo
298232846Sgonzo	*pc = *ra;
299232846Sgonzo	*sp += stksize;
300232846Sgonzo
301232846Sgonzo	return (0);
302232846Sgonzo}
303232846Sgonzo
304204635Sgnnstruct pmc_mdep *
305204635Sgnnpmc_md_initialize()
306204635Sgnn{
307204635Sgnn  /*	if (cpu_class == CPU_CLASS_MIPS24K)*/
308204635Sgnn		return pmc_mips24k_initialize();
309204635Sgnn		/*	else
310204635Sgnn			return NULL;*/
311204635Sgnn}
312204635Sgnn
313204635Sgnnvoid
314204635Sgnnpmc_md_finalize(struct pmc_mdep *md)
315204635Sgnn{
316204635Sgnn  /*	if (cpu_class == CPU_CLASS_MIPS24K) */
317204635Sgnn		pmc_mips24k_finalize(md);
318204635Sgnn		/*	else
319204635Sgnn		KASSERT(0, ("[mips,%d] Unknown CPU Class 0x%x", __LINE__,
320204635Sgnn		cpu_class));*/
321204635Sgnn}
322204635Sgnn
323204635Sgnnint
324232846Sgonzopmc_save_kernel_callchain(uintptr_t *cc, int nframes,
325204635Sgnn    struct trapframe *tf)
326204635Sgnn{
327232846Sgonzo	register_t pc, ra, sp;
328232846Sgonzo	int frames = 0;
329232846Sgonzo
330232846Sgonzo	pc = (uint64_t)tf->pc;
331232846Sgonzo	sp = (uint64_t)tf->sp;
332232846Sgonzo	ra = (uint64_t)tf->ra;
333232846Sgonzo
334232846Sgonzo	/*
335232846Sgonzo	 * Unwind, and unwind, and unwind
336232846Sgonzo	 */
337232846Sgonzo	while (1) {
338232846Sgonzo		cc[frames++] = pc;
339232846Sgonzo		if (frames >= nframes)
340232846Sgonzo			break;
341232846Sgonzo
342232846Sgonzo		if (pmc_next_frame(&pc, &sp) < 0)
343232846Sgonzo			break;
344232846Sgonzo	}
345232846Sgonzo
346232846Sgonzo	return (frames);
347204635Sgnn}
348204635Sgnn
349204635Sgnnint
350232846Sgonzopmc_save_user_callchain(uintptr_t *cc, int nframes,
351204635Sgnn    struct trapframe *tf)
352204635Sgnn{
353232846Sgonzo	register_t pc, ra, sp;
354232846Sgonzo	int frames = 0;
355232846Sgonzo
356232846Sgonzo	pc = (uint64_t)tf->pc;
357232846Sgonzo	sp = (uint64_t)tf->sp;
358232846Sgonzo	ra = (uint64_t)tf->ra;
359232846Sgonzo
360232846Sgonzo	/*
361232846Sgonzo	 * Unwind, and unwind, and unwind
362232846Sgonzo	 */
363232846Sgonzo	while (1) {
364232846Sgonzo		cc[frames++] = pc;
365232846Sgonzo		if (frames >= nframes)
366232846Sgonzo			break;
367232846Sgonzo
368232846Sgonzo		if (pmc_next_uframe(&pc, &sp, &ra) < 0)
369232846Sgonzo			break;
370232846Sgonzo	}
371232846Sgonzo
372232846Sgonzo	return (frames);
373204635Sgnn}
374