| 
									
										
										
										
											2009-06-10 04:45:29 -04:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * ftrace graph code | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2010-07-21 09:13:02 -04:00
										 |  |  |  * Copyright (C) 2009-2010 Analog Devices Inc. | 
					
						
							| 
									
										
										
										
											2009-06-10 04:45:29 -04:00
										 |  |  |  * Licensed under the GPL-2 or later. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/ftrace.h>
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/sched.h>
 | 
					
						
							| 
									
										
										
										
											2010-07-21 09:13:02 -04:00
										 |  |  | #include <linux/uaccess.h>
 | 
					
						
							| 
									
										
										
										
											2011-07-26 16:09:06 -07:00
										 |  |  | #include <linux/atomic.h>
 | 
					
						
							| 
									
										
										
										
											2010-07-21 09:13:02 -04:00
										 |  |  | #include <asm/cacheflush.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_DYNAMIC_FTRACE
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const unsigned char mnop[] = { | 
					
						
							|  |  |  | 	0x03, 0xc0, 0x00, 0x18, /* MNOP; */ | 
					
						
							|  |  |  | 	0x03, 0xc0, 0x00, 0x18, /* MNOP; */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void bfin_make_pcrel24(unsigned char *insn, unsigned long src, | 
					
						
							|  |  |  |                               unsigned long dst) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	uint32_t pcrel = (dst - src) >> 1; | 
					
						
							|  |  |  | 	insn[0] = pcrel >> 16; | 
					
						
							|  |  |  | 	insn[1] = 0xe3; | 
					
						
							|  |  |  | 	insn[2] = pcrel; | 
					
						
							|  |  |  | 	insn[3] = pcrel >> 8; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #define bfin_make_pcrel24(insn, src, dst) bfin_make_pcrel24(insn, src, (unsigned long)(dst))
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int ftrace_modify_code(unsigned long ip, const unsigned char *code, | 
					
						
							|  |  |  |                               unsigned long len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret = probe_kernel_write((void *)ip, (void *)code, len); | 
					
						
							|  |  |  | 	flush_icache_range(ip, ip + len); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, | 
					
						
							|  |  |  |                     unsigned long addr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* Turn the mcount call site into two MNOPs as those are 32bit insns */ | 
					
						
							|  |  |  | 	return ftrace_modify_code(rec->ip, mnop, sizeof(mnop)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* Restore the mcount call site */ | 
					
						
							|  |  |  | 	unsigned char call[8]; | 
					
						
							|  |  |  | 	call[0] = 0x67; /* [--SP] = RETS; */ | 
					
						
							|  |  |  | 	call[1] = 0x01; | 
					
						
							|  |  |  | 	bfin_make_pcrel24(&call[2], rec->ip + 2, addr); | 
					
						
							|  |  |  | 	call[6] = 0x27; /* RETS = [SP++]; */ | 
					
						
							|  |  |  | 	call[7] = 0x01; | 
					
						
							|  |  |  | 	return ftrace_modify_code(rec->ip, call, sizeof(call)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int ftrace_update_ftrace_func(ftrace_func_t func) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned char call[4]; | 
					
						
							|  |  |  | 	unsigned long ip = (unsigned long)&ftrace_call; | 
					
						
							|  |  |  | 	bfin_make_pcrel24(call, ip, func); | 
					
						
							|  |  |  | 	return ftrace_modify_code(ip, call, sizeof(call)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-24 19:59:59 +01:00
										 |  |  | int __init ftrace_dyn_arch_init(void) | 
					
						
							| 
									
										
										
										
											2010-07-21 09:13:02 -04:00
										 |  |  | { | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2009-06-10 04:45:29 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_FUNCTION_GRAPH_TRACER
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-21 09:13:02 -04:00
										 |  |  | # ifdef CONFIG_DYNAMIC_FTRACE
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | extern void ftrace_graph_call(void); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int ftrace_enable_ftrace_graph_caller(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned long ip = (unsigned long)&ftrace_graph_call; | 
					
						
							|  |  |  | 	uint16_t jump_pcrel12 = ((unsigned long)&ftrace_graph_caller - ip) >> 1; | 
					
						
							|  |  |  | 	jump_pcrel12 |= 0x2000; | 
					
						
							|  |  |  | 	return ftrace_modify_code(ip, (void *)&jump_pcrel12, sizeof(jump_pcrel12)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int ftrace_disable_ftrace_graph_caller(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return ftrace_modify_code((unsigned long)&ftrace_graph_call, empty_zero_page, 2); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-10 04:45:29 -04:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Hook the return address and push it in the stack of return addrs | 
					
						
							|  |  |  |  * in current thread info. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2010-01-22 07:59:32 -05:00
										 |  |  | void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, | 
					
						
							|  |  |  |                            unsigned long frame_pointer) | 
					
						
							| 
									
										
										
										
											2009-06-10 04:45:29 -04:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct ftrace_graph_ent trace; | 
					
						
							|  |  |  | 	unsigned long return_hooker = (unsigned long)&return_to_handler; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (unlikely(atomic_read(¤t->tracing_graph_pause))) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-22 07:59:32 -05:00
										 |  |  | 	if (ftrace_push_return_trace(*parent, self_addr, &trace.depth, | 
					
						
							|  |  |  | 	                             frame_pointer) == -EBUSY) | 
					
						
							| 
									
										
										
										
											2009-06-10 04:45:29 -04:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	trace.func = self_addr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Only trace if the calling function expects to */ | 
					
						
							|  |  |  | 	if (!ftrace_graph_entry(&trace)) { | 
					
						
							|  |  |  | 		current->curr_ret_stack--; | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* all is well in the world !  hijack RETS ... */ | 
					
						
							|  |  |  | 	*parent = return_hooker; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif
 |