aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/ftrace.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86/kernel/ftrace.c')
-rw-r--r--arch/x86/kernel/ftrace.c133
1 files changed, 48 insertions, 85 deletions
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
index 9dbb527e1652..cd37469b54ee 100644
--- a/arch/x86/kernel/ftrace.c
+++ b/arch/x86/kernel/ftrace.c
@@ -9,6 +9,8 @@
9 * the dangers of modifying code on the run. 9 * the dangers of modifying code on the run.
10 */ 10 */
11 11
12#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13
12#include <linux/spinlock.h> 14#include <linux/spinlock.h>
13#include <linux/hardirq.h> 15#include <linux/hardirq.h>
14#include <linux/uaccess.h> 16#include <linux/uaccess.h>
@@ -28,14 +30,32 @@
28 30
29#ifdef CONFIG_DYNAMIC_FTRACE 31#ifdef CONFIG_DYNAMIC_FTRACE
30 32
33/*
34 * modifying_code is set to notify NMIs that they need to use
35 * memory barriers when entering or exiting. But we don't want
36 * to burden NMIs with unnecessary memory barriers when code
37 * modification is not being done (which is most of the time).
38 *
39 * A mutex is already held when ftrace_arch_code_modify_prepare
40 * and post_process are called. No locks need to be taken here.
41 *
42 * Stop machine will make sure currently running NMIs are done
43 * and new NMIs will see the updated variable before we need
44 * to worry about NMIs doing memory barriers.
45 */
46static int modifying_code __read_mostly;
47static DEFINE_PER_CPU(int, save_modifying_code);
48
31int ftrace_arch_code_modify_prepare(void) 49int ftrace_arch_code_modify_prepare(void)
32{ 50{
33 set_kernel_text_rw(); 51 set_kernel_text_rw();
52 modifying_code = 1;
34 return 0; 53 return 0;
35} 54}
36 55
37int ftrace_arch_code_modify_post_process(void) 56int ftrace_arch_code_modify_post_process(void)
38{ 57{
58 modifying_code = 0;
39 set_kernel_text_ro(); 59 set_kernel_text_ro();
40 return 0; 60 return 0;
41} 61}
@@ -147,6 +167,11 @@ static void ftrace_mod_code(void)
147 167
148void ftrace_nmi_enter(void) 168void ftrace_nmi_enter(void)
149{ 169{
170 __get_cpu_var(save_modifying_code) = modifying_code;
171
172 if (!__get_cpu_var(save_modifying_code))
173 return;
174
150 if (atomic_inc_return(&nmi_running) & MOD_CODE_WRITE_FLAG) { 175 if (atomic_inc_return(&nmi_running) & MOD_CODE_WRITE_FLAG) {
151 smp_rmb(); 176 smp_rmb();
152 ftrace_mod_code(); 177 ftrace_mod_code();
@@ -158,6 +183,9 @@ void ftrace_nmi_enter(void)
158 183
159void ftrace_nmi_exit(void) 184void ftrace_nmi_exit(void)
160{ 185{
186 if (!__get_cpu_var(save_modifying_code))
187 return;
188
161 /* Finish all executions before clearing nmi_running */ 189 /* Finish all executions before clearing nmi_running */
162 smp_mb(); 190 smp_mb();
163 atomic_dec(&nmi_running); 191 atomic_dec(&nmi_running);
@@ -187,9 +215,26 @@ static void wait_for_nmi(void)
187 nmi_wait_count++; 215 nmi_wait_count++;
188} 216}
189 217
218static inline int
219within(unsigned long addr, unsigned long start, unsigned long end)
220{
221 return addr >= start && addr < end;
222}
223
190static int 224static int
191do_ftrace_mod_code(unsigned long ip, void *new_code) 225do_ftrace_mod_code(unsigned long ip, void *new_code)
192{ 226{
227 /*
228 * On x86_64, kernel text mappings are mapped read-only with
229 * CONFIG_DEBUG_RODATA. So we use the kernel identity mapping instead
230 * of the kernel text mapping to modify the kernel text.
231 *
232 * For 32bit kernels, these mappings are same and we can use
233 * kernel identity mapping to modify code.
234 */
235 if (within(ip, (unsigned long)_text, (unsigned long)_etext))
236 ip = (unsigned long)__va(__pa(ip));
237
193 mod_code_ip = (void *)ip; 238 mod_code_ip = (void *)ip;
194 mod_code_newcode = new_code; 239 mod_code_newcode = new_code;
195 240
@@ -336,15 +381,15 @@ int __init ftrace_dyn_arch_init(void *data)
336 381
337 switch (faulted) { 382 switch (faulted) {
338 case 0: 383 case 0:
339 pr_info("ftrace: converting mcount calls to 0f 1f 44 00 00\n"); 384 pr_info("converting mcount calls to 0f 1f 44 00 00\n");
340 memcpy(ftrace_nop, ftrace_test_p6nop, MCOUNT_INSN_SIZE); 385 memcpy(ftrace_nop, ftrace_test_p6nop, MCOUNT_INSN_SIZE);
341 break; 386 break;
342 case 1: 387 case 1:
343 pr_info("ftrace: converting mcount calls to 66 66 66 66 90\n"); 388 pr_info("converting mcount calls to 66 66 66 66 90\n");
344 memcpy(ftrace_nop, ftrace_test_nop5, MCOUNT_INSN_SIZE); 389 memcpy(ftrace_nop, ftrace_test_nop5, MCOUNT_INSN_SIZE);
345 break; 390 break;
346 case 2: 391 case 2:
347 pr_info("ftrace: converting mcount calls to jmp . + 5\n"); 392 pr_info("converting mcount calls to jmp . + 5\n");
348 memcpy(ftrace_nop, ftrace_test_jmp, MCOUNT_INSN_SIZE); 393 memcpy(ftrace_nop, ftrace_test_jmp, MCOUNT_INSN_SIZE);
349 break; 394 break;
350 } 395 }
@@ -465,85 +510,3 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
465 } 510 }
466} 511}
467#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ 512#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
468
469#ifdef CONFIG_FTRACE_SYSCALLS
470
471extern unsigned long __start_syscalls_metadata[];
472extern unsigned long __stop_syscalls_metadata[];
473extern unsigned long *sys_call_table;
474
475static struct syscall_metadata **syscalls_metadata;
476
477static struct syscall_metadata *find_syscall_meta(unsigned long *syscall)
478{
479 struct syscall_metadata *start;
480 struct syscall_metadata *stop;
481 char str[KSYM_SYMBOL_LEN];
482
483
484 start = (struct syscall_metadata *)__start_syscalls_metadata;
485 stop = (struct syscall_metadata *)__stop_syscalls_metadata;
486 kallsyms_lookup((unsigned long) syscall, NULL, NULL, NULL, str);
487
488 for ( ; start < stop; start++) {
489 if (start->name && !strcmp(start->name, str))
490 return start;
491 }
492 return NULL;
493}
494
495struct syscall_metadata *syscall_nr_to_meta(int nr)
496{
497 if (!syscalls_metadata || nr >= NR_syscalls || nr < 0)
498 return NULL;
499
500 return syscalls_metadata[nr];
501}
502
503int syscall_name_to_nr(char *name)
504{
505 int i;
506
507 if (!syscalls_metadata)
508 return -1;
509
510 for (i = 0; i < NR_syscalls; i++) {
511 if (syscalls_metadata[i]) {
512 if (!strcmp(syscalls_metadata[i]->name, name))
513 return i;
514 }
515 }
516 return -1;
517}
518
519void set_syscall_enter_id(int num, int id)
520{
521 syscalls_metadata[num]->enter_id = id;
522}
523
524void set_syscall_exit_id(int num, int id)
525{
526 syscalls_metadata[num]->exit_id = id;
527}
528
529static int __init arch_init_ftrace_syscalls(void)
530{
531 int i;
532 struct syscall_metadata *meta;
533 unsigned long **psys_syscall_table = &sys_call_table;
534
535 syscalls_metadata = kzalloc(sizeof(*syscalls_metadata) *
536 NR_syscalls, GFP_KERNEL);
537 if (!syscalls_metadata) {
538 WARN_ON(1);
539 return -ENOMEM;
540 }
541
542 for (i = 0; i < NR_syscalls; i++) {
543 meta = find_syscall_meta(psys_syscall_table[i]);
544 syscalls_metadata[i] = meta;
545 }
546 return 0;
547}
548arch_initcall(arch_init_ftrace_syscalls);
549#endif