WSL2-Linux-Kernel/arch/nds32/kernel/ftrace.c

296 строки
7.4 KiB
C
Исходник Обычный вид История

// SPDX-License-Identifier: GPL-2.0
#include <linux/ftrace.h>
#include <linux/uaccess.h>
#include <asm/cacheflush.h>
#ifndef CONFIG_DYNAMIC_FTRACE
extern void (*ftrace_trace_function)(unsigned long, unsigned long,
struct ftrace_ops*, struct pt_regs*);
extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace);
extern void ftrace_graph_caller(void);
noinline void __naked ftrace_stub(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *op, struct pt_regs *regs)
{
__asm__ (""); /* avoid to optimize as pure function */
}
noinline void _mcount(unsigned long parent_ip)
{
/* save all state by the compiler prologue */
unsigned long ip = (unsigned long)__builtin_return_address(0);
if (ftrace_trace_function != ftrace_stub)
ftrace_trace_function(ip - MCOUNT_INSN_SIZE, parent_ip,
NULL, NULL);
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
if (ftrace_graph_return != (trace_func_graph_ret_t)ftrace_stub
|| ftrace_graph_entry != ftrace_graph_entry_stub)
ftrace_graph_caller();
#endif
/* restore all state by the compiler epilogue */
}
EXPORT_SYMBOL(_mcount);
#else /* CONFIG_DYNAMIC_FTRACE */
noinline void __naked ftrace_stub(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *op, struct pt_regs *regs)
{
__asm__ (""); /* avoid to optimize as pure function */
}
noinline void __naked _mcount(unsigned long parent_ip)
{
__asm__ (""); /* avoid to optimize as pure function */
}
EXPORT_SYMBOL(_mcount);
#define XSTR(s) STR(s)
#define STR(s) #s
void _ftrace_caller(unsigned long parent_ip)
{
/* save all state needed by the compiler prologue */
/*
* prepare arguments for real tracing function
* first arg : __builtin_return_address(0) - MCOUNT_INSN_SIZE
* second arg : parent_ip
*/
__asm__ __volatile__ (
"move $r1, %0 \n\t"
"addi $r0, %1, #-" XSTR(MCOUNT_INSN_SIZE) "\n\t"
:
: "r" (parent_ip), "r" (__builtin_return_address(0)));
/* a placeholder for the call to a real tracing function */
__asm__ __volatile__ (
"ftrace_call: \n\t"
"nop \n\t"
"nop \n\t"
"nop \n\t");
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
/* a placeholder for the call to ftrace_graph_caller */
__asm__ __volatile__ (
"ftrace_graph_call: \n\t"
"nop \n\t"
"nop \n\t"
"nop \n\t");
#endif
/* restore all state needed by the compiler epilogue */
}
int __init ftrace_dyn_arch_init(void)
{
return 0;
}
int ftrace_arch_code_modify_prepare(void)
{
set_all_modules_text_rw();
return 0;
}
int ftrace_arch_code_modify_post_process(void)
{
set_all_modules_text_ro();
return 0;
}
static unsigned long gen_sethi_insn(unsigned long addr)
{
unsigned long opcode = 0x46000000;
unsigned long imm = addr >> 12;
unsigned long rt_num = 0xf << 20;
return ENDIAN_CONVERT(opcode | rt_num | imm);
}
static unsigned long gen_ori_insn(unsigned long addr)
{
unsigned long opcode = 0x58000000;
unsigned long imm = addr & 0x0000fff;
unsigned long rt_num = 0xf << 20;
unsigned long ra_num = 0xf << 15;
return ENDIAN_CONVERT(opcode | rt_num | ra_num | imm);
}
static unsigned long gen_jral_insn(unsigned long addr)
{
unsigned long opcode = 0x4a000001;
unsigned long rt_num = 0x1e << 20;
unsigned long rb_num = 0xf << 10;
return ENDIAN_CONVERT(opcode | rt_num | rb_num);
}
static void ftrace_gen_call_insn(unsigned long *call_insns,
unsigned long addr)
{
call_insns[0] = gen_sethi_insn(addr); /* sethi $r15, imm20u */
call_insns[1] = gen_ori_insn(addr); /* ori $r15, $r15, imm15u */
call_insns[2] = gen_jral_insn(addr); /* jral $lp, $r15 */
}
static int __ftrace_modify_code(unsigned long pc, unsigned long *old_insn,
unsigned long *new_insn, bool validate)
{
unsigned long orig_insn[3];
if (validate) {
if (probe_kernel_read(orig_insn, (void *)pc, MCOUNT_INSN_SIZE))
return -EFAULT;
if (memcmp(orig_insn, old_insn, MCOUNT_INSN_SIZE))
return -EINVAL;
}
if (probe_kernel_write((void *)pc, new_insn, MCOUNT_INSN_SIZE))
return -EPERM;
return 0;
}
static int ftrace_modify_code(unsigned long pc, unsigned long *old_insn,
unsigned long *new_insn, bool validate)
{
int ret;
ret = __ftrace_modify_code(pc, old_insn, new_insn, validate);
if (ret)
return ret;
flush_icache_range(pc, pc + MCOUNT_INSN_SIZE);
return ret;
}
int ftrace_update_ftrace_func(ftrace_func_t func)
{
unsigned long pc = (unsigned long)&ftrace_call;
unsigned long old_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
unsigned long new_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
if (func != ftrace_stub)
ftrace_gen_call_insn(new_insn, (unsigned long)func);
return ftrace_modify_code(pc, old_insn, new_insn, false);
}
int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
{
unsigned long pc = rec->ip;
unsigned long nop_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
unsigned long call_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
ftrace_gen_call_insn(call_insn, addr);
return ftrace_modify_code(pc, nop_insn, call_insn, true);
}
int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
unsigned long addr)
{
unsigned long pc = rec->ip;
unsigned long nop_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
unsigned long call_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
ftrace_gen_call_insn(call_insn, addr);
return ftrace_modify_code(pc, call_insn, nop_insn, true);
}
#endif /* CONFIG_DYNAMIC_FTRACE */
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
unsigned long frame_pointer)
{
unsigned long return_hooker = (unsigned long)&return_to_handler;
unsigned long old;
if (unlikely(atomic_read(&current->tracing_graph_pause)))
return;
old = *parent;
if (!function_graph_enter(old, self_addr, frame_pointer, NULL))
*parent = return_hooker;
}
noinline void ftrace_graph_caller(void)
{
unsigned long *parent_ip =
(unsigned long *)(__builtin_frame_address(2) - 4);
unsigned long selfpc =
(unsigned long)(__builtin_return_address(1) - MCOUNT_INSN_SIZE);
unsigned long frame_pointer =
(unsigned long)__builtin_frame_address(3);
prepare_ftrace_return(parent_ip, selfpc, frame_pointer);
}
extern unsigned long ftrace_return_to_handler(unsigned long frame_pointer);
void __naked return_to_handler(void)
{
__asm__ __volatile__ (
/* save state needed by the ABI */
"smw.adm $r0,[$sp],$r1,#0x0 \n\t"
/* get original return address */
"move $r0, $fp \n\t"
"bal ftrace_return_to_handler\n\t"
"move $lp, $r0 \n\t"
/* restore state nedded by the ABI */
"lmw.bim $r0,[$sp],$r1,#0x0 \n\t");
}
#ifdef CONFIG_DYNAMIC_FTRACE
extern unsigned long ftrace_graph_call;
static int ftrace_modify_graph_caller(bool enable)
{
unsigned long pc = (unsigned long)&ftrace_graph_call;
unsigned long nop_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
unsigned long call_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
ftrace_gen_call_insn(call_insn, (unsigned long)ftrace_graph_caller);
if (enable)
return ftrace_modify_code(pc, nop_insn, call_insn, true);
else
return ftrace_modify_code(pc, call_insn, nop_insn, true);
}
int ftrace_enable_ftrace_graph_caller(void)
{
return ftrace_modify_graph_caller(true);
}
int ftrace_disable_ftrace_graph_caller(void)
{
return ftrace_modify_graph_caller(false);
}
#endif /* CONFIG_DYNAMIC_FTRACE */
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
nds32: Fix a kernel panic issue because of wrong frame pointer access. It can make sure that trace_hardirqs_off/trace_hardirqs_on can get a correct return address by frame pointer through __builtin_return_address() in this fix. Unable to handle kernel paging request at virtual address fffffffc pgd = 3c42e9cf [fffffffc] *pgd=02a9c000 Internal error: Oops: 1 [#1] Modules linked in: CPU: 0 PC is at trace_hardirqs_off+0x78/0xec LP is at common_exception_handler+0xda/0xf4 pc : [<b23ea5a4>] lp : [<b2352eba>] Tainted: G W sp : ada60ab0 fp : efcaff48 gp : 3a020490 r25: efcb0000 r24: 00000000 r23: 00000000 r22: 00000000 r21: 00000000 r20: 000700c1 r19: 000700ca r18: 3a21b018 r17: 00000001 r16: 00000002 r15: 00000001 r14: 0000002a r13: 3a00a804 r12: ada60ab0 r11: 3a113af8 r10: 3a01c530 r9 : 3a124404 r8 : 00120f9c r7 : b2352eba r6 : 00000000 r5 : 3a126b58 r4 : 00000000 r3 : 3a1726a8 r2 : b2921000 r1 : 00000000 r0 : 00000000 IRQs off Segment user Process init (pid: 1, stack limit = 0x069d7f15) Stack: (0xada60ab0 to 0xada61000) Stack: 0aa0: 00000000 00000003 3a110000 0011f000 Stack: 0ac0: 00000005 00000000 00000000 00000000 ada60b10 3a01fe68 ada60b0c ada60b08 Stack: 0ae0: 00000000 ada60ab8 ada60b30 3a020550 00000000 00000001 3a11c2f8 3a01c6e8 Stack: 0b00: 3a01cb80 fffffba8 3a113af8 3a21b018 3a122c28 00003ec4 00000165 00000000 Stack: 0b20: 3a126aec 0000006c 00000000 00000001 3a01fe68 00000000 00000003 00000000 Stack: 0b40: 00000001 000003f8 3a020930 3a01c530 00000008 ada60c18 3a020490 3a003120 Stack: 0b60: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 Stack: 0b80: 00000000 00000000 00000000 00000000 ffff8000 00000000 00000000 00000000 Stack: 0ba0: 00000000 00000001 3a020550 00000000 3a01d020 00000000 fffff000 fffff000 Stack: 0bc0: 00000000 00000000 00000000 00000000 ada60f2c 00000000 00000001 00000000 Stack: 0be0: 00000000 00000000 3a01fe68 fffffab0 00008034 00000008 3a0010cc 3a01fe68 Stack: 0c00: 00000000 00000000 00000001 ada60c88 3a020490 3a0139d4 0009dc6f 00000000 Stack: 0c20: 00000000 00000000 ada60fce fffff000 00000000 0000ebe0 3a020038 3a020550 Stack: 0c40: ada60f20 ada60c90 3a0007f0 3a0002a8 ada60c8c 00000000 00000000 ada60c88 Stack: 0c60: 3a020490 3a004570 00000000 00000000 ada60f20 3a0007f0 3a000000 00000000 Stack: 0c80: 3a020490 3a004850 00000000 3a013f24 3a000000 00000000 3a01ff44 00000000 Stack: 0ca0: 00000000 00000000 00000000 00000000 00000000 00000000 3a01ff84 3a01ff7c Stack: 0cc0: 3a01ff4c 3a01ff5c 3a01ff64 3a01ff9c 3a01ffa4 3a01ffac 3a01ff6c 3a01ff74 Stack: 0ce0: 00000000 00000000 3a01ff44 00000000 00000000 00000000 00000000 00000000 Stack: 0d00: 3a01ff8c 00000000 00000000 3a01ff94 00000000 00000000 00000000 00000000 Stack: 0d20: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 Stack: 0d40: 3a01ffbc 3a01ffb4 00000000 00000000 00000000 00000000 00000000 00000000 Stack: 0d60: 00000000 00000000 00000000 00000000 00000000 3a01ffc4 00000000 00000000 Stack: 0d80: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 Stack: 0da0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 Stack: 0dc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 3a01ff54 Stack: 0de0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 Stack: 0e00: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 Stack: 0e20: 00000000 00000004 00000000 00000000 00000000 00000000 00000000 00000000 Stack: 0e40: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 Stack: 0e60: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 Stack: 0e80: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 Stack: 0ea0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 Stack: 0ec0: 00000000 00000000 00000000 00000000 ffffffff 00000000 00000000 00000000 Stack: 0ee0: 00000000 00000000 00000000 00000000 ada60f20 00000000 00000000 00000000 Stack: 0f00: 00000000 00000000 00000000 00000000 00000000 00000000 3a020490 3a000b24 Stack: 0f20: 00000001 ada60fde 00000000 ada60fe4 ada60feb 00000000 00000021 3a038000 Stack: 0f40: 00000010 0009dc6f 00000006 00001000 00000011 00000064 00000003 00008034 Stack: 0f60: 00000004 00000020 00000005 00000008 00000007 3a000000 00000008 00000000 Stack: 0f80: 00000009 0000ebe0 0000000b 00000000 0000000c 00000000 0000000d 00000000 Stack: 0fa0: 0000000e 00000000 00000017 00000000 00000019 ada60fce 0000001f ada60ff6 Stack: 0fc0: 00000000 00000000 00000000 b5010000 fa839914 23b5dd89 a2aea540 692fc82e Stack: 0fe0: 0074696e 454d4f48 54002f3d 3d4d5245 756e696c 692f0078 0074696e 00000000 CPU: 0 PID: 1 Comm: init Tainted: G W 4.18.0-00015-g1888b64a2558-dirty #112 Hardware name: andestech,ae3xx (DT) Call Trace: [<b27a8e34>] dump_stack+0x2c/0x38 [<b2354874>] die+0x128/0x18c [<b2356f4c>] do_page_fault+0x3b8/0x4e0 [<b2352ed4>] ret_from_exception+0x0/0x10 [<b2352eba>] common_exception_handler+0xda/0xf4 Signed-off-by: Greentime Hu <greentime@andestech.com>
2018-08-23 10:05:46 +03:00
#ifdef CONFIG_TRACE_IRQFLAGS
noinline void __trace_hardirqs_off(void)
{
trace_hardirqs_off();
}
noinline void __trace_hardirqs_on(void)
{
trace_hardirqs_on();
}
#endif /* CONFIG_TRACE_IRQFLAGS */