xref: /openbmc/linux/arch/microblaze/kernel/ftrace.c (revision 6644c654)
17d241ff0SMichal Simek /*
27d241ff0SMichal Simek  * Ftrace support for Microblaze.
37d241ff0SMichal Simek  *
47d241ff0SMichal Simek  * Copyright (C) 2009 Michal Simek <monstr@monstr.eu>
57d241ff0SMichal Simek  * Copyright (C) 2009 PetaLogix
67d241ff0SMichal Simek  *
77d241ff0SMichal Simek  * Based on MIPS and PowerPC ftrace code
87d241ff0SMichal Simek  *
97d241ff0SMichal Simek  * This file is subject to the terms and conditions of the GNU General Public
107d241ff0SMichal Simek  * License. See the file "COPYING" in the main directory of this archive
117d241ff0SMichal Simek  * for more details.
127d241ff0SMichal Simek  */
137d241ff0SMichal Simek 
147d241ff0SMichal Simek #include <asm/cacheflush.h>
157d241ff0SMichal Simek #include <linux/ftrace.h>
167d241ff0SMichal Simek 
17a0d3e665SMichal Simek #ifdef CONFIG_FUNCTION_GRAPH_TRACER
18a0d3e665SMichal Simek /*
19a0d3e665SMichal Simek  * Hook the return address and push it in the stack of return addrs
20a0d3e665SMichal Simek  * in current thread info.
21a0d3e665SMichal Simek  */
prepare_ftrace_return(unsigned long * parent,unsigned long self_addr)22a0d3e665SMichal Simek void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr)
23a0d3e665SMichal Simek {
24a0d3e665SMichal Simek 	unsigned long old;
25556763e5SSteven Rostedt (VMware) 	int faulted;
26a0d3e665SMichal Simek 	unsigned long return_hooker = (unsigned long)
27a0d3e665SMichal Simek 				&return_to_handler;
28a0d3e665SMichal Simek 
29e1dc5007SSteven Rostedt (Red Hat) 	if (unlikely(ftrace_graph_is_dead()))
30e1dc5007SSteven Rostedt (Red Hat) 		return;
31e1dc5007SSteven Rostedt (Red Hat) 
32a0d3e665SMichal Simek 	if (unlikely(atomic_read(&current->tracing_graph_pause)))
33a0d3e665SMichal Simek 		return;
34a0d3e665SMichal Simek 
35a0d3e665SMichal Simek 	/*
36a0d3e665SMichal Simek 	 * Protect against fault, even if it shouldn't
37a0d3e665SMichal Simek 	 * happen. This tool is too much intrusive to
38a0d3e665SMichal Simek 	 * ignore such a protection.
39a0d3e665SMichal Simek 	 */
406bd55f0bSMichal Simek 	asm volatile("	1:	lwi	%0, %2, 0;"		\
416bd55f0bSMichal Simek 			"2:	swi	%3, %2, 0;"		\
426bd55f0bSMichal Simek 			"	addik	%1, r0, 0;"		\
436bd55f0bSMichal Simek 			"3:"					\
446bd55f0bSMichal Simek 			"	.section .fixup, \"ax\";"	\
456bd55f0bSMichal Simek 			"4:	brid	3b;"			\
466bd55f0bSMichal Simek 			"	addik	%1, r0, 1;"		\
476bd55f0bSMichal Simek 			"	.previous;"			\
486bd55f0bSMichal Simek 			"	.section __ex_table,\"a\";"	\
496bd55f0bSMichal Simek 			"	.word	1b,4b;"			\
506bd55f0bSMichal Simek 			"	.word	2b,4b;"			\
516bd55f0bSMichal Simek 			"	.previous;"			\
52a0d3e665SMichal Simek 			: "=&r" (old), "=r" (faulted)
53a0d3e665SMichal Simek 			: "r" (parent), "r" (return_hooker)
54a0d3e665SMichal Simek 	);
55a0d3e665SMichal Simek 
569e1491deSMichal Simek 	flush_dcache_range((u32)parent, (u32)parent + 4);
579e1491deSMichal Simek 	flush_icache_range((u32)parent, (u32)parent + 4);
589e1491deSMichal Simek 
59a0d3e665SMichal Simek 	if (unlikely(faulted)) {
60a0d3e665SMichal Simek 		ftrace_graph_stop();
61a0d3e665SMichal Simek 		WARN_ON(1);
62a0d3e665SMichal Simek 		return;
63a0d3e665SMichal Simek 	}
64a0d3e665SMichal Simek 
65556763e5SSteven Rostedt (VMware) 	if (function_graph_enter(old, self_addr, 0, NULL))
66a0d3e665SMichal Simek 		*parent = old;
67a0d3e665SMichal Simek }
68a0d3e665SMichal Simek #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
69a0d3e665SMichal Simek 
707d241ff0SMichal Simek #ifdef CONFIG_DYNAMIC_FTRACE
717d241ff0SMichal Simek /* save value to addr - it is save to do it in asm */
ftrace_modify_code(unsigned long addr,unsigned int value)727d241ff0SMichal Simek static int ftrace_modify_code(unsigned long addr, unsigned int value)
737d241ff0SMichal Simek {
747d241ff0SMichal Simek 	int faulted = 0;
757d241ff0SMichal Simek 
766bd55f0bSMichal Simek 	__asm__ __volatile__("	1:	swi	%2, %1, 0;"		\
776bd55f0bSMichal Simek 				"	addik	%0, r0, 0;"		\
786bd55f0bSMichal Simek 				"2:"					\
796bd55f0bSMichal Simek 				"	.section .fixup, \"ax\";"	\
806bd55f0bSMichal Simek 				"3:	brid	2b;"			\
816bd55f0bSMichal Simek 				"	addik	%0, r0, 1;"		\
826bd55f0bSMichal Simek 				"	.previous;"			\
836bd55f0bSMichal Simek 				"	.section __ex_table,\"a\";"	\
846bd55f0bSMichal Simek 				"	.word	1b,3b;"			\
856bd55f0bSMichal Simek 				"	.previous;"			\
867d241ff0SMichal Simek 				: "=r" (faulted)
877d241ff0SMichal Simek 				: "r" (addr), "r" (value)
887d241ff0SMichal Simek 	);
897d241ff0SMichal Simek 
907d241ff0SMichal Simek 	if (unlikely(faulted))
917d241ff0SMichal Simek 		return -EFAULT;
927d241ff0SMichal Simek 
939e1491deSMichal Simek 	flush_dcache_range(addr, addr + 4);
949e1491deSMichal Simek 	flush_icache_range(addr, addr + 4);
959e1491deSMichal Simek 
967d241ff0SMichal Simek 	return 0;
977d241ff0SMichal Simek }
987d241ff0SMichal Simek 
997d241ff0SMichal Simek #define MICROBLAZE_NOP 0x80000000
1007d241ff0SMichal Simek #define MICROBLAZE_BRI 0xb800000C
1017d241ff0SMichal Simek 
1027d241ff0SMichal Simek static unsigned int recorded; /* if save was or not */
1037d241ff0SMichal Simek static unsigned int imm; /* saving whole imm instruction */
1047d241ff0SMichal Simek 
1057d241ff0SMichal Simek /* There are two approaches howto solve ftrace_make nop function - look below */
1067d241ff0SMichal Simek #undef USE_FTRACE_NOP
1077d241ff0SMichal Simek 
1087d241ff0SMichal Simek #ifdef USE_FTRACE_NOP
1097d241ff0SMichal Simek static unsigned int bralid; /* saving whole bralid instruction */
1107d241ff0SMichal Simek #endif
1117d241ff0SMichal Simek 
ftrace_make_nop(struct module * mod,struct dyn_ftrace * rec,unsigned long addr)1127d241ff0SMichal Simek int ftrace_make_nop(struct module *mod,
1137d241ff0SMichal Simek 			struct dyn_ftrace *rec, unsigned long addr)
1147d241ff0SMichal Simek {
1157d241ff0SMichal Simek 	/* we have this part of code which we are working with
1167d241ff0SMichal Simek 	 * b000c000        imm     -16384
1177d241ff0SMichal Simek 	 * b9fc8e30        bralid  r15, -29136     // c0008e30 <_mcount>
1187d241ff0SMichal Simek 	 * 80000000        or      r0, r0, r0
1197d241ff0SMichal Simek 	 *
1207d241ff0SMichal Simek 	 * The first solution (!USE_FTRACE_NOP-could be called branch solution)
1217d241ff0SMichal Simek 	 * b000c000        bri	12 (0xC - jump to any other instruction)
1227d241ff0SMichal Simek 	 * b9fc8e30        bralid  r15, -29136     // c0008e30 <_mcount>
1237d241ff0SMichal Simek 	 * 80000000        or      r0, r0, r0
1247d241ff0SMichal Simek 	 * any other instruction
1257d241ff0SMichal Simek 	 *
1267d241ff0SMichal Simek 	 * The second solution (USE_FTRACE_NOP) - no jump just nops
1277d241ff0SMichal Simek 	 * 80000000        or      r0, r0, r0
1287d241ff0SMichal Simek 	 * 80000000        or      r0, r0, r0
1297d241ff0SMichal Simek 	 * 80000000        or      r0, r0, r0
1307d241ff0SMichal Simek 	 */
1317d241ff0SMichal Simek 	int ret = 0;
1327d241ff0SMichal Simek 
1337d241ff0SMichal Simek 	if (recorded == 0) {
1347d241ff0SMichal Simek 		recorded = 1;
1357d241ff0SMichal Simek 		imm = *(unsigned int *)rec->ip;
1367d241ff0SMichal Simek 		pr_debug("%s: imm:0x%x\n", __func__, imm);
1377d241ff0SMichal Simek #ifdef USE_FTRACE_NOP
1387d241ff0SMichal Simek 		bralid = *(unsigned int *)(rec->ip + 4);
1397d241ff0SMichal Simek 		pr_debug("%s: bralid 0x%x\n", __func__, bralid);
1407d241ff0SMichal Simek #endif /* USE_FTRACE_NOP */
1417d241ff0SMichal Simek 	}
1427d241ff0SMichal Simek 
1437d241ff0SMichal Simek #ifdef USE_FTRACE_NOP
1447d241ff0SMichal Simek 	ret = ftrace_modify_code(rec->ip, MICROBLAZE_NOP);
1457d241ff0SMichal Simek 	ret += ftrace_modify_code(rec->ip + 4, MICROBLAZE_NOP);
1467d241ff0SMichal Simek #else /* USE_FTRACE_NOP */
1477d241ff0SMichal Simek 	ret = ftrace_modify_code(rec->ip, MICROBLAZE_BRI);
1487d241ff0SMichal Simek #endif /* USE_FTRACE_NOP */
1497d241ff0SMichal Simek 	return ret;
1507d241ff0SMichal Simek }
1517d241ff0SMichal Simek 
1527d241ff0SMichal Simek /* I believe that first is called ftrace_make_nop before this function */
ftrace_make_call(struct dyn_ftrace * rec,unsigned long addr)1537d241ff0SMichal Simek int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
1547d241ff0SMichal Simek {
1557d241ff0SMichal Simek 	int ret;
1567d241ff0SMichal Simek 	pr_debug("%s: addr:0x%x, rec->ip: 0x%x, imm:0x%x\n",
1577d241ff0SMichal Simek 		__func__, (unsigned int)addr, (unsigned int)rec->ip, imm);
1587d241ff0SMichal Simek 	ret = ftrace_modify_code(rec->ip, imm);
1597d241ff0SMichal Simek #ifdef USE_FTRACE_NOP
1607d241ff0SMichal Simek 	pr_debug("%s: bralid:0x%x\n", __func__, bralid);
1617d241ff0SMichal Simek 	ret += ftrace_modify_code(rec->ip + 4, bralid);
1627d241ff0SMichal Simek #endif /* USE_FTRACE_NOP */
1637d241ff0SMichal Simek 	return ret;
1647d241ff0SMichal Simek }
1657d241ff0SMichal Simek 
ftrace_update_ftrace_func(ftrace_func_t func)1667d241ff0SMichal Simek int ftrace_update_ftrace_func(ftrace_func_t func)
1677d241ff0SMichal Simek {
1687d241ff0SMichal Simek 	unsigned long ip = (unsigned long)(&ftrace_call);
1697d241ff0SMichal Simek 	unsigned int upper = (unsigned int)func;
1707d241ff0SMichal Simek 	unsigned int lower = (unsigned int)func;
1717d241ff0SMichal Simek 	int ret = 0;
1727d241ff0SMichal Simek 
1737d241ff0SMichal Simek 	/* create proper saving to ftrace_call poll */
1747d241ff0SMichal Simek 	upper = 0xb0000000 + (upper >> 16); /* imm func_upper */
1757d241ff0SMichal Simek 	lower = 0x32800000 + (lower & 0xFFFF); /* addik r20, r0, func_lower */
1767d241ff0SMichal Simek 
1777d241ff0SMichal Simek 	pr_debug("%s: func=0x%x, ip=0x%x, upper=0x%x, lower=0x%x\n",
1787d241ff0SMichal Simek 		__func__, (unsigned int)func, (unsigned int)ip, upper, lower);
1797d241ff0SMichal Simek 
1807d241ff0SMichal Simek 	/* save upper and lower code */
1817d241ff0SMichal Simek 	ret = ftrace_modify_code(ip, upper);
1827d241ff0SMichal Simek 	ret += ftrace_modify_code(ip + 4, lower);
1837d241ff0SMichal Simek 
184d2bf98e6SSteven J. Magnani 	/* We just need to replace the rtsd r15, 8 with NOP */
185d2bf98e6SSteven J. Magnani 	ret += ftrace_modify_code((unsigned long)&ftrace_caller,
186d2bf98e6SSteven J. Magnani 				  MICROBLAZE_NOP);
1877d241ff0SMichal Simek 
1887d241ff0SMichal Simek 	return ret;
1897d241ff0SMichal Simek }
1907d241ff0SMichal Simek 
1914f911b0dSMichal Simek #ifdef CONFIG_FUNCTION_GRAPH_TRACER
1924f911b0dSMichal Simek unsigned int old_jump; /* saving place for jump instruction */
1934f911b0dSMichal Simek 
ftrace_enable_ftrace_graph_caller(void)1944f911b0dSMichal Simek int ftrace_enable_ftrace_graph_caller(void)
1954f911b0dSMichal Simek {
1964f911b0dSMichal Simek 	unsigned int ret;
1974f911b0dSMichal Simek 	unsigned long ip = (unsigned long)(&ftrace_call_graph);
1984f911b0dSMichal Simek 
1994f911b0dSMichal Simek 	old_jump = *(unsigned int *)ip; /* save jump over instruction */
2004f911b0dSMichal Simek 	ret = ftrace_modify_code(ip, MICROBLAZE_NOP);
2014f911b0dSMichal Simek 
2024f911b0dSMichal Simek 	pr_debug("%s: Replace instruction: 0x%x\n", __func__, old_jump);
2034f911b0dSMichal Simek 	return ret;
2044f911b0dSMichal Simek }
2054f911b0dSMichal Simek 
ftrace_disable_ftrace_graph_caller(void)2064f911b0dSMichal Simek int ftrace_disable_ftrace_graph_caller(void)
2074f911b0dSMichal Simek {
2084f911b0dSMichal Simek 	unsigned int ret;
2094f911b0dSMichal Simek 	unsigned long ip = (unsigned long)(&ftrace_call_graph);
2104f911b0dSMichal Simek 
2114f911b0dSMichal Simek 	ret = ftrace_modify_code(ip, old_jump);
2124f911b0dSMichal Simek 
2134f911b0dSMichal Simek 	pr_debug("%s\n", __func__);
2144f911b0dSMichal Simek 	return ret;
2154f911b0dSMichal Simek }
2164f911b0dSMichal Simek #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
2177d241ff0SMichal Simek #endif /* CONFIG_DYNAMIC_FTRACE */
218