blob: 55828149e01e90fccf6ac2f9f4fd677b496c8766 [file] [log] [blame]
Steven Rostedt3d083392008-05-12 21:20:42 +02001/*
2 * Code for replacing ftrace calls with jumps.
3 *
4 * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
5 *
6 * Thanks goes to Ingo Molnar, for suggesting the idea.
7 * Mathieu Desnoyers, for suggesting postponing the modifications.
8 * Arjan van de Ven, for keeping me straight, and explaining to me
9 * the dangers of modifying code on the run.
10 */
11
12#include <linux/spinlock.h>
13#include <linux/hardirq.h>
14#include <linux/ftrace.h>
15#include <linux/percpu.h>
16#include <linux/init.h>
17#include <linux/list.h>
18
Steven Rostedtdfa60ab2008-05-12 21:20:43 +020019#include <asm/alternative.h>
20
Steven Rostedt3d083392008-05-12 21:20:42 +020021#define CALL_BACK 5
22
Steven Rostedtdfa60ab2008-05-12 21:20:43 +020023/* Long is fine, even if it is only 4 bytes ;-) */
24static long *ftrace_nop;
Steven Rostedt3d083392008-05-12 21:20:42 +020025
Steven Rostedt3d083392008-05-12 21:20:42 +020026union ftrace_code_union {
27 char code[5];
28 struct {
29 char e8;
30 int offset;
31 } __attribute__((packed));
32};
33
Steven Rostedt3c1720f2008-05-12 21:20:43 +020034static int notrace ftrace_calc_offset(long ip, long addr)
35{
36 return (int)(addr - ip);
37}
38
39notrace unsigned char *ftrace_nop_replace(void)
40{
41 return (char *)ftrace_nop;
42}
43
44notrace unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
45{
46 static union ftrace_code_union calc;
47
48 calc.e8 = 0xe8;
49 calc.offset = ftrace_calc_offset(ip, addr);
50
51 /*
52 * No locking needed, this must be called via kstop_machine
53 * which in essence is like running on a uniprocessor machine.
54 */
55 return calc.code;
56}
57
58notrace int
Steven Rostedt3d083392008-05-12 21:20:42 +020059ftrace_modify_code(unsigned long ip, unsigned char *old_code,
60 unsigned char *new_code)
61{
Steven Rostedtdfa60ab2008-05-12 21:20:43 +020062 unsigned replaced;
63 unsigned old = *(unsigned *)old_code; /* 4 bytes */
64 unsigned new = *(unsigned *)new_code; /* 4 bytes */
65 unsigned char newch = new_code[4];
Steven Rostedt3d083392008-05-12 21:20:42 +020066 int faulted = 0;
67
Steven Rostedt3c1720f2008-05-12 21:20:43 +020068 /* move the IP back to the start of the call */
69 ip -= CALL_BACK;
70
Steven Rostedt3d083392008-05-12 21:20:42 +020071 /*
72 * Note: Due to modules and __init, code can
73 * disappear and change, we need to protect against faulting
74 * as well as code changing.
75 *
76 * No real locking needed, this code is run through
77 * kstop_machine.
78 */
79 asm volatile (
80 "1: lock\n"
Steven Rostedtdfa60ab2008-05-12 21:20:43 +020081 " cmpxchg %3, (%2)\n"
82 " jnz 2f\n"
83 " movb %b4, 4(%2)\n"
Steven Rostedt3d083392008-05-12 21:20:42 +020084 "2:\n"
85 ".section .fixup, \"ax\"\n"
Steven Rostedta56be3f2008-05-12 21:20:56 +020086 "3: movl $1, %0\n"
87 " jmp 2b\n"
Steven Rostedt3d083392008-05-12 21:20:42 +020088 ".previous\n"
89 _ASM_EXTABLE(1b, 3b)
90 : "=r"(faulted), "=a"(replaced)
Ingo Molnaree4311a2008-06-17 17:43:02 +020091 : "r"(ip), "r"(new), "c"(newch),
Steven Rostedtdfa60ab2008-05-12 21:20:43 +020092 "0"(faulted), "a"(old)
Steven Rostedt3d083392008-05-12 21:20:42 +020093 : "memory");
94 sync_core();
95
Steven Rostedtdfa60ab2008-05-12 21:20:43 +020096 if (replaced != old && replaced != new)
Steven Rostedt3d083392008-05-12 21:20:42 +020097 faulted = 2;
98
99 return faulted;
100}
101
Steven Rostedtd61f82d2008-05-12 21:20:43 +0200102notrace int ftrace_update_ftrace_func(ftrace_func_t func)
103{
104 unsigned long ip = (unsigned long)(&ftrace_call);
105 unsigned char old[5], *new;
106 int ret;
107
108 ip += CALL_BACK;
109
110 memcpy(old, &ftrace_call, 5);
111 new = ftrace_call_replace(ip, (unsigned long)func);
112 ret = ftrace_modify_code(ip, old, new);
113
114 return ret;
115}
116
117notrace int ftrace_mcount_set(unsigned long *data)
118{
119 unsigned long ip = (long)(&mcount_call);
120 unsigned long *addr = data;
121 unsigned char old[5], *new;
122
123 /* ip is at the location, but modify code will subtact this */
124 ip += CALL_BACK;
125
126 /*
127 * Replace the mcount stub with a pointer to the
128 * ip recorder function.
129 */
130 memcpy(old, &mcount_call, 5);
131 new = ftrace_call_replace(ip, *addr);
132 *addr = ftrace_modify_code(ip, old, new);
133
134 return 0;
135}
136
137int __init ftrace_dyn_arch_init(void *data)
Steven Rostedt3d083392008-05-12 21:20:42 +0200138{
Steven Rostedtdfa60ab2008-05-12 21:20:43 +0200139 const unsigned char *const *noptable = find_nop_table();
Steven Rostedt3d083392008-05-12 21:20:42 +0200140
Steven Rostedtd61f82d2008-05-12 21:20:43 +0200141 /* This is running in kstop_machine */
142
143 ftrace_mcount_set(data);
144
Steven Rostedtdfa60ab2008-05-12 21:20:43 +0200145 ftrace_nop = (unsigned long *)noptable[CALL_BACK];
146
Steven Rostedt3d083392008-05-12 21:20:42 +0200147 return 0;
148}
Steven Rostedt3c1720f2008-05-12 21:20:43 +0200149