Oops在Linux 2.6内核+PowerPC架构下的前世今生
Sailor_forever sailing_9806#163.com
(本原创文章发表于Sailor_forever 的个人blog,未经本人许可,不得用于商业用途。任何个人、媒体、其他网站不得私自抄袭;网络媒体转载请注明出处,增加原文链接,否则属于侵权行为。如有任何问题,请留言或者发邮件给sailing_9806#163.com)
http://blog.csdn.net/sailor_8318/archive/2010/01/31/5273890.aspx
【摘要】本文详细分析了2.6内核下Oops的来龙去脉,重点介绍了Oops的转储机制。首先介绍了Oops的基本概念、内核的异常级别及PowerPC的异常类型,接着介绍了Oops的处理流程。基于这个流程和嵌入式系统的文件系统及存储介质的特点,详细介绍了用户态和内核态中Oops的转储机制。最后介绍了PowerPC的EABI规范及如何根据此规则分析Oops log信息。
【关键字】Oops 转储,panic,backtrace,syslogd,klogd,PowerPC, EABI
目录
1 何谓OOPS 2
2 内核的异常级别 3
2.1 Bug 3
2.2 Oops 3
2.3 Panic 4
3 PowerPC的异常类型 4
4 OOPS的处理流程 6
4.1 异常入口 6
4.2 Die 10
4.3 Panic 15
5 OOPS的记录及转储 17
5.1 Printk 17
5.2 符号化记录backtrace 17
5.3 Klogd 18
5.4 Syslogd 18
5.5 用户态OOPS转储 18
5.6 内核态OOPS转储 19
5.6.1 进程上下文 19
5.6.2 中断上下文 25
6 PowerPC的EABI规范 27
6.1 寄存器的使用规则 27
6.2 堆栈的结构 28
6.2.1 栈的增减规则 28
6.2.2 栈的结构 29
6.3 从反汇编来看EABI的实现 30
6.4 从show_stack来看函数是如何调用的 34
7 如何分析OOPS 35
7.1 如何分析backtrace 35
7.2 如何分析栈 35
8 Oops典型实例 35
9 参考资料 35
1 何谓OOPS
Oops是美国人比较常有的口语。就是有点意外,吃惊,或突然的意思。“Oops”并不是很严重,正如在Britney Spears的 “Oops I Did It Again”那首歌的歌词中,也是一种轻描淡写,有时含有抱歉的意思。
http://v.youku.com/v_show/id_XMTM0ODgxMDYw.html
对于Linux内核来说,Oops就意外着内核出了异常,此时会将产生异常时CPU的状态,出错的指令地址、数据地址及其他寄存器,函数调用的顺序甚至是栈里面的内容都打印出来,然后根据异常的严重程度来决定下一步的操作:杀死导致异常的进程或者挂起系统。
最典型的异常是在内核态引用了一个非法地址,通常是未初始化的野指针Null,这将导致页表异常,最终引发Oops。
Linux系统足够健壮,能够正常的反应各种异常。异常通常导致当前进程的死亡,而系统依然能够继续运转,但是这种运转都处在一种不稳定的状态,随时可能出问题。对于中断上下文的异常及系统关键资源的破坏,通常会导致内核挂起,不再响应任何事件。
2 内核的异常级别
2.1 Bug
Bug是指那些不符合内核的正常设计,但内核能够检测出来并且对系统运行不会产生影响的问题,比如在原子上下文中休眠。如:
BUG: scheduling while atomic: insmod/826/0x00000002
Call Trace:
[ef12f700] [c00081e0] show_stack+0x3c/0x194 (unreliable)
[ef12f730] [c0019b2c] __schedule_bug+0x64/0x78
[ef12f750] [c0350f50] schedule+0x324/0x34c
[ef12f7a0] [c03515c0] schedule_timeout+0x68/0xe4
[ef12f7e0] [c027938c] fsl_elbc_run_command+0x138/0x1c0
[ef12f820] [c0275820] nand_do_read_ops+0x130/0x3dc
[ef12f880] [c0275ebc] nand_read+0xac/0xe0
[ef12f8b0] [c0262d98] part_read+0x5c/0xe4
[ef12f8c0] [c017bcac] jffs2_flash_read+0x68/0x254
[ef12f8f0] [c0170550] jffs2_read_dnode+0x60/0x304
[ef12f940] [c017088c] jffs2_read_inode_range+0x98/0x180
[ef12f970] [c016e610] jffs2_do_readpage_nolock+0x94/0x1ac
[ef12f990] [c016ee04] jffs2_write_begin+0x2b0/0x330
[ef12fa10] [c005144c] generic_file_buffered_write+0x11c/0x8d0
[ef12fab0] [c0051e48] __generic_file_aio_write_nolock+0x248/0x500
[ef12fb20] [c0052168] generic_file_aio_write+0x68/0x10c
[ef12fb50] [c007ca80] do_sync_write+0xc4/0x138
[ef12fc10] [f107c0dc] oops_log+0xdc/0x1e8 [oopslog]
[ef12fe70] [f3087058] oops_log_init+0x58/0xa0 [oopslog]
[ef12fe80] [c00477bc] sys_init_module+0x130/0x17dc
[ef12ff40] [c00104b0] ret_from_syscall+0x0/0x38
--- Exception: c01 at 0xff29658
LR = 0x10031300
2.2 Oops
程序在内核态时,进入一种异常情况,比如引用非法指针导致的数据异常,数组越界导致的取指异常,此时异常处理机制能够捕获此异常,并将系统关键信息打印到串口上,正常情况下Oops消息会被记录到系统日志中去。
Oops发生时,进程处在内核态,很可能正在访问系统关键资源,并且获取了一些锁,当进程由于Oops异常退出时,无法释放已经获取的资源,导致其他需要获取此资源的进程挂起,对系统的正常运行造成影响。通常这种情况,系统处在不稳定的状态,很可能崩溃。
2.3 Panic
当Oops发生在中断上下文中或者在进程0和1中,系统将彻底挂起,因为中断服务程序异常后,将无法恢复,这种情况即称为内核panic。另外当系统设置了panic标志时,无论Oops发生在中断上下文还是进程上下文,都将导致内核Panic。由于在中断复位程序中panic后,系统将不再进行调度,Syslogd将不会再运行,因此这种情况下,Oops的消息仅仅打印到串口上,不会被记录在系统日志中。
3 PowerPC的异常类型
32位处理器通常有各种异常机制来响应系统运行过程中的异常。以MPC8378为例,其异常类型如下:
其中以200、300、400、1000、1100所代表的machine check,取指,取数及TLB等异常为主。大部分是非法地址及非法指令造成的。TLB miss经过简单的处理后会最终转向300和400异常处理。
4 OOPS的处理流程
4.1 异常入口
异常处理在内核中的相关代码为:
http://lxr.linux.no/#linux+v2.6.25/arch/powerpc/kernel/head_32.S#L384
汇编级的异常入口。
329/* Machine check */
330/*
331 * On CHRP, this is complicated by the fact that we could get a
332 * machine check inside RTAS, and we have no guarantee that certain
333 * critical registers will have the values we expect. The set of
334 * registers that might have bad values includes all the GPRs
335 * and all the BATs. We indicate that we are in RTAS by putting
336 * a non-zero value, the address of the exception frame to use,
337 * in SPRG2. The machine check handler checks SPRG2 and uses its
338 * value if it is non-zero. If we ever needed to free up SPRG2,
339 * we could use a field in the thread_info or thread_struct instead.
340 * (Other exception handlers assume that r1 is a valid kernel stack
341 * pointer when we take an exception from supervisor mode.)
342 * -- paulus.
343 */
344 . = 0x200
345 mtspr SPRN_SPRG0,r10
346 mtspr SPRN_SPRG1,r11
347 mfcr r10
348#ifdef CONFIG_PPC_CHRP
349 mfspr r11,SPRN_SPRG2
350 cmpwi 0,r11,0
351 bne 7f
352#endif /* CONFIG_PPC_CHRP */
353 EXCEPTION_PROLOG_1
3547: EXCEPTION_PROLOG_2
355 addi r3,r1,STACK_FRAME_OVERHEAD
356#ifdef CONFIG_PPC_CHRP
357 mfspr r4,SPRN_SPRG2
358 cmpwi cr1,r4,0
359 bne cr1,1f
360#endif
361 EXC_XFER_STD(0x200, machine_check_exception)
362#ifdef CONFIG_PPC_CHRP
3631: b machine_check_in_rtas
364#endif
365
366/* Data access exception. */
367 . = 0x300
368DataAccess:
369 EXCEPTION_PROLOG
370 mfspr r10,SPRN_DSISR
371 andis. r0,r10,0xa470 /* weird error? */
372 bne 1f /* if not, try to put a PTE */
373 mfspr r4,SPRN_DAR /* into the hash table */
374 rlwinm r3,r10,32-15,21,21 /* DSISR_STORE -> _PAGE_RW */
375 bl hash_page
3761: stw r10,_DSISR(r11)
377 mr r5,r10
378 mfspr r4,SPRN_DAR
379 EXC_XFER_EE_LITE(0x300, handle_page_fault)
380
381
382/* Instruction access exception. */
383 . = 0x400
384InstructionAccess:
385 EXCEPTION_PROLOG
386 andis. r0,r9,0x4000 /* no pte found? */
387 beq 1f /* if so, try to put a PTE */
388 li r3,0 /* into the hash table */
389 mr r4,r12 /* SRR0 is fault address */
390 bl hash_page
3911: mr r4,r12
392 mr r5,r9
393 EXC_XFER_EE_LITE(0x400, handle_page_fault)
handle_page_fault在
http://lxr.linux.no/#linux+v2.6.25/arch/powerpc/kernel/entry_32.S#L466
/*
464 * Top-level page fault handling.
465 * This is in assembler because if do_page_fault tells us that
466 * it is a bad kernel page fault, we want to save the non-volatile
467 * registers before calling bad_page_fault.
468 */
469 .globl handle_page_fault
470handle_page_fault:
471 stw r4,_DAR(r1)
472 addi r3,r1,STACK_FRAME_OVERHEAD
473 bl do_page_fault
474 cmpwi r3,0
475 beq+ ret_from_except
476 SAVE_NVGPRS(r1)
477 lwz r0,_TRAP(r1)
478 clrrwi r0,r0,1
479 stw r0,_TRAP(r1)
480 mr r5,r3
481 addi r3,r1,STACK_FRAME_OVERHEAD
482 lwz r4,_DAR(r1)
483 bl bad_page_fault
484 b ret_from_except_full
并最终调用bad_page_fault或者do_page_fault
http://lxr.linux.no/#linux+v2.6.25/arch/powerpc/mm/fault.c#L404
399/*
400 * bad_page_fault is called when we have a bad access from the kernel.
401 * It is called from the DSI and ISI handlers in head.S and from some
402 * of the procedures in traps.c.
403 */
404void bad_page_fault(struct pt_regs *regs, unsigned long address, int sig)
405{
406 const struct exception_table_entry *entry;
407
408 /* Are we prepared to handle this fault? */
409 if ((entry = search_exception_tables(regs->nip)) != NULL) {
410 regs->nip = entry->fixup;
411 return;
412 }
413
414 /* kernel has accessed a bad area */
415
416 switch (regs->trap) {
417 case 0x300:
418 case 0x380:
419 printk(KERN_ALERT "Unable to handle kernel paging request for "
420 "data at address 0x%08lx/n", regs->dar);
421 break;
422 case 0x400:
423 case 0x480:
424 printk(KERN_ALERT "Unable to handle kernel paging request for "
425 "instruction fetch/n");
426 break;
427 default:
428 printk(KERN_ALERT "Unable to handle kernel paging request for "
429 "