xref: /openbmc/linux/arch/x86/kernel/ftrace.c (revision dfa60aba04dae7833d75b2e2be124bb7cfb8239f)
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 
19*dfa60abaSSteven Rostedt #include <asm/alternative.h>
20*dfa60abaSSteven Rostedt 
213d083395SSteven Rostedt #define CALL_BACK		5
223d083395SSteven Rostedt 
23*dfa60abaSSteven Rostedt /* Long is fine, even if it is only 4 bytes ;-) */
24*dfa60abaSSteven Rostedt static long *ftrace_nop;
253d083395SSteven Rostedt 
263d083395SSteven Rostedt struct ftrace_record {
273d083395SSteven Rostedt 	struct dyn_ftrace	rec;
283d083395SSteven Rostedt 	int			failed;
293d083395SSteven Rostedt } __attribute__((packed));
303d083395SSteven Rostedt 
313d083395SSteven Rostedt struct ftrace_page {
323d083395SSteven Rostedt 	struct ftrace_page	*next;
333d083395SSteven Rostedt 	int			index;
343d083395SSteven Rostedt 	struct ftrace_record	records[];
353d083395SSteven Rostedt } __attribute__((packed));
363d083395SSteven Rostedt 
373d083395SSteven Rostedt #define ENTRIES_PER_PAGE \
383d083395SSteven Rostedt   ((PAGE_SIZE - sizeof(struct ftrace_page)) / sizeof(struct ftrace_record))
393d083395SSteven Rostedt 
403d083395SSteven Rostedt /* estimate from running different kernels */
413d083395SSteven Rostedt #define NR_TO_INIT		10000
423d083395SSteven Rostedt 
433d083395SSteven Rostedt #define MCOUNT_ADDR ((long)(&mcount))
443d083395SSteven Rostedt 
453d083395SSteven Rostedt union ftrace_code_union {
463d083395SSteven Rostedt 	char code[5];
473d083395SSteven Rostedt 	struct {
483d083395SSteven Rostedt 		char e8;
493d083395SSteven Rostedt 		int offset;
503d083395SSteven Rostedt 	} __attribute__((packed));
513d083395SSteven Rostedt };
523d083395SSteven Rostedt 
533d083395SSteven Rostedt static struct ftrace_page	*ftrace_pages_start;
543d083395SSteven Rostedt static struct ftrace_page	*ftrace_pages;
553d083395SSteven Rostedt 
563d083395SSteven Rostedt notrace struct dyn_ftrace *ftrace_alloc_shutdown_node(unsigned long ip)
573d083395SSteven Rostedt {
583d083395SSteven Rostedt 	struct ftrace_record *rec;
59*dfa60abaSSteven Rostedt 	unsigned long save;
603d083395SSteven Rostedt 
613d083395SSteven Rostedt 	ip -= CALL_BACK;
62*dfa60abaSSteven Rostedt 	save = *(long *)ip;
633d083395SSteven Rostedt 
643d083395SSteven Rostedt 	/* If this was already converted, skip it */
65*dfa60abaSSteven Rostedt 	if (save == *ftrace_nop)
663d083395SSteven Rostedt 		return NULL;
673d083395SSteven Rostedt 
683d083395SSteven Rostedt 	if (ftrace_pages->index == ENTRIES_PER_PAGE) {
693d083395SSteven Rostedt 		if (!ftrace_pages->next)
703d083395SSteven Rostedt 			return NULL;
713d083395SSteven Rostedt 		ftrace_pages = ftrace_pages->next;
723d083395SSteven Rostedt 	}
733d083395SSteven Rostedt 
743d083395SSteven Rostedt 	rec = &ftrace_pages->records[ftrace_pages->index++];
753d083395SSteven Rostedt 
763d083395SSteven Rostedt 	return &rec->rec;
773d083395SSteven Rostedt }
783d083395SSteven Rostedt 
793d083395SSteven Rostedt static int notrace
803d083395SSteven Rostedt ftrace_modify_code(unsigned long ip, unsigned char *old_code,
813d083395SSteven Rostedt 		   unsigned char *new_code)
823d083395SSteven Rostedt {
83*dfa60abaSSteven Rostedt 	unsigned replaced;
84*dfa60abaSSteven Rostedt 	unsigned old = *(unsigned *)old_code; /* 4 bytes */
85*dfa60abaSSteven Rostedt 	unsigned new = *(unsigned *)new_code; /* 4 bytes */
86*dfa60abaSSteven Rostedt 	unsigned char newch = new_code[4];
873d083395SSteven Rostedt 	int faulted = 0;
883d083395SSteven Rostedt 
893d083395SSteven Rostedt 	/*
903d083395SSteven Rostedt 	 * Note: Due to modules and __init, code can
913d083395SSteven Rostedt 	 *  disappear and change, we need to protect against faulting
923d083395SSteven Rostedt 	 *  as well as code changing.
933d083395SSteven Rostedt 	 *
943d083395SSteven Rostedt 	 * No real locking needed, this code is run through
953d083395SSteven Rostedt 	 * kstop_machine.
963d083395SSteven Rostedt 	 */
973d083395SSteven Rostedt 	asm volatile (
983d083395SSteven Rostedt 		"1: lock\n"
99*dfa60abaSSteven Rostedt 		"   cmpxchg %3, (%2)\n"
100*dfa60abaSSteven Rostedt 		"   jnz 2f\n"
101*dfa60abaSSteven Rostedt 		"   movb %b4, 4(%2)\n"
1023d083395SSteven Rostedt 		"2:\n"
1033d083395SSteven Rostedt 		".section .fixup, \"ax\"\n"
1043d083395SSteven Rostedt 		"	movl $1, %0\n"
1053d083395SSteven Rostedt 		"3:	jmp 2b\n"
1063d083395SSteven Rostedt 		".previous\n"
1073d083395SSteven Rostedt 		_ASM_EXTABLE(1b, 3b)
1083d083395SSteven Rostedt 		: "=r"(faulted), "=a"(replaced)
109*dfa60abaSSteven Rostedt 		: "r"(ip), "r"(new), "r"(newch),
110*dfa60abaSSteven Rostedt 		  "0"(faulted), "a"(old)
1113d083395SSteven Rostedt 		: "memory");
1123d083395SSteven Rostedt 	sync_core();
1133d083395SSteven Rostedt 
114*dfa60abaSSteven Rostedt 	if (replaced != old && replaced != new)
1153d083395SSteven Rostedt 		faulted = 2;
1163d083395SSteven Rostedt 
1173d083395SSteven Rostedt 	return faulted;
1183d083395SSteven Rostedt }
1193d083395SSteven Rostedt 
1203d083395SSteven Rostedt static int notrace ftrace_calc_offset(long ip)
1213d083395SSteven Rostedt {
1223d083395SSteven Rostedt 	return (int)(MCOUNT_ADDR - ip);
1233d083395SSteven Rostedt }
1243d083395SSteven Rostedt 
1253d083395SSteven Rostedt notrace void ftrace_code_disable(struct dyn_ftrace *rec)
1263d083395SSteven Rostedt {
1273d083395SSteven Rostedt 	unsigned long ip;
1283d083395SSteven Rostedt 	union ftrace_code_union save;
1293d083395SSteven Rostedt 	struct ftrace_record *r =
1303d083395SSteven Rostedt 		container_of(rec, struct ftrace_record, rec);
1313d083395SSteven Rostedt 
1323d083395SSteven Rostedt 	ip = rec->ip;
1333d083395SSteven Rostedt 
1343d083395SSteven Rostedt 	save.e8		= 0xe8;
1353d083395SSteven Rostedt 	save.offset 	= ftrace_calc_offset(ip);
1363d083395SSteven Rostedt 
1373d083395SSteven Rostedt 	/* move the IP back to the start of the call */
1383d083395SSteven Rostedt 	ip -= CALL_BACK;
1393d083395SSteven Rostedt 
140*dfa60abaSSteven Rostedt 	r->failed = ftrace_modify_code(ip, save.code, (char *)ftrace_nop);
1413d083395SSteven Rostedt }
1423d083395SSteven Rostedt 
1433d083395SSteven Rostedt static void notrace ftrace_replace_code(int saved)
1443d083395SSteven Rostedt {
1453d083395SSteven Rostedt 	unsigned char *new = NULL, *old = NULL;
1463d083395SSteven Rostedt 	struct ftrace_record *rec;
1473d083395SSteven Rostedt 	struct ftrace_page *pg;
1483d083395SSteven Rostedt 	unsigned long ip;
1493d083395SSteven Rostedt 	int i;
1503d083395SSteven Rostedt 
1513d083395SSteven Rostedt 	if (saved)
152*dfa60abaSSteven Rostedt 		old = (char *)ftrace_nop;
1533d083395SSteven Rostedt 	else
154*dfa60abaSSteven Rostedt 		new = (char *)ftrace_nop;
1553d083395SSteven Rostedt 
1563d083395SSteven Rostedt 	for (pg = ftrace_pages_start; pg; pg = pg->next) {
1573d083395SSteven Rostedt 		for (i = 0; i < pg->index; i++) {
1583d083395SSteven Rostedt 			union ftrace_code_union calc;
1593d083395SSteven Rostedt 			rec = &pg->records[i];
1603d083395SSteven Rostedt 
1613d083395SSteven Rostedt 			/* don't modify code that has already faulted */
1623d083395SSteven Rostedt 			if (rec->failed)
1633d083395SSteven Rostedt 				continue;
1643d083395SSteven Rostedt 
1653d083395SSteven Rostedt 			ip = rec->rec.ip;
1663d083395SSteven Rostedt 
1673d083395SSteven Rostedt 			calc.e8		= 0xe8;
1683d083395SSteven Rostedt 			calc.offset	= ftrace_calc_offset(ip);
1693d083395SSteven Rostedt 
1703d083395SSteven Rostedt 			if (saved)
1713d083395SSteven Rostedt 				new = calc.code;
1723d083395SSteven Rostedt 			else
1733d083395SSteven Rostedt 				old = calc.code;
1743d083395SSteven Rostedt 
1753d083395SSteven Rostedt 			ip -= CALL_BACK;
1763d083395SSteven Rostedt 
1773d083395SSteven Rostedt 			rec->failed = ftrace_modify_code(ip, old, new);
1783d083395SSteven Rostedt 		}
1793d083395SSteven Rostedt 	}
1803d083395SSteven Rostedt 
1813d083395SSteven Rostedt }
1823d083395SSteven Rostedt 
1833d083395SSteven Rostedt notrace void ftrace_startup_code(void)
1843d083395SSteven Rostedt {
1853d083395SSteven Rostedt 	ftrace_replace_code(1);
1863d083395SSteven Rostedt }
1873d083395SSteven Rostedt 
1883d083395SSteven Rostedt notrace void ftrace_shutdown_code(void)
1893d083395SSteven Rostedt {
1903d083395SSteven Rostedt 	ftrace_replace_code(0);
1913d083395SSteven Rostedt }
1923d083395SSteven Rostedt 
1933d083395SSteven Rostedt notrace void ftrace_shutdown_replenish(void)
1943d083395SSteven Rostedt {
1953d083395SSteven Rostedt 	if (ftrace_pages->next)
1963d083395SSteven Rostedt 		return;
1973d083395SSteven Rostedt 
1983d083395SSteven Rostedt 	/* allocate another page */
1993d083395SSteven Rostedt 	ftrace_pages->next = (void *)get_zeroed_page(GFP_KERNEL);
2003d083395SSteven Rostedt }
2013d083395SSteven Rostedt 
202*dfa60abaSSteven Rostedt notrace int __init ftrace_shutdown_arch_init(void)
2033d083395SSteven Rostedt {
204*dfa60abaSSteven Rostedt 	const unsigned char *const *noptable = find_nop_table();
2053d083395SSteven Rostedt 	struct ftrace_page *pg;
2063d083395SSteven Rostedt 	int cnt;
2073d083395SSteven Rostedt 	int i;
2083d083395SSteven Rostedt 
209*dfa60abaSSteven Rostedt 	ftrace_nop = (unsigned long *)noptable[CALL_BACK];
210*dfa60abaSSteven Rostedt 
2113d083395SSteven Rostedt 	/* allocate a few pages */
2123d083395SSteven Rostedt 	ftrace_pages_start = (void *)get_zeroed_page(GFP_KERNEL);
2133d083395SSteven Rostedt 	if (!ftrace_pages_start)
2143d083395SSteven Rostedt 		return -1;
2153d083395SSteven Rostedt 
2163d083395SSteven Rostedt 	/*
2173d083395SSteven Rostedt 	 * Allocate a few more pages.
2183d083395SSteven Rostedt 	 *
2193d083395SSteven Rostedt 	 * TODO: have some parser search vmlinux before
2203d083395SSteven Rostedt 	 *   final linking to find all calls to ftrace.
2213d083395SSteven Rostedt 	 *   Then we can:
2223d083395SSteven Rostedt 	 *    a) know how many pages to allocate.
2233d083395SSteven Rostedt 	 *     and/or
2243d083395SSteven Rostedt 	 *    b) set up the table then.
2253d083395SSteven Rostedt 	 *
2263d083395SSteven Rostedt 	 *  The dynamic code is still necessary for
2273d083395SSteven Rostedt 	 *  modules.
2283d083395SSteven Rostedt 	 */
2293d083395SSteven Rostedt 
2303d083395SSteven Rostedt 	pg = ftrace_pages = ftrace_pages_start;
2313d083395SSteven Rostedt 
2323d083395SSteven Rostedt 	cnt = NR_TO_INIT / ENTRIES_PER_PAGE;
2333d083395SSteven Rostedt 
2343d083395SSteven Rostedt 	for (i = 0; i < cnt; i++) {
2353d083395SSteven Rostedt 		pg->next = (void *)get_zeroed_page(GFP_KERNEL);
2363d083395SSteven Rostedt 
2373d083395SSteven Rostedt 		/* If we fail, we'll try later anyway */
2383d083395SSteven Rostedt 		if (!pg->next)
2393d083395SSteven Rostedt 			break;
2403d083395SSteven Rostedt 
2413d083395SSteven Rostedt 		pg = pg->next;
2423d083395SSteven Rostedt 	}
2433d083395SSteven Rostedt 
2443d083395SSteven Rostedt 	return 0;
2453d083395SSteven Rostedt }
246