1/*
2 * Copyright (C) 2000 Silicon Graphics, Inc.
3 *
4 * Written by Ulf Carlsson (ulfc@engr.sgi.com)
5 */
6
7#ifndef _ASM_KERNPROF_H
8#define _ASM_KERNPROF_H
9
10#ifdef __KERNEL__
11
12#include <asm/system.h>
13#include <asm/ptrace.h>
14#include <asm/processor.h>
15
16#define DFL_PC_RES 4		/* default PC resolution for this platform */
17
18#define in_firmware(regs) 0	/* never in the PROM during normal execution */
19
20extern char stext;
21extern char _etext;
22extern int prof_freq[];
23
24extern int setup_profiling_timer(unsigned int);
25
26typedef struct frame_info frame_info_t;
27
28struct frame_info {
29	unsigned long ra;
30	unsigned long pc;
31	unsigned long sp;
32	unsigned long top;
33};
34
35#define frame_get_pc(p)		((p)->pc)
36
37/*
38 * A function tests up its stack frame with the instructions
39 *
40 *	addiu	$sp,$sp,-stacksize
41 *	sw	$ra,stacksize-8($sp)
42 *
43 * The timer interrupt may arrive at any time including right at the moment
44 * that the new frame is being set up, so we need to distinguish a few cases.
45 */
46static __inline__ void get_top_frame(struct pt_regs *regs, frame_info_t *p)
47{
48	unsigned long pc = regs->cp0_epc;
49
50	pc = regs->cp0_epc;
51
52#ifndef CONFIG_SMP
53	{
54		extern unsigned long kernelsp;
55		p->top = kernelsp;
56	}
57#else
58	{
59		unsigned int lo, hi;
60		lo = read_32bit_cp0_register(CP0_WATCHLO);
61		hi = read_32bit_cp0_register(CP0_WATCHHI);
62		p->top = ((unsigned long) hi << 32) | lo;
63	}
64#endif
65
66	do {
67		unsigned int inst = *(unsigned int *)pc;
68		/* First we look for a ``addiu $sp,$sp,...'' and then we look
69		   for a ``jr $ra'' in case this is a leaf function without
70		   stack frame.  */
71		if ((inst & 0xffff0000) == 0x27bd0000) {
72			p->sp = regs->regs[29] - (short) (inst & 0xffff);
73			p->ra = *((unsigned long *)p->sp - 1);
74			p->pc = regs->cp0_epc;
75			return;
76		} else if (inst == 0x03e00008) {
77			/* N32 says that routines aren't restricted to a single
78			   exit block.  In that case we lose.  The thing is
79			   that the .mdebug format doesn't handle that either
80			   so we should be pretty safe.  */
81			p->sp = regs->regs[29];
82			p->ra = regs->regs[31];
83			p->pc = regs->cp0_epc;
84		}
85	} while (--pc > (unsigned long) &stext);
86
87	BUG();
88}
89
90static unsigned long this_pc(void)
91{
92	return (unsigned long)return_address();
93}
94
95/* Fabricate a stack frame that is sufficient to begin walking up the stack */
96static __inline__ int build_fake_frame(frame_info_t *p)
97{
98#ifndef CONFIG_SMP
99	{
100		extern unsigned long kernelsp;
101		p->top = kernelsp;
102	}
103#else
104	{
105		unsigned int lo, hi;
106		lo = read_32bit_cp0_register(CP0_WATCHLO);
107		hi = read_32bit_cp0_register(CP0_WATCHHI);
108		p->top = ((unsigned long) hi << 32) | lo;
109	}
110#endif
111	__asm__ __volatile__("sw\t$29,%0\t\n" : "=m" (p->sp));
112	p->pc = this_pc();
113	return 1;
114}
115
116static __inline__ int last_frame(frame_info_t *p)
117{
118	if (p->sp < (unsigned long) current + sizeof(*current))
119		BUG();
120
121	return (p->sp < p->top);
122}
123
124static __inline__ int get_next_frame(frame_info_t *p)
125{
126	unsigned int *sp = (unsigned int *)p->sp;
127	unsigned int *pc = (unsigned int *)p->pc;
128
129	if (last_frame(p))
130		return 0;
131
132	/*
133	 * First, scan backwards to find the stack-decrement that signals the
134	 * beginning of this routine in which we're inlined.  That tells us
135	 * how to roll back the stack.
136	 */
137	do {
138		unsigned int inst = *pc;
139		/* Look for a ``addiu $sp,$sp,...'' */
140		if ((inst & 0xffff0000) == 0x27bd0000) {
141			p->sp = (unsigned long)sp - (short) (inst & 0xffff);
142			break;
143		}
144	} while (--pc > (unsigned int *)&stext);
145
146	if (pc == (unsigned int *)&stext)
147		return 0;
148
149	/*
150	 * Now scan forwards to find the $ra-save, so we can decode the
151	 * instruction and retrieve the return address from the stack.
152	 */
153	pc++;
154	do {
155		unsigned int inst = *pc;
156		/* Look for a ``sw $ra,NN($sp)'' */
157		if ((inst & 0xffff0000) == 0xafbf0000) {
158			p->pc = *(unsigned long *)((unsigned long)sp + (short)(inst & 0xffff));
159			return 1;
160		}
161	} while (++pc <= (unsigned int *)&_etext);
162
163	return 0;
164}
165
166#define supports_call_graph prof_have_mcount
167
168#define get_prof_freq() prof_freq[0]
169
170/* No performance counters for the time being */
171
172#define have_perfctr() 0
173#define valid_perfctr_event(e) 0
174#define valid_perfctr_freq(n) 0
175#define perfctr_reload(x)
176#define __perfctr_stop()
177#define __perfctr_commence(x,y)
178
179#endif /* __KERNEL__ */
180
181#endif /* !_ASM_KERNPROF_H */
182