• ARM Linux异常处理之data abort(一)【转】


    转自:https://blog.csdn.net/walkingman321/article/details/6230334

    本文简要分析了ARM Linux的data abort异常处理过程,内核版本2.6.28,s3c6410平台。

    异常向量与程序跳转

    data abort是ARM体系定义的异常之一。异常发生时,ARM会自动跳转到异常向量表中,通过向量表中的跳转命令跳转到相应的异常处理中去。

    ARM的异常处理向量表在entry-armv.S文件中:

           .globl      __vectors_start

    __vectors_start:

           swi   SYS_ERROR0

           b     vector_und + stubs_offset

           ldr   pc, .LCvswi + stubs_offset

           b     vector_pabt + stubs_offset

           b     vector_dabt + stubs_offset

           b     vector_addrexcptn + stubs_offset

           b     vector_irq + stubs_offset

           b     vector_fiq + stubs_offset

    对于data abort,对应的跳转地址是vector_dabt + stubs_offset。这个地址的指令定义也在entry-armv.S:

           vector_stub     dabt, ABT_MODE, 8

           .long       __dabt_usr                       @  0  (USR_26 / USR_32)

           .long       __dabt_invalid                     @  1  (FIQ_26 / FIQ_32)

           .long       __dabt_invalid                     @  2  (IRQ_26 / IRQ_32)

           .long       __dabt_svc                       @  3  (SVC_26 / SVC_32)

           .long       __dabt_invalid                     @  4

           .long       __dabt_invalid                     @  5

           .long       __dabt_invalid                     @  6

           .long       __dabt_invalid                     @  7

           .long       __dabt_invalid                     @  8

           .long       __dabt_invalid                     @  9

           .long       __dabt_invalid                     @  a

           .long       __dabt_invalid                     @  b

           .long       __dabt_invalid                     @  c

           .long       __dabt_invalid                     @  d

           .long       __dabt_invalid                     @  e

           .long       __dabt_invalid                     @  f

    vector_stub是一个宏定义:

           .macro     vector_stub, name, mode, correction=0

           .align      5

    vector_/name:

           .if /correction

           sub  lr, lr, #/correction

           .endif

     

           @

           @ Save r0, lr_<exception> (parent PC) and spsr_<exception>

           @ (parent CPSR)

           @

           stmia       sp, {r0, lr}             @ save r0, lr

           mrs  lr, spsr                          @ 保存跳转之前的CPSRlr寄存器

           str    lr, [sp, #8]                    @ save spsr

     

           @

           @ Prepare for SVC32 mode.  IRQs remain disabled.

           @

           mrs  r0, cpsr

           eor   r0, r0, #(/mode ^ SVC_MODE)

           msr  spsr_cxsf, r0                 @ 准备进入svc模式

     

           @

           @ the branch table must immediately follow this code

           @

           and  lr, lr, #0x0f                    @ 得到跳转前所处的模式(usrsvr等)

           mov r0, sp

           ldr   lr, [pc, lr, lsl #2]            @ 根据模式跳转到相应的data abort指令,并进入svc模式

           movs       pc, lr                     @ branch to handler in SVC mode

    ENDPROC(vector_/name)

           .endm

    由代码中红色标注部分可看出,对于同一个异常,根据进入异常之前所处的模式,会跳转到不同的指令分支,这些指令分支紧跟在vector_stub宏定义的后面。如果进入data abort之前处于usr模式,那么跳转到__dabt_usr;如果处于svc模式,那么跳转到__dabt_svc;否则跳转到__dabt_invalid。

    实际上,进入异常向量前Linux只能处于usr或者svc两种模式之一。这时因为irq等异常在跳转表中都要经过vector_stub宏,而不管之前是哪种状态,这个宏都会将CPU状态改为svc模式。

    usr模式即Linux中的用户态模式,svc即内核模式。

    下面看一下在不同模式下进入data abort时的处理过程。

    svc模式进入data abort

    svc模式进入data abort,也就是Linux的内核模式进入data aboart时,会跳转到__dabt_svc。

    __dabt_svc:

           svc_entry               @ 保护寄存器现场

     

           mrs  r9, cpsr

           tst    r3, #PSR_I_BIT            @ 检查是否要开中断

           biceq       r9, r9, #PSR_I_BIT

           bl    CPU_DABORT_HANDLER  @ 处理异常之前的准备工作

     

           msr  cpsr_c, r9

           mov r2, sp

           bl    do_DataAbort        @ 主要操作都在这里,本文暂不研究

     

           disable_irq

     

           ldr   r0, [sp, #S_PSR]

           msr  spsr_cxsf, r0

           ldmia      sp, {r0 - pc}^                @ load r0 - pc, cpsr

    ENDPROC(__dabt_svc)

    CPU_DABORT_HANDLER的定义在glue.h:

    #define CPU_DABORT_HANDLER v6_early_abort

    对于s3c6410,v6_early_abort的定义在abort-ev6.S中,里面涉及到很多ARM的细节操作,但对我们来说,只需要了解下面这两句即可:

           mrc  p15, 0, r1, c5, c0, 0              @ get FSR

           mrc  p15, 0, r0, c6, c0, 0              @ get FAR

    这两句用于读取协处理器CP15的C5、C6寄存器。当data abort异常发生时,C5寄存器中保存的值指明了是哪种原因导致的异常,具体原因可在介绍arm的资料中找到。C6寄存器中保存的是导致data abort的存储地址。

    usr模式进入data abort

    usr模式进入data abort,也就是Linux的用户模式进入data bort时,会跳转到__dabt_usr。

    __dabt_usr:

           usr_entry                                    @ 保护寄存器现场

           kuser_cmpxchg_check

     

           bl    CPU_DABORT_HANDLER  @ svc模式时处理过程一样

     

           enable_irq                                  @ 开中断

           mov r2, sp

           adr  lr, ret_from_exception            @ 重设返回地址

           b     do_DataAbort                      @ svc模式时处理过程一样

    ENDPROC(__dabt_usr)

    由代码可知,用户模式和内核模式的data abort处理过程类似,区别在于:

    l         用户模式下data abort处理一定是开中断的;内核模式下则由具体情况决定。

    l         用户模式下异常处理返回地址被设为ret_from_exception (entry-armv.S文件);内核模式下则返回到出现异常的那条语句。

    下面看一下ret_from_exception:

    ENTRY(ret_from_exception)

           get_thread_info tsk

           mov why, #0

           b     ret_to_user

    ENDPROC(__pabt_usr)

    ret_to_user会判断是否需要进行进程调度,并最终返回到用户空间。用户空间data abort时可能产生进程调度的原因就在这里。

    未定义状态的data abort

    除了usr和svc模式之外,其它模式下发生data abort时,都会调用__dabt_invalid函数。这里所说的其它模式在linux正常运行过程中是不应该存在的,所以如果进入__dabt_invalid函数,那就代表Linux内核应该崩溃了。

    __dabt_invalid:

           inv_entry BAD_DATA

           b     common_invalid

    ENDPROC(__dabt_invalid)

    inv_entry宏做的主要工作是保存寄存器现场(压栈)。

    common_invalid做一些必要的设置,最终调用C函数bad_mode (traps.c)。

    asmlinkage void bad_mode(struct pt_regs *regs, int reason)

    {

           console_verbose();

     

           printk(KERN_CRIT "Bad mode in %s handler detected/n", handler[reason]);

     

           die("Oops - bad mode", regs, 0);

           local_irq_disable();

           panic("bad mode");

    }

    由代码可知,bad_mode主要是输出一些必要的信息,然后调用panic函数,进入死循环。

    【作者】张昺华
    【大饼教你学系列】https://edu.csdn.net/course/detail/10393
    【新浪微博】 张昺华--sky
    【twitter】 @sky2030_
    【微信公众号】 张昺华
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    Java中如何判断一个字符串是否为数字
    Web发展简史
    常用编程语言
    浏览器运行原理
    [LeetCode]69. x 的平方根(数学,二分)
    [计算机网络]TCP/IP协议-运输层
    [剑指Offer]33-根据后序序列判断是否能组成BST
    [剑指Offer]17-打印从1到最大的n位数(递归)
    [剑指Offer]56-数组中数字出现的次数(位运算)
    [剑指Offer]18-题目一:删除链表的节点 题目二:删除链表中重复节点
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/14373403.html
Copyright © 2020-2023  润新知