xref: /openbmc/linux/arch/x86/kernel/ftrace.c (revision 732f3ca7d4ba3c1be8d051d52302ef441ee7748b)
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 
123d083395SSteven Rostedt #include <linux/spinlock.h>
133d083395SSteven Rostedt #include <linux/hardirq.h>
143d083395SSteven Rostedt #include <linux/ftrace.h>
153d083395SSteven Rostedt #include <linux/percpu.h>
163d083395SSteven Rostedt #include <linux/init.h>
173d083395SSteven Rostedt #include <linux/list.h>
183d083395SSteven Rostedt 
19395a59d0SAbhishek Sagar #include <asm/ftrace.h>
20*732f3ca7SSteven Rostedt #include <asm/nops.h>
21dfa60abaSSteven Rostedt 
223d083395SSteven Rostedt 
23dfa60abaSSteven Rostedt /* Long is fine, even if it is only 4 bytes ;-) */
24dfa60abaSSteven Rostedt static long *ftrace_nop;
253d083395SSteven Rostedt 
263d083395SSteven Rostedt union ftrace_code_union {
27395a59d0SAbhishek Sagar 	char code[MCOUNT_INSN_SIZE];
283d083395SSteven Rostedt 	struct {
293d083395SSteven Rostedt 		char e8;
303d083395SSteven Rostedt 		int offset;
313d083395SSteven Rostedt 	} __attribute__((packed));
323d083395SSteven Rostedt };
333d083395SSteven Rostedt 
34395a59d0SAbhishek Sagar 
353c1720f0SSteven Rostedt static int notrace ftrace_calc_offset(long ip, long addr)
363c1720f0SSteven Rostedt {
373c1720f0SSteven Rostedt 	return (int)(addr - ip);
383d083395SSteven Rostedt }
393d083395SSteven Rostedt 
403c1720f0SSteven Rostedt notrace unsigned char *ftrace_nop_replace(void)
413c1720f0SSteven Rostedt {
423c1720f0SSteven Rostedt 	return (char *)ftrace_nop;
433c1720f0SSteven Rostedt }
443c1720f0SSteven Rostedt 
453c1720f0SSteven Rostedt notrace unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
463c1720f0SSteven Rostedt {
473c1720f0SSteven Rostedt 	static union ftrace_code_union calc;
483c1720f0SSteven Rostedt 
493c1720f0SSteven Rostedt 	calc.e8		= 0xe8;
50395a59d0SAbhishek Sagar 	calc.offset	= ftrace_calc_offset(ip + MCOUNT_INSN_SIZE, addr);
513c1720f0SSteven Rostedt 
523c1720f0SSteven Rostedt 	/*
533c1720f0SSteven Rostedt 	 * No locking needed, this must be called via kstop_machine
543c1720f0SSteven Rostedt 	 * which in essence is like running on a uniprocessor machine.
553c1720f0SSteven Rostedt 	 */
563c1720f0SSteven Rostedt 	return calc.code;
573c1720f0SSteven Rostedt }
583c1720f0SSteven Rostedt 
593c1720f0SSteven Rostedt notrace int
603d083395SSteven Rostedt ftrace_modify_code(unsigned long ip, unsigned char *old_code,
613d083395SSteven Rostedt 		   unsigned char *new_code)
623d083395SSteven Rostedt {
63dfa60abaSSteven Rostedt 	unsigned replaced;
64dfa60abaSSteven Rostedt 	unsigned old = *(unsigned *)old_code; /* 4 bytes */
65dfa60abaSSteven Rostedt 	unsigned new = *(unsigned *)new_code; /* 4 bytes */
66dfa60abaSSteven Rostedt 	unsigned char newch = new_code[4];
673d083395SSteven Rostedt 	int faulted = 0;
683d083395SSteven Rostedt 
693d083395SSteven Rostedt 	/*
703d083395SSteven Rostedt 	 * Note: Due to modules and __init, code can
713d083395SSteven Rostedt 	 *  disappear and change, we need to protect against faulting
723d083395SSteven Rostedt 	 *  as well as code changing.
733d083395SSteven Rostedt 	 *
743d083395SSteven Rostedt 	 * No real locking needed, this code is run through
753d083395SSteven Rostedt 	 * kstop_machine.
763d083395SSteven Rostedt 	 */
773d083395SSteven Rostedt 	asm volatile (
783d083395SSteven Rostedt 		"1: lock\n"
79dfa60abaSSteven Rostedt 		"   cmpxchg %3, (%2)\n"
80dfa60abaSSteven Rostedt 		"   jnz 2f\n"
81dfa60abaSSteven Rostedt 		"   movb %b4, 4(%2)\n"
823d083395SSteven Rostedt 		"2:\n"
833d083395SSteven Rostedt 		".section .fixup, \"ax\"\n"
84a56be3feSSteven Rostedt 		"3:	movl $1, %0\n"
85a56be3feSSteven Rostedt 		"	jmp 2b\n"
863d083395SSteven Rostedt 		".previous\n"
873d083395SSteven Rostedt 		_ASM_EXTABLE(1b, 3b)
883d083395SSteven Rostedt 		: "=r"(faulted), "=a"(replaced)
89ee4311adSIngo Molnar 		: "r"(ip), "r"(new), "c"(newch),
90dfa60abaSSteven Rostedt 		  "0"(faulted), "a"(old)
913d083395SSteven Rostedt 		: "memory");
923d083395SSteven Rostedt 	sync_core();
933d083395SSteven Rostedt 
94dfa60abaSSteven Rostedt 	if (replaced != old && replaced != new)
953d083395SSteven Rostedt 		faulted = 2;
963d083395SSteven Rostedt 
973d083395SSteven Rostedt 	return faulted;
983d083395SSteven Rostedt }
993d083395SSteven Rostedt 
100d61f82d0SSteven Rostedt notrace int ftrace_update_ftrace_func(ftrace_func_t func)
101d61f82d0SSteven Rostedt {
102d61f82d0SSteven Rostedt 	unsigned long ip = (unsigned long)(&ftrace_call);
103395a59d0SAbhishek Sagar 	unsigned char old[MCOUNT_INSN_SIZE], *new;
104d61f82d0SSteven Rostedt 	int ret;
105d61f82d0SSteven Rostedt 
106395a59d0SAbhishek Sagar 	memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE);
107d61f82d0SSteven Rostedt 	new = ftrace_call_replace(ip, (unsigned long)func);
108d61f82d0SSteven Rostedt 	ret = ftrace_modify_code(ip, old, new);
109d61f82d0SSteven Rostedt 
110d61f82d0SSteven Rostedt 	return ret;
111d61f82d0SSteven Rostedt }
112d61f82d0SSteven Rostedt 
113d61f82d0SSteven Rostedt notrace int ftrace_mcount_set(unsigned long *data)
114d61f82d0SSteven Rostedt {
1150a37605cSSteven Rostedt 	/* mcount is initialized as a nop */
1160a37605cSSteven Rostedt 	*data = 0;
117d61f82d0SSteven Rostedt 	return 0;
118d61f82d0SSteven Rostedt }
119d61f82d0SSteven Rostedt 
120d61f82d0SSteven Rostedt int __init ftrace_dyn_arch_init(void *data)
1213d083395SSteven Rostedt {
122*732f3ca7SSteven Rostedt 	extern const unsigned char ftrace_test_p6nop[];
123*732f3ca7SSteven Rostedt 	extern const unsigned char ftrace_test_nop5[];
124*732f3ca7SSteven Rostedt 	extern const unsigned char ftrace_test_jmp[];
125*732f3ca7SSteven Rostedt 	int faulted = 0;
1263d083395SSteven Rostedt 
127*732f3ca7SSteven Rostedt 	/*
128*732f3ca7SSteven Rostedt 	 * There is no good nop for all x86 archs.
129*732f3ca7SSteven Rostedt 	 * We will default to using the P6_NOP5, but first we
130*732f3ca7SSteven Rostedt 	 * will test to make sure that the nop will actually
131*732f3ca7SSteven Rostedt 	 * work on this CPU. If it faults, we will then
132*732f3ca7SSteven Rostedt 	 * go to a lesser efficient 5 byte nop. If that fails
133*732f3ca7SSteven Rostedt 	 * we then just use a jmp as our nop. This isn't the most
134*732f3ca7SSteven Rostedt 	 * efficient nop, but we can not use a multi part nop
135*732f3ca7SSteven Rostedt 	 * since we would then risk being preempted in the middle
136*732f3ca7SSteven Rostedt 	 * of that nop, and if we enabled tracing then, it might
137*732f3ca7SSteven Rostedt 	 * cause a system crash.
138*732f3ca7SSteven Rostedt 	 *
139*732f3ca7SSteven Rostedt 	 * TODO: check the cpuid to determine the best nop.
140*732f3ca7SSteven Rostedt 	 */
141*732f3ca7SSteven Rostedt 	asm volatile (
142*732f3ca7SSteven Rostedt 		"jmp ftrace_test_jmp\n"
143*732f3ca7SSteven Rostedt 		/* This code needs to stay around */
144*732f3ca7SSteven Rostedt 		".section .text, \"ax\"\n"
145*732f3ca7SSteven Rostedt 		"ftrace_test_jmp:"
146*732f3ca7SSteven Rostedt 		"jmp ftrace_test_p6nop\n"
147*732f3ca7SSteven Rostedt 		".byte 0x00,0x00,0x00\n"  /* 2 byte jmp + 3 bytes */
148*732f3ca7SSteven Rostedt 		"ftrace_test_p6nop:"
149*732f3ca7SSteven Rostedt 		P6_NOP5
150*732f3ca7SSteven Rostedt 		"jmp 1f\n"
151*732f3ca7SSteven Rostedt 		"ftrace_test_nop5:"
152*732f3ca7SSteven Rostedt 		".byte 0x66,0x66,0x66,0x66,0x90\n"
153*732f3ca7SSteven Rostedt 		"jmp 1f\n"
154*732f3ca7SSteven Rostedt 		".previous\n"
155*732f3ca7SSteven Rostedt 		"1:"
156*732f3ca7SSteven Rostedt 		".section .fixup, \"ax\"\n"
157*732f3ca7SSteven Rostedt 		"2:	movl $1, %0\n"
158*732f3ca7SSteven Rostedt 		"	jmp ftrace_test_nop5\n"
159*732f3ca7SSteven Rostedt 		"3:	movl $2, %0\n"
160*732f3ca7SSteven Rostedt 		"	jmp 1b\n"
161*732f3ca7SSteven Rostedt 		".previous\n"
162*732f3ca7SSteven Rostedt 		_ASM_EXTABLE(ftrace_test_p6nop, 2b)
163*732f3ca7SSteven Rostedt 		_ASM_EXTABLE(ftrace_test_nop5, 3b)
164*732f3ca7SSteven Rostedt 		: "=r"(faulted) : "0" (faulted));
165d61f82d0SSteven Rostedt 
166*732f3ca7SSteven Rostedt 	switch (faulted) {
167*732f3ca7SSteven Rostedt 	case 0:
168*732f3ca7SSteven Rostedt 		pr_info("ftrace: converting mcount calls to 0f 1f 44 00 00\n");
169*732f3ca7SSteven Rostedt 		ftrace_nop = (unsigned long *)ftrace_test_p6nop;
170*732f3ca7SSteven Rostedt 		break;
171*732f3ca7SSteven Rostedt 	case 1:
172*732f3ca7SSteven Rostedt 		pr_info("ftrace: converting mcount calls to 66 66 66 66 90\n");
173*732f3ca7SSteven Rostedt 		ftrace_nop = (unsigned long *)ftrace_test_nop5;
174*732f3ca7SSteven Rostedt 		break;
175*732f3ca7SSteven Rostedt 	case 2:
176*732f3ca7SSteven Rostedt 		pr_info("ftrace: converting mcount calls to jmp 1f\n");
177*732f3ca7SSteven Rostedt 		ftrace_nop = (unsigned long *)ftrace_test_jmp;
178*732f3ca7SSteven Rostedt 		break;
179*732f3ca7SSteven Rostedt 	}
180d61f82d0SSteven Rostedt 
181*732f3ca7SSteven Rostedt 	/* The return code is retured via data */
182*732f3ca7SSteven Rostedt 	*(unsigned long *)data = 0;
183dfa60abaSSteven Rostedt 
1843d083395SSteven Rostedt 	return 0;
1853d083395SSteven Rostedt }
186