xref: /openbmc/linux/arch/x86/kernel/ftrace.c (revision 4a6d70c9505fef1d8906b1d61db3de5d8ecf9454)
13d083395SSteven Rostedt /*
23d083395SSteven Rostedt  * Code for replacing ftrace calls with jumps.
33d083395SSteven Rostedt  *
43d083395SSteven Rostedt  * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
53d083395SSteven Rostedt  *
63d083395SSteven Rostedt  * Thanks goes to Ingo Molnar, for suggesting the idea.
73d083395SSteven Rostedt  * Mathieu Desnoyers, for suggesting postponing the modifications.
83d083395SSteven Rostedt  * Arjan van de Ven, for keeping me straight, and explaining to me
93d083395SSteven Rostedt  * the dangers of modifying code on the run.
103d083395SSteven Rostedt  */
113d083395SSteven Rostedt 
123bb258bfSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
133bb258bfSJoe Perches 
143d083395SSteven Rostedt #include <linux/spinlock.h>
153d083395SSteven Rostedt #include <linux/hardirq.h>
166f93fc07SSteven Rostedt #include <linux/uaccess.h>
173d083395SSteven Rostedt #include <linux/ftrace.h>
183d083395SSteven Rostedt #include <linux/percpu.h>
1919b3e967SIngo Molnar #include <linux/sched.h>
203d083395SSteven Rostedt #include <linux/init.h>
213d083395SSteven Rostedt #include <linux/list.h>
2284e1c6bbSmatthieu castet #include <linux/module.h>
2308d636b6SSteven Rostedt #include <linux/kprobes.h>
243d083395SSteven Rostedt 
2547788c58SFrederic Weisbecker #include <trace/syscall.h>
2647788c58SFrederic Weisbecker 
2716239630SSteven Rostedt #include <asm/cacheflush.h>
28395a59d0SAbhishek Sagar #include <asm/ftrace.h>
29732f3ca7SSteven Rostedt #include <asm/nops.h>
303d083395SSteven Rostedt 
31caf4b323SFrederic Weisbecker #ifdef CONFIG_DYNAMIC_FTRACE
323d083395SSteven Rostedt 
3316239630SSteven Rostedt int ftrace_arch_code_modify_prepare(void)
3416239630SSteven Rostedt {
3516239630SSteven Rostedt 	set_kernel_text_rw();
3684e1c6bbSmatthieu castet 	set_all_modules_text_rw();
3716239630SSteven Rostedt 	return 0;
3816239630SSteven Rostedt }
3916239630SSteven Rostedt 
4016239630SSteven Rostedt int ftrace_arch_code_modify_post_process(void)
4116239630SSteven Rostedt {
4284e1c6bbSmatthieu castet 	set_all_modules_text_ro();
4316239630SSteven Rostedt 	set_kernel_text_ro();
4416239630SSteven Rostedt 	return 0;
4516239630SSteven Rostedt }
4616239630SSteven Rostedt 
473d083395SSteven Rostedt union ftrace_code_union {
48395a59d0SAbhishek Sagar 	char code[MCOUNT_INSN_SIZE];
493d083395SSteven Rostedt 	struct {
503d083395SSteven Rostedt 		char e8;
513d083395SSteven Rostedt 		int offset;
523d083395SSteven Rostedt 	} __attribute__((packed));
533d083395SSteven Rostedt };
543d083395SSteven Rostedt 
5515adc048SSteven Rostedt static int ftrace_calc_offset(long ip, long addr)
563c1720f0SSteven Rostedt {
573c1720f0SSteven Rostedt 	return (int)(addr - ip);
583d083395SSteven Rostedt }
593d083395SSteven Rostedt 
6031e88909SSteven Rostedt static unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
613c1720f0SSteven Rostedt {
623c1720f0SSteven Rostedt 	static union ftrace_code_union calc;
633c1720f0SSteven Rostedt 
643c1720f0SSteven Rostedt 	calc.e8		= 0xe8;
65395a59d0SAbhishek Sagar 	calc.offset	= ftrace_calc_offset(ip + MCOUNT_INSN_SIZE, addr);
663c1720f0SSteven Rostedt 
673c1720f0SSteven Rostedt 	/*
683c1720f0SSteven Rostedt 	 * No locking needed, this must be called via kstop_machine
693c1720f0SSteven Rostedt 	 * which in essence is like running on a uniprocessor machine.
703c1720f0SSteven Rostedt 	 */
713c1720f0SSteven Rostedt 	return calc.code;
723c1720f0SSteven Rostedt }
733c1720f0SSteven Rostedt 
7455ca3cc1SSuresh Siddha static inline int
7555ca3cc1SSuresh Siddha within(unsigned long addr, unsigned long start, unsigned long end)
7655ca3cc1SSuresh Siddha {
7755ca3cc1SSuresh Siddha 	return addr >= start && addr < end;
7855ca3cc1SSuresh Siddha }
7955ca3cc1SSuresh Siddha 
8017666f02SSteven Rostedt static int
810d098a7dSRakib Mullick do_ftrace_mod_code(unsigned long ip, const void *new_code)
8217666f02SSteven Rostedt {
8355ca3cc1SSuresh Siddha 	/*
8455ca3cc1SSuresh Siddha 	 * On x86_64, kernel text mappings are mapped read-only with
8555ca3cc1SSuresh Siddha 	 * CONFIG_DEBUG_RODATA. So we use the kernel identity mapping instead
8655ca3cc1SSuresh Siddha 	 * of the kernel text mapping to modify the kernel text.
8755ca3cc1SSuresh Siddha 	 *
8855ca3cc1SSuresh Siddha 	 * For 32bit kernels, these mappings are same and we can use
8955ca3cc1SSuresh Siddha 	 * kernel identity mapping to modify code.
9055ca3cc1SSuresh Siddha 	 */
9155ca3cc1SSuresh Siddha 	if (within(ip, (unsigned long)_text, (unsigned long)_etext))
9255ca3cc1SSuresh Siddha 		ip = (unsigned long)__va(__pa(ip));
9355ca3cc1SSuresh Siddha 
94*4a6d70c9SSteven Rostedt 	return probe_kernel_write((void *)ip, new_code, MCOUNT_INSN_SIZE);
9517666f02SSteven Rostedt }
9617666f02SSteven Rostedt 
97dc326fcaSH. Peter Anvin static const unsigned char *ftrace_nop_replace(void)
98caf4b323SFrederic Weisbecker {
99dc326fcaSH. Peter Anvin 	return ideal_nops[NOP_ATOMIC5];
100caf4b323SFrederic Weisbecker }
101caf4b323SFrederic Weisbecker 
10231e88909SSteven Rostedt static int
1030d098a7dSRakib Mullick ftrace_modify_code(unsigned long ip, unsigned const char *old_code,
1040d098a7dSRakib Mullick 		   unsigned const char *new_code)
1053d083395SSteven Rostedt {
1066f93fc07SSteven Rostedt 	unsigned char replaced[MCOUNT_INSN_SIZE];
1073d083395SSteven Rostedt 
1083d083395SSteven Rostedt 	/*
1093d083395SSteven Rostedt 	 * Note: Due to modules and __init, code can
1103d083395SSteven Rostedt 	 *  disappear and change, we need to protect against faulting
11176aefee5SSteven Rostedt 	 *  as well as code changing. We do this by using the
112ab9a0918SSteven Rostedt 	 *  probe_kernel_* functions.
1133d083395SSteven Rostedt 	 *
1143d083395SSteven Rostedt 	 * No real locking needed, this code is run through
1156f93fc07SSteven Rostedt 	 * kstop_machine, or before SMP starts.
1163d083395SSteven Rostedt 	 */
11776aefee5SSteven Rostedt 
11876aefee5SSteven Rostedt 	/* read the text we want to modify */
119ab9a0918SSteven Rostedt 	if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE))
120593eb8a2SSteven Rostedt 		return -EFAULT;
1216f93fc07SSteven Rostedt 
12276aefee5SSteven Rostedt 	/* Make sure it is what we expect it to be */
1236f93fc07SSteven Rostedt 	if (memcmp(replaced, old_code, MCOUNT_INSN_SIZE) != 0)
124593eb8a2SSteven Rostedt 		return -EINVAL;
1256f93fc07SSteven Rostedt 
12676aefee5SSteven Rostedt 	/* replace the text with the new text */
12717666f02SSteven Rostedt 	if (do_ftrace_mod_code(ip, new_code))
128593eb8a2SSteven Rostedt 		return -EPERM;
1296f93fc07SSteven Rostedt 
1303d083395SSteven Rostedt 	sync_core();
1313d083395SSteven Rostedt 
1326f93fc07SSteven Rostedt 	return 0;
1333d083395SSteven Rostedt }
1343d083395SSteven Rostedt 
13531e88909SSteven Rostedt int ftrace_make_nop(struct module *mod,
13631e88909SSteven Rostedt 		    struct dyn_ftrace *rec, unsigned long addr)
13731e88909SSteven Rostedt {
1380d098a7dSRakib Mullick 	unsigned const char *new, *old;
13931e88909SSteven Rostedt 	unsigned long ip = rec->ip;
14031e88909SSteven Rostedt 
14131e88909SSteven Rostedt 	old = ftrace_call_replace(ip, addr);
14231e88909SSteven Rostedt 	new = ftrace_nop_replace();
14331e88909SSteven Rostedt 
14431e88909SSteven Rostedt 	return ftrace_modify_code(rec->ip, old, new);
14531e88909SSteven Rostedt }
14631e88909SSteven Rostedt 
14731e88909SSteven Rostedt int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
14831e88909SSteven Rostedt {
1490d098a7dSRakib Mullick 	unsigned const char *new, *old;
15031e88909SSteven Rostedt 	unsigned long ip = rec->ip;
15131e88909SSteven Rostedt 
15231e88909SSteven Rostedt 	old = ftrace_nop_replace();
15331e88909SSteven Rostedt 	new = ftrace_call_replace(ip, addr);
15431e88909SSteven Rostedt 
15531e88909SSteven Rostedt 	return ftrace_modify_code(rec->ip, old, new);
15631e88909SSteven Rostedt }
15731e88909SSteven Rostedt 
15815adc048SSteven Rostedt int ftrace_update_ftrace_func(ftrace_func_t func)
159d61f82d0SSteven Rostedt {
160d61f82d0SSteven Rostedt 	unsigned long ip = (unsigned long)(&ftrace_call);
161395a59d0SAbhishek Sagar 	unsigned char old[MCOUNT_INSN_SIZE], *new;
162d61f82d0SSteven Rostedt 	int ret;
163d61f82d0SSteven Rostedt 
164395a59d0SAbhishek Sagar 	memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE);
165d61f82d0SSteven Rostedt 	new = ftrace_call_replace(ip, (unsigned long)func);
166d61f82d0SSteven Rostedt 	ret = ftrace_modify_code(ip, old, new);
167d61f82d0SSteven Rostedt 
168d61f82d0SSteven Rostedt 	return ret;
169d61f82d0SSteven Rostedt }
170d61f82d0SSteven Rostedt 
17108d636b6SSteven Rostedt int modifying_ftrace_code __read_mostly;
17208d636b6SSteven Rostedt 
17308d636b6SSteven Rostedt /*
17408d636b6SSteven Rostedt  * A breakpoint was added to the code address we are about to
17508d636b6SSteven Rostedt  * modify, and this is the handle that will just skip over it.
17608d636b6SSteven Rostedt  * We are either changing a nop into a trace call, or a trace
17708d636b6SSteven Rostedt  * call to a nop. While the change is taking place, we treat
17808d636b6SSteven Rostedt  * it just like it was a nop.
17908d636b6SSteven Rostedt  */
18008d636b6SSteven Rostedt int ftrace_int3_handler(struct pt_regs *regs)
18108d636b6SSteven Rostedt {
18208d636b6SSteven Rostedt 	if (WARN_ON_ONCE(!regs))
18308d636b6SSteven Rostedt 		return 0;
18408d636b6SSteven Rostedt 
18508d636b6SSteven Rostedt 	if (!ftrace_location(regs->ip - 1))
18608d636b6SSteven Rostedt 		return 0;
18708d636b6SSteven Rostedt 
18808d636b6SSteven Rostedt 	regs->ip += MCOUNT_INSN_SIZE - 1;
18908d636b6SSteven Rostedt 
19008d636b6SSteven Rostedt 	return 1;
19108d636b6SSteven Rostedt }
19208d636b6SSteven Rostedt 
19308d636b6SSteven Rostedt static int ftrace_write(unsigned long ip, const char *val, int size)
19408d636b6SSteven Rostedt {
19508d636b6SSteven Rostedt 	/*
19608d636b6SSteven Rostedt 	 * On x86_64, kernel text mappings are mapped read-only with
19708d636b6SSteven Rostedt 	 * CONFIG_DEBUG_RODATA. So we use the kernel identity mapping instead
19808d636b6SSteven Rostedt 	 * of the kernel text mapping to modify the kernel text.
19908d636b6SSteven Rostedt 	 *
20008d636b6SSteven Rostedt 	 * For 32bit kernels, these mappings are same and we can use
20108d636b6SSteven Rostedt 	 * kernel identity mapping to modify code.
20208d636b6SSteven Rostedt 	 */
20308d636b6SSteven Rostedt 	if (within(ip, (unsigned long)_text, (unsigned long)_etext))
20408d636b6SSteven Rostedt 		ip = (unsigned long)__va(__pa(ip));
20508d636b6SSteven Rostedt 
20608d636b6SSteven Rostedt 	return probe_kernel_write((void *)ip, val, size);
20708d636b6SSteven Rostedt }
20808d636b6SSteven Rostedt 
20908d636b6SSteven Rostedt static int add_break(unsigned long ip, const char *old)
21008d636b6SSteven Rostedt {
21108d636b6SSteven Rostedt 	unsigned char replaced[MCOUNT_INSN_SIZE];
21208d636b6SSteven Rostedt 	unsigned char brk = BREAKPOINT_INSTRUCTION;
21308d636b6SSteven Rostedt 
21408d636b6SSteven Rostedt 	if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE))
21508d636b6SSteven Rostedt 		return -EFAULT;
21608d636b6SSteven Rostedt 
21708d636b6SSteven Rostedt 	/* Make sure it is what we expect it to be */
21808d636b6SSteven Rostedt 	if (memcmp(replaced, old, MCOUNT_INSN_SIZE) != 0)
21908d636b6SSteven Rostedt 		return -EINVAL;
22008d636b6SSteven Rostedt 
22108d636b6SSteven Rostedt 	if (ftrace_write(ip, &brk, 1))
22208d636b6SSteven Rostedt 		return -EPERM;
22308d636b6SSteven Rostedt 
22408d636b6SSteven Rostedt 	return 0;
22508d636b6SSteven Rostedt }
22608d636b6SSteven Rostedt 
22708d636b6SSteven Rostedt static int add_brk_on_call(struct dyn_ftrace *rec, unsigned long addr)
22808d636b6SSteven Rostedt {
22908d636b6SSteven Rostedt 	unsigned const char *old;
23008d636b6SSteven Rostedt 	unsigned long ip = rec->ip;
23108d636b6SSteven Rostedt 
23208d636b6SSteven Rostedt 	old = ftrace_call_replace(ip, addr);
23308d636b6SSteven Rostedt 
23408d636b6SSteven Rostedt 	return add_break(rec->ip, old);
23508d636b6SSteven Rostedt }
23608d636b6SSteven Rostedt 
23708d636b6SSteven Rostedt 
23808d636b6SSteven Rostedt static int add_brk_on_nop(struct dyn_ftrace *rec)
23908d636b6SSteven Rostedt {
24008d636b6SSteven Rostedt 	unsigned const char *old;
24108d636b6SSteven Rostedt 
24208d636b6SSteven Rostedt 	old = ftrace_nop_replace();
24308d636b6SSteven Rostedt 
24408d636b6SSteven Rostedt 	return add_break(rec->ip, old);
24508d636b6SSteven Rostedt }
24608d636b6SSteven Rostedt 
24708d636b6SSteven Rostedt static int add_breakpoints(struct dyn_ftrace *rec, int enable)
24808d636b6SSteven Rostedt {
24908d636b6SSteven Rostedt 	unsigned long ftrace_addr;
25008d636b6SSteven Rostedt 	int ret;
25108d636b6SSteven Rostedt 
25208d636b6SSteven Rostedt 	ret = ftrace_test_record(rec, enable);
25308d636b6SSteven Rostedt 
25408d636b6SSteven Rostedt 	ftrace_addr = (unsigned long)FTRACE_ADDR;
25508d636b6SSteven Rostedt 
25608d636b6SSteven Rostedt 	switch (ret) {
25708d636b6SSteven Rostedt 	case FTRACE_UPDATE_IGNORE:
25808d636b6SSteven Rostedt 		return 0;
25908d636b6SSteven Rostedt 
26008d636b6SSteven Rostedt 	case FTRACE_UPDATE_MAKE_CALL:
26108d636b6SSteven Rostedt 		/* converting nop to call */
26208d636b6SSteven Rostedt 		return add_brk_on_nop(rec);
26308d636b6SSteven Rostedt 
26408d636b6SSteven Rostedt 	case FTRACE_UPDATE_MAKE_NOP:
26508d636b6SSteven Rostedt 		/* converting a call to a nop */
26608d636b6SSteven Rostedt 		return add_brk_on_call(rec, ftrace_addr);
26708d636b6SSteven Rostedt 	}
26808d636b6SSteven Rostedt 	return 0;
26908d636b6SSteven Rostedt }
27008d636b6SSteven Rostedt 
27108d636b6SSteven Rostedt /*
27208d636b6SSteven Rostedt  * On error, we need to remove breakpoints. This needs to
27308d636b6SSteven Rostedt  * be done caefully. If the address does not currently have a
27408d636b6SSteven Rostedt  * breakpoint, we know we are done. Otherwise, we look at the
27508d636b6SSteven Rostedt  * remaining 4 bytes of the instruction. If it matches a nop
27608d636b6SSteven Rostedt  * we replace the breakpoint with the nop. Otherwise we replace
27708d636b6SSteven Rostedt  * it with the call instruction.
27808d636b6SSteven Rostedt  */
27908d636b6SSteven Rostedt static int remove_breakpoint(struct dyn_ftrace *rec)
28008d636b6SSteven Rostedt {
28108d636b6SSteven Rostedt 	unsigned char ins[MCOUNT_INSN_SIZE];
28208d636b6SSteven Rostedt 	unsigned char brk = BREAKPOINT_INSTRUCTION;
28308d636b6SSteven Rostedt 	const unsigned char *nop;
28408d636b6SSteven Rostedt 	unsigned long ftrace_addr;
28508d636b6SSteven Rostedt 	unsigned long ip = rec->ip;
28608d636b6SSteven Rostedt 
28708d636b6SSteven Rostedt 	/* If we fail the read, just give up */
28808d636b6SSteven Rostedt 	if (probe_kernel_read(ins, (void *)ip, MCOUNT_INSN_SIZE))
28908d636b6SSteven Rostedt 		return -EFAULT;
29008d636b6SSteven Rostedt 
29108d636b6SSteven Rostedt 	/* If this does not have a breakpoint, we are done */
29208d636b6SSteven Rostedt 	if (ins[0] != brk)
29308d636b6SSteven Rostedt 		return -1;
29408d636b6SSteven Rostedt 
29508d636b6SSteven Rostedt 	nop = ftrace_nop_replace();
29608d636b6SSteven Rostedt 
29708d636b6SSteven Rostedt 	/*
29808d636b6SSteven Rostedt 	 * If the last 4 bytes of the instruction do not match
29908d636b6SSteven Rostedt 	 * a nop, then we assume that this is a call to ftrace_addr.
30008d636b6SSteven Rostedt 	 */
30108d636b6SSteven Rostedt 	if (memcmp(&ins[1], &nop[1], MCOUNT_INSN_SIZE - 1) != 0) {
30208d636b6SSteven Rostedt 		/*
30308d636b6SSteven Rostedt 		 * For extra paranoidism, we check if the breakpoint is on
30408d636b6SSteven Rostedt 		 * a call that would actually jump to the ftrace_addr.
30508d636b6SSteven Rostedt 		 * If not, don't touch the breakpoint, we make just create
30608d636b6SSteven Rostedt 		 * a disaster.
30708d636b6SSteven Rostedt 		 */
30808d636b6SSteven Rostedt 		ftrace_addr = (unsigned long)FTRACE_ADDR;
30908d636b6SSteven Rostedt 		nop = ftrace_call_replace(ip, ftrace_addr);
31008d636b6SSteven Rostedt 
31108d636b6SSteven Rostedt 		if (memcmp(&ins[1], &nop[1], MCOUNT_INSN_SIZE - 1) != 0)
31208d636b6SSteven Rostedt 			return -EINVAL;
31308d636b6SSteven Rostedt 	}
31408d636b6SSteven Rostedt 
31508d636b6SSteven Rostedt 	return probe_kernel_write((void *)ip, &nop[0], 1);
31608d636b6SSteven Rostedt }
31708d636b6SSteven Rostedt 
31808d636b6SSteven Rostedt static int add_update_code(unsigned long ip, unsigned const char *new)
31908d636b6SSteven Rostedt {
32008d636b6SSteven Rostedt 	/* skip breakpoint */
32108d636b6SSteven Rostedt 	ip++;
32208d636b6SSteven Rostedt 	new++;
32308d636b6SSteven Rostedt 	if (ftrace_write(ip, new, MCOUNT_INSN_SIZE - 1))
32408d636b6SSteven Rostedt 		return -EPERM;
32508d636b6SSteven Rostedt 	return 0;
32608d636b6SSteven Rostedt }
32708d636b6SSteven Rostedt 
32808d636b6SSteven Rostedt static int add_update_call(struct dyn_ftrace *rec, unsigned long addr)
32908d636b6SSteven Rostedt {
33008d636b6SSteven Rostedt 	unsigned long ip = rec->ip;
33108d636b6SSteven Rostedt 	unsigned const char *new;
33208d636b6SSteven Rostedt 
33308d636b6SSteven Rostedt 	new = ftrace_call_replace(ip, addr);
33408d636b6SSteven Rostedt 	return add_update_code(ip, new);
33508d636b6SSteven Rostedt }
33608d636b6SSteven Rostedt 
33708d636b6SSteven Rostedt static int add_update_nop(struct dyn_ftrace *rec)
33808d636b6SSteven Rostedt {
33908d636b6SSteven Rostedt 	unsigned long ip = rec->ip;
34008d636b6SSteven Rostedt 	unsigned const char *new;
34108d636b6SSteven Rostedt 
34208d636b6SSteven Rostedt 	new = ftrace_nop_replace();
34308d636b6SSteven Rostedt 	return add_update_code(ip, new);
34408d636b6SSteven Rostedt }
34508d636b6SSteven Rostedt 
34608d636b6SSteven Rostedt static int add_update(struct dyn_ftrace *rec, int enable)
34708d636b6SSteven Rostedt {
34808d636b6SSteven Rostedt 	unsigned long ftrace_addr;
34908d636b6SSteven Rostedt 	int ret;
35008d636b6SSteven Rostedt 
35108d636b6SSteven Rostedt 	ret = ftrace_test_record(rec, enable);
35208d636b6SSteven Rostedt 
35308d636b6SSteven Rostedt 	ftrace_addr = (unsigned long)FTRACE_ADDR;
35408d636b6SSteven Rostedt 
35508d636b6SSteven Rostedt 	switch (ret) {
35608d636b6SSteven Rostedt 	case FTRACE_UPDATE_IGNORE:
35708d636b6SSteven Rostedt 		return 0;
35808d636b6SSteven Rostedt 
35908d636b6SSteven Rostedt 	case FTRACE_UPDATE_MAKE_CALL:
36008d636b6SSteven Rostedt 		/* converting nop to call */
36108d636b6SSteven Rostedt 		return add_update_call(rec, ftrace_addr);
36208d636b6SSteven Rostedt 
36308d636b6SSteven Rostedt 	case FTRACE_UPDATE_MAKE_NOP:
36408d636b6SSteven Rostedt 		/* converting a call to a nop */
36508d636b6SSteven Rostedt 		return add_update_nop(rec);
36608d636b6SSteven Rostedt 	}
36708d636b6SSteven Rostedt 
36808d636b6SSteven Rostedt 	return 0;
36908d636b6SSteven Rostedt }
37008d636b6SSteven Rostedt 
37108d636b6SSteven Rostedt static int finish_update_call(struct dyn_ftrace *rec, unsigned long addr)
37208d636b6SSteven Rostedt {
37308d636b6SSteven Rostedt 	unsigned long ip = rec->ip;
37408d636b6SSteven Rostedt 	unsigned const char *new;
37508d636b6SSteven Rostedt 
37608d636b6SSteven Rostedt 	new = ftrace_call_replace(ip, addr);
37708d636b6SSteven Rostedt 
37808d636b6SSteven Rostedt 	if (ftrace_write(ip, new, 1))
37908d636b6SSteven Rostedt 		return -EPERM;
38008d636b6SSteven Rostedt 
38108d636b6SSteven Rostedt 	return 0;
38208d636b6SSteven Rostedt }
38308d636b6SSteven Rostedt 
38408d636b6SSteven Rostedt static int finish_update_nop(struct dyn_ftrace *rec)
38508d636b6SSteven Rostedt {
38608d636b6SSteven Rostedt 	unsigned long ip = rec->ip;
38708d636b6SSteven Rostedt 	unsigned const char *new;
38808d636b6SSteven Rostedt 
38908d636b6SSteven Rostedt 	new = ftrace_nop_replace();
39008d636b6SSteven Rostedt 
39108d636b6SSteven Rostedt 	if (ftrace_write(ip, new, 1))
39208d636b6SSteven Rostedt 		return -EPERM;
39308d636b6SSteven Rostedt 	return 0;
39408d636b6SSteven Rostedt }
39508d636b6SSteven Rostedt 
39608d636b6SSteven Rostedt static int finish_update(struct dyn_ftrace *rec, int enable)
39708d636b6SSteven Rostedt {
39808d636b6SSteven Rostedt 	unsigned long ftrace_addr;
39908d636b6SSteven Rostedt 	int ret;
40008d636b6SSteven Rostedt 
40108d636b6SSteven Rostedt 	ret = ftrace_update_record(rec, enable);
40208d636b6SSteven Rostedt 
40308d636b6SSteven Rostedt 	ftrace_addr = (unsigned long)FTRACE_ADDR;
40408d636b6SSteven Rostedt 
40508d636b6SSteven Rostedt 	switch (ret) {
40608d636b6SSteven Rostedt 	case FTRACE_UPDATE_IGNORE:
40708d636b6SSteven Rostedt 		return 0;
40808d636b6SSteven Rostedt 
40908d636b6SSteven Rostedt 	case FTRACE_UPDATE_MAKE_CALL:
41008d636b6SSteven Rostedt 		/* converting nop to call */
41108d636b6SSteven Rostedt 		return finish_update_call(rec, ftrace_addr);
41208d636b6SSteven Rostedt 
41308d636b6SSteven Rostedt 	case FTRACE_UPDATE_MAKE_NOP:
41408d636b6SSteven Rostedt 		/* converting a call to a nop */
41508d636b6SSteven Rostedt 		return finish_update_nop(rec);
41608d636b6SSteven Rostedt 	}
41708d636b6SSteven Rostedt 
41808d636b6SSteven Rostedt 	return 0;
41908d636b6SSteven Rostedt }
42008d636b6SSteven Rostedt 
42108d636b6SSteven Rostedt static void do_sync_core(void *data)
42208d636b6SSteven Rostedt {
42308d636b6SSteven Rostedt 	sync_core();
42408d636b6SSteven Rostedt }
42508d636b6SSteven Rostedt 
42608d636b6SSteven Rostedt static void run_sync(void)
42708d636b6SSteven Rostedt {
42808d636b6SSteven Rostedt 	int enable_irqs = irqs_disabled();
42908d636b6SSteven Rostedt 
43008d636b6SSteven Rostedt 	/* We may be called with interrupts disbled (on bootup). */
43108d636b6SSteven Rostedt 	if (enable_irqs)
43208d636b6SSteven Rostedt 		local_irq_enable();
43308d636b6SSteven Rostedt 	on_each_cpu(do_sync_core, NULL, 1);
43408d636b6SSteven Rostedt 	if (enable_irqs)
43508d636b6SSteven Rostedt 		local_irq_disable();
43608d636b6SSteven Rostedt }
43708d636b6SSteven Rostedt 
43808d636b6SSteven Rostedt static void ftrace_replace_code(int enable)
43908d636b6SSteven Rostedt {
44008d636b6SSteven Rostedt 	struct ftrace_rec_iter *iter;
44108d636b6SSteven Rostedt 	struct dyn_ftrace *rec;
44208d636b6SSteven Rostedt 	const char *report = "adding breakpoints";
44308d636b6SSteven Rostedt 	int count = 0;
44408d636b6SSteven Rostedt 	int ret;
44508d636b6SSteven Rostedt 
44608d636b6SSteven Rostedt 	for_ftrace_rec_iter(iter) {
44708d636b6SSteven Rostedt 		rec = ftrace_rec_iter_record(iter);
44808d636b6SSteven Rostedt 
44908d636b6SSteven Rostedt 		ret = add_breakpoints(rec, enable);
45008d636b6SSteven Rostedt 		if (ret)
45108d636b6SSteven Rostedt 			goto remove_breakpoints;
45208d636b6SSteven Rostedt 		count++;
45308d636b6SSteven Rostedt 	}
45408d636b6SSteven Rostedt 
45508d636b6SSteven Rostedt 	run_sync();
45608d636b6SSteven Rostedt 
45708d636b6SSteven Rostedt 	report = "updating code";
45808d636b6SSteven Rostedt 
45908d636b6SSteven Rostedt 	for_ftrace_rec_iter(iter) {
46008d636b6SSteven Rostedt 		rec = ftrace_rec_iter_record(iter);
46108d636b6SSteven Rostedt 
46208d636b6SSteven Rostedt 		ret = add_update(rec, enable);
46308d636b6SSteven Rostedt 		if (ret)
46408d636b6SSteven Rostedt 			goto remove_breakpoints;
46508d636b6SSteven Rostedt 	}
46608d636b6SSteven Rostedt 
46708d636b6SSteven Rostedt 	run_sync();
46808d636b6SSteven Rostedt 
46908d636b6SSteven Rostedt 	report = "removing breakpoints";
47008d636b6SSteven Rostedt 
47108d636b6SSteven Rostedt 	for_ftrace_rec_iter(iter) {
47208d636b6SSteven Rostedt 		rec = ftrace_rec_iter_record(iter);
47308d636b6SSteven Rostedt 
47408d636b6SSteven Rostedt 		ret = finish_update(rec, enable);
47508d636b6SSteven Rostedt 		if (ret)
47608d636b6SSteven Rostedt 			goto remove_breakpoints;
47708d636b6SSteven Rostedt 	}
47808d636b6SSteven Rostedt 
47908d636b6SSteven Rostedt 	run_sync();
48008d636b6SSteven Rostedt 
48108d636b6SSteven Rostedt 	return;
48208d636b6SSteven Rostedt 
48308d636b6SSteven Rostedt  remove_breakpoints:
48408d636b6SSteven Rostedt 	ftrace_bug(ret, rec ? rec->ip : 0);
48508d636b6SSteven Rostedt 	printk(KERN_WARNING "Failed on %s (%d):\n", report, count);
48608d636b6SSteven Rostedt 	for_ftrace_rec_iter(iter) {
48708d636b6SSteven Rostedt 		rec = ftrace_rec_iter_record(iter);
48808d636b6SSteven Rostedt 		remove_breakpoint(rec);
48908d636b6SSteven Rostedt 	}
49008d636b6SSteven Rostedt }
49108d636b6SSteven Rostedt 
49208d636b6SSteven Rostedt void arch_ftrace_update_code(int command)
49308d636b6SSteven Rostedt {
49408d636b6SSteven Rostedt 	modifying_ftrace_code++;
49508d636b6SSteven Rostedt 
49608d636b6SSteven Rostedt 	if (command & FTRACE_UPDATE_CALLS)
49708d636b6SSteven Rostedt 		ftrace_replace_code(1);
49808d636b6SSteven Rostedt 	else if (command & FTRACE_DISABLE_CALLS)
49908d636b6SSteven Rostedt 		ftrace_replace_code(0);
50008d636b6SSteven Rostedt 
50108d636b6SSteven Rostedt 	if (command & FTRACE_UPDATE_TRACE_FUNC)
50208d636b6SSteven Rostedt 		ftrace_update_ftrace_func(ftrace_trace_function);
50308d636b6SSteven Rostedt 
50408d636b6SSteven Rostedt 	if (command & FTRACE_START_FUNC_RET)
50508d636b6SSteven Rostedt 		ftrace_enable_ftrace_graph_caller();
50608d636b6SSteven Rostedt 	else if (command & FTRACE_STOP_FUNC_RET)
50708d636b6SSteven Rostedt 		ftrace_disable_ftrace_graph_caller();
50808d636b6SSteven Rostedt 
50908d636b6SSteven Rostedt 	modifying_ftrace_code--;
51008d636b6SSteven Rostedt }
51108d636b6SSteven Rostedt 
512d61f82d0SSteven Rostedt int __init ftrace_dyn_arch_init(void *data)
5133d083395SSteven Rostedt {
514732f3ca7SSteven Rostedt 	/* The return code is retured via data */
515732f3ca7SSteven Rostedt 	*(unsigned long *)data = 0;
516dfa60abaSSteven Rostedt 
5173d083395SSteven Rostedt 	return 0;
5183d083395SSteven Rostedt }
519caf4b323SFrederic Weisbecker #endif
520e7d3737eSFrederic Weisbecker 
521fb52607aSFrederic Weisbecker #ifdef CONFIG_FUNCTION_GRAPH_TRACER
522e7d3737eSFrederic Weisbecker 
5235a45cfe1SSteven Rostedt #ifdef CONFIG_DYNAMIC_FTRACE
5245a45cfe1SSteven Rostedt extern void ftrace_graph_call(void);
5255a45cfe1SSteven Rostedt 
5265a45cfe1SSteven Rostedt static int ftrace_mod_jmp(unsigned long ip,
5275a45cfe1SSteven Rostedt 			  int old_offset, int new_offset)
5285a45cfe1SSteven Rostedt {
5295a45cfe1SSteven Rostedt 	unsigned char code[MCOUNT_INSN_SIZE];
5305a45cfe1SSteven Rostedt 
5315a45cfe1SSteven Rostedt 	if (probe_kernel_read(code, (void *)ip, MCOUNT_INSN_SIZE))
5325a45cfe1SSteven Rostedt 		return -EFAULT;
5335a45cfe1SSteven Rostedt 
5345a45cfe1SSteven Rostedt 	if (code[0] != 0xe9 || old_offset != *(int *)(&code[1]))
5355a45cfe1SSteven Rostedt 		return -EINVAL;
5365a45cfe1SSteven Rostedt 
5375a45cfe1SSteven Rostedt 	*(int *)(&code[1]) = new_offset;
5385a45cfe1SSteven Rostedt 
5395a45cfe1SSteven Rostedt 	if (do_ftrace_mod_code(ip, &code))
5405a45cfe1SSteven Rostedt 		return -EPERM;
5415a45cfe1SSteven Rostedt 
5425a45cfe1SSteven Rostedt 	return 0;
5435a45cfe1SSteven Rostedt }
5445a45cfe1SSteven Rostedt 
5455a45cfe1SSteven Rostedt int ftrace_enable_ftrace_graph_caller(void)
5465a45cfe1SSteven Rostedt {
5475a45cfe1SSteven Rostedt 	unsigned long ip = (unsigned long)(&ftrace_graph_call);
5485a45cfe1SSteven Rostedt 	int old_offset, new_offset;
5495a45cfe1SSteven Rostedt 
5505a45cfe1SSteven Rostedt 	old_offset = (unsigned long)(&ftrace_stub) - (ip + MCOUNT_INSN_SIZE);
5515a45cfe1SSteven Rostedt 	new_offset = (unsigned long)(&ftrace_graph_caller) - (ip + MCOUNT_INSN_SIZE);
5525a45cfe1SSteven Rostedt 
5535a45cfe1SSteven Rostedt 	return ftrace_mod_jmp(ip, old_offset, new_offset);
5545a45cfe1SSteven Rostedt }
5555a45cfe1SSteven Rostedt 
5565a45cfe1SSteven Rostedt int ftrace_disable_ftrace_graph_caller(void)
5575a45cfe1SSteven Rostedt {
5585a45cfe1SSteven Rostedt 	unsigned long ip = (unsigned long)(&ftrace_graph_call);
5595a45cfe1SSteven Rostedt 	int old_offset, new_offset;
5605a45cfe1SSteven Rostedt 
5615a45cfe1SSteven Rostedt 	old_offset = (unsigned long)(&ftrace_graph_caller) - (ip + MCOUNT_INSN_SIZE);
5625a45cfe1SSteven Rostedt 	new_offset = (unsigned long)(&ftrace_stub) - (ip + MCOUNT_INSN_SIZE);
5635a45cfe1SSteven Rostedt 
5645a45cfe1SSteven Rostedt 	return ftrace_mod_jmp(ip, old_offset, new_offset);
5655a45cfe1SSteven Rostedt }
5665a45cfe1SSteven Rostedt 
567e7d3737eSFrederic Weisbecker #endif /* !CONFIG_DYNAMIC_FTRACE */
568e7d3737eSFrederic Weisbecker 
569e7d3737eSFrederic Weisbecker /*
570e7d3737eSFrederic Weisbecker  * Hook the return address and push it in the stack of return addrs
571e7d3737eSFrederic Weisbecker  * in current thread info.
572e7d3737eSFrederic Weisbecker  */
57371e308a2SSteven Rostedt void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
57471e308a2SSteven Rostedt 			   unsigned long frame_pointer)
575e7d3737eSFrederic Weisbecker {
576e7d3737eSFrederic Weisbecker 	unsigned long old;
577e7d3737eSFrederic Weisbecker 	int faulted;
578287b6e68SFrederic Weisbecker 	struct ftrace_graph_ent trace;
579e7d3737eSFrederic Weisbecker 	unsigned long return_hooker = (unsigned long)
580e7d3737eSFrederic Weisbecker 				&return_to_handler;
581e7d3737eSFrederic Weisbecker 
582380c4b14SFrederic Weisbecker 	if (unlikely(atomic_read(&current->tracing_graph_pause)))
583e7d3737eSFrederic Weisbecker 		return;
584e7d3737eSFrederic Weisbecker 
585e7d3737eSFrederic Weisbecker 	/*
586e7d3737eSFrederic Weisbecker 	 * Protect against fault, even if it shouldn't
587e7d3737eSFrederic Weisbecker 	 * happen. This tool is too much intrusive to
588e7d3737eSFrederic Weisbecker 	 * ignore such a protection.
589e7d3737eSFrederic Weisbecker 	 */
590e7d3737eSFrederic Weisbecker 	asm volatile(
59196665788SSteven Rostedt 		"1: " _ASM_MOV " (%[parent]), %[old]\n"
59296665788SSteven Rostedt 		"2: " _ASM_MOV " %[return_hooker], (%[parent])\n"
593e7d3737eSFrederic Weisbecker 		"   movl $0, %[faulted]\n"
594e3944bfaSSteven Rostedt 		"3:\n"
595e7d3737eSFrederic Weisbecker 
596e7d3737eSFrederic Weisbecker 		".section .fixup, \"ax\"\n"
597e3944bfaSSteven Rostedt 		"4: movl $1, %[faulted]\n"
598e3944bfaSSteven Rostedt 		"   jmp 3b\n"
599e7d3737eSFrederic Weisbecker 		".previous\n"
600e7d3737eSFrederic Weisbecker 
601e3944bfaSSteven Rostedt 		_ASM_EXTABLE(1b, 4b)
602e3944bfaSSteven Rostedt 		_ASM_EXTABLE(2b, 4b)
603e7d3737eSFrederic Weisbecker 
604aa512a27SSteven Rostedt 		: [old] "=&r" (old), [faulted] "=r" (faulted)
60596665788SSteven Rostedt 		: [parent] "r" (parent), [return_hooker] "r" (return_hooker)
606e7d3737eSFrederic Weisbecker 		: "memory"
607e7d3737eSFrederic Weisbecker 	);
608e7d3737eSFrederic Weisbecker 
60914a866c5SSteven Rostedt 	if (unlikely(faulted)) {
61014a866c5SSteven Rostedt 		ftrace_graph_stop();
61114a866c5SSteven Rostedt 		WARN_ON(1);
612e7d3737eSFrederic Weisbecker 		return;
613e7d3737eSFrederic Weisbecker 	}
614e7d3737eSFrederic Weisbecker 
615722b3c74SSteven Rostedt 	trace.func = self_addr;
616722b3c74SSteven Rostedt 	trace.depth = current->curr_ret_stack + 1;
617722b3c74SSteven Rostedt 
618722b3c74SSteven Rostedt 	/* Only trace if the calling function expects to */
619722b3c74SSteven Rostedt 	if (!ftrace_graph_entry(&trace)) {
620e7d3737eSFrederic Weisbecker 		*parent = old;
621287b6e68SFrederic Weisbecker 		return;
622287b6e68SFrederic Weisbecker 	}
623287b6e68SFrederic Weisbecker 
624722b3c74SSteven Rostedt 	if (ftrace_push_return_trace(old, self_addr, &trace.depth,
625722b3c74SSteven Rostedt 		    frame_pointer) == -EBUSY) {
626e49dc19cSSteven Rostedt 		*parent = old;
627722b3c74SSteven Rostedt 		return;
628e49dc19cSSteven Rostedt 	}
629e7d3737eSFrederic Weisbecker }
630fb52607aSFrederic Weisbecker #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
631