0cde56e028
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>
310 lines
7.6 KiB
C
310 lines
7.6 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;
|
|
struct ftrace_graph_ent trace;
|
|
unsigned long old;
|
|
int err;
|
|
|
|
if (unlikely(atomic_read(¤t->tracing_graph_pause)))
|
|
return;
|
|
|
|
old = *parent;
|
|
|
|
trace.func = self_addr;
|
|
trace.depth = current->curr_ret_stack + 1;
|
|
|
|
/* Only trace if the calling function expects to */
|
|
if (!ftrace_graph_entry(&trace))
|
|
return;
|
|
|
|
err = ftrace_push_return_trace(old, self_addr, &trace.depth,
|
|
frame_pointer, NULL);
|
|
|
|
if (err == -EBUSY)
|
|
return;
|
|
|
|
*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 */
|
|
|
|
|
|
#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 */
|