• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/arch/microblaze/kernel/
1/*
2 * Ftrace support for Microblaze.
3 *
4 * Copyright (C) 2009 Michal Simek <monstr@monstr.eu>
5 * Copyright (C) 2009 PetaLogix
6 *
7 * Based on MIPS and PowerPC ftrace code
8 *
9 * This file is subject to the terms and conditions of the GNU General Public
10 * License. See the file "COPYING" in the main directory of this archive
11 * for more details.
12 */
13
14#include <asm/cacheflush.h>
15#include <linux/ftrace.h>
16
17#ifdef CONFIG_FUNCTION_GRAPH_TRACER
18/*
19 * Hook the return address and push it in the stack of return addrs
20 * in current thread info.
21 */
22void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr)
23{
24	unsigned long old;
25	int faulted, err;
26	struct ftrace_graph_ent trace;
27	unsigned long return_hooker = (unsigned long)
28				&return_to_handler;
29
30	if (unlikely(atomic_read(&current->tracing_graph_pause)))
31		return;
32
33	/*
34	 * Protect against fault, even if it shouldn't
35	 * happen. This tool is too much intrusive to
36	 * ignore such a protection.
37	 */
38	asm volatile("	1:	lwi	%0, %2, 0;		\
39			2:	swi	%3, %2, 0;		\
40				addik	%1, r0, 0;		\
41			3:					\
42				.section .fixup, \"ax\";	\
43			4:	brid	3b;			\
44				addik	%1, r0, 1;		\
45				.previous;			\
46				.section __ex_table,\"a\";	\
47				.word	1b,4b;			\
48				.word	2b,4b;			\
49				.previous;"			\
50			: "=&r" (old), "=r" (faulted)
51			: "r" (parent), "r" (return_hooker)
52	);
53
54	if (unlikely(faulted)) {
55		ftrace_graph_stop();
56		WARN_ON(1);
57		return;
58	}
59
60	err = ftrace_push_return_trace(old, self_addr, &trace.depth, 0);
61	if (err == -EBUSY) {
62		*parent = old;
63		return;
64	}
65
66	trace.func = self_addr;
67	/* Only trace if the calling function expects to */
68	if (!ftrace_graph_entry(&trace)) {
69		current->curr_ret_stack--;
70		*parent = old;
71	}
72}
73#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
74
75#ifdef CONFIG_DYNAMIC_FTRACE
76/* save value to addr - it is save to do it in asm */
77static int ftrace_modify_code(unsigned long addr, unsigned int value)
78{
79	int faulted = 0;
80
81	__asm__ __volatile__("	1:	swi	%2, %1, 0;		\
82					addik	%0, r0, 0;		\
83				2:					\
84					.section .fixup, \"ax\";	\
85				3:	brid	2b;			\
86					addik	%0, r0, 1;		\
87					.previous;			\
88					.section __ex_table,\"a\";	\
89					.word	1b,3b;			\
90					.previous;"			\
91				: "=r" (faulted)
92				: "r" (addr), "r" (value)
93	);
94
95	if (unlikely(faulted))
96		return -EFAULT;
97
98	return 0;
99}
100
101#define MICROBLAZE_NOP 0x80000000
102#define MICROBLAZE_BRI 0xb800000C
103
104static unsigned int recorded; /* if save was or not */
105static unsigned int imm; /* saving whole imm instruction */
106
107/* There are two approaches howto solve ftrace_make nop function - look below */
108#undef USE_FTRACE_NOP
109
110#ifdef USE_FTRACE_NOP
111static unsigned int bralid; /* saving whole bralid instruction */
112#endif
113
114int ftrace_make_nop(struct module *mod,
115			struct dyn_ftrace *rec, unsigned long addr)
116{
117	/* we have this part of code which we are working with
118	 * b000c000        imm     -16384
119	 * b9fc8e30        bralid  r15, -29136     // c0008e30 <_mcount>
120	 * 80000000        or      r0, r0, r0
121	 *
122	 * The first solution (!USE_FTRACE_NOP-could be called branch solution)
123	 * b000c000        bri	12 (0xC - jump to any other instruction)
124	 * b9fc8e30        bralid  r15, -29136     // c0008e30 <_mcount>
125	 * 80000000        or      r0, r0, r0
126	 * any other instruction
127	 *
128	 * The second solution (USE_FTRACE_NOP) - no jump just nops
129	 * 80000000        or      r0, r0, r0
130	 * 80000000        or      r0, r0, r0
131	 * 80000000        or      r0, r0, r0
132	 */
133	int ret = 0;
134
135	if (recorded == 0) {
136		recorded = 1;
137		imm = *(unsigned int *)rec->ip;
138		pr_debug("%s: imm:0x%x\n", __func__, imm);
139#ifdef USE_FTRACE_NOP
140		bralid = *(unsigned int *)(rec->ip + 4);
141		pr_debug("%s: bralid 0x%x\n", __func__, bralid);
142#endif /* USE_FTRACE_NOP */
143	}
144
145#ifdef USE_FTRACE_NOP
146	ret = ftrace_modify_code(rec->ip, MICROBLAZE_NOP);
147	ret += ftrace_modify_code(rec->ip + 4, MICROBLAZE_NOP);
148#else /* USE_FTRACE_NOP */
149	ret = ftrace_modify_code(rec->ip, MICROBLAZE_BRI);
150#endif /* USE_FTRACE_NOP */
151	return ret;
152}
153
154/* I believe that first is called ftrace_make_nop before this function */
155int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
156{
157	int ret;
158	pr_debug("%s: addr:0x%x, rec->ip: 0x%x, imm:0x%x\n",
159		__func__, (unsigned int)addr, (unsigned int)rec->ip, imm);
160	ret = ftrace_modify_code(rec->ip, imm);
161#ifdef USE_FTRACE_NOP
162	pr_debug("%s: bralid:0x%x\n", __func__, bralid);
163	ret += ftrace_modify_code(rec->ip + 4, bralid);
164#endif /* USE_FTRACE_NOP */
165	return ret;
166}
167
168int __init ftrace_dyn_arch_init(void *data)
169{
170	/* The return code is retured via data */
171	*(unsigned long *)data = 0;
172
173	return 0;
174}
175
176int ftrace_update_ftrace_func(ftrace_func_t func)
177{
178	unsigned long ip = (unsigned long)(&ftrace_call);
179	unsigned int upper = (unsigned int)func;
180	unsigned int lower = (unsigned int)func;
181	int ret = 0;
182
183	/* create proper saving to ftrace_call poll */
184	upper = 0xb0000000 + (upper >> 16); /* imm func_upper */
185	lower = 0x32800000 + (lower & 0xFFFF); /* addik r20, r0, func_lower */
186
187	pr_debug("%s: func=0x%x, ip=0x%x, upper=0x%x, lower=0x%x\n",
188		__func__, (unsigned int)func, (unsigned int)ip, upper, lower);
189
190	/* save upper and lower code */
191	ret = ftrace_modify_code(ip, upper);
192	ret += ftrace_modify_code(ip + 4, lower);
193
194	/* We just need to replace the rtsd r15, 8 with NOP */
195	ret += ftrace_modify_code((unsigned long)&ftrace_caller,
196				  MICROBLAZE_NOP);
197
198	/* All changes are done - lets do caches consistent */
199	flush_icache();
200	return ret;
201}
202
203#ifdef CONFIG_FUNCTION_GRAPH_TRACER
204unsigned int old_jump; /* saving place for jump instruction */
205
206int ftrace_enable_ftrace_graph_caller(void)
207{
208	unsigned int ret;
209	unsigned long ip = (unsigned long)(&ftrace_call_graph);
210
211	old_jump = *(unsigned int *)ip; /* save jump over instruction */
212	ret = ftrace_modify_code(ip, MICROBLAZE_NOP);
213	flush_icache();
214
215	pr_debug("%s: Replace instruction: 0x%x\n", __func__, old_jump);
216	return ret;
217}
218
219int ftrace_disable_ftrace_graph_caller(void)
220{
221	unsigned int ret;
222	unsigned long ip = (unsigned long)(&ftrace_call_graph);
223
224	ret = ftrace_modify_code(ip, old_jump);
225	flush_icache();
226
227	pr_debug("%s\n", __func__);
228	return ret;
229}
230#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
231#endif /* CONFIG_DYNAMIC_FTRACE */
232