| Steven Rostedt | 4e491d1 | 2008-05-14 23:49:44 -0400 | [diff] [blame] | 1 | /* | 
|  | 2 | * Code for replacing ftrace calls with jumps. | 
|  | 3 | * | 
|  | 4 | * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com> | 
|  | 5 | * | 
|  | 6 | * Thanks goes out to P.A. Semi, Inc for supplying me with a PPC64 box. | 
|  | 7 | * | 
|  | 8 | */ | 
|  | 9 |  | 
|  | 10 | #include <linux/spinlock.h> | 
|  | 11 | #include <linux/hardirq.h> | 
|  | 12 | #include <linux/ftrace.h> | 
|  | 13 | #include <linux/percpu.h> | 
|  | 14 | #include <linux/init.h> | 
|  | 15 | #include <linux/list.h> | 
|  | 16 |  | 
|  | 17 | #include <asm/cacheflush.h> | 
| Abhishek Sagar | 395a59d | 2008-06-21 23:47:27 +0530 | [diff] [blame] | 18 | #include <asm/ftrace.h> | 
| Steven Rostedt | 4e491d1 | 2008-05-14 23:49:44 -0400 | [diff] [blame] | 19 |  | 
| Steven Rostedt | 4e491d1 | 2008-05-14 23:49:44 -0400 | [diff] [blame] | 20 |  | 
|  | 21 | static unsigned int ftrace_nop = 0x60000000; | 
|  | 22 |  | 
|  | 23 | #ifdef CONFIG_PPC32 | 
|  | 24 | # define GET_ADDR(addr) addr | 
|  | 25 | #else | 
|  | 26 | /* PowerPC64's functions are data that points to the functions */ | 
|  | 27 | # define GET_ADDR(addr) *(unsigned long *)addr | 
|  | 28 | #endif | 
|  | 29 |  | 
| Abhishek Sagar | 395a59d | 2008-06-21 23:47:27 +0530 | [diff] [blame] | 30 |  | 
| Steven Rostedt | 4e491d1 | 2008-05-14 23:49:44 -0400 | [diff] [blame] | 31 | static unsigned int notrace ftrace_calc_offset(long ip, long addr) | 
|  | 32 | { | 
| Abhishek Sagar | 395a59d | 2008-06-21 23:47:27 +0530 | [diff] [blame] | 33 | return (int)(addr - ip); | 
| Steven Rostedt | 4e491d1 | 2008-05-14 23:49:44 -0400 | [diff] [blame] | 34 | } | 
|  | 35 |  | 
|  | 36 | notrace unsigned char *ftrace_nop_replace(void) | 
|  | 37 | { | 
|  | 38 | return (char *)&ftrace_nop; | 
|  | 39 | } | 
|  | 40 |  | 
|  | 41 | notrace unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr) | 
|  | 42 | { | 
|  | 43 | static unsigned int op; | 
|  | 44 |  | 
| Steven Rostedt | ccbfac2 | 2008-05-22 14:31:07 -0400 | [diff] [blame] | 45 | /* | 
|  | 46 | * It would be nice to just use create_function_call, but that will | 
|  | 47 | * update the code itself. Here we need to just return the | 
|  | 48 | * instruction that is going to be modified, without modifying the | 
|  | 49 | * code. | 
|  | 50 | */ | 
| Steven Rostedt | 4e491d1 | 2008-05-14 23:49:44 -0400 | [diff] [blame] | 51 | addr = GET_ADDR(addr); | 
|  | 52 |  | 
|  | 53 | /* Set to "bl addr" */ | 
| Steven Rostedt | ccbfac2 | 2008-05-22 14:31:07 -0400 | [diff] [blame] | 54 | op = 0x48000001 | (ftrace_calc_offset(ip, addr) & 0x03fffffc); | 
| Steven Rostedt | 4e491d1 | 2008-05-14 23:49:44 -0400 | [diff] [blame] | 55 |  | 
|  | 56 | /* | 
|  | 57 | * No locking needed, this must be called via kstop_machine | 
|  | 58 | * which in essence is like running on a uniprocessor machine. | 
|  | 59 | */ | 
|  | 60 | return (unsigned char *)&op; | 
|  | 61 | } | 
|  | 62 |  | 
|  | 63 | #ifdef CONFIG_PPC64 | 
|  | 64 | # define _ASM_ALIGN	" .align 3 " | 
|  | 65 | # define _ASM_PTR	" .llong " | 
|  | 66 | #else | 
|  | 67 | # define _ASM_ALIGN	" .align 2 " | 
|  | 68 | # define _ASM_PTR	" .long " | 
|  | 69 | #endif | 
|  | 70 |  | 
|  | 71 | notrace int | 
|  | 72 | ftrace_modify_code(unsigned long ip, unsigned char *old_code, | 
|  | 73 | unsigned char *new_code) | 
|  | 74 | { | 
|  | 75 | unsigned replaced; | 
|  | 76 | unsigned old = *(unsigned *)old_code; | 
|  | 77 | unsigned new = *(unsigned *)new_code; | 
|  | 78 | int faulted = 0; | 
|  | 79 |  | 
| Steven Rostedt | 4e491d1 | 2008-05-14 23:49:44 -0400 | [diff] [blame] | 80 | /* | 
|  | 81 | * Note: Due to modules and __init, code can | 
|  | 82 | *  disappear and change, we need to protect against faulting | 
|  | 83 | *  as well as code changing. | 
|  | 84 | * | 
|  | 85 | * No real locking needed, this code is run through | 
|  | 86 | * kstop_machine. | 
|  | 87 | */ | 
|  | 88 | asm volatile ( | 
|  | 89 | "1: lwz		%1, 0(%2)\n" | 
|  | 90 | "   cmpw	%1, %5\n" | 
|  | 91 | "   bne		2f\n" | 
|  | 92 | "   stwu	%3, 0(%2)\n" | 
|  | 93 | "2:\n" | 
|  | 94 | ".section .fixup, \"ax\"\n" | 
|  | 95 | "3:	li %0, 1\n" | 
|  | 96 | "	b 2b\n" | 
|  | 97 | ".previous\n" | 
|  | 98 | ".section __ex_table,\"a\"\n" | 
|  | 99 | _ASM_ALIGN "\n" | 
|  | 100 | _ASM_PTR "1b, 3b\n" | 
|  | 101 | ".previous" | 
|  | 102 | : "=r"(faulted), "=r"(replaced) | 
|  | 103 | : "r"(ip), "r"(new), | 
|  | 104 | "0"(faulted), "r"(old) | 
|  | 105 | : "memory"); | 
|  | 106 |  | 
|  | 107 | if (replaced != old && replaced != new) | 
|  | 108 | faulted = 2; | 
|  | 109 |  | 
|  | 110 | if (!faulted) | 
|  | 111 | flush_icache_range(ip, ip + 8); | 
|  | 112 |  | 
|  | 113 | return faulted; | 
|  | 114 | } | 
|  | 115 |  | 
|  | 116 | notrace int ftrace_update_ftrace_func(ftrace_func_t func) | 
|  | 117 | { | 
|  | 118 | unsigned long ip = (unsigned long)(&ftrace_call); | 
| Abhishek Sagar | 395a59d | 2008-06-21 23:47:27 +0530 | [diff] [blame] | 119 | unsigned char old[MCOUNT_INSN_SIZE], *new; | 
| Steven Rostedt | 4e491d1 | 2008-05-14 23:49:44 -0400 | [diff] [blame] | 120 | int ret; | 
|  | 121 |  | 
| Abhishek Sagar | 395a59d | 2008-06-21 23:47:27 +0530 | [diff] [blame] | 122 | memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE); | 
| Steven Rostedt | 4e491d1 | 2008-05-14 23:49:44 -0400 | [diff] [blame] | 123 | new = ftrace_call_replace(ip, (unsigned long)func); | 
|  | 124 | ret = ftrace_modify_code(ip, old, new); | 
|  | 125 |  | 
|  | 126 | return ret; | 
|  | 127 | } | 
|  | 128 |  | 
|  | 129 | notrace int ftrace_mcount_set(unsigned long *data) | 
|  | 130 | { | 
|  | 131 | unsigned long ip = (long)(&mcount_call); | 
|  | 132 | unsigned long *addr = data; | 
| Abhishek Sagar | 395a59d | 2008-06-21 23:47:27 +0530 | [diff] [blame] | 133 | unsigned char old[MCOUNT_INSN_SIZE], *new; | 
| Steven Rostedt | 4e491d1 | 2008-05-14 23:49:44 -0400 | [diff] [blame] | 134 |  | 
|  | 135 | /* | 
|  | 136 | * Replace the mcount stub with a pointer to the | 
|  | 137 | * ip recorder function. | 
|  | 138 | */ | 
| Abhishek Sagar | 395a59d | 2008-06-21 23:47:27 +0530 | [diff] [blame] | 139 | memcpy(old, &mcount_call, MCOUNT_INSN_SIZE); | 
| Steven Rostedt | 4e491d1 | 2008-05-14 23:49:44 -0400 | [diff] [blame] | 140 | new = ftrace_call_replace(ip, *addr); | 
|  | 141 | *addr = ftrace_modify_code(ip, old, new); | 
|  | 142 |  | 
|  | 143 | return 0; | 
|  | 144 | } | 
|  | 145 |  | 
|  | 146 | int __init ftrace_dyn_arch_init(void *data) | 
|  | 147 | { | 
|  | 148 | /* This is running in kstop_machine */ | 
|  | 149 |  | 
|  | 150 | ftrace_mcount_set(data); | 
|  | 151 |  | 
|  | 152 | return 0; | 
|  | 153 | } | 
|  | 154 |  |