13d083395SSteven Rostedt /* 23d083395SSteven Rostedt * Code for replacing ftrace calls with jumps. 33d083395SSteven Rostedt * 43d083395SSteven Rostedt * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com> 53d083395SSteven Rostedt * 63d083395SSteven Rostedt * Thanks goes to Ingo Molnar, for suggesting the idea. 73d083395SSteven Rostedt * Mathieu Desnoyers, for suggesting postponing the modifications. 83d083395SSteven Rostedt * Arjan van de Ven, for keeping me straight, and explaining to me 93d083395SSteven Rostedt * the dangers of modifying code on the run. 103d083395SSteven Rostedt */ 113d083395SSteven Rostedt 123d083395SSteven Rostedt #include <linux/spinlock.h> 133d083395SSteven Rostedt #include <linux/hardirq.h> 146f93fc07SSteven Rostedt #include <linux/uaccess.h> 153d083395SSteven Rostedt #include <linux/ftrace.h> 163d083395SSteven Rostedt #include <linux/percpu.h> 173d083395SSteven Rostedt #include <linux/init.h> 183d083395SSteven Rostedt #include <linux/list.h> 193d083395SSteven Rostedt 20395a59d0SAbhishek Sagar #include <asm/ftrace.h> 21732f3ca7SSteven Rostedt #include <asm/nops.h> 22dfa60abaSSteven Rostedt 233d083395SSteven Rostedt 24dfa60abaSSteven Rostedt /* Long is fine, even if it is only 4 bytes ;-) */ 2537a52f5eSHarvey Harrison static unsigned long *ftrace_nop; 263d083395SSteven Rostedt 273d083395SSteven Rostedt union ftrace_code_union { 28395a59d0SAbhishek Sagar char code[MCOUNT_INSN_SIZE]; 293d083395SSteven Rostedt struct { 303d083395SSteven Rostedt char e8; 313d083395SSteven Rostedt int offset; 323d083395SSteven Rostedt } __attribute__((packed)); 333d083395SSteven Rostedt }; 343d083395SSteven Rostedt 35395a59d0SAbhishek Sagar 36*15adc048SSteven Rostedt static int ftrace_calc_offset(long ip, long addr) 373c1720f0SSteven Rostedt { 383c1720f0SSteven Rostedt return (int)(addr - ip); 393d083395SSteven Rostedt } 403d083395SSteven Rostedt 41*15adc048SSteven Rostedt unsigned char *ftrace_nop_replace(void) 423c1720f0SSteven Rostedt { 433c1720f0SSteven Rostedt return (char *)ftrace_nop; 443c1720f0SSteven Rostedt } 453c1720f0SSteven Rostedt 46*15adc048SSteven Rostedt unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr) 473c1720f0SSteven Rostedt { 483c1720f0SSteven Rostedt static union ftrace_code_union calc; 493c1720f0SSteven Rostedt 503c1720f0SSteven Rostedt calc.e8 = 0xe8; 51395a59d0SAbhishek Sagar calc.offset = ftrace_calc_offset(ip + MCOUNT_INSN_SIZE, addr); 523c1720f0SSteven Rostedt 533c1720f0SSteven Rostedt /* 543c1720f0SSteven Rostedt * No locking needed, this must be called via kstop_machine 553c1720f0SSteven Rostedt * which in essence is like running on a uniprocessor machine. 563c1720f0SSteven Rostedt */ 573c1720f0SSteven Rostedt return calc.code; 583c1720f0SSteven Rostedt } 593c1720f0SSteven Rostedt 60*15adc048SSteven Rostedt int 613d083395SSteven Rostedt ftrace_modify_code(unsigned long ip, unsigned char *old_code, 623d083395SSteven Rostedt unsigned char *new_code) 633d083395SSteven Rostedt { 646f93fc07SSteven Rostedt unsigned char replaced[MCOUNT_INSN_SIZE]; 653d083395SSteven Rostedt 663d083395SSteven Rostedt /* 673d083395SSteven Rostedt * Note: Due to modules and __init, code can 683d083395SSteven Rostedt * disappear and change, we need to protect against faulting 6976aefee5SSteven Rostedt * as well as code changing. We do this by using the 70ab9a0918SSteven Rostedt * probe_kernel_* functions. 713d083395SSteven Rostedt * 723d083395SSteven Rostedt * No real locking needed, this code is run through 736f93fc07SSteven Rostedt * kstop_machine, or before SMP starts. 743d083395SSteven Rostedt */ 7576aefee5SSteven Rostedt 7676aefee5SSteven Rostedt /* read the text we want to modify */ 77ab9a0918SSteven Rostedt if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE)) 78593eb8a2SSteven Rostedt return -EFAULT; 796f93fc07SSteven Rostedt 8076aefee5SSteven Rostedt /* Make sure it is what we expect it to be */ 816f93fc07SSteven Rostedt if (memcmp(replaced, old_code, MCOUNT_INSN_SIZE) != 0) 82593eb8a2SSteven Rostedt return -EINVAL; 836f93fc07SSteven Rostedt 8476aefee5SSteven Rostedt /* replace the text with the new text */ 85ab9a0918SSteven Rostedt if (probe_kernel_write((void *)ip, new_code, MCOUNT_INSN_SIZE)) 86593eb8a2SSteven Rostedt return -EPERM; 876f93fc07SSteven Rostedt 883d083395SSteven Rostedt sync_core(); 893d083395SSteven Rostedt 906f93fc07SSteven Rostedt return 0; 913d083395SSteven Rostedt } 923d083395SSteven Rostedt 93*15adc048SSteven Rostedt int ftrace_update_ftrace_func(ftrace_func_t func) 94d61f82d0SSteven Rostedt { 95d61f82d0SSteven Rostedt unsigned long ip = (unsigned long)(&ftrace_call); 96395a59d0SAbhishek Sagar unsigned char old[MCOUNT_INSN_SIZE], *new; 97d61f82d0SSteven Rostedt int ret; 98d61f82d0SSteven Rostedt 99395a59d0SAbhishek Sagar memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE); 100d61f82d0SSteven Rostedt new = ftrace_call_replace(ip, (unsigned long)func); 101d61f82d0SSteven Rostedt ret = ftrace_modify_code(ip, old, new); 102d61f82d0SSteven Rostedt 103d61f82d0SSteven Rostedt return ret; 104d61f82d0SSteven Rostedt } 105d61f82d0SSteven Rostedt 106d61f82d0SSteven Rostedt int __init ftrace_dyn_arch_init(void *data) 1073d083395SSteven Rostedt { 108732f3ca7SSteven Rostedt extern const unsigned char ftrace_test_p6nop[]; 109732f3ca7SSteven Rostedt extern const unsigned char ftrace_test_nop5[]; 110732f3ca7SSteven Rostedt extern const unsigned char ftrace_test_jmp[]; 111732f3ca7SSteven Rostedt int faulted = 0; 1123d083395SSteven Rostedt 113732f3ca7SSteven Rostedt /* 114732f3ca7SSteven Rostedt * There is no good nop for all x86 archs. 115732f3ca7SSteven Rostedt * We will default to using the P6_NOP5, but first we 116732f3ca7SSteven Rostedt * will test to make sure that the nop will actually 117732f3ca7SSteven Rostedt * work on this CPU. If it faults, we will then 118732f3ca7SSteven Rostedt * go to a lesser efficient 5 byte nop. If that fails 119732f3ca7SSteven Rostedt * we then just use a jmp as our nop. This isn't the most 120732f3ca7SSteven Rostedt * efficient nop, but we can not use a multi part nop 121732f3ca7SSteven Rostedt * since we would then risk being preempted in the middle 122732f3ca7SSteven Rostedt * of that nop, and if we enabled tracing then, it might 123732f3ca7SSteven Rostedt * cause a system crash. 124732f3ca7SSteven Rostedt * 125732f3ca7SSteven Rostedt * TODO: check the cpuid to determine the best nop. 126732f3ca7SSteven Rostedt */ 127732f3ca7SSteven Rostedt asm volatile ( 128732f3ca7SSteven Rostedt "jmp ftrace_test_jmp\n" 129732f3ca7SSteven Rostedt /* This code needs to stay around */ 130732f3ca7SSteven Rostedt ".section .text, \"ax\"\n" 131732f3ca7SSteven Rostedt "ftrace_test_jmp:" 132732f3ca7SSteven Rostedt "jmp ftrace_test_p6nop\n" 1338b27386aSAnders Kaseorg "nop\n" 1348b27386aSAnders Kaseorg "nop\n" 1358b27386aSAnders Kaseorg "nop\n" /* 2 byte jmp + 3 bytes */ 136732f3ca7SSteven Rostedt "ftrace_test_p6nop:" 137732f3ca7SSteven Rostedt P6_NOP5 138732f3ca7SSteven Rostedt "jmp 1f\n" 139732f3ca7SSteven Rostedt "ftrace_test_nop5:" 140732f3ca7SSteven Rostedt ".byte 0x66,0x66,0x66,0x66,0x90\n" 141732f3ca7SSteven Rostedt "jmp 1f\n" 142732f3ca7SSteven Rostedt ".previous\n" 143732f3ca7SSteven Rostedt "1:" 144732f3ca7SSteven Rostedt ".section .fixup, \"ax\"\n" 145732f3ca7SSteven Rostedt "2: movl $1, %0\n" 146732f3ca7SSteven Rostedt " jmp ftrace_test_nop5\n" 147732f3ca7SSteven Rostedt "3: movl $2, %0\n" 148732f3ca7SSteven Rostedt " jmp 1b\n" 149732f3ca7SSteven Rostedt ".previous\n" 150732f3ca7SSteven Rostedt _ASM_EXTABLE(ftrace_test_p6nop, 2b) 151732f3ca7SSteven Rostedt _ASM_EXTABLE(ftrace_test_nop5, 3b) 152732f3ca7SSteven Rostedt : "=r"(faulted) : "0" (faulted)); 153d61f82d0SSteven Rostedt 154732f3ca7SSteven Rostedt switch (faulted) { 155732f3ca7SSteven Rostedt case 0: 156732f3ca7SSteven Rostedt pr_info("ftrace: converting mcount calls to 0f 1f 44 00 00\n"); 157732f3ca7SSteven Rostedt ftrace_nop = (unsigned long *)ftrace_test_p6nop; 158732f3ca7SSteven Rostedt break; 159732f3ca7SSteven Rostedt case 1: 160732f3ca7SSteven Rostedt pr_info("ftrace: converting mcount calls to 66 66 66 66 90\n"); 161732f3ca7SSteven Rostedt ftrace_nop = (unsigned long *)ftrace_test_nop5; 162732f3ca7SSteven Rostedt break; 163732f3ca7SSteven Rostedt case 2: 1648b27386aSAnders Kaseorg pr_info("ftrace: converting mcount calls to jmp . + 5\n"); 165732f3ca7SSteven Rostedt ftrace_nop = (unsigned long *)ftrace_test_jmp; 166732f3ca7SSteven Rostedt break; 167732f3ca7SSteven Rostedt } 168d61f82d0SSteven Rostedt 169732f3ca7SSteven Rostedt /* The return code is retured via data */ 170732f3ca7SSteven Rostedt *(unsigned long *)data = 0; 171dfa60abaSSteven Rostedt 1723d083395SSteven Rostedt return 0; 1733d083395SSteven Rostedt } 174