xref: /openbmc/linux/arch/parisc/kernel/ftrace.c (revision ecc23d0a422a3118fcf6e4f0a46e17a6c2047b02)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2d75f054aSHelge Deller /*
3d75f054aSHelge Deller  * Code for tracing calls in Linux kernel.
4366dd4eaSHelge Deller  * Copyright (C) 2009-2016 Helge Deller <deller@gmx.de>
5d75f054aSHelge Deller  *
6d75f054aSHelge Deller  * based on code for x86 which is:
7d75f054aSHelge Deller  * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
8d75f054aSHelge Deller  *
9d75f054aSHelge Deller  * future possible enhancements:
10d75f054aSHelge Deller  *	- add CONFIG_STACK_TRACER
11d75f054aSHelge Deller  */
12d75f054aSHelge Deller 
13d75f054aSHelge Deller #include <linux/init.h>
14d75f054aSHelge Deller #include <linux/ftrace.h>
156ca63662SSven Schnelle #include <linux/uaccess.h>
1652a22e6cSSven Schnelle #include <linux/kprobes.h>
1752a22e6cSSven Schnelle #include <linux/ptrace.h>
1898f29261SSven Schnelle #include <linux/jump_label.h>
19d75f054aSHelge Deller 
20366dd4eaSHelge Deller #include <asm/assembly.h>
21d75f054aSHelge Deller #include <asm/sections.h>
22d75f054aSHelge Deller #include <asm/ftrace.h>
236ca63662SSven Schnelle #include <asm/patch.h>
24d75f054aSHelge Deller 
2533def849SJoe Perches #define __hot __section(".text.hot")
264df3c9ecSHelge Deller 
27d75f054aSHelge Deller #ifdef CONFIG_FUNCTION_GRAPH_TRACER
2898f29261SSven Schnelle static DEFINE_STATIC_KEY_FALSE(ftrace_graph_enable);
2998f29261SSven Schnelle 
30d75f054aSHelge Deller /*
31d75f054aSHelge Deller  * Hook the return address and push it in the stack of return addrs
32d75f054aSHelge Deller  * in current thread info.
33d75f054aSHelge Deller  */
prepare_ftrace_return(unsigned long * parent,unsigned long self_addr)344df3c9ecSHelge Deller static void __hot prepare_ftrace_return(unsigned long *parent,
354df3c9ecSHelge Deller 					unsigned long self_addr)
36d75f054aSHelge Deller {
37d75f054aSHelge Deller 	unsigned long old;
38366dd4eaSHelge Deller 	extern int parisc_return_to_handler;
39d75f054aSHelge Deller 
403a46588eSSteven Rostedt (Red Hat) 	if (unlikely(ftrace_graph_is_dead()))
413a46588eSSteven Rostedt (Red Hat) 		return;
423a46588eSSteven Rostedt (Red Hat) 
43d75f054aSHelge Deller 	if (unlikely(atomic_read(&current->tracing_graph_pause)))
44d75f054aSHelge Deller 		return;
45d75f054aSHelge Deller 
46d75f054aSHelge Deller 	old = *parent;
47d75f054aSHelge Deller 
48a87532c7SSteven Rostedt (VMware) 	if (!function_graph_enter(old, self_addr, 0, NULL))
49366dd4eaSHelge Deller 		/* activate parisc_return_to_handler() as return point */
50366dd4eaSHelge Deller 		*parent = (unsigned long) &parisc_return_to_handler;
51366dd4eaSHelge Deller }
52d75f054aSHelge Deller #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
53d75f054aSHelge Deller 
5444382af8SSven Schnelle static ftrace_func_t ftrace_func;
5544382af8SSven Schnelle 
ftrace_function_trampoline(unsigned long parent,unsigned long self_addr,unsigned long org_sp_gr3,struct ftrace_regs * fregs)562794f8ecSHelge Deller asmlinkage void notrace __hot ftrace_function_trampoline(unsigned long parent,
57d75f054aSHelge Deller 				unsigned long self_addr,
5852a22e6cSSven Schnelle 				unsigned long org_sp_gr3,
59d19ad077SSteven Rostedt (VMware) 				struct ftrace_regs *fregs)
60d75f054aSHelge Deller {
61d562aca3SSven Schnelle 	extern struct ftrace_ops *function_trace_op;
62d562aca3SSven Schnelle 
6344382af8SSven Schnelle 	ftrace_func(self_addr, parent, function_trace_op, fregs);
64366dd4eaSHelge Deller 
65d75f054aSHelge Deller #ifdef CONFIG_FUNCTION_GRAPH_TRACER
6698f29261SSven Schnelle 	if (static_branch_unlikely(&ftrace_graph_enable)) {
67d75f054aSHelge Deller 		unsigned long *parent_rp;
68d75f054aSHelge Deller 
69d75f054aSHelge Deller 		/* calculate pointer to %rp in stack */
70366dd4eaSHelge Deller 		parent_rp = (unsigned long *) (org_sp_gr3 - RP_OFFSET);
71d75f054aSHelge Deller 		/* sanity check: parent_rp should hold parent */
72d75f054aSHelge Deller 		if (*parent_rp != parent)
73d75f054aSHelge Deller 			return;
74d75f054aSHelge Deller 
75d75f054aSHelge Deller 		prepare_ftrace_return(parent_rp, self_addr);
76d75f054aSHelge Deller 		return;
77d75f054aSHelge Deller 	}
78d75f054aSHelge Deller #endif
79d75f054aSHelge Deller }
80d75f054aSHelge Deller 
813a28164dSMax Kellermann #if defined(CONFIG_DYNAMIC_FTRACE) && defined(CONFIG_FUNCTION_GRAPH_TRACER)
ftrace_enable_ftrace_graph_caller(void)826ca63662SSven Schnelle int ftrace_enable_ftrace_graph_caller(void)
836ca63662SSven Schnelle {
8498f29261SSven Schnelle 	static_key_enable(&ftrace_graph_enable.key);
856ca63662SSven Schnelle 	return 0;
866ca63662SSven Schnelle }
876ca63662SSven Schnelle 
ftrace_disable_ftrace_graph_caller(void)886ca63662SSven Schnelle int ftrace_disable_ftrace_graph_caller(void)
896ca63662SSven Schnelle {
90*9665bbefSJosh Poimboeuf 	static_key_disable(&ftrace_graph_enable.key);
916ca63662SSven Schnelle 	return 0;
926ca63662SSven Schnelle }
936ca63662SSven Schnelle #endif
946ca63662SSven Schnelle 
956ca63662SSven Schnelle #ifdef CONFIG_DYNAMIC_FTRACE
ftrace_update_ftrace_func(ftrace_func_t func)966ca63662SSven Schnelle int ftrace_update_ftrace_func(ftrace_func_t func)
976ca63662SSven Schnelle {
9844382af8SSven Schnelle 	ftrace_func = func;
996ca63662SSven Schnelle 	return 0;
1006ca63662SSven Schnelle }
1016ca63662SSven Schnelle 
ftrace_modify_call(struct dyn_ftrace * rec,unsigned long old_addr,unsigned long addr)10252a22e6cSSven Schnelle int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
10352a22e6cSSven Schnelle 			unsigned long addr)
10452a22e6cSSven Schnelle {
10552a22e6cSSven Schnelle 	return 0;
10652a22e6cSSven Schnelle }
10752a22e6cSSven Schnelle 
ftrace_call_adjust(unsigned long addr)1086ca63662SSven Schnelle unsigned long ftrace_call_adjust(unsigned long addr)
1096ca63662SSven Schnelle {
1106ca63662SSven Schnelle 	return addr+(FTRACE_PATCHABLE_FUNCTION_SIZE-1)*4;
1116ca63662SSven Schnelle }
1126ca63662SSven Schnelle 
ftrace_make_call(struct dyn_ftrace * rec,unsigned long addr)1136ca63662SSven Schnelle int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
1146ca63662SSven Schnelle {
1156ca63662SSven Schnelle 	u32 insn[FTRACE_PATCHABLE_FUNCTION_SIZE];
1166ca63662SSven Schnelle 	u32 *tramp;
1176ca63662SSven Schnelle 	int size, ret, i;
1186ca63662SSven Schnelle 	void *ip;
1196ca63662SSven Schnelle 
1206ca63662SSven Schnelle #ifdef CONFIG_64BIT
1216ca63662SSven Schnelle 	unsigned long addr2 =
1226ca63662SSven Schnelle 		(unsigned long)dereference_function_descriptor((void *)addr);
1236ca63662SSven Schnelle 
1246ca63662SSven Schnelle 	u32 ftrace_trampoline[] = {
1256ca63662SSven Schnelle 		0x73c10208, /* std,ma r1,100(sp) */
1266ca63662SSven Schnelle 		0x0c2110c1, /* ldd -10(r1),r1 */
1276ca63662SSven Schnelle 		0xe820d002, /* bve,n (r1) */
1286ca63662SSven Schnelle 		addr2 >> 32,
1296ca63662SSven Schnelle 		addr2 & 0xffffffff,
1306ca63662SSven Schnelle 		0xe83f1fd7, /* b,l,n .-14,r1 */
1316ca63662SSven Schnelle 	};
1326ca63662SSven Schnelle 
1336ca63662SSven Schnelle 	u32 ftrace_trampoline_unaligned[] = {
1346ca63662SSven Schnelle 		addr2 >> 32,
1356ca63662SSven Schnelle 		addr2 & 0xffffffff,
1366ca63662SSven Schnelle 		0x37de0200, /* ldo 100(sp),sp */
1376ca63662SSven Schnelle 		0x73c13e01, /* std r1,-100(sp) */
1386ca63662SSven Schnelle 		0x34213ff9, /* ldo -4(r1),r1 */
1396ca63662SSven Schnelle 		0x50213fc1, /* ldd -20(r1),r1 */
1406ca63662SSven Schnelle 		0xe820d002, /* bve,n (r1) */
1416ca63662SSven Schnelle 		0xe83f1fcf, /* b,l,n .-20,r1 */
1426ca63662SSven Schnelle 	};
1436ca63662SSven Schnelle 
1446ca63662SSven Schnelle 	BUILD_BUG_ON(ARRAY_SIZE(ftrace_trampoline_unaligned) >
1456ca63662SSven Schnelle 				FTRACE_PATCHABLE_FUNCTION_SIZE);
1466ca63662SSven Schnelle #else
1476ca63662SSven Schnelle 	u32 ftrace_trampoline[] = {
1486ca63662SSven Schnelle 		(u32)addr,
1496ca63662SSven Schnelle 		0x6fc10080, /* stw,ma r1,40(sp) */
1506ca63662SSven Schnelle 		0x48213fd1, /* ldw -18(r1),r1 */
1516ca63662SSven Schnelle 		0xe820c002, /* bv,n r0(r1) */
1526ca63662SSven Schnelle 		0xe83f1fdf, /* b,l,n .-c,r1 */
1536ca63662SSven Schnelle 	};
1546ca63662SSven Schnelle #endif
1556ca63662SSven Schnelle 
1566ca63662SSven Schnelle 	BUILD_BUG_ON(ARRAY_SIZE(ftrace_trampoline) >
1576ca63662SSven Schnelle 				FTRACE_PATCHABLE_FUNCTION_SIZE);
1586ca63662SSven Schnelle 
1596ca63662SSven Schnelle 	size = sizeof(ftrace_trampoline);
1606ca63662SSven Schnelle 	tramp = ftrace_trampoline;
1616ca63662SSven Schnelle 
1626ca63662SSven Schnelle #ifdef CONFIG_64BIT
1636ca63662SSven Schnelle 	if (rec->ip & 0x4) {
1646ca63662SSven Schnelle 		size = sizeof(ftrace_trampoline_unaligned);
1656ca63662SSven Schnelle 		tramp = ftrace_trampoline_unaligned;
1666ca63662SSven Schnelle 	}
1676ca63662SSven Schnelle #endif
1686ca63662SSven Schnelle 
1696ca63662SSven Schnelle 	ip = (void *)(rec->ip + 4 - size);
1706ca63662SSven Schnelle 
171fe557319SChristoph Hellwig 	ret = copy_from_kernel_nofault(insn, ip, size);
1726ca63662SSven Schnelle 	if (ret)
1736ca63662SSven Schnelle 		return ret;
1746ca63662SSven Schnelle 
1756ca63662SSven Schnelle 	for (i = 0; i < size / 4; i++) {
1766ca63662SSven Schnelle 		if (insn[i] != INSN_NOP)
1776ca63662SSven Schnelle 			return -EINVAL;
1786ca63662SSven Schnelle 	}
1796ca63662SSven Schnelle 
1806ca63662SSven Schnelle 	__patch_text_multiple(ip, tramp, size);
1816ca63662SSven Schnelle 	return 0;
1826ca63662SSven Schnelle }
1836ca63662SSven Schnelle 
ftrace_make_nop(struct module * mod,struct dyn_ftrace * rec,unsigned long addr)1846ca63662SSven Schnelle int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
1856ca63662SSven Schnelle 		    unsigned long addr)
1866ca63662SSven Schnelle {
1876ca63662SSven Schnelle 	u32 insn[FTRACE_PATCHABLE_FUNCTION_SIZE];
1886ca63662SSven Schnelle 	int i;
1896ca63662SSven Schnelle 
1906ca63662SSven Schnelle 	for (i = 0; i < ARRAY_SIZE(insn); i++)
1916ca63662SSven Schnelle 		insn[i] = INSN_NOP;
1926ca63662SSven Schnelle 
193740f05f3SSven Schnelle 	__patch_text((void *)rec->ip, INSN_NOP);
1946ca63662SSven Schnelle 	__patch_text_multiple((void *)rec->ip + 4 - sizeof(insn),
195740f05f3SSven Schnelle 			      insn, sizeof(insn)-4);
1966ca63662SSven Schnelle 	return 0;
1976ca63662SSven Schnelle }
1986ca63662SSven Schnelle #endif
19952a22e6cSSven Schnelle 
20052a22e6cSSven Schnelle #ifdef CONFIG_KPROBES_ON_FTRACE
kprobe_ftrace_handler(unsigned long ip,unsigned long parent_ip,struct ftrace_ops * ops,struct ftrace_regs * fregs)20152a22e6cSSven Schnelle void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
202d19ad077SSteven Rostedt (VMware) 			   struct ftrace_ops *ops, struct ftrace_regs *fregs)
20352a22e6cSSven Schnelle {
20452a22e6cSSven Schnelle 	struct kprobe_ctlblk *kcb;
205d19ad077SSteven Rostedt (VMware) 	struct pt_regs *regs;
206c536aa1cSSteven Rostedt (VMware) 	struct kprobe *p;
207c536aa1cSSteven Rostedt (VMware) 	int bit;
20852a22e6cSSven Schnelle 
209ae0d1ea3SStephen Brennan 	if (unlikely(kprobe_ftrace_disabled))
210ae0d1ea3SStephen Brennan 		return;
211ae0d1ea3SStephen Brennan 
212773c1670SSteven Rostedt (VMware) 	bit = ftrace_test_recursion_trylock(ip, parent_ip);
213c536aa1cSSteven Rostedt (VMware) 	if (bit < 0)
21452a22e6cSSven Schnelle 		return;
21552a22e6cSSven Schnelle 
216d19ad077SSteven Rostedt (VMware) 	regs = ftrace_get_regs(fregs);
217c536aa1cSSteven Rostedt (VMware) 	p = get_kprobe((kprobe_opcode_t *)ip);
218c536aa1cSSteven Rostedt (VMware) 	if (unlikely(!p) || kprobe_disabled(p))
219c536aa1cSSteven Rostedt (VMware) 		goto out;
220c536aa1cSSteven Rostedt (VMware) 
22152a22e6cSSven Schnelle 	if (kprobe_running()) {
22252a22e6cSSven Schnelle 		kprobes_inc_nmissed_count(p);
223c536aa1cSSteven Rostedt (VMware) 		goto out;
22452a22e6cSSven Schnelle 	}
22552a22e6cSSven Schnelle 
22652a22e6cSSven Schnelle 	__this_cpu_write(current_kprobe, p);
22752a22e6cSSven Schnelle 
22852a22e6cSSven Schnelle 	kcb = get_kprobe_ctlblk();
22952a22e6cSSven Schnelle 	kcb->kprobe_status = KPROBE_HIT_ACTIVE;
23052a22e6cSSven Schnelle 
23152a22e6cSSven Schnelle 	regs->iaoq[0] = ip;
23252a22e6cSSven Schnelle 	regs->iaoq[1] = ip + 4;
23352a22e6cSSven Schnelle 
23452a22e6cSSven Schnelle 	if (!p->pre_handler || !p->pre_handler(p, regs)) {
23552a22e6cSSven Schnelle 		regs->iaoq[0] = ip + 4;
23652a22e6cSSven Schnelle 		regs->iaoq[1] = ip + 8;
23752a22e6cSSven Schnelle 
23852a22e6cSSven Schnelle 		if (unlikely(p->post_handler)) {
23952a22e6cSSven Schnelle 			kcb->kprobe_status = KPROBE_HIT_SSDONE;
24052a22e6cSSven Schnelle 			p->post_handler(p, regs, 0);
24152a22e6cSSven Schnelle 		}
24252a22e6cSSven Schnelle 	}
24352a22e6cSSven Schnelle 	__this_cpu_write(current_kprobe, NULL);
244c536aa1cSSteven Rostedt (VMware) out:
245c536aa1cSSteven Rostedt (VMware) 	ftrace_test_recursion_unlock(bit);
24652a22e6cSSven Schnelle }
24752a22e6cSSven Schnelle NOKPROBE_SYMBOL(kprobe_ftrace_handler);
24852a22e6cSSven Schnelle 
arch_prepare_kprobe_ftrace(struct kprobe * p)24952a22e6cSSven Schnelle int arch_prepare_kprobe_ftrace(struct kprobe *p)
25052a22e6cSSven Schnelle {
25152a22e6cSSven Schnelle 	p->ainsn.insn = NULL;
25252a22e6cSSven Schnelle 	return 0;
25352a22e6cSSven Schnelle }
25452a22e6cSSven Schnelle #endif
255