2005-04-17 02:20:36 +04:00
/*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
* Copyright ( C ) 1994 - 1999 , 2000 by Ralf Baechle and others .
2006-02-08 16:38:18 +03:00
* Copyright ( C ) 2005 , 2006 by Ralf Baechle ( ralf @ linux - mips . org )
2005-04-17 02:20:36 +04:00
* Copyright ( C ) 1999 , 2000 Silicon Graphics , Inc .
* Copyright ( C ) 2004 Thiemo Seufer
2013-03-25 22:18:07 +04:00
* Copyright ( C ) 2013 Imagination Technologies Ltd .
2005-04-17 02:20:36 +04:00
*/
# include <linux/errno.h>
# include <linux/sched.h>
2007-10-12 02:46:09 +04:00
# include <linux/tick.h>
2005-04-17 02:20:36 +04:00
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/stddef.h>
# include <linux/unistd.h>
2011-07-29 02:46:31 +04:00
# include <linux/export.h>
2005-04-17 02:20:36 +04:00
# include <linux/ptrace.h>
# include <linux/mman.h>
# include <linux/personality.h>
# include <linux/sys.h>
# include <linux/init.h>
# include <linux/completion.h>
2006-02-07 19:48:03 +03:00
# include <linux/kallsyms.h>
2007-07-19 16:04:21 +04:00
# include <linux/random.h>
2005-04-17 02:20:36 +04:00
2007-07-19 16:04:21 +04:00
# include <asm/asm.h>
2005-04-17 02:20:36 +04:00
# include <asm/bootinfo.h>
# include <asm/cpu.h>
2005-05-31 15:49:19 +04:00
# include <asm/dsp.h>
2005-04-17 02:20:36 +04:00
# include <asm/fpu.h>
2014-01-27 19:23:11 +04:00
# include <asm/msa.h>
2005-04-17 02:20:36 +04:00
# include <asm/pgtable.h>
# include <asm/mipsregs.h>
# include <asm/processor.h>
2014-07-23 17:40:15 +04:00
# include <asm/reg.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
# include <asm/io.h>
# include <asm/elf.h>
# include <asm/isadep.h>
# include <asm/inst.h>
2006-09-26 18:44:01 +04:00
# include <asm/stacktrace.h>
2005-04-17 02:20:36 +04:00
2013-03-22 01:49:52 +04:00
# ifdef CONFIG_HOTPLUG_CPU
void arch_cpu_idle_dead ( void )
2005-04-17 02:20:36 +04:00
{
2013-03-22 01:49:52 +04:00
/* What the heck is this check doing ? */
if ( ! cpu_isset ( smp_processor_id ( ) , cpu_callin_map ) )
play_dead ( ) ;
}
# endif
2009-06-23 13:00:31 +04:00
2005-04-17 02:20:36 +04:00
asmlinkage void ret_from_fork ( void ) ;
2012-10-10 00:27:45 +04:00
asmlinkage void ret_from_kernel_thread ( void ) ;
2005-04-17 02:20:36 +04:00
void start_thread ( struct pt_regs * regs , unsigned long pc , unsigned long sp )
{
unsigned long status ;
/* New thread loses kernel privileges. */
2007-12-14 01:42:19 +03:00
status = regs - > cp0_status & ~ ( ST0_CU0 | ST0_CU1 | ST0_FR | KU_MASK ) ;
2005-04-17 02:20:36 +04:00
status | = KU_USER ;
regs - > cp0_status = status ;
clear_used_math ( ) ;
2006-10-08 19:10:01 +04:00
clear_fpu_owner ( ) ;
2013-11-19 21:30:38 +04:00
init_dsp ( ) ;
2014-01-27 19:23:11 +04:00
clear_thread_flag ( TIF_MSA_CTX_LIVE ) ;
disable_msa ( ) ;
2005-04-17 02:20:36 +04:00
regs - > cp0_epc = pc ;
regs - > regs [ 29 ] = sp ;
}
void exit_thread ( void )
{
}
void flush_thread ( void )
{
}
2009-04-03 03:56:59 +04:00
int copy_thread ( unsigned long clone_flags , unsigned long usp ,
2012-10-23 06:51:14 +04:00
unsigned long arg , struct task_struct * p )
2005-04-17 02:20:36 +04:00
{
2006-01-12 12:06:08 +03:00
struct thread_info * ti = task_thread_info ( p ) ;
2012-10-23 06:51:14 +04:00
struct pt_regs * childregs , * regs = current_pt_regs ( ) ;
2009-07-08 21:07:50 +04:00
unsigned long childksp ;
2005-04-13 21:43:59 +04:00
p - > set_child_tid = p - > clear_child_tid = NULL ;
2005-04-17 02:20:36 +04:00
2006-01-12 12:06:08 +03:00
childksp = ( unsigned long ) task_stack_page ( p ) + THREAD_SIZE - 32 ;
2005-04-17 02:20:36 +04:00
preempt_disable ( ) ;
2014-01-27 19:23:11 +04:00
if ( is_msa_enabled ( ) )
save_msa ( p ) ;
else if ( is_fpu_owner ( ) )
2005-04-17 02:20:36 +04:00
save_fp ( p ) ;
2005-05-31 15:49:19 +04:00
if ( cpu_has_dsp )
save_dsp ( p ) ;
2005-04-17 02:20:36 +04:00
preempt_enable ( ) ;
/* set up new TSS. */
childregs = ( struct pt_regs * ) childksp - 1 ;
2009-07-08 21:07:50 +04:00
/* Put the stack after the struct pt_regs. */
childksp = ( unsigned long ) childregs ;
2012-10-10 00:27:45 +04:00
p - > thread . cp0_status = read_c0_status ( ) & ~ ( ST0_CU2 | ST0_CU1 ) ;
if ( unlikely ( p - > flags & PF_KTHREAD ) ) {
unsigned long status = p - > thread . cp0_status ;
memset ( childregs , 0 , sizeof ( struct pt_regs ) ) ;
ti - > addr_limit = KERNEL_DS ;
p - > thread . reg16 = usp ; /* fn */
p - > thread . reg17 = arg ;
p - > thread . reg29 = childksp ;
p - > thread . reg31 = ( unsigned long ) ret_from_kernel_thread ;
# if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
status = ( status & ~ ( ST0_KUP | ST0_IEP | ST0_IEC ) ) |
( ( status & ( ST0_KUC | ST0_IEC ) ) < < 2 ) ;
# else
status | = ST0_EXL ;
# endif
childregs - > cp0_status = status ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
* childregs = * regs ;
2013-01-22 15:59:30 +04:00
childregs - > regs [ 7 ] = 0 ; /* Clear error flag */
childregs - > regs [ 2 ] = 0 ; /* Child gets zero as return value */
2012-12-27 20:52:32 +04:00
if ( usp )
childregs - > regs [ 29 ] = usp ;
2012-10-10 00:27:45 +04:00
ti - > addr_limit = USER_DS ;
2005-04-17 02:20:36 +04:00
p - > thread . reg29 = ( unsigned long ) childregs ;
p - > thread . reg31 = ( unsigned long ) ret_from_fork ;
/*
* New tasks lose permission to use the fpu . This accelerates context
* switching for most programs since they don ' t use the fpu .
*/
childregs - > cp0_status & = ~ ( ST0_CU2 | ST0_CU1 ) ;
clear_tsk_thread_flag ( p , TIF_USEDFPU ) ;
2006-04-05 12:45:47 +04:00
# ifdef CONFIG_MIPS_MT_FPAFF
2008-09-09 17:19:10 +04:00
clear_tsk_thread_flag ( p , TIF_FPUBOUND ) ;
2006-04-05 12:45:47 +04:00
# endif /* CONFIG_MIPS_MT_FPAFF */
2005-04-13 21:43:59 +04:00
if ( clone_flags & CLONE_SETTLS )
ti - > tp_value = regs - > regs [ 7 ] ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2013-06-12 21:08:54 +04:00
# ifdef CONFIG_CC_STACKPROTECTOR
# include <linux/stackprotector.h>
unsigned long __stack_chk_guard __read_mostly ;
EXPORT_SYMBOL ( __stack_chk_guard ) ;
# endif
2006-08-18 18:18:09 +04:00
struct mips_frame_info {
void * func ;
unsigned long func_size ;
int frame_size ;
int pc_offset ;
} ;
2005-02-21 13:55:16 +03:00
2013-05-12 19:05:34 +04:00
# define J_TARGET(pc,target) \
( ( ( unsigned long ) ( pc ) & 0xf0000000 ) | ( ( target ) < < 2 ) )
2006-08-03 11:29:15 +04:00
static inline int is_ra_save_ins ( union mips_instruction * ip )
{
2013-03-25 22:18:07 +04:00
# ifdef CONFIG_CPU_MICROMIPS
union mips_instruction mmi ;
/*
* swsp ra , offset
* swm16 reglist , offset ( sp )
* swm32 reglist , offset ( sp )
* sw32 ra , offset ( sp )
* jradiussp - NOT SUPPORTED
*
* microMIPS is way more fun . . .
*/
if ( mm_insn_16bit ( ip - > halfword [ 0 ] ) ) {
mmi . word = ( ip - > halfword [ 0 ] < < 16 ) ;
return ( ( mmi . mm16_r5_format . opcode = = mm_swsp16_op & &
mmi . mm16_r5_format . rt = = 31 ) | |
( mmi . mm16_m_format . opcode = = mm_pool16c_op & &
mmi . mm16_m_format . func = = mm_swm16_op ) ) ;
}
else {
mmi . halfword [ 0 ] = ip - > halfword [ 1 ] ;
mmi . halfword [ 1 ] = ip - > halfword [ 0 ] ;
return ( ( mmi . mm_m_format . opcode = = mm_pool32b_op & &
mmi . mm_m_format . rd > 9 & &
mmi . mm_m_format . base = = 29 & &
mmi . mm_m_format . func = = mm_swm32_func ) | |
( mmi . i_format . opcode = = mm_sw32_op & &
mmi . i_format . rs = = 29 & &
mmi . i_format . rt = = 31 ) ) ;
}
# else
2006-08-03 11:29:15 +04:00
/* sw / sd $ra, offset($sp) */
return ( ip - > i_format . opcode = = sw_op | | ip - > i_format . opcode = = sd_op ) & &
ip - > i_format . rs = = 29 & &
ip - > i_format . rt = = 31 ;
2013-03-25 22:18:07 +04:00
# endif
2006-08-03 11:29:15 +04:00
}
MIPS: Fix sibling call handling in get_frame_info
Given a function, get_frame_info() analyzes its instructions
to figure out frame size and return address. get_frame_info()
works as follows:
1. analyze up to 128 instructions if the function size is unknown
2. search for 'addiu/daddiu sp,sp,-immed' for frame size
3. search for 'sw ra,offset(sp)' for return address
4. end search when it sees jr/jal/jalr
This leads to an issue when the given function is a sibling
call, example shown as follows.
801ca110 <schedule>:
801ca110: 8f820000 lw v0,0(gp)
801ca114: 8c420000 lw v0,0(v0)
801ca118: 080726f0 j 801c9bc0 <__schedule>
801ca11c: 00000000 nop
801ca120 <io_schedule>:
801ca120: 27bdffe8 addiu sp,sp,-24
801ca124: 3c028022 lui v0,0x8022
801ca128: afbf0014 sw ra,20(sp)
In this case, get_frame_info() cannot properly detect schedule's
frame info, and eventually returns io_schedule's instead.
This patch adds 'j' to the end search condition to workaround
sibling call cases.
Signed-off-by: Tony Wu <tung7970@gmail.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/5236/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2013-05-12 19:04:29 +04:00
static inline int is_jump_ins ( union mips_instruction * ip )
2006-08-03 11:29:15 +04:00
{
2013-03-25 22:18:07 +04:00
# ifdef CONFIG_CPU_MICROMIPS
/*
* jr16 , jrc , jalr16 , jalr16
* jal
* jalr / jr , jalr . hb / jr . hb , jalrs , jalrs . hb
* jraddiusp - NOT SUPPORTED
*
* microMIPS is kind of more fun . . .
*/
union mips_instruction mmi ;
mmi . word = ( ip - > halfword [ 0 ] < < 16 ) ;
if ( ( mmi . mm16_r5_format . opcode = = mm_pool16c_op & &
( mmi . mm16_r5_format . rt & mm_jr16_op ) = = mm_jr16_op ) | |
ip - > j_format . opcode = = mm_jal32_op )
return 1 ;
if ( ip - > r_format . opcode ! = mm_pool32a_op | |
ip - > r_format . func ! = mm_pool32axf_op )
return 0 ;
return ( ( ( ip - > u_format . uimmediate > > 6 ) & mm_jalr_op ) = = mm_jalr_op ) ;
# else
MIPS: Fix sibling call handling in get_frame_info
Given a function, get_frame_info() analyzes its instructions
to figure out frame size and return address. get_frame_info()
works as follows:
1. analyze up to 128 instructions if the function size is unknown
2. search for 'addiu/daddiu sp,sp,-immed' for frame size
3. search for 'sw ra,offset(sp)' for return address
4. end search when it sees jr/jal/jalr
This leads to an issue when the given function is a sibling
call, example shown as follows.
801ca110 <schedule>:
801ca110: 8f820000 lw v0,0(gp)
801ca114: 8c420000 lw v0,0(v0)
801ca118: 080726f0 j 801c9bc0 <__schedule>
801ca11c: 00000000 nop
801ca120 <io_schedule>:
801ca120: 27bdffe8 addiu sp,sp,-24
801ca124: 3c028022 lui v0,0x8022
801ca128: afbf0014 sw ra,20(sp)
In this case, get_frame_info() cannot properly detect schedule's
frame info, and eventually returns io_schedule's instead.
This patch adds 'j' to the end search condition to workaround
sibling call cases.
Signed-off-by: Tony Wu <tung7970@gmail.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/5236/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2013-05-12 19:04:29 +04:00
if ( ip - > j_format . opcode = = j_op )
return 1 ;
2006-08-03 11:29:15 +04:00
if ( ip - > j_format . opcode = = jal_op )
return 1 ;
if ( ip - > r_format . opcode ! = spec_op )
return 0 ;
return ip - > r_format . func = = jalr_op | | ip - > r_format . func = = jr_op ;
2013-03-25 22:18:07 +04:00
# endif
2006-08-03 11:29:15 +04:00
}
static inline int is_sp_move_ins ( union mips_instruction * ip )
{
2013-03-25 22:18:07 +04:00
# ifdef CONFIG_CPU_MICROMIPS
/*
* addiusp - imm
* addius5 sp , - imm
* addiu32 sp , sp , - imm
* jradiussp - NOT SUPPORTED
*
* microMIPS is not more fun . . .
*/
if ( mm_insn_16bit ( ip - > halfword [ 0 ] ) ) {
union mips_instruction mmi ;
mmi . word = ( ip - > halfword [ 0 ] < < 16 ) ;
return ( ( mmi . mm16_r3_format . opcode = = mm_pool16d_op & &
mmi . mm16_r3_format . simmediate & & mm_addiusp_func ) | |
( mmi . mm16_r5_format . opcode = = mm_pool16d_op & &
mmi . mm16_r5_format . rt = = 29 ) ) ;
}
return ( ip - > mm_i_format . opcode = = mm_addiu32_op & &
ip - > mm_i_format . rt = = 29 & & ip - > mm_i_format . rs = = 29 ) ;
# else
2006-08-03 11:29:15 +04:00
/* addiu/daddiu sp,sp,-imm */
if ( ip - > i_format . rs ! = 29 | | ip - > i_format . rt ! = 29 )
return 0 ;
if ( ip - > i_format . opcode = = addiu_op | | ip - > i_format . opcode = = daddiu_op )
return 1 ;
2013-03-25 22:18:07 +04:00
# endif
2006-08-03 11:29:15 +04:00
return 0 ;
}
2006-07-29 18:27:20 +04:00
static int get_frame_info ( struct mips_frame_info * info )
2005-04-17 02:20:36 +04:00
{
2013-03-25 22:18:07 +04:00
# ifdef CONFIG_CPU_MICROMIPS
union mips_instruction * ip = ( void * ) ( ( ( char * ) info - > func ) - 1 ) ;
# else
2006-08-03 11:29:15 +04:00
union mips_instruction * ip = info - > func ;
2013-03-25 22:18:07 +04:00
# endif
2006-08-18 18:18:08 +04:00
unsigned max_insns = info - > func_size / sizeof ( union mips_instruction ) ;
unsigned i ;
2006-08-03 11:29:15 +04:00
2005-04-17 02:20:36 +04:00
info - > pc_offset = - 1 ;
2006-02-07 19:48:03 +03:00
info - > frame_size = 0 ;
2005-04-17 02:20:36 +04:00
2006-08-18 18:18:08 +04:00
if ( ! ip )
goto err ;
if ( max_insns = = 0 )
max_insns = 128U ; /* unknown function size */
max_insns = min ( 128U , max_insns ) ;
2006-08-03 11:29:15 +04:00
for ( i = 0 ; i < max_insns ; i + + , ip + + ) {
MIPS: Fix sibling call handling in get_frame_info
Given a function, get_frame_info() analyzes its instructions
to figure out frame size and return address. get_frame_info()
works as follows:
1. analyze up to 128 instructions if the function size is unknown
2. search for 'addiu/daddiu sp,sp,-immed' for frame size
3. search for 'sw ra,offset(sp)' for return address
4. end search when it sees jr/jal/jalr
This leads to an issue when the given function is a sibling
call, example shown as follows.
801ca110 <schedule>:
801ca110: 8f820000 lw v0,0(gp)
801ca114: 8c420000 lw v0,0(v0)
801ca118: 080726f0 j 801c9bc0 <__schedule>
801ca11c: 00000000 nop
801ca120 <io_schedule>:
801ca120: 27bdffe8 addiu sp,sp,-24
801ca124: 3c028022 lui v0,0x8022
801ca128: afbf0014 sw ra,20(sp)
In this case, get_frame_info() cannot properly detect schedule's
frame info, and eventually returns io_schedule's instead.
This patch adds 'j' to the end search condition to workaround
sibling call cases.
Signed-off-by: Tony Wu <tung7970@gmail.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/5236/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2013-05-12 19:04:29 +04:00
if ( is_jump_ins ( ip ) )
2006-02-07 19:48:03 +03:00
break ;
2006-08-03 11:29:20 +04:00
if ( ! info - > frame_size ) {
if ( is_sp_move_ins ( ip ) )
2013-03-25 22:18:07 +04:00
{
# ifdef CONFIG_CPU_MICROMIPS
if ( mm_insn_16bit ( ip - > halfword [ 0 ] ) )
{
unsigned short tmp ;
if ( ip - > halfword [ 0 ] & mm_addiusp_func )
{
tmp = ( ( ( ip - > halfword [ 0 ] > > 1 ) & 0x1ff ) < < 2 ) ;
info - > frame_size = - ( signed short ) ( tmp | ( ( tmp & 0x100 ) ? 0xfe00 : 0 ) ) ;
} else {
tmp = ( ip - > halfword [ 0 ] > > 1 ) ;
info - > frame_size = - ( signed short ) ( tmp & 0xf ) ;
}
ip = ( void * ) & ip - > halfword [ 1 ] ;
ip - - ;
} else
# endif
2006-08-03 11:29:20 +04:00
info - > frame_size = - ip - > i_format . simmediate ;
2013-03-25 22:18:07 +04:00
}
2006-08-03 11:29:20 +04:00
continue ;
2006-02-07 19:48:03 +03:00
}
2006-08-03 11:29:20 +04:00
if ( info - > pc_offset = = - 1 & & is_ra_save_ins ( ip ) ) {
2006-02-07 19:48:03 +03:00
info - > pc_offset =
ip - > i_format . simmediate / sizeof ( long ) ;
2006-08-03 11:29:20 +04:00
break ;
2005-04-17 02:20:36 +04:00
}
}
2006-07-29 18:27:20 +04:00
if ( info - > frame_size & & info - > pc_offset > = 0 ) /* nested */
return 0 ;
if ( info - > pc_offset < 0 ) /* leaf */
return 1 ;
/* prologue seems boggus... */
2006-08-18 18:18:08 +04:00
err :
2006-07-29 18:27:20 +04:00
return - 1 ;
2005-04-17 02:20:36 +04:00
}
2006-08-18 18:18:09 +04:00
static struct mips_frame_info schedule_mfi __read_mostly ;
2013-05-12 19:05:34 +04:00
# ifdef CONFIG_KALLSYMS
static unsigned long get___schedule_addr ( void )
{
return kallsyms_lookup_name ( " __schedule " ) ;
}
# else
static unsigned long get___schedule_addr ( void )
{
union mips_instruction * ip = ( void * ) schedule ;
int max_insns = 8 ;
int i ;
for ( i = 0 ; i < max_insns ; i + + , ip + + ) {
if ( ip - > j_format . opcode = = j_op )
return J_TARGET ( ip , ip - > j_format . target ) ;
}
return 0 ;
}
# endif
2005-04-17 02:20:36 +04:00
static int __init frame_info_init ( void )
{
2006-08-18 18:18:09 +04:00
unsigned long size = 0 ;
2006-02-07 19:48:03 +03:00
# ifdef CONFIG_KALLSYMS
2006-08-18 18:18:09 +04:00
unsigned long ofs ;
2013-05-12 19:05:34 +04:00
# endif
unsigned long addr ;
2006-08-18 18:18:09 +04:00
2013-05-12 19:05:34 +04:00
addr = get___schedule_addr ( ) ;
if ( ! addr )
addr = ( unsigned long ) schedule ;
# ifdef CONFIG_KALLSYMS
kallsyms_lookup_size_offset ( addr , & size , & ofs ) ;
2006-02-07 19:48:03 +03:00
# endif
2013-05-12 19:05:34 +04:00
schedule_mfi . func = ( void * ) addr ;
2006-08-18 18:18:09 +04:00
schedule_mfi . func_size = size ;
get_frame_info ( & schedule_mfi ) ;
2006-08-03 11:29:18 +04:00
/*
* Without schedule ( ) frame info , result given by
* thread_saved_pc ( ) and get_wchan ( ) are not reliable .
*/
2006-08-18 18:18:09 +04:00
if ( schedule_mfi . pc_offset < 0 )
2006-08-03 11:29:18 +04:00
printk ( " Can't analyze schedule() prologue at %p \n " , schedule ) ;
2006-02-07 19:48:03 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
arch_initcall ( frame_info_init ) ;
/*
* Return saved PC of a blocked thread .
*/
unsigned long thread_saved_pc ( struct task_struct * tsk )
{
struct thread_struct * t = & tsk - > thread ;
/* New born processes are a special case */
if ( t - > reg31 = = ( unsigned long ) ret_from_fork )
return t - > reg31 ;
2006-08-18 18:18:09 +04:00
if ( schedule_mfi . pc_offset < 0 )
2005-04-17 02:20:36 +04:00
return 0 ;
2006-08-18 18:18:09 +04:00
return ( ( unsigned long * ) t - > reg29 ) [ schedule_mfi . pc_offset ] ;
2005-04-17 02:20:36 +04:00
}
2006-07-29 18:27:20 +04:00
# ifdef CONFIG_KALLSYMS
2011-05-13 16:38:04 +04:00
/* generic stack unwinding function */
unsigned long notrace unwind_stack_by_address ( unsigned long stack_page ,
unsigned long * sp ,
unsigned long pc ,
unsigned long * ra )
2006-07-29 18:27:20 +04:00
{
struct mips_frame_info info ;
unsigned long size , ofs ;
2006-08-03 11:29:21 +04:00
int leaf ;
2006-09-29 13:02:51 +04:00
extern void ret_from_irq ( void ) ;
extern void ret_from_exception ( void ) ;
2006-07-29 18:27:20 +04:00
if ( ! stack_page )
return 0 ;
2006-09-29 13:02:51 +04:00
/*
* If we reached the bottom of interrupt context ,
* return saved pc in pt_regs .
*/
if ( pc = = ( unsigned long ) ret_from_irq | |
pc = = ( unsigned long ) ret_from_exception ) {
struct pt_regs * regs ;
if ( * sp > = stack_page & &
* sp + sizeof ( * regs ) < = stack_page + THREAD_SIZE - 32 ) {
regs = ( struct pt_regs * ) * sp ;
pc = regs - > cp0_epc ;
if ( __kernel_text_address ( pc ) ) {
* sp = regs - > regs [ 29 ] ;
* ra = regs - > regs [ 31 ] ;
return pc ;
}
}
return 0 ;
}
2006-10-13 15:37:35 +04:00
if ( ! kallsyms_lookup_size_offset ( pc , & size , & ofs ) )
2006-07-29 18:27:20 +04:00
return 0 ;
2006-08-18 18:18:07 +04:00
/*
2011-03-31 05:57:33 +04:00
* Return ra if an exception occurred at the first instruction
2006-08-18 18:18:07 +04:00
*/
2006-09-29 13:02:51 +04:00
if ( unlikely ( ofs = = 0 ) ) {
pc = * ra ;
* ra = 0 ;
return pc ;
}
2006-07-29 18:27:20 +04:00
info . func = ( void * ) ( pc - ofs ) ;
info . func_size = ofs ; /* analyze from start to ofs */
2006-08-03 11:29:21 +04:00
leaf = get_frame_info ( & info ) ;
if ( leaf < 0 )
2006-07-29 18:27:20 +04:00
return 0 ;
2006-08-03 11:29:21 +04:00
if ( * sp < stack_page | |
* sp + info . frame_size > stack_page + THREAD_SIZE - 32 )
2006-07-29 18:27:20 +04:00
return 0 ;
2006-08-03 11:29:21 +04:00
if ( leaf )
/*
* For some extreme cases , get_frame_info ( ) can
* consider wrongly a nested function as a leaf
* one . In that cases avoid to return always the
* same value .
*/
2006-09-29 13:02:51 +04:00
pc = pc ! = * ra ? * ra : 0 ;
2006-08-03 11:29:21 +04:00
else
pc = ( ( unsigned long * ) ( * sp ) ) [ info . pc_offset ] ;
* sp + = info . frame_size ;
2006-09-29 13:02:51 +04:00
* ra = 0 ;
2006-08-03 11:29:21 +04:00
return __kernel_text_address ( pc ) ? pc : 0 ;
2006-07-29 18:27:20 +04:00
}
2011-05-13 16:38:04 +04:00
EXPORT_SYMBOL ( unwind_stack_by_address ) ;
/* used by show_backtrace() */
unsigned long unwind_stack ( struct task_struct * task , unsigned long * sp ,
unsigned long pc , unsigned long * ra )
{
unsigned long stack_page = ( unsigned long ) task_stack_page ( task ) ;
return unwind_stack_by_address ( stack_page , sp , pc , ra ) ;
}
2006-07-29 18:27:20 +04:00
# endif
2006-08-18 18:18:09 +04:00
/*
* get_wchan - a maintenance nightmare ^ W ^ Wpain in the ass . . .
*/
unsigned long get_wchan ( struct task_struct * task )
{
unsigned long pc = 0 ;
# ifdef CONFIG_KALLSYMS
unsigned long sp ;
2006-09-29 13:02:51 +04:00
unsigned long ra = 0 ;
2006-08-18 18:18:09 +04:00
# endif
if ( ! task | | task = = current | | task - > state = = TASK_RUNNING )
goto out ;
if ( ! task_stack_page ( task ) )
goto out ;
pc = thread_saved_pc ( task ) ;
# ifdef CONFIG_KALLSYMS
sp = task - > thread . reg29 + schedule_mfi . frame_size ;
while ( in_sched_functions ( pc ) )
2006-09-29 13:02:51 +04:00
pc = unwind_stack ( task , & sp , pc , & ra ) ;
2006-08-18 18:18:09 +04:00
# endif
out :
return pc ;
}
2007-07-19 16:04:21 +04:00
/*
* Don ' t forget that the stack pointer must be aligned on a 8 bytes
* boundary for 32 - bits ABI and 16 bytes for 64 - bits ABI .
*/
unsigned long arch_align_stack ( unsigned long sp )
{
if ( ! ( current - > personality & ADDR_NO_RANDOMIZE ) & & randomize_va_space )
sp - = get_random_int ( ) & ~ PAGE_MASK ;
return sp & ALMASK ;
}