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.c257
1 files changed, 146 insertions, 111 deletions
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index 7ab67f786bfe..2613a0dd4b82 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -273,104 +273,107 @@ long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
273 return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL); 273 return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
274} 274}
275 275
276static struct mips_frame_info { 276/*
277 void *func; 277 *
278 unsigned long func_size; 278 */
279 int frame_size; 279struct mips_frame_info {
280 int pc_offset; 280 void *func;
281} *schedule_frame, mfinfo[64]; 281 unsigned long func_size;
282static int mfinfo_num; 282 int frame_size;
283 283 int pc_offset;
284static int __init get_frame_info(struct mips_frame_info *info) 284};
285
286static inline int is_ra_save_ins(union mips_instruction *ip)
285{ 287{
286 int i; 288 /* sw / sd $ra, offset($sp) */
287 void *func = info->func; 289 return (ip->i_format.opcode == sw_op || ip->i_format.opcode == sd_op) &&
288 union mips_instruction *ip = (union mips_instruction *)func; 290 ip->i_format.rs == 29 &&
291 ip->i_format.rt == 31;
292}
293
294static inline int is_jal_jalr_jr_ins(union mips_instruction *ip)
295{
296 if (ip->j_format.opcode == jal_op)
297 return 1;
298 if (ip->r_format.opcode != spec_op)
299 return 0;
300 return ip->r_format.func == jalr_op || ip->r_format.func == jr_op;
301}
302
303static inline int is_sp_move_ins(union mips_instruction *ip)
304{
305 /* addiu/daddiu sp,sp,-imm */
306 if (ip->i_format.rs != 29 || ip->i_format.rt != 29)
307 return 0;
308 if (ip->i_format.opcode == addiu_op || ip->i_format.opcode == daddiu_op)
309 return 1;
310 return 0;
311}
312
313static int get_frame_info(struct mips_frame_info *info)
314{
315 union mips_instruction *ip = info->func;
316 unsigned max_insns = info->func_size / sizeof(union mips_instruction);
317 unsigned i;
318
289 info->pc_offset = -1; 319 info->pc_offset = -1;
290 info->frame_size = 0; 320 info->frame_size = 0;
291 for (i = 0; i < 128; i++, ip++) {
292 /* if jal, jalr, jr, stop. */
293 if (ip->j_format.opcode == jal_op ||
294 (ip->r_format.opcode == spec_op &&
295 (ip->r_format.func == jalr_op ||
296 ip->r_format.func == jr_op)))
297 break;
298 321
299 if (info->func_size && i >= info->func_size / 4) 322 if (!ip)
323 goto err;
324
325 if (max_insns == 0)
326 max_insns = 128U; /* unknown function size */
327 max_insns = min(128U, max_insns);
328
329 for (i = 0; i < max_insns; i++, ip++) {
330
331 if (is_jal_jalr_jr_ins(ip))
300 break; 332 break;
301 if ( 333 if (!info->frame_size) {
302#ifdef CONFIG_32BIT 334 if (is_sp_move_ins(ip))
303 ip->i_format.opcode == addiu_op && 335 info->frame_size = - ip->i_format.simmediate;
304#endif 336 continue;
305#ifdef CONFIG_64BIT
306 ip->i_format.opcode == daddiu_op &&
307#endif
308 ip->i_format.rs == 29 &&
309 ip->i_format.rt == 29) {
310 /* addiu/daddiu sp,sp,-imm */
311 if (info->frame_size)
312 continue;
313 info->frame_size = - ip->i_format.simmediate;
314 } 337 }
315 338 if (info->pc_offset == -1 && is_ra_save_ins(ip)) {
316 if (
317#ifdef CONFIG_32BIT
318 ip->i_format.opcode == sw_op &&
319#endif
320#ifdef CONFIG_64BIT
321 ip->i_format.opcode == sd_op &&
322#endif
323 ip->i_format.rs == 29 &&
324 ip->i_format.rt == 31) {
325 /* sw / sd $ra, offset($sp) */
326 if (info->pc_offset != -1)
327 continue;
328 info->pc_offset = 339 info->pc_offset =
329 ip->i_format.simmediate / sizeof(long); 340 ip->i_format.simmediate / sizeof(long);
341 break;
330 } 342 }
331 } 343 }
332 if (info->pc_offset == -1 || info->frame_size == 0) { 344 if (info->frame_size && info->pc_offset >= 0) /* nested */
333 if (func == schedule) 345 return 0;
334 printk("Can't analyze prologue code at %p\n", func); 346 if (info->pc_offset < 0) /* leaf */
335 info->pc_offset = -1; 347 return 1;
336 info->frame_size = 0; 348 /* prologue seems boggus... */
337 } 349err:
338 350 return -1;
339 return 0;
340} 351}
341 352
353static struct mips_frame_info schedule_mfi __read_mostly;
354
342static int __init frame_info_init(void) 355static int __init frame_info_init(void)
343{ 356{
344 int i; 357 unsigned long size = 0;
345#ifdef CONFIG_KALLSYMS 358#ifdef CONFIG_KALLSYMS
359 unsigned long ofs;
346 char *modname; 360 char *modname;
347 char namebuf[KSYM_NAME_LEN + 1]; 361 char namebuf[KSYM_NAME_LEN + 1];
348 unsigned long start, size, ofs; 362
349 extern char __sched_text_start[], __sched_text_end[]; 363 kallsyms_lookup((unsigned long)schedule, &size, &ofs, &modname, namebuf);
350 extern char __lock_text_start[], __lock_text_end[];
351
352 start = (unsigned long)__sched_text_start;
353 for (i = 0; i < ARRAY_SIZE(mfinfo); i++) {
354 if (start == (unsigned long)schedule)
355 schedule_frame = &mfinfo[i];
356 if (!kallsyms_lookup(start, &size, &ofs, &modname, namebuf))
357 break;
358 mfinfo[i].func = (void *)(start + ofs);
359 mfinfo[i].func_size = size;
360 start += size - ofs;
361 if (start >= (unsigned long)__lock_text_end)
362 break;
363 if (start == (unsigned long)__sched_text_end)
364 start = (unsigned long)__lock_text_start;
365 }
366#else
367 mfinfo[0].func = schedule;
368 schedule_frame = &mfinfo[0];
369#endif 364#endif
370 for (i = 0; i < ARRAY_SIZE(mfinfo) && mfinfo[i].func; i++) 365 schedule_mfi.func = schedule;
371 get_frame_info(&mfinfo[i]); 366 schedule_mfi.func_size = size;
367
368 get_frame_info(&schedule_mfi);
369
370 /*
371 * Without schedule() frame info, result given by
372 * thread_saved_pc() and get_wchan() are not reliable.
373 */
374 if (schedule_mfi.pc_offset < 0)
375 printk("Can't analyze schedule() prologue at %p\n", schedule);
372 376
373 mfinfo_num = i;
374 return 0; 377 return 0;
375} 378}
376 379
@@ -386,54 +389,86 @@ unsigned long thread_saved_pc(struct task_struct *tsk)
386 /* New born processes are a special case */ 389 /* New born processes are a special case */
387 if (t->reg31 == (unsigned long) ret_from_fork) 390 if (t->reg31 == (unsigned long) ret_from_fork)
388 return t->reg31; 391 return t->reg31;
389 392 if (schedule_mfi.pc_offset < 0)
390 if (!schedule_frame || schedule_frame->pc_offset < 0)
391 return 0; 393 return 0;
392 return ((unsigned long *)t->reg29)[schedule_frame->pc_offset]; 394 return ((unsigned long *)t->reg29)[schedule_mfi.pc_offset];
393} 395}
394 396
395/* get_wchan - a maintenance nightmare^W^Wpain in the ass ... */ 397
396unsigned long get_wchan(struct task_struct *p) 398#ifdef CONFIG_KALLSYMS
399/* used by show_backtrace() */
400unsigned long unwind_stack(struct task_struct *task, unsigned long *sp,
401 unsigned long pc, unsigned long ra)
397{ 402{
398 unsigned long stack_page; 403 unsigned long stack_page;
399 unsigned long pc; 404 struct mips_frame_info info;
400#ifdef CONFIG_KALLSYMS 405 char *modname;
401 unsigned long frame; 406 char namebuf[KSYM_NAME_LEN + 1];
402#endif 407 unsigned long size, ofs;
408 int leaf;
403 409
404 if (!p || p == current || p->state == TASK_RUNNING) 410 stack_page = (unsigned long)task_stack_page(task);
411 if (!stack_page)
405 return 0; 412 return 0;
406 413
407 stack_page = (unsigned long)task_stack_page(p); 414 if (!kallsyms_lookup(pc, &size, &ofs, &modname, namebuf))
408 if (!stack_page || !mfinfo_num) 415 return 0;
416 /*
417 * Return ra if an exception occured at the first instruction
418 */
419 if (unlikely(ofs == 0))
420 return ra;
421
422 info.func = (void *)(pc - ofs);
423 info.func_size = ofs; /* analyze from start to ofs */
424 leaf = get_frame_info(&info);
425 if (leaf < 0)
426 return 0;
427
428 if (*sp < stack_page ||
429 *sp + info.frame_size > stack_page + THREAD_SIZE - 32)
409 return 0; 430 return 0;
410 431
411 pc = thread_saved_pc(p); 432 if (leaf)
433 /*
434 * For some extreme cases, get_frame_info() can
435 * consider wrongly a nested function as a leaf
436 * one. In that cases avoid to return always the
437 * same value.
438 */
439 pc = pc != ra ? ra : 0;
440 else
441 pc = ((unsigned long *)(*sp))[info.pc_offset];
442
443 *sp += info.frame_size;
444 return __kernel_text_address(pc) ? pc : 0;
445}
446#endif
447
448/*
449 * get_wchan - a maintenance nightmare^W^Wpain in the ass ...
450 */
451unsigned long get_wchan(struct task_struct *task)
452{
453 unsigned long pc = 0;
412#ifdef CONFIG_KALLSYMS 454#ifdef CONFIG_KALLSYMS
413 if (!in_sched_functions(pc)) 455 unsigned long sp;
414 return pc; 456#endif
415 457
416 frame = p->thread.reg29 + schedule_frame->frame_size; 458 if (!task || task == current || task->state == TASK_RUNNING)
417 do { 459 goto out;
418 int i; 460 if (!task_stack_page(task))
461 goto out;
419 462
420 if (frame < stack_page || frame > stack_page + THREAD_SIZE - 32) 463 pc = thread_saved_pc(task);
421 return 0;
422 464
423 for (i = mfinfo_num - 1; i >= 0; i--) { 465#ifdef CONFIG_KALLSYMS
424 if (pc >= (unsigned long) mfinfo[i].func) 466 sp = task->thread.reg29 + schedule_mfi.frame_size;
425 break;
426 }
427 if (i < 0)
428 break;
429 467
430 pc = ((unsigned long *)frame)[mfinfo[i].pc_offset]; 468 while (in_sched_functions(pc))
431 if (!mfinfo[i].frame_size) 469 pc = unwind_stack(task, &sp, pc, 0);
432 break;
433 frame += mfinfo[i].frame_size;
434 } while (in_sched_functions(pc));
435#endif 470#endif
436 471
472out:
437 return pc; 473 return pc;
438} 474}
439