forked from mirrors/linux
		
	metag: ftrace support
Add ftrace support for metag. Signed-off-by: James Hogan <james.hogan@imgtec.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Ingo Molnar <mingo@redhat.com> Reviewed-by: Steven Rostedt <rostedt@goodmis.org>
This commit is contained in:
		
							parent
							
								
									903b20ad68
								
							
						
					
					
						commit
						00512bdd45
					
				
					 7 changed files with 248 additions and 1 deletions
				
			
		|  | @ -12,7 +12,12 @@ config METAG | ||||||
| 	select GENERIC_SMP_IDLE_THREAD | 	select GENERIC_SMP_IDLE_THREAD | ||||||
| 	select HAVE_64BIT_ALIGNED_ACCESS | 	select HAVE_64BIT_ALIGNED_ACCESS | ||||||
| 	select HAVE_ARCH_TRACEHOOK | 	select HAVE_ARCH_TRACEHOOK | ||||||
|  | 	select HAVE_C_RECORDMCOUNT | ||||||
| 	select HAVE_DEBUG_KMEMLEAK | 	select HAVE_DEBUG_KMEMLEAK | ||||||
|  | 	select HAVE_DYNAMIC_FTRACE | ||||||
|  | 	select HAVE_FTRACE_MCOUNT_RECORD | ||||||
|  | 	select HAVE_FUNCTION_TRACER | ||||||
|  | 	select HAVE_FUNCTION_TRACE_MCOUNT_TEST | ||||||
| 	select HAVE_GENERIC_HARDIRQS | 	select HAVE_GENERIC_HARDIRQS | ||||||
| 	select HAVE_IRQ_WORK | 	select HAVE_IRQ_WORK | ||||||
| 	select HAVE_KERNEL_BZIP2 | 	select HAVE_KERNEL_BZIP2 | ||||||
|  |  | ||||||
|  | @ -11,7 +11,6 @@ generic-y += errno.h | ||||||
| generic-y += exec.h | generic-y += exec.h | ||||||
| generic-y += fb.h | generic-y += fb.h | ||||||
| generic-y += fcntl.h | generic-y += fcntl.h | ||||||
| generic-y += ftrace.h |  | ||||||
| generic-y += futex.h | generic-y += futex.h | ||||||
| generic-y += hardirq.h | generic-y += hardirq.h | ||||||
| generic-y += hw_irq.h | generic-y += hw_irq.h | ||||||
|  |  | ||||||
							
								
								
									
										23
									
								
								arch/metag/include/asm/ftrace.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								arch/metag/include/asm/ftrace.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | ||||||
|  | #ifndef _ASM_METAG_FTRACE | ||||||
|  | #define _ASM_METAG_FTRACE | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_FUNCTION_TRACER | ||||||
|  | #define MCOUNT_INSN_SIZE	8 /* sizeof mcount call */ | ||||||
|  | 
 | ||||||
|  | #ifndef __ASSEMBLY__ | ||||||
|  | extern void mcount_wrapper(void); | ||||||
|  | #define MCOUNT_ADDR		((long)(mcount_wrapper)) | ||||||
|  | 
 | ||||||
