【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Oops在Linux 2.6内核+PowerPC架构下的前世今生


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                        "

  • 1
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值