aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/process.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/kernel/process.c')
-rw-r--r--arch/mips/kernel/process.c163
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 @@
4 * for more details. 4 * for more details.
5 * 5 *
6 * Copyright (C) 1994 - 1999, 2000 by Ralf Baechle and others. 6 * Copyright (C) 1994 - 1999, 2000 by Ralf Baechle and others.
7 * Copyright (C) 2005, 2006 by Ralf Baechle (ralf@linux-mips.org)
7 * Copyright (C) 1999, 2000 Silicon Graphics, Inc. 8 * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
8 * Copyright (C) 2004 Thiemo Seufer 9 * Copyright (C) 2004 Thiemo Seufer
9 */ 10 */
@@ -24,6 +25,7 @@
24#include <linux/a.out.h> 25#include <linux/a.out.h>
25#include <linux/init.h> 26#include <linux/init.h>
26#include <linux/completion.h> 27#include <linux/completion.h>
28#include <linux/kallsyms.h>
27 29
28#include <asm/abi.h> 30#include <asm/abi.h>
29#include <asm/bootinfo.h> 31#include <asm/bootinfo.h>
@@ -58,8 +60,8 @@ ATTRIB_NORET void cpu_idle(void)
58 } 60 }
59} 61}
60 62
61extern int do_signal(sigset_t *oldset, struct pt_regs *regs); 63extern void do_signal(struct pt_regs *regs);
62extern int do_signal32(sigset_t *oldset, struct pt_regs *regs); 64extern void do_signal32(struct pt_regs *regs);
63 65
64/* 66/*
65 * Native o32 and N64 ABI without DSP ASE 67 * Native o32 and N64 ABI without DSP ASE
@@ -271,46 +273,19 @@ long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
271 273
272static struct mips_frame_info { 274static struct mips_frame_info {
273 void *func; 275 void *func;
274 int omit_fp; /* compiled without fno-omit-frame-pointer */ 276 unsigned long func_size;
275 int frame_offset; 277 int frame_size;
276 int pc_offset; 278 int pc_offset;
277} schedule_frame, mfinfo[] = { 279} *schedule_frame, mfinfo[64];
278 { schedule, 0 }, /* must be first */ 280static int mfinfo_num;
279 /* arch/mips/kernel/semaphore.c */
280 { __down, 1 },
281 { __down_interruptible, 1 },
282 /* kernel/sched.c */
283#ifdef CONFIG_PREEMPT
284 { preempt_schedule, 0 },
285#endif
286 { wait_for_completion, 0 },
287 { interruptible_sleep_on, 0 },
288 { interruptible_sleep_on_timeout, 0 },
289 { sleep_on, 0 },
290 { sleep_on_timeout, 0 },
291 { yield, 0 },
292 { io_schedule, 0 },
293 { io_schedule_timeout, 0 },
294#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT)
295 { __preempt_spin_lock, 0 },
296 { __preempt_write_lock, 0 },
297#endif
298 /* kernel/timer.c */
299 { schedule_timeout, 1 },
300/* { nanosleep_restart, 1 }, */
301 /* lib/rwsem-spinlock.c */
302 { __down_read, 1 },
303 { __down_write, 1 },
304};
305 281
306static int mips_frame_info_initialized;
307static int __init get_frame_info(struct mips_frame_info *info) 282static int __init get_frame_info(struct mips_frame_info *info)
308{ 283{
309 int i; 284 int i;
310 void *func = info->func; 285 void *func = info->func;
311 union mips_instruction *ip = (union mips_instruction *)func; 286 union mips_instruction *ip = (union mips_instruction *)func;
312 info->pc_offset = -1; 287 info->pc_offset = -1;
313 info->frame_offset = info->omit_fp ? 0 : -1; 288 info->frame_size = 0;
314 for (i = 0; i < 128; i++, ip++) { 289 for (i = 0; i < 128; i++, ip++) {
315 /* if jal, jalr, jr, stop. */ 290 /* if jal, jalr, jr, stop. */
316 if (ip->j_format.opcode == jal_op || 291 if (ip->j_format.opcode == jal_op ||
@@ -319,6 +294,23 @@ static int __init get_frame_info(struct mips_frame_info *info)
319 ip->r_format.func == jr_op))) 294 ip->r_format.func == jr_op)))
320 break; 295 break;
321 296
297 if (info->func_size && i >= info->func_size / 4)
298 break;
299 if (
300#ifdef CONFIG_32BIT
301 ip->i_format.opcode == addiu_op &&
302#endif
303#ifdef CONFIG_64BIT
304 ip->i_format.opcode == daddiu_op &&
305#endif
306 ip->i_format.rs == 29 &&
307 ip->i_format.rt == 29) {
308 /* addiu/daddiu sp,sp,-imm */
309 if (info->frame_size)
310 continue;
311 info->frame_size = - ip->i_format.simmediate;
312 }
313
322 if ( 314 if (
323#ifdef CONFIG_32BIT 315#ifdef CONFIG_32BIT
324 ip->i_format.opcode == sw_op && 316 ip->i_format.opcode == sw_op &&
@@ -326,31 +318,20 @@ static int __init get_frame_info(struct mips_frame_info *info)
326#ifdef CONFIG_64BIT 318#ifdef CONFIG_64BIT
327 ip->i_format.opcode == sd_op && 319 ip->i_format.opcode == sd_op &&
328#endif 320#endif
329 ip->i_format.rs == 29) 321 ip->i_format.rs == 29 &&
330 { 322 ip->i_format.rt == 31) {
331 /* sw / sd $ra, offset($sp) */ 323 /* sw / sd $ra, offset($sp) */
332 if (ip->i_format.rt == 31) { 324 if (info->pc_offset != -1)
333 if (info->pc_offset != -1) 325 continue;
334 continue; 326 info->pc_offset =
335 info->pc_offset = 327 ip->i_format.simmediate / sizeof(long);
336 ip->i_format.simmediate / sizeof(long);
337 }
338 /* sw / sd $s8, offset($sp) */
339 if (ip->i_format.rt == 30) {
340//#if 0 /* gcc 3.4 does aggressive optimization... */
341 if (info->frame_offset != -1)
342 continue;
343//#endif
344 info->frame_offset =
345 ip->i_format.simmediate / sizeof(long);
346 }
347 } 328 }
348 } 329 }
349 if (info->pc_offset == -1 || info->frame_offset == -1) { 330 if (info->pc_offset == -1 || info->frame_size == 0) {
350 printk("Can't analyze prologue code at %p\n", func); 331 if (func == schedule)
332 printk("Can't analyze prologue code at %p\n", func);
351 info->pc_offset = -1; 333 info->pc_offset = -1;
352 info->frame_offset = -1; 334 info->frame_size = 0;
353 return -1;
354 } 335 }
355 336
356 return 0; 337 return 0;
@@ -358,25 +339,36 @@ static int __init get_frame_info(struct mips_frame_info *info)
358 339
359static int __init frame_info_init(void) 340static int __init frame_info_init(void)
360{ 341{
361 int i, found; 342 int i;
362 for (i = 0; i < ARRAY_SIZE(mfinfo); i++) 343#ifdef CONFIG_KALLSYMS
363 if (get_frame_info(&mfinfo[i])) 344 char *modname;
364 return -1; 345 char namebuf[KSYM_NAME_LEN + 1];
365 schedule_frame = mfinfo[0]; 346 unsigned long start, size, ofs;
366 /* bubble sort */ 347 extern char __sched_text_start[], __sched_text_end[];
367 do { 348 extern char __lock_text_start[], __lock_text_end[];
368 struct mips_frame_info tmp; 349
369 found = 0; 350 start = (unsigned long)__sched_text_start;
370 for (i = 1; i < ARRAY_SIZE(mfinfo); i++) { 351 for (i = 0; i < ARRAY_SIZE(mfinfo); i++) {
371 if (mfinfo[i-1].func > mfinfo[i].func) { 352 if (start == (unsigned long)schedule)
372 tmp = mfinfo[i]; 353 schedule_frame = &mfinfo[i];
373 mfinfo[i] = mfinfo[i-1]; 354 if (!kallsyms_lookup(start, &size, &ofs, &modname, namebuf))
374 mfinfo[i-1] = tmp; 355 break;
375 found = 1; 356 mfinfo[i].func = (void *)(start + ofs);
376 } 357 mfinfo[i].func_size = size;
377 } 358 start += size - ofs;
378 } while (found); 359 if (start >= (unsigned long)__lock_text_end)
379 mips_frame_info_initialized = 1; 360 break;
361 if (start == (unsigned long)__sched_text_end)
362 start = (unsigned long)__lock_text_start;
363 }
364#else
365 mfinfo[0].func = schedule;
366 schedule_frame = &mfinfo[0];
367#endif
368 for (i = 0; i < ARRAY_SIZE(mfinfo) && mfinfo[i].func; i++)
369 get_frame_info(&mfinfo[i]);
370
371 mfinfo_num = i;
380 return 0; 372 return 0;
381} 373}
382 374
@@ -393,47 +385,52 @@ unsigned long thread_saved_pc(struct task_struct *tsk)
393 if (t->reg31 == (unsigned long) ret_from_fork) 385 if (t->reg31 == (unsigned long) ret_from_fork)
394 return t->reg31; 386 return t->reg31;
395 387
396 if (schedule_frame.pc_offset < 0) 388 if (!schedule_frame || schedule_frame->pc_offset < 0)
397 return 0; 389 return 0;
398 return ((unsigned long *)t->reg29)[schedule_frame.pc_offset]; 390 return ((unsigned long *)t->reg29)[schedule_frame->pc_offset];
399} 391}
400 392
401/* get_wchan - a maintenance nightmare^W^Wpain in the ass ... */ 393/* get_wchan - a maintenance nightmare^W^Wpain in the ass ... */
402unsigned long get_wchan(struct task_struct *p) 394unsigned long get_wchan(struct task_struct *p)
403{ 395{
404 unsigned long stack_page; 396 unsigned long stack_page;
405 unsigned long frame, pc; 397 unsigned long pc;
398#ifdef CONFIG_KALLSYMS
399 unsigned long frame;
400#endif
406 401
407 if (!p || p == current || p->state == TASK_RUNNING) 402 if (!p || p == current || p->state == TASK_RUNNING)
408 return 0; 403 return 0;
409 404
410 stack_page = (unsigned long)task_stack_page(p); 405 stack_page = (unsigned long)task_stack_page(p);
411 if (!stack_page || !mips_frame_info_initialized) 406 if (!stack_page || !mfinfo_num)
412 return 0; 407 return 0;
413 408
414 pc = thread_saved_pc(p); 409 pc = thread_saved_pc(p);
410#ifdef CONFIG_KALLSYMS
415 if (!in_sched_functions(pc)) 411 if (!in_sched_functions(pc))
416 return pc; 412 return pc;
417 413
418 frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset]; 414 frame = p->thread.reg29 + schedule_frame->frame_size;
419 do { 415 do {
420 int i; 416 int i;
421 417
422 if (frame < stack_page || frame > stack_page + THREAD_SIZE - 32) 418 if (frame < stack_page || frame > stack_page + THREAD_SIZE - 32)
423 return 0; 419 return 0;
424 420
425 for (i = ARRAY_SIZE(mfinfo) - 1; i >= 0; i--) { 421 for (i = mfinfo_num - 1; i >= 0; i--) {
426 if (pc >= (unsigned long) mfinfo[i].func) 422 if (pc >= (unsigned long) mfinfo[i].func)
427 break; 423 break;
428 } 424 }
429 if (i < 0) 425 if (i < 0)
430 break; 426 break;
431 427
432 if (mfinfo[i].omit_fp)
433 break;
434 pc = ((unsigned long *)frame)[mfinfo[i].pc_offset]; 428 pc = ((unsigned long *)frame)[mfinfo[i].pc_offset];
435 frame = ((unsigned long *)frame)[mfinfo[i].frame_offset]; 429 if (!mfinfo[i].frame_size)
430 break;
431 frame += mfinfo[i].frame_size;
436 } while (in_sched_functions(pc)); 432 } while (in_sched_functions(pc));
433#endif
437 434
438 return pc; 435 return pc;
439} 436}