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;
25*556763e5SSteven 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(¤t->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
65*556763e5SSteven 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