mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	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 HAVE_64BIT_ALIGNED_ACCESS | ||||
| 	select HAVE_ARCH_TRACEHOOK | ||||
| 	select HAVE_C_RECORDMCOUNT | ||||
| 	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_IRQ_WORK | ||||
| 	select HAVE_KERNEL_BZIP2 | ||||
|  |  | |||
|  | @ -11,7 +11,6 @@ generic-y += errno.h | |||
| generic-y += exec.h | ||||
| generic-y += fb.h | ||||
| generic-y += fcntl.h | ||||
| generic-y += ftrace.h | ||||
| generic-y += futex.h | ||||
| generic-y += hardirq.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/uaccess.h> | ||||
| #include <asm/traps.h> | ||||
| #include <asm/ftrace.h> | ||||
| #include <asm/tbx.h> | ||||
| 
 | ||||
| /* uaccess symbols */ | ||||
|  | @ -73,3 +74,7 @@ EXPORT_SYMBOL(div_s64); | |||
| EXPORT_SYMBOL(memcpy); | ||||
| EXPORT_SYMBOL(memset); | ||||
| EXPORT_SYMBOL(memmove); | ||||
| 
 | ||||
| #ifdef CONFIG_FUNCTION_TRACER | ||||
| EXPORT_SYMBOL(mcount_wrapper); | ||||
| #endif | ||||
|  |  | |||
|  | @ -33,6 +33,13 @@ | |||
| #include <string.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 mmap_failed; /* Boolean flag. */ | ||||
| static void *ehdr_curr; /* current ElfXX_Ehdr *  for resource cleanup */ | ||||
|  | @ -341,6 +348,12 @@ do_file(char const *const fname) | |||
| 			 altmcount = "__gnu_mcount_nc"; | ||||
| 			 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_PPC:	 reltype = R_PPC_ADDR32;   gpfx = '_'; break; | ||||
| 	case EM_PPC64:	 reltype = R_PPC64_ADDR64; gpfx = '_'; break; | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 James Hogan
						James Hogan