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