diff options
Diffstat (limited to 'arch/mips/kernel/process.c')
| -rw-r--r-- | arch/mips/kernel/process.c | 163 | 
1 files changed, 80 insertions, 83 deletions
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index fa98f10d0132..092679c2dca9 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -4,6 +4,7 @@   * for more details.   *   * Copyright (C) 1994 - 1999, 2000 by Ralf Baechle and others. + * Copyright (C) 2005, 2006 by Ralf Baechle (ralf@linux-mips.org)   * Copyright (C) 1999, 2000 Silicon Graphics, Inc.   * Copyright (C) 2004 Thiemo Seufer   */ @@ -24,6 +25,7 @@  #include <linux/a.out.h>  #include <linux/init.h>  #include <linux/completion.h> +#include <linux/kallsyms.h>  #include <asm/abi.h>  #include <asm/bootinfo.h> @@ -58,8 +60,8 @@ ATTRIB_NORET void cpu_idle(void)  	}  } -extern int do_signal(sigset_t *oldset, struct pt_regs *regs); -extern int do_signal32(sigset_t *oldset, struct pt_regs *regs); +extern void do_signal(struct pt_regs *regs); +extern void do_signal32(struct pt_regs *regs);  /*   * Native o32 and N64 ABI without DSP ASE @@ -271,46 +273,19 @@ long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)  static struct mips_frame_info {  	void *func; -	int omit_fp;	/* compiled without fno-omit-frame-pointer */ -	int frame_offset; +	unsigned long func_size; +	int frame_size;  	int pc_offset; -} schedule_frame, mfinfo[] = { -	{ schedule, 0 },	/* must be first */ -	/* arch/mips/kernel/semaphore.c */ -	{ __down, 1 }, -	{ __down_interruptible, 1 }, -	/* kernel/sched.c */ -#ifdef CONFIG_PREEMPT -	{ preempt_schedule, 0 }, -#endif -	{ wait_for_completion, 0 }, -	{ interruptible_sleep_on, 0 }, -	{ interruptible_sleep_on_timeout, 0 }, -	{ sleep_on, 0 }, -	{ sleep_on_timeout, 0 }, -	{ yield, 0 }, -	{ io_schedule, 0 }, -	{ io_schedule_timeout, 0 }, -#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) -	{ __preempt_spin_lock, 0 }, -	{ __preempt_write_lock, 0 }, -#endif -	/* kernel/timer.c */ -	{ schedule_timeout, 1 }, -/*	{ nanosleep_restart, 1 }, */ -	/* lib/rwsem-spinlock.c */ -	{ __down_read, 1 }, -	{ __down_write, 1 }, -}; +} *schedule_frame, mfinfo[64]; +static int mfinfo_num; -static int mips_frame_info_initialized;  static int __init get_frame_info(struct mips_frame_info *info)  {  	int i;  	void *func = info->func;  	union mips_instruction *ip = (union mips_instruction *)func;  	info->pc_offset = -1; -	info->frame_offset = info->omit_fp ? 0 : -1; +	info->frame_size = 0;  	for (i = 0; i < 128; i++, ip++) {  		/* if jal, jalr, jr, stop. */  		if (ip->j_format.opcode == jal_op || @@ -319,6 +294,23 @@ static int __init get_frame_info(struct mips_frame_info *info)  		      ip->r_format.func == jr_op)))  			break; +		if (info->func_size && i >= info->func_size / 4) +			break; +		if ( +#ifdef CONFIG_32BIT +		    ip->i_format.opcode == addiu_op && +#endif +#ifdef CONFIG_64BIT +		    ip->i_format.opcode == daddiu_op && +#endif +		    ip->i_format.rs == 29 && +		    ip->i_format.rt == 29) { +			/* addiu/daddiu sp,sp,-imm */ +			if (info->frame_size) +				continue; +			info->frame_size = - ip->i_format.simmediate; +		} +  		if (  #ifdef CONFIG_32BIT  		    ip->i_format.opcode == sw_op && @@ -326,31 +318,20 @@ static int __init get_frame_info(struct mips_frame_info *info)  #ifdef CONFIG_64BIT  		    ip->i_format.opcode == sd_op &&  #endif -		    ip->i_format.rs == 29) -		{ +		    ip->i_format.rs == 29 && +		    ip->i_format.rt == 31) {  			/* sw / sd $ra, offset($sp) */ -			if (ip->i_format.rt == 31) { -				if (info->pc_offset != -1) -					continue; -				info->pc_offset = -					ip->i_format.simmediate / sizeof(long); -			} -			/* sw / sd $s8, offset($sp) */ -			if (ip->i_format.rt == 30) { -//#if 0	/* gcc 3.4 does aggressive optimization... */ -				if (info->frame_offset != -1) -					continue; -//#endif -				info->frame_offset = -					ip->i_format.simmediate / sizeof(long); -			} +			if (info->pc_offset != -1) +				continue; +			info->pc_offset = +				ip->i_format.simmediate / sizeof(long);  		}  	} -	if (info->pc_offset == -1 || info->frame_offset == -1) { -		printk("Can't analyze prologue code at %p\n", func); +	if (info->pc_offset == -1 || info->frame_size == 0) { +		if (func == schedule) +			printk("Can't analyze prologue code at %p\n", func);  		info->pc_offset = -1; -		info->frame_offset = -1; -		return -1; +		info->frame_size = 0;  	}  	return 0; @@ -358,25 +339,36 @@ static int __init get_frame_info(struct mips_frame_info *info)  static int __init frame_info_init(void)  { -	int i, found; -	for (i = 0; i < ARRAY_SIZE(mfinfo); i++) -		if (get_frame_info(&mfinfo[i])) -			return -1; -	schedule_frame = mfinfo[0]; -	/* bubble sort */ -	do { -		struct mips_frame_info tmp; -		found = 0; -		for (i = 1; i < ARRAY_SIZE(mfinfo); i++) { -			if (mfinfo[i-1].func > mfinfo[i].func) { -				tmp = mfinfo[i]; -				mfinfo[i] = mfinfo[i-1]; -				mfinfo[i-1] = tmp; -				found = 1; -			} -		} -	} while (found); -	mips_frame_info_initialized = 1; +	int i; +#ifdef CONFIG_KALLSYMS +	char *modname; +	char namebuf[KSYM_NAME_LEN + 1]; +	unsigned long start, size, ofs; +	extern char __sched_text_start[], __sched_text_end[]; +	extern char __lock_text_start[], __lock_text_end[]; + +	start = (unsigned long)__sched_text_start; +	for (i = 0; i < ARRAY_SIZE(mfinfo); i++) { +		if (start == (unsigned long)schedule) +			schedule_frame = &mfinfo[i]; +		if (!kallsyms_lookup(start, &size, &ofs, &modname, namebuf)) +			break; +		mfinfo[i].func = (void *)(start + ofs); +		mfinfo[i].func_size = size; +		start += size - ofs; +		if (start >= (unsigned long)__lock_text_end) +			break; +		if (start == (unsigned long)__sched_text_end) +			start = (unsigned long)__lock_text_start; +	} +#else +	mfinfo[0].func = schedule; +	schedule_frame = &mfinfo[0]; +#endif +	for (i = 0; i < ARRAY_SIZE(mfinfo) && mfinfo[i].func; i++) +		get_frame_info(&mfinfo[i]); + +	mfinfo_num = i;  	return 0;  } @@ -393,47 +385,52 @@ unsigned long thread_saved_pc(struct task_struct *tsk)  	if (t->reg31 == (unsigned long) ret_from_fork)  		return t->reg31; -	if (schedule_frame.pc_offset < 0) +	if (!schedule_frame || schedule_frame->pc_offset < 0)  		return 0; -	return ((unsigned long *)t->reg29)[schedule_frame.pc_offset]; +	return ((unsigned long *)t->reg29)[schedule_frame->pc_offset];  }  /* get_wchan - a maintenance nightmare^W^Wpain in the ass ...  */  unsigned long get_wchan(struct task_struct *p)  {  	unsigned long stack_page; -	unsigned long frame, pc; +	unsigned long pc; +#ifdef CONFIG_KALLSYMS +	unsigned long frame; +#endif  	if (!p || p == current || p->state == TASK_RUNNING)  		return 0;  	stack_page = (unsigned long)task_stack_page(p); -	if (!stack_page || !mips_frame_info_initialized) +	if (!stack_page || !mfinfo_num)  		return 0;  	pc = thread_saved_pc(p); +#ifdef CONFIG_KALLSYMS  	if (!in_sched_functions(pc))  		return pc; -	frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset]; +	frame = p->thread.reg29 + schedule_frame->frame_size;  	do {  		int i;  		if (frame < stack_page || frame > stack_page + THREAD_SIZE - 32)  			return 0; -		for (i = ARRAY_SIZE(mfinfo) - 1; i >= 0; i--) { +		for (i = mfinfo_num - 1; i >= 0; i--) {  			if (pc >= (unsigned long) mfinfo[i].func)  				break;  		}  		if (i < 0)  			break; -		if (mfinfo[i].omit_fp) -			break;  		pc = ((unsigned long *)frame)[mfinfo[i].pc_offset]; -		frame = ((unsigned long *)frame)[mfinfo[i].frame_offset]; +		if (!mfinfo[i].frame_size) +			break; +		frame += mfinfo[i].frame_size;  	} while (in_sched_functions(pc)); +#endif  	return pc;  }  | 
