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> 143d083395SSteven Rostedt #include <linux/ftrace.h> 153d083395SSteven Rostedt #include <linux/percpu.h> 163d083395SSteven Rostedt #include <linux/init.h> 173d083395SSteven Rostedt #include <linux/list.h> 183d083395SSteven Rostedt 19395a59d0SAbhishek Sagar #include <asm/ftrace.h> 20*732f3ca7SSteven Rostedt #include <asm/nops.h> 21dfa60abaSSteven Rostedt 223d083395SSteven Rostedt 23dfa60abaSSteven Rostedt /* Long is fine, even if it is only 4 bytes ;-) */ 24dfa60abaSSteven Rostedt static long *ftrace_nop; 253d083395SSteven Rostedt 263d083395SSteven Rostedt union ftrace_code_union { 27395a59d0SAbhishek Sagar char code[MCOUNT_INSN_SIZE]; 283d083395SSteven Rostedt struct { 293d083395SSteven Rostedt char e8; 303d083395SSteven Rostedt int offset; 313d083395SSteven Rostedt } __attribute__((packed)); 323d083395SSteven Rostedt }; 333d083395SSteven Rostedt 34395a59d0SAbhishek Sagar 353c1720f0SSteven Rostedt static int notrace ftrace_calc_offset(long ip, long addr) 363c1720f0SSteven Rostedt { 373c1720f0SSteven Rostedt return (int)(addr - ip); 383d083395SSteven Rostedt } 393d083395SSteven Rostedt 403c1720f0SSteven Rostedt notrace unsigned char *ftrace_nop_replace(void) 413c1720f0SSteven Rostedt { 423c1720f0SSteven Rostedt return (char *)ftrace_nop; 433c1720f0SSteven Rostedt } 443c1720f0SSteven Rostedt 453c1720f0SSteven Rostedt notrace unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr) 463c1720f0SSteven Rostedt { 473c1720f0SSteven Rostedt static union ftrace_code_union calc; 483c1720f0SSteven Rostedt 493c1720f0SSteven Rostedt calc.e8 = 0xe8; 50395a59d0SAbhishek Sagar calc.offset = ftrace_calc_offset(ip + MCOUNT_INSN_SIZE, addr); 513c1720f0SSteven Rostedt 523c1720f0SSteven Rostedt /* 533c1720f0SSteven Rostedt * No locking needed, this must be called via kstop_machine 543c1720f0SSteven Rostedt * which in essence is like running on a uniprocessor machine. 553c1720f0SSteven Rostedt */ 563c1720f0SSteven Rostedt return calc.code; 573c1720f0SSteven Rostedt } 583c1720f0SSteven Rostedt 593c1720f0SSteven Rostedt notrace int 603d083395SSteven Rostedt ftrace_modify_code(unsigned long ip, unsigned char *old_code, 613d083395SSteven Rostedt unsigned char *new_code) 623d083395SSteven Rostedt { 63dfa60abaSSteven Rostedt unsigned replaced; 64dfa60abaSSteven Rostedt unsigned old = *(unsigned *)old_code; /* 4 bytes */ 65dfa60abaSSteven Rostedt unsigned new = *(unsigned *)new_code; /* 4 bytes */ 66dfa60abaSSteven Rostedt unsigned char newch = new_code[4]; 673d083395SSteven Rostedt int faulted = 0; 683d083395SSteven Rostedt 693d083395SSteven Rostedt /* 703d083395SSteven Rostedt * Note: Due to modules and __init, code can 713d083395SSteven Rostedt * disappear and change, we need to protect against faulting 723d083395SSteven Rostedt * as well as code changing. 733d083395SSteven Rostedt * 743d083395SSteven Rostedt * No real locking needed, this code is run through 753d083395SSteven Rostedt * kstop_machine. 763d083395SSteven Rostedt */ 773d083395SSteven Rostedt asm volatile ( 783d083395SSteven Rostedt "1: lock\n" 79dfa60abaSSteven Rostedt " cmpxchg %3, (%2)\n" 80dfa60abaSSteven Rostedt " jnz 2f\n" 81dfa60abaSSteven Rostedt " movb %b4, 4(%2)\n" 823d083395SSteven Rostedt "2:\n" 833d083395SSteven Rostedt ".section .fixup, \"ax\"\n" 84a56be3feSSteven Rostedt "3: movl $1, %0\n" 85a56be3feSSteven Rostedt " jmp 2b\n" 863d083395SSteven Rostedt ".previous\n" 873d083395SSteven Rostedt _ASM_EXTABLE(1b, 3b) 883d083395SSteven Rostedt : "=r"(faulted), "=a"(replaced) 89ee4311adSIngo Molnar : "r"(ip), "r"(new), "c"(newch), 90dfa60abaSSteven Rostedt "0"(faulted), "a"(old) 913d083395SSteven Rostedt : "memory"); 923d083395SSteven Rostedt sync_core(); 933d083395SSteven Rostedt 94dfa60abaSSteven Rostedt if (replaced != old && replaced != new) 953d083395SSteven Rostedt faulted = 2; 963d083395SSteven Rostedt 973d083395SSteven Rostedt return faulted; 983d083395SSteven Rostedt } 993d083395SSteven Rostedt 100d61f82d0SSteven Rostedt notrace int ftrace_update_ftrace_func(ftrace_func_t func) 101d61f82d0SSteven Rostedt { 102d61f82d0SSteven Rostedt unsigned long ip = (unsigned long)(&ftrace_call); 103395a59d0SAbhishek Sagar unsigned char old[MCOUNT_INSN_SIZE], *new; 104d61f82d0SSteven Rostedt int ret; 105d61f82d0SSteven Rostedt 106395a59d0SAbhishek Sagar memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE); 107d61f82d0SSteven Rostedt new = ftrace_call_replace(ip, (unsigned long)func); 108d61f82d0SSteven Rostedt ret = ftrace_modify_code(ip, old, new); 109d61f82d0SSteven Rostedt 110d61f82d0SSteven Rostedt return ret; 111d61f82d0SSteven Rostedt } 112d61f82d0SSteven Rostedt 113d61f82d0SSteven Rostedt notrace int ftrace_mcount_set(unsigned long *data) 114d61f82d0SSteven Rostedt { 1150a37605cSSteven Rostedt /* mcount is initialized as a nop */ 1160a37605cSSteven Rostedt *data = 0; 117d61f82d0SSteven Rostedt return 0; 118d61f82d0SSteven Rostedt } 119d61f82d0SSteven Rostedt 120d61f82d0SSteven Rostedt int __init ftrace_dyn_arch_init(void *data) 1213d083395SSteven Rostedt { 122*732f3ca7SSteven Rostedt extern const unsigned char ftrace_test_p6nop[]; 123*732f3ca7SSteven Rostedt extern const unsigned char ftrace_test_nop5[]; 124*732f3ca7SSteven Rostedt extern const unsigned char ftrace_test_jmp[]; 125*732f3ca7SSteven Rostedt int faulted = 0; 1263d083395SSteven Rostedt 127*732f3ca7SSteven Rostedt /* 128*732f3ca7SSteven Rostedt * There is no good nop for all x86 archs. 129*732f3ca7SSteven Rostedt * We will default to using the P6_NOP5, but first we 130*732f3ca7SSteven Rostedt * will test to make sure that the nop will actually 131*732f3ca7SSteven Rostedt * work on this CPU. If it faults, we will then 132*732f3ca7SSteven Rostedt * go to a lesser efficient 5 byte nop. If that fails 133*732f3ca7SSteven Rostedt * we then just use a jmp as our nop. This isn't the most 134*732f3ca7SSteven Rostedt * efficient nop, but we can not use a multi part nop 135*732f3ca7SSteven Rostedt * since we would then risk being preempted in the middle 136*732f3ca7SSteven Rostedt * of that nop, and if we enabled tracing then, it might 137*732f3ca7SSteven Rostedt * cause a system crash. 138*732f3ca7SSteven Rostedt * 139*732f3ca7SSteven Rostedt * TODO: check the cpuid to determine the best nop. 140*732f3ca7SSteven Rostedt */ 141*732f3ca7SSteven Rostedt asm volatile ( 142*732f3ca7SSteven Rostedt "jmp ftrace_test_jmp\n" 143*732f3ca7SSteven Rostedt /* This code needs to stay around */ 144*732f3ca7SSteven Rostedt ".section .text, \"ax\"\n" 145*732f3ca7SSteven Rostedt "ftrace_test_jmp:" 146*732f3ca7SSteven Rostedt "jmp ftrace_test_p6nop\n" 147*732f3ca7SSteven Rostedt ".byte 0x00,0x00,0x00\n" /* 2 byte jmp + 3 bytes */ 148*732f3ca7SSteven Rostedt "ftrace_test_p6nop:" 149*732f3ca7SSteven Rostedt P6_NOP5 150*732f3ca7SSteven Rostedt "jmp 1f\n" 151*732f3ca7SSteven Rostedt "ftrace_test_nop5:" 152*732f3ca7SSteven Rostedt ".byte 0x66,0x66,0x66,0x66,0x90\n" 153*732f3ca7SSteven Rostedt "jmp 1f\n" 154*732f3ca7SSteven Rostedt ".previous\n" 155*732f3ca7SSteven Rostedt "1:" 156*732f3ca7SSteven Rostedt ".section .fixup, \"ax\"\n" 157*732f3ca7SSteven Rostedt "2: movl $1, %0\n" 158*732f3ca7SSteven Rostedt " jmp ftrace_test_nop5\n" 159*732f3ca7SSteven Rostedt "3: movl $2, %0\n" 160*732f3ca7SSteven Rostedt " jmp 1b\n" 161*732f3ca7SSteven Rostedt ".previous\n" 162*732f3ca7SSteven Rostedt _ASM_EXTABLE(ftrace_test_p6nop, 2b) 163*732f3ca7SSteven Rostedt _ASM_EXTABLE(ftrace_test_nop5, 3b) 164*732f3ca7SSteven Rostedt : "=r"(faulted) : "0" (faulted)); 165d61f82d0SSteven Rostedt 166*732f3ca7SSteven Rostedt switch (faulted) { 167*732f3ca7SSteven Rostedt case 0: 168*732f3ca7SSteven Rostedt pr_info("ftrace: converting mcount calls to 0f 1f 44 00 00\n"); 169*732f3ca7SSteven Rostedt ftrace_nop = (unsigned long *)ftrace_test_p6nop; 170*732f3ca7SSteven Rostedt break; 171*732f3ca7SSteven Rostedt case 1: 172*732f3ca7SSteven Rostedt pr_info("ftrace: converting mcount calls to 66 66 66 66 90\n"); 173*732f3ca7SSteven Rostedt ftrace_nop = (unsigned long *)ftrace_test_nop5; 174*732f3ca7SSteven Rostedt break; 175*732f3ca7SSteven Rostedt case 2: 176*732f3ca7SSteven Rostedt pr_info("ftrace: converting mcount calls to jmp 1f\n"); 177*732f3ca7SSteven Rostedt ftrace_nop = (unsigned long *)ftrace_test_jmp; 178*732f3ca7SSteven Rostedt break; 179*732f3ca7SSteven Rostedt } 180d61f82d0SSteven Rostedt 181*732f3ca7SSteven Rostedt /* The return code is retured via data */ 182*732f3ca7SSteven Rostedt *(unsigned long *)data = 0; 183dfa60abaSSteven Rostedt 1843d083395SSteven Rostedt return 0; 1853d083395SSteven Rostedt } 186