2019-05-27 08:55:21 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2017-07-10 18:03:19 -07:00
/*
* Copyright ( C ) 2008 ARM Limited
* Copyright ( C ) 2014 Regents of the University of California
*/
# include <linux/export.h>
# include <linux/kallsyms.h>
# include <linux/sched.h>
# include <linux/sched/debug.h>
# include <linux/sched/task_stack.h>
# include <linux/stacktrace.h>
2018-02-13 13:13:21 +08:00
# include <linux/ftrace.h>
2017-07-10 18:03:19 -07:00
2020-11-13 14:42:21 +08:00
# include <asm/stacktrace.h>
2021-03-17 23:08:38 +08:00
register unsigned long sp_in_global __asm__ ( " sp " ) ;
2020-04-13 09:12:34 -07:00
2017-07-10 18:03:19 -07:00
# ifdef CONFIG_FRAME_POINTER
2019-08-29 14:57:00 +08:00
void notrace walk_stackframe ( struct task_struct * task , struct pt_regs * regs ,
2020-11-13 14:42:23 +08:00
bool ( * fn ) ( void * , unsigned long ) , void * arg )
2017-07-10 18:03:19 -07:00
{
unsigned long fp , sp , pc ;
if ( regs ) {
2019-04-15 11:14:40 +02:00
fp = frame_pointer ( regs ) ;
sp = user_stack_pointer ( regs ) ;
pc = instruction_pointer ( regs ) ;
2021-04-29 07:03:48 +00:00
} else if ( task = = current ) {
fp = ( unsigned long ) __builtin_frame_address ( 1 ) ;
sp = ( unsigned long ) __builtin_frame_address ( 0 ) ;
pc = ( unsigned long ) __builtin_return_address ( 0 ) ;
2017-07-10 18:03:19 -07:00
} else {
/* task blocked in __switch_to */
fp = task - > thread . s [ 0 ] ;
sp = task - > thread . sp ;
pc = task - > thread . ra ;
}
for ( ; ; ) {
unsigned long low , high ;
struct stackframe * frame ;
2020-11-13 14:42:23 +08:00
if ( unlikely ( ! __kernel_text_address ( pc ) | | ! fn ( arg , pc ) ) )
2017-07-10 18:03:19 -07:00
break ;
/* Validate frame pointer */
low = sp + sizeof ( struct stackframe ) ;
high = ALIGN ( sp , THREAD_SIZE ) ;
if ( unlikely ( fp < low | | fp > high | | fp & 0x7 ) )
break ;
/* Unwind stack frame */
frame = ( struct stackframe * ) fp - 1 ;
sp = fp ;
riscv/stacktrace: Fix stack output without ra on the stack top
When a function doesn't have a callee, then it will not
push ra into the stack, such as lkdtm_BUG() function,
addi sp,sp,-16
sd s0,8(sp)
addi s0,sp,16
ebreak
The struct stackframe use {fp,ra} to get information from
stack, if walk_stackframe() with pr_regs, we will obtain
wrong value and bad stacktrace,
[<ffffffe00066c56c>] lkdtm_BUG+0x6/0x8
---[ end trace 18da3fbdf08e25d5 ]---
Correct the next fp and pc, after that, full stacktrace
shown as expects,
[<ffffffe00066c56c>] lkdtm_BUG+0x6/0x8
[<ffffffe0008b24a4>] lkdtm_do_action+0x14/0x1c
[<ffffffe00066c372>] direct_entry+0xc0/0x10a
[<ffffffe000439f86>] full_proxy_write+0x42/0x6a
[<ffffffe000309626>] vfs_write+0x7e/0x214
[<ffffffe00030992a>] ksys_write+0x98/0xc0
[<ffffffe000309960>] sys_write+0xe/0x16
[<ffffffe0002014bc>] ret_from_syscall+0x0/0x2
---[ end trace 61917f3d9a9fadcd ]---
Signed-off-by: Chen Huang <chenhuang5@huawei.com>
Signed-off-by: Kefeng Wang <wangkefeng.wang@huawei.com>
Signed-off-by: Palmer Dabbelt <palmerdabbelt@google.com>
2021-01-11 20:40:14 +08:00
if ( regs & & ( regs - > epc = = pc ) & & ( frame - > fp & 0x7 ) ) {
fp = frame - > ra ;
pc = regs - > ra ;
} else {
fp = frame - > fp ;
pc = ftrace_graph_ret_addr ( current , NULL , frame - > ra ,
( unsigned long * ) ( fp - 8 ) ) ;
}
2017-07-10 18:03:19 -07:00
}
}
# else /* !CONFIG_FRAME_POINTER */
2020-05-11 10:19:53 +08:00
void notrace walk_stackframe ( struct task_struct * task ,
2020-11-13 14:42:22 +08:00
struct pt_regs * regs , bool ( * fn ) ( void * , unsigned long ) , void * arg )
2017-07-10 18:03:19 -07:00
{
unsigned long sp , pc ;
unsigned long * ksp ;
if ( regs ) {
2019-04-15 11:14:40 +02:00
sp = user_stack_pointer ( regs ) ;
pc = instruction_pointer ( regs ) ;
2017-07-10 18:03:19 -07:00
} else if ( task = = NULL | | task = = current ) {
2020-02-27 11:07:28 -08:00
sp = sp_in_global ;
2017-07-10 18:03:19 -07:00
pc = ( unsigned long ) walk_stackframe ;
} else {
/* task blocked in __switch_to */
sp = task - > thread . sp ;
pc = task - > thread . ra ;
}
if ( unlikely ( sp & 0x7 ) )
return ;
ksp = ( unsigned long * ) sp ;
while ( ! kstack_end ( ksp ) ) {
2020-11-13 14:42:23 +08:00
if ( __kernel_text_address ( pc ) & & unlikely ( ! fn ( arg , pc ) ) )
2017-07-10 18:03:19 -07:00
break ;
pc = ( * ksp + + ) - 0x4 ;
}
}
# endif /* CONFIG_FRAME_POINTER */
2020-11-13 14:42:23 +08:00
static bool print_trace_address ( void * arg , unsigned long pc )
2017-07-10 18:03:19 -07:00
{
2020-06-08 21:31:17 -07:00
const char * loglvl = arg ;
print_ip_sym ( loglvl , pc ) ;
2020-11-13 14:42:23 +08:00
return true ;
2017-07-10 18:03:19 -07:00
}
2021-04-29 07:03:48 +00:00
noinline void dump_backtrace ( struct pt_regs * regs , struct task_struct * task ,
2021-01-11 20:40:12 +08:00
const char * loglvl )
{
walk_stackframe ( task , regs , print_trace_address , ( void * ) loglvl ) ;
}
2020-06-08 21:32:29 -07:00
void show_stack ( struct task_struct * task , unsigned long * sp , const char * loglvl )
2017-07-10 18:03:19 -07:00
{
2021-04-29 07:03:48 +00:00
pr_cont ( " %sCall Trace: \n " , loglvl ) ;
2021-01-11 20:40:12 +08:00
dump_backtrace ( NULL , task , loglvl ) ;
2017-07-10 18:03:19 -07:00
}
2020-11-13 14:42:22 +08:00
static bool save_wchan ( void * arg , unsigned long pc )
2017-07-10 18:03:19 -07:00
{
if ( ! in_sched_functions ( pc ) ) {
unsigned long * p = arg ;
* p = pc ;
2020-11-13 14:42:23 +08:00
return false ;
2017-07-10 18:03:19 -07:00
}
2020-11-13 14:42:23 +08:00
return true ;
2017-07-10 18:03:19 -07:00
}
unsigned long get_wchan ( struct task_struct * task )
{
unsigned long pc = 0 ;
if ( likely ( task & & task ! = current & & task - > state ! = TASK_RUNNING ) )
walk_stackframe ( task , NULL , save_wchan , & pc ) ;
return pc ;
}
# ifdef CONFIG_STACKTRACE
2021-04-29 07:03:48 +00:00
noinline void arch_stack_walk ( stack_trace_consume_fn consume_entry , void * cookie ,
2020-11-13 14:42:23 +08:00
struct task_struct * task , struct pt_regs * regs )
2017-07-10 18:03:19 -07:00
{
2020-11-13 14:42:23 +08:00
walk_stackframe ( task , regs , consume_entry , cookie ) ;
2017-07-10 18:03:19 -07:00
}
# endif /* CONFIG_STACKTRACE */