|  | static inline unsigned long ftrace_call_adjust(unsigned long addr) | ||||||
|  | { | ||||||
|  | 	return addr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct dyn_arch_ftrace { | ||||||
|  | 	/* No extra data needed on metag */ | ||||||
|  | }; | ||||||
|  | #endif /* __ASSEMBLY__ */ | ||||||
|  | 
 | ||||||
|  | #endif /* CONFIG_FUNCTION_TRACER */ | ||||||
|  | 
 | ||||||
|  | #endif /* _ASM_METAG_FTRACE */ | ||||||
							
								
								
									
										126
									
								
								arch/metag/kernel/ftrace.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								arch/metag/kernel/ftrace.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,126 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2008 Imagination Technologies Ltd. | ||||||
|  |  * Licensed under the GPL | ||||||
|  |  * | ||||||
|  |  * Dynamic ftrace support. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <linux/ftrace.h> | ||||||
|  | #include <linux/io.h> | ||||||
|  | #include <linux/uaccess.h> | ||||||
|  | 
 | ||||||
|  | #include <asm/cacheflush.h> | ||||||
|  | 
 | ||||||
|  | #define D04_MOVT_TEMPLATE	0x02200005 | ||||||
|  | #define D04_CALL_TEMPLATE	0xAC200005 | ||||||
|  | #define D1RTP_MOVT_TEMPLATE	0x03200005 | ||||||
|  | #define D1RTP_CALL_TEMPLATE	0xAC200006 | ||||||
|  | 
 | ||||||
|  | static const unsigned long NOP[2] = {0xa0fffffe, 0xa0fffffe}; | ||||||
|  | static unsigned long movt_and_call_insn[2]; | ||||||
|  | 
 | ||||||
|  | static unsigned char *ftrace_nop_replace(void) | ||||||
|  | { | ||||||
|  | 	return (char *)&NOP[0]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static unsigned char *ftrace_call_replace(unsigned long pc, unsigned long addr) | ||||||
|  | { | ||||||
|  | 	unsigned long hi16, low16; | ||||||
|  | 
 | ||||||
|  | 	hi16 = (addr & 0xffff0000) >> 13; | ||||||
|  | 	low16 = (addr & 0x0000ffff) << 3; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * The compiler makes the call to mcount_wrapper() | ||||||
|  | 	 * (Meta's wrapper around mcount()) through the register | ||||||
|  | 	 * D0.4. So whenever we're patching one of those compiler-generated | ||||||
|  | 	 * calls we also need to go through D0.4. Otherwise use D1RtP. | ||||||
|  | 	 */ | ||||||
|  | 	if (pc == (unsigned long)&ftrace_call) { | ||||||
|  | 		writel(D1RTP_MOVT_TEMPLATE | hi16, &movt_and_call_insn[0]); | ||||||
|  | 		writel(D1RTP_CALL_TEMPLATE | low16, &movt_and_call_insn[1]); | ||||||
|  | 	} else { | ||||||
|  | 		writel(D04_MOVT_TEMPLATE | hi16, &movt_and_call_insn[0]); | ||||||
|  | 		writel(D04_CALL_TEMPLATE | low16, &movt_and_call_insn[1]); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return (unsigned char *)&movt_and_call_insn[0]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int ftrace_modify_code(unsigned long pc, unsigned char *old_code, | ||||||
|  | 			      unsigned char *new_code) | ||||||
|  | { | ||||||
|  | 	unsigned char replaced[MCOUNT_INSN_SIZE]; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Note: Due to modules and __init, code can | ||||||
|  | 	 *  disappear and change, we need to protect against faulting | ||||||
|  | 	 *  as well as code changing. | ||||||
|  | 	 * | ||||||
|  | 	 * No real locking needed, this code is run through | ||||||
|  | 	 * kstop_machine. | ||||||
|  | 	 */ | ||||||
|  | 
 | ||||||
|  | 	/* read the text we want to modify */ | ||||||
|  | 	if (probe_kernel_read(replaced, (void *)pc, MCOUNT_INSN_SIZE)) | ||||||
|  | 		return -EFAULT; | ||||||
|  | 
 | ||||||
|  | 	/* Make sure it is what we expect it to be */ | ||||||
|  | 	if (memcmp(replaced, old_code, MCOUNT_INSN_SIZE) != 0) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	/* replace the text with the new text */ | ||||||
|  | 	if (probe_kernel_write((void *)pc, new_code, MCOUNT_INSN_SIZE)) | ||||||
|  | 		return -EPERM; | ||||||
|  | 
 | ||||||
|  | 	flush_icache_range(pc, pc + MCOUNT_INSN_SIZE); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int ftrace_update_ftrace_func(ftrace_func_t func) | ||||||
|  | { | ||||||
|  | 	int ret; | ||||||
|  | 	unsigned long pc; | ||||||
|  | 	unsigned char old[MCOUNT_INSN_SIZE], *new; | ||||||
|  | 
 | ||||||
|  | 	pc = (unsigned long)&ftrace_call; | ||||||
|  | 	memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE); | ||||||
|  | 	new = ftrace_call_replace(pc, (unsigned long)func); | ||||||
|  | 	ret = ftrace_modify_code(pc, old, new); | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int ftrace_make_nop(struct module *mod, | ||||||
|  | 		    struct dyn_ftrace *rec, unsigned long addr) | ||||||
|  | { | ||||||
|  | 	unsigned char *new, *old; | ||||||
|  | 	unsigned long ip = rec->ip; | ||||||
|  | 
 | ||||||
|  | 	old = ftrace_call_replace(ip, addr); | ||||||
|  | 	new = ftrace_nop_replace(); | ||||||
|  | 
 | ||||||
|  | 	return ftrace_modify_code(ip, old, new); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | ||||||
|  | { | ||||||
|  | 	unsigned char *new, *old; | ||||||
|  | 	unsigned long ip = rec->ip; | ||||||
|  | 
 | ||||||
|  | 	old = ftrace_nop_replace(); | ||||||
|  | 	new = ftrace_call_replace(ip, addr); | ||||||
|  | 
 | ||||||
|  | 	return ftrace_modify_code(ip, old, new); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* run from kstop_machine */ | ||||||
|  | int __init ftrace_dyn_arch_init(void *data) | ||||||
|  | { | ||||||
|  | 	/* The return code is returned via data */ | ||||||
|  | 	writel(0, data); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
							
								
								
									
										76
									
								
								arch/metag/kernel/ftrace_stub.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								arch/metag/kernel/ftrace_stub.S
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,76 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (C) 2008 Imagination Technologies Ltd. | ||||||
|  |  * Licensed under the GPL | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <asm/ftrace.h> | ||||||
|  | 
 | ||||||
|  | 	.text | ||||||
|  | #ifdef CONFIG_DYNAMIC_FTRACE | ||||||
|  | 	.global	_mcount_wrapper
 | ||||||
|  | 	.type	_mcount_wrapper,function | ||||||
|  | _mcount_wrapper: | ||||||
|  | 	MOV	PC,D0.4 | ||||||
|  | 
 | ||||||
|  | 	.global _ftrace_caller
 | ||||||
|  | 	.type	_ftrace_caller,function | ||||||
|  | _ftrace_caller: | ||||||
|  | 	MOVT    D0Re0,#HI(_function_trace_stop) | ||||||
|  | 	ADD	D0Re0,D0Re0,#LO(_function_trace_stop) | ||||||
|  | 	GETD	D0Re0,[D0Re0] | ||||||
|  | 	CMP	D0Re0,#0 | ||||||
|  | 	BEQ	$Lcall_stub | ||||||
|  | 	MOV	PC,D0.4 | ||||||
|  | $Lcall_stub: | ||||||
|  | 	MSETL   [A0StP], D0Ar6, D0Ar4, D0Ar2, D0.4 | ||||||
|  | 	MOV     D1Ar1, D0.4 | ||||||
|  | 	MOV     D0Ar2, D1RtP | ||||||
|  | 	SUB	D1Ar1,D1Ar1,#MCOUNT_INSN_SIZE | ||||||
|  | 
 | ||||||
|  | 	.global _ftrace_call
 | ||||||
|  | _ftrace_call: | ||||||
|  | 	MOVT	D1RtP,#HI(_ftrace_stub) | ||||||
|  | 	CALL	D1RtP,#LO(_ftrace_stub) | ||||||
|  | 	GETL    D0.4,  D1RtP, [A0StP++#(-8)] | ||||||
|  | 	GETL    D0Ar2, D1Ar1, [A0StP++#(-8)] | ||||||
|  | 	GETL    D0Ar4, D1Ar3, [A0StP++#(-8)] | ||||||
|  | 	GETL    D0Ar6, D1Ar5, [A0StP++#(-8)] | ||||||
|  | 	MOV     PC, D0.4 | ||||||
|  | #else | ||||||
|  | 
 | ||||||
|  | 	.global	_mcount_wrapper
 | ||||||
|  | 	.type	_mcount_wrapper,function | ||||||
|  | _mcount_wrapper: | ||||||
|  | 	MOVT    D0Re0,#HI(_function_trace_stop) | ||||||
|  | 	ADD	D0Re0,D0Re0,#LO(_function_trace_stop) | ||||||
|  | 	GETD	D0Re0,[D0Re0] | ||||||
|  | 	CMP	D0Re0,#0 | ||||||
|  | 	BEQ	$Lcall_mcount | ||||||
|  | 	MOV	PC,D0.4 | ||||||
|  | $Lcall_mcount: | ||||||
|  | 	MSETL   [A0StP], D0Ar6, D0Ar4, D0Ar2, D0.4 | ||||||
|  | 	MOV     D1Ar1, D0.4 | ||||||
|  | 	MOV     D0Ar2, D1RtP | ||||||
|  | 	MOVT    D0Re0,#HI(_ftrace_trace_function) | ||||||
|  | 	ADD	D0Re0,D0Re0,#LO(_ftrace_trace_function) | ||||||
|  | 	GET	D1Ar3,[D0Re0] | ||||||
|  | 	MOVT	D1Re0,#HI(_ftrace_stub) | ||||||
|  | 	ADD	D1Re0,D1Re0,#LO(_ftrace_stub) | ||||||
|  | 	CMP	D1Ar3,D1Re0 | ||||||
|  | 	BEQ	$Ltrace_exit | ||||||
|  | 	MOV	D1RtP,D1Ar3 | ||||||
|  | 	SUB	D1Ar1,D1Ar1,#MCOUNT_INSN_SIZE | ||||||
|  | 	SWAP	PC,D1RtP | ||||||
|  | $Ltrace_exit: | ||||||
|  | 	GETL    D0.4,  D1RtP, [A0StP++#(-8)] | ||||||
|  | 	GETL    D0Ar2, D1Ar1, [A0StP++#(-8)] | ||||||
|  | 	GETL    D0Ar4, D1Ar3, [A0StP++#(-8)] | ||||||
|  | 	GETL    D0Ar6, D1Ar5, [A0StP++#(-8)] | ||||||
|  | 	MOV     PC, D0.4 | ||||||
|  | 
 | ||||||
|  | #endif	/* CONFIG_DYNAMIC_FTRACE */ | ||||||
|  | 
 | ||||||
|  | 	.global _ftrace_stub
 | ||||||
|  | _ftrace_stub: | ||||||
|  | 	MOV 	PC,D1RtP | ||||||
|  | @ -10,6 +10,7 @@ | ||||||
| #include <asm/checksum.h> | #include <asm/checksum.h> | ||||||
| #include <asm/uaccess.h> | #include <asm/uaccess.h> | ||||||
| #include <asm/traps.h> | #include <asm/traps.h> | ||||||
|  | #include <asm/ftrace.h> | ||||||
| #include <asm/tbx.h> | #include <asm/tbx.h> | ||||||
| 
 | 
 | ||||||
| /* uaccess symbols */ | /* uaccess symbols */ | ||||||
|  | @ -73,3 +74,7 @@ EXPORT_SYMBOL(div_s64); | ||||||
| EXPORT_SYMBOL(memcpy); | EXPORT_SYMBOL(memcpy); | ||||||
| EXPORT_SYMBOL(memset); | EXPORT_SYMBOL(memset); | ||||||
| EXPORT_SYMBOL(memmove); | EXPORT_SYMBOL(memmove); | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_FUNCTION_TRACER | ||||||
|  | EXPORT_SYMBOL(mcount_wrapper); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | @ -33,6 +33,13 @@ | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
| 
 | 
 | ||||||
|  | #ifndef EM_METAG | ||||||
|  | /* Remove this when these make it to the standard system elf.h. */ | ||||||
|  | #define EM_METAG      174 | ||||||
|  | #define R_METAG_ADDR32                   2 | ||||||
|  | #define R_METAG_NONE                     3 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| static int fd_map;	/* File descriptor for file being modified. */ | static int fd_map;	/* File descriptor for file being modified. */ | ||||||
| static int mmap_failed; /* Boolean flag. */ | static int mmap_failed; /* Boolean flag. */ | ||||||
| static void *ehdr_curr; /* current ElfXX_Ehdr *  for resource cleanup */ | static void *ehdr_curr; /* current ElfXX_Ehdr *  for resource cleanup */ | ||||||
|  | @ -341,6 +348,12 @@ do_file(char const *const fname) | ||||||
| 			 altmcount = "__gnu_mcount_nc"; | 			 altmcount = "__gnu_mcount_nc"; | ||||||
| 			 break; | 			 break; | ||||||
| 	case EM_IA_64:	 reltype = R_IA64_IMM64;   gpfx = '_'; break; | 	case EM_IA_64:	 reltype = R_IA64_IMM64;   gpfx = '_'; break; | ||||||
|  | 	case EM_METAG:	 reltype = R_METAG_ADDR32; | ||||||
|  | 			 altmcount = "_mcount_wrapper"; | ||||||
|  | 			 rel_type_nop = R_METAG_NONE; | ||||||
|  | 			 /* We happen to have the same requirement as MIPS */ | ||||||
|  | 			 is_fake_mcount32 = MIPS32_is_fake_mcount; | ||||||
|  | 			 break; | ||||||
| 	case EM_MIPS:	 /* reltype: e_class    */ gpfx = '_'; break; | 	case EM_MIPS:	 /* reltype: e_class    */ gpfx = '_'; break; | ||||||
| 	case EM_PPC:	 reltype = R_PPC_ADDR32;   gpfx = '_'; break; | 	case EM_PPC:	 reltype = R_PPC_ADDR32;   gpfx = '_'; break; | ||||||
| 	case EM_PPC64:	 reltype = R_PPC64_ADDR64; gpfx = '_'; break; | 	case EM_PPC64:	 reltype = R_PPC64_ADDR64; gpfx = '_'; break; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 James Hogan
						James Hogan