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函數,進入死循環。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章