| Mike Frysinger | 1ee76d7 | 2009-06-10 04:45:29 -0400 | [diff] [blame] | 1 | /* | 
|  | 2 | * ftrace graph code | 
|  | 3 | * | 
| Mike Frysinger | f507442 | 2010-07-21 09:13:02 -0400 | [diff] [blame] | 4 | * Copyright (C) 2009-2010 Analog Devices Inc. | 
| Mike Frysinger | 1ee76d7 | 2009-06-10 04:45:29 -0400 | [diff] [blame] | 5 | * Licensed under the GPL-2 or later. | 
|  | 6 | */ | 
|  | 7 |  | 
|  | 8 | #include <linux/ftrace.h> | 
|  | 9 | #include <linux/kernel.h> | 
|  | 10 | #include <linux/sched.h> | 
| Mike Frysinger | f507442 | 2010-07-21 09:13:02 -0400 | [diff] [blame] | 11 | #include <linux/uaccess.h> | 
| Mike Frysinger | 1ee76d7 | 2009-06-10 04:45:29 -0400 | [diff] [blame] | 12 | #include <asm/atomic.h> | 
| Mike Frysinger | f507442 | 2010-07-21 09:13:02 -0400 | [diff] [blame] | 13 | #include <asm/cacheflush.h> | 
|  | 14 |  | 
|  | 15 | #ifdef CONFIG_DYNAMIC_FTRACE | 
|  | 16 |  | 
|  | 17 | static const unsigned char mnop[] = { | 
|  | 18 | 0x03, 0xc0, 0x00, 0x18, /* MNOP; */ | 
|  | 19 | 0x03, 0xc0, 0x00, 0x18, /* MNOP; */ | 
|  | 20 | }; | 
|  | 21 |  | 
|  | 22 | static void bfin_make_pcrel24(unsigned char *insn, unsigned long src, | 
|  | 23 | unsigned long dst) | 
|  | 24 | { | 
|  | 25 | uint32_t pcrel = (dst - src) >> 1; | 
|  | 26 | insn[0] = pcrel >> 16; | 
|  | 27 | insn[1] = 0xe3; | 
|  | 28 | insn[2] = pcrel; | 
|  | 29 | insn[3] = pcrel >> 8; | 
|  | 30 | } | 
|  | 31 | #define bfin_make_pcrel24(insn, src, dst) bfin_make_pcrel24(insn, src, (unsigned long)(dst)) | 
|  | 32 |  | 
|  | 33 | static int ftrace_modify_code(unsigned long ip, const unsigned char *code, | 
|  | 34 | unsigned long len) | 
|  | 35 | { | 
|  | 36 | int ret = probe_kernel_write((void *)ip, (void *)code, len); | 
|  | 37 | flush_icache_range(ip, ip + len); | 
|  | 38 | return ret; | 
|  | 39 | } | 
|  | 40 |  | 
|  | 41 | int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, | 
|  | 42 | unsigned long addr) | 
|  | 43 | { | 
|  | 44 | /* Turn the mcount call site into two MNOPs as those are 32bit insns */ | 
|  | 45 | return ftrace_modify_code(rec->ip, mnop, sizeof(mnop)); | 
|  | 46 | } | 
|  | 47 |  | 
|  | 48 | int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | 
|  | 49 | { | 
|  | 50 | /* Restore the mcount call site */ | 
|  | 51 | unsigned char call[8]; | 
|  | 52 | call[0] = 0x67; /* [--SP] = RETS; */ | 
|  | 53 | call[1] = 0x01; | 
|  | 54 | bfin_make_pcrel24(&call[2], rec->ip + 2, addr); | 
|  | 55 | call[6] = 0x27; /* RETS = [SP++]; */ | 
|  | 56 | call[7] = 0x01; | 
|  | 57 | return ftrace_modify_code(rec->ip, call, sizeof(call)); | 
|  | 58 | } | 
|  | 59 |  | 
|  | 60 | int ftrace_update_ftrace_func(ftrace_func_t func) | 
|  | 61 | { | 
|  | 62 | unsigned char call[4]; | 
|  | 63 | unsigned long ip = (unsigned long)&ftrace_call; | 
|  | 64 | bfin_make_pcrel24(call, ip, func); | 
|  | 65 | return ftrace_modify_code(ip, call, sizeof(call)); | 
|  | 66 | } | 
|  | 67 |  | 
|  | 68 | int __init ftrace_dyn_arch_init(void *data) | 
|  | 69 | { | 
|  | 70 | /* return value is done indirectly via data */ | 
|  | 71 | *(unsigned long *)data = 0; | 
|  | 72 |  | 
|  | 73 | return 0; | 
|  | 74 | } | 
|  | 75 |  | 
|  | 76 | #endif | 
| Mike Frysinger | 1ee76d7 | 2009-06-10 04:45:29 -0400 | [diff] [blame] | 77 |  | 
|  | 78 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | 
|  | 79 |  | 
| Mike Frysinger | f507442 | 2010-07-21 09:13:02 -0400 | [diff] [blame] | 80 | # ifdef CONFIG_DYNAMIC_FTRACE | 
|  | 81 |  | 
|  | 82 | extern void ftrace_graph_call(void); | 
|  | 83 |  | 
|  | 84 | int ftrace_enable_ftrace_graph_caller(void) | 
|  | 85 | { | 
|  | 86 | unsigned long ip = (unsigned long)&ftrace_graph_call; | 
|  | 87 | uint16_t jump_pcrel12 = ((unsigned long)&ftrace_graph_caller - ip) >> 1; | 
|  | 88 | jump_pcrel12 |= 0x2000; | 
|  | 89 | return ftrace_modify_code(ip, (void *)&jump_pcrel12, sizeof(jump_pcrel12)); | 
|  | 90 | } | 
|  | 91 |  | 
|  | 92 | int ftrace_disable_ftrace_graph_caller(void) | 
|  | 93 | { | 
|  | 94 | return ftrace_modify_code((unsigned long)&ftrace_graph_call, empty_zero_page, 2); | 
|  | 95 | } | 
|  | 96 |  | 
|  | 97 | # endif | 
|  | 98 |  | 
| Mike Frysinger | 1ee76d7 | 2009-06-10 04:45:29 -0400 | [diff] [blame] | 99 | /* | 
|  | 100 | * Hook the return address and push it in the stack of return addrs | 
|  | 101 | * in current thread info. | 
|  | 102 | */ | 
| Mike Frysinger | b73faf7 | 2010-01-22 07:59:32 -0500 | [diff] [blame] | 103 | void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, | 
|  | 104 | unsigned long frame_pointer) | 
| Mike Frysinger | 1ee76d7 | 2009-06-10 04:45:29 -0400 | [diff] [blame] | 105 | { | 
|  | 106 | struct ftrace_graph_ent trace; | 
|  | 107 | unsigned long return_hooker = (unsigned long)&return_to_handler; | 
|  | 108 |  | 
|  | 109 | if (unlikely(atomic_read(¤t->tracing_graph_pause))) | 
|  | 110 | return; | 
|  | 111 |  | 
| Mike Frysinger | b73faf7 | 2010-01-22 07:59:32 -0500 | [diff] [blame] | 112 | if (ftrace_push_return_trace(*parent, self_addr, &trace.depth, | 
|  | 113 | frame_pointer) == -EBUSY) | 
| Mike Frysinger | 1ee76d7 | 2009-06-10 04:45:29 -0400 | [diff] [blame] | 114 | return; | 
|  | 115 |  | 
|  | 116 | trace.func = self_addr; | 
|  | 117 |  | 
|  | 118 | /* Only trace if the calling function expects to */ | 
|  | 119 | if (!ftrace_graph_entry(&trace)) { | 
|  | 120 | current->curr_ret_stack--; | 
|  | 121 | return; | 
|  | 122 | } | 
|  | 123 |  | 
|  | 124 | /* all is well in the world !  hijack RETS ... */ | 
|  | 125 | *parent = return_hooker; | 
|  | 126 | } | 
|  | 127 |  | 
|  | 128 | #endif |