2006-03-27 11:57:01 +11:00
/**
* Copyright ( C ) 2005 Brian Rogan < bcr6 @ cornell . edu > , IBM
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
* */
2018-03-13 21:03:25 -07:00
# include <linux/compat_time.h>
2006-03-27 11:57:01 +11:00
# include <linux/oprofile.h>
# include <linux/sched.h>
# include <asm/processor.h>
2014-11-21 11:23:19 +08:00
# include <linux/uaccess.h>
2006-09-23 01:37:41 +01:00
# include <asm/compat.h>
2014-08-20 08:55:19 +10:00
# include <asm/oprofile_impl.h>
2006-03-27 11:57:01 +11:00
# define STACK_SP(STACK) *(STACK)
# define STACK_LR64(STACK) *((unsigned long *)(STACK) + 2)
# define STACK_LR32(STACK) *((unsigned int *)(STACK) + 1)
# ifdef CONFIG_PPC64
# define STACK_LR(STACK) STACK_LR64(STACK)
# else
# define STACK_LR(STACK) STACK_LR32(STACK)
# endif
static unsigned int user_getsp32 ( unsigned int sp , int is_first )
{
unsigned int stack_frame [ 2 ] ;
2006-09-23 01:37:41 +01:00
void __user * p = compat_ptr ( sp ) ;
2006-03-27 11:57:01 +11:00
2006-09-23 01:37:41 +01:00
if ( ! access_ok ( VERIFY_READ , p , sizeof ( stack_frame ) ) )
2006-03-27 11:57:01 +11:00
return 0 ;
/*
* The most likely reason for this is that we returned - EFAULT ,
* which means that we ' ve done all that we can do from
* interrupt context .
*/
2006-09-23 01:37:41 +01:00
if ( __copy_from_user_inatomic ( stack_frame , p , sizeof ( stack_frame ) ) )
2006-03-27 11:57:01 +11:00
return 0 ;
if ( ! is_first )
oprofile_add_trace ( STACK_LR32 ( stack_frame ) ) ;
/*
* We do not enforce increasing stack addresses here because
* we may transition to a different stack , eg a signal handler .
*/
return STACK_SP ( stack_frame ) ;
}
# ifdef CONFIG_PPC64
static unsigned long user_getsp64 ( unsigned long sp , int is_first )
{
unsigned long stack_frame [ 3 ] ;
2006-09-23 01:37:41 +01:00
if ( ! access_ok ( VERIFY_READ , ( void __user * ) sp , sizeof ( stack_frame ) ) )
2006-03-27 11:57:01 +11:00
return 0 ;
2006-09-23 01:37:41 +01:00
if ( __copy_from_user_inatomic ( stack_frame , ( void __user * ) sp ,
2006-03-27 11:57:01 +11:00
sizeof ( stack_frame ) ) )
return 0 ;
if ( ! is_first )
oprofile_add_trace ( STACK_LR64 ( stack_frame ) ) ;
return STACK_SP ( stack_frame ) ;
}
# endif
static unsigned long kernel_getsp ( unsigned long sp , int is_first )
{
unsigned long * stack_frame = ( unsigned long * ) sp ;
if ( ! validate_sp ( sp , current , STACK_FRAME_OVERHEAD ) )
return 0 ;
if ( ! is_first )
oprofile_add_trace ( STACK_LR ( stack_frame ) ) ;
/*
* We do not enforce increasing stack addresses here because
* we might be transitioning from an interrupt stack to a kernel
* stack . validate_sp ( ) is designed to understand this , so just
* use it .
*/
return STACK_SP ( stack_frame ) ;
}
void op_powerpc_backtrace ( struct pt_regs * const regs , unsigned int depth )
{
unsigned long sp = regs - > gpr [ 1 ] ;
int first_frame = 1 ;
/* We ditch the top stackframe so need to loop through an extra time */
depth + = 1 ;
if ( ! user_mode ( regs ) ) {
while ( depth - - ) {
sp = kernel_getsp ( sp , first_frame ) ;
if ( ! sp )
break ;
first_frame = 0 ;
}
} else {
2014-11-21 11:23:19 +08:00
pagefault_disable ( ) ;
2006-03-27 11:57:01 +11:00
# ifdef CONFIG_PPC64
2010-08-27 03:49:11 +00:00
if ( ! is_32bit_task ( ) ) {
2006-03-27 11:57:01 +11:00
while ( depth - - ) {
sp = user_getsp64 ( sp , first_frame ) ;
if ( ! sp )
break ;
first_frame = 0 ;
}
2014-11-21 11:23:19 +08:00
pagefault_enable ( ) ;
2006-03-27 11:57:01 +11:00
return ;
}
# endif
while ( depth - - ) {
sp = user_getsp32 ( sp , first_frame ) ;
if ( ! sp )
break ;
first_frame = 0 ;
}
2014-11-21 11:23:19 +08:00
pagefault_enable ( ) ;
2006-03-27 11:57:01 +11:00
}
}