ARM中斷向量表與響應流程【轉】


轉自:http://blog.csdn.net/honour2sword/article/details/40213417

首先中斷向量表定義在哪里?如何加載?

  中斷向量表與中斷服務程序

處理流程

////////////////////////////////////////////////////////////////////////////////////////////////////

首先中斷向量表定義在哪里?__vectors_start

首先中斷向量表定義的是什么?定義的就是中斷服務程序的跳轉指令因為每個中斷向量在向量表中只有一個字節的存儲空間,只能存放一條指令,所以通常存放跳轉指令,使程序跳轉到存儲器的其他地方,再執行中斷處理。這里cpu就可以找中斷服務程序,跳轉指令如例如:

LDRPC, =ISR_HANDLER;

或者

指令與不同的cpu平台有關系。

 

1.1 vector表定義的方式:往往是變量地址:

如 .

 .globl      __vectors_start  定義__vectors_start符號,這樣外部程序可以訪問到。entry-armv.S

__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

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

1.2 中斷向量表 類型

 

                   From ARM

 

       .globl      __vectors_start  定義__vectors_start符號,這樣外部程序可以訪問到。

__vectors_start:定義異常(地址邏輯自上而下0x00----0x1c)跟具體的cpu特性有關

 ARM(        swi        SYS_ERROR0        )向量0:reset,但是這里被修改了,如果是cpu跑到了0地址,用軟件中斷SYS_ERROR0來處理.

 THUMB(        svc        #0                )向量1

 THUMB(        nop                        )向量2

W(b)        vector_und+ stubs_offset  向量3 #未定義指令異常

W(ldr)        pc,.LCvswi + stubs_offset  向量4#軟中斷

W(b)        vector_pabt+ stubs_offset  #向量5指令預取異常中斷(Prefetch Abort)

W(b)        vector_dabt+ stubs_offset   #向量6數據中止

W(b)        vector_addrexcptn+ stubs_offset  #向量7地址異常Thesearen't too critical.

W(b)        vector_irq+ stubs_offset  #向量8.IRQ(一般中斷)

W(b)        vector_fiq+ stubs_offset  #向量9  FIQ(快速中斷)

 

 

/*關於.globl指令:

    .global/.globl 命令

 

    .global symbol

 

    .global 使得連接程序(ld)能夠識別symbl

    聲明symbol是全局可見的標號_start是GNU鏈接器用來指定第一個要執行指令所必須的,同樣的是全局可見的(並且只能出現在一個模塊中)

 

    例如:

        .global_start    #定義_start為外部程序可以訪問的標簽

 

__vectors_start符號,又存放在哪里呢?

有不同的方式,可以指定加載的ram地址,如\kernel\arch\c6x\kernel平台

SECTIONS

{

/*

 * Start kernel read only segment

 */

READONLY_SEGMENT_START

 

.vectors :

{

_vectors_start =.;

*(.vectors)

. = ALIGN(0x400);

_vectors_end = .;

}

指定好了vector在內核鏡像加載到內存后的地址0x400

 

但是arm就不指定,如下,在啟動之后存放的地址:

//中斷服務處理程序

    c000b500 T__kuser_helper_start

    c000b500 t__kuser_memory_barrier

    c000b520 t__kuser_cmpxchg

    c000b540 t__kuser_get_tls

    c000b55c t__kuser_helper_version

    c000b560 T__kuser_helper_end

    c000b560 T __stubs_start //中斷服務處理程序

    c000b560 tvector_irq

    c000b5e0 tvector_dabt

    c000b660 tvector_pabt

    c000b6e0 tvector_und

    c000b760 tvector_fiq

    c000b764 tvector_addrexcptn

    c000b784 T__stubs_end

   c000b784 T __vectors_start中斷向量表的起始地址 32字節

    c000b7a4 T__vectors_end

 

 

2.其次 向量表在系統bootup的時候被鏈接在哪里?

/out/target/product/huaqin82_cwet_kk/obj/KERNEL_OBJ/arch/arm/kernel/entry-armv.o  打包成build-in.o

 

3,最后內核建立向量表vector的拷貝

__trap_init函數填充后的向量表如下:

虛擬地址

異常

 處理匯編代碼

0xffff0000

reset swi

SYS_ERROR0

0xffff0004

 undefined

b __real_stubs_start + (vector_undefinstr - __stubs_start)

0xffff0008

軟件中斷

ldr pc, __real_stubs_start + (.LCvswi - __stubs_start)

0xffff000c

取指令異常

b __real_stubs_start + (vector_prefetch - __stubs_start)

0xffff0010

數據異常

b __real_stubs_start + (vector_data - __stubs_start)

0xffff0014

 reserved

 b __real_stubs_start + (vector_addrexcptn - __stubs_start)

0xffff0018

irq

b __real_stubs_start + (vector_IRQ - __stubs_start)

0xffff001c

Fiq

 b __real_stubs_start + (vector_FIQ - __stubs_start)

 

///////////////////////////////////////////////////////////////////////////////////////////////////////////////

  中斷向量表與中斷服務程序

總的來說對於中斷向量表的定義和存放(加載)和處理流程如下:

首先理解相關概念:

 

中斷控制器

負責(1)屏蔽和過濾中斷信號(2)喚醒cpu。

分為向量中斷模式和非向量中斷模式:

---向量中斷模式用於RESET、NMI、異常處理。當向量中斷產生時,控制器直接將PC賦值,如IRQ異常 跳到0x0000000d處,而在0x0000000d地址處通常放置ISR服務程序地址LDR PC, =ISR_HANDLER。

 

---非向量中斷模式,有一個寄存器標識位,跳轉到統一的函數地址,此函數通過判別寄存器標識位和優先級關系進行中斷-處理。

跳轉指令:

我分為兩種:

 1是中斷控制器的跳轉指令(實際上編譯好的機器碼):為何需要?

因為當cpu在中斷發生的時候,cpu如何知道把pc指針執行哪里去執行指令呢。所以通過中斷控制器的跳轉指令幫助把cpu的執行指針pc,執行相應的中斷向量表。  

 

2是cpu相關的跳轉指令,如arm處理器:b   bl ,ldr等:完成跳轉到不同的中斷服務處理程序。

 

 

 

 

1中斷服務程序 定義在哪里?如arm的dataabort異常處理程序:

首先跳轉指令:b  vector_dabt + stubs_offset  ---->這個地址的指令定義也在entry-armv.S:

       vector_stub     dabt, ABT_MODE,8

----》__dabt_svc (內核模式發生dataabort) 或者 __dabt_usr(用戶模式發生dataabort)

-----》dabt_helper是一個宏--->bl        CPU_DABORT_HANDLER

 

2)存放(加載)的地址?中斷向量表定義好了之后,存放了ram的哪里呢?也就是__vectors_start存在內存什么地址?

 

答案:可以定在你需要的任何可訪問ram地址(這里指的虛擬地址,不是物理ram地址)。

 

例子1 :單片機

非向量中斷模式

假定非向量中斷表定義在0x00400000開始的外部RAM空間:

                                  引用網絡 圖2  中斷解析示例流程 

 

圖2中實線表示的流程都用ARM匯編語言編寫,一般作為boot代碼的一部分放在系統的底層模塊中。

填寫向量表的操作可以在上層應用程序中方便地實現,比如在C語言中: *( int *(0x00400018)) = (int) ISR_IRQ

這樣就將IRQ中斷的服務程序入口地址0x00300260)填寫到中斷向量表中的固定地址0x00400018開始的4字節空間了。 

 

簡單說就是:

0x00000018的地址的跳轉指令是:B0x00000600 

0x00000600存放的指令是:ldr r0 =0x004000018

 0x004000018 存放的是0x00300260:=中斷的服務程序ISR_IRQ的入口地址0x00300260

 

 

例子2:

ARM vector是存放在

    c000b500 T__kuser_helper_start

    c000b500 t__kuser_memory_barrier

    c000b520 t__kuser_cmpxchg

    c000b540 t__kuser_get_tls

    c000b55c t__kuser_helper_version

    c000b560 T__kuser_helper_end

   c000b560 T __stubs_start

    c000b560 tvector_irq

    c000b5e0 tvector_dabt

    c000b660 tvector_pabt

    c000b6e0 tvector_und

    c000b760 tvector_fiq

    c000b764 tvector_addrexcptn

    c000b784 T__stubs_end

   c000b784 T __vectors_start中斷向量表的起始地址

   c000b7a4 T __vectors_end

內核建立vector的拷貝

__trap_init函數填充后的向量表如下:

虛擬地址

異常

 處理匯編代碼

0xffff0000

reset swi

SYS_ERROR0

0xffff0004

 undefined

b __real_stubs_start + (vector_undefinstr - __stubs_start)

0xffff0008

軟件中斷

ldr pc, __real_stubs_start + (.LCvswi - __stubs_start)

0xffff000c

取指令異常

b __real_stubs_start + (vector_prefetch - __stubs_start)

0xffff0010

數據異常

b __real_stubs_start + (vector_data - __stubs_start)

0xffff0014

 reserved

 b __real_stubs_start + (vector_addrexcptn - __stubs_start)

0xffff0018

irq

b __real_stubs_start + (vector_IRQ - __stubs_start)

0xffff001c

Fiq

 b __real_stubs_start + (vector_FIQ - __stubs_start)

---為何內核要拷貝到0xffff0000?這個是arm cpu的規定:對於ARMv4及其以上的版本,異常向量表的起始位置由協處理器15(cp15)的控制寄存器(c1)里的V位(bit13)有關,當V=0時,異常向量表的起始位置在0x00000000,而當V=1時,異常向量表就起始於0xffff0000位置。當有異常發生時,處理器會跳轉到對應的0xffff0000起始的向量處取指令,然后,通過b指令散轉到異常處理代碼.因為ARM中b指令是相對跳轉,而且只有+/-32MB的尋址范圍,所以把__stubs_start~__stubs_end之間的異常處理代碼復制到了0xffff0200起始處.這里可直接用b指令跳轉過去,這樣比使用絕對跳轉(ldr)效率高。

 

處理流程?cpu發生中斷的時候,PC指針如何知道到0x000000-0x0000001c(linux內核copy到0xffff0000)的 地址(也就是到中斷向量表vector哪一種異常:swi,數據異常,irq等)去執行中斷跳轉指令呢?

答案是:中斷控制器完成。如下:

                                                                                          上圖(來自網絡ppt)

向量中斷模式用於RESET、NMI、異常處理。當向量中斷產生時,控制器直接將PC賦值,如跳到0x0000000d處,而在0x0000000d地址處通常放置ISR服務程序地址。

 

處理流程分為兩部分:如下

 

1。硬件部分EINT orIRQ硬件信號-----》中斷控制器跳轉---到對應的異常----(硬件do it-----》改變pc指針的地址------

2。軟件部分:中斷向量表跳轉指令(如b __real_stubs_start)-------》對應的中斷處理程序,比如一般的irq流程 ---entry-armv.S@  -----vector_stub        irq,IRQ_MODE, 4

-).macrovector_stub, name, mode, correction=0完成中斷現場保護,CPU異常模式切換

-) 根據進入中斷前的工作模式不同,程序下一步將跳轉到_irq_usr、或__irq_svc等位置

.long        __irq_usr                        @  0 (USR_26 / USR_32)

.long        __irq_invalid                        @  1 (FIQ_26 / FIQ_32)

.long        __irq_invalid                        @  2 (IRQ_26 / IRQ_32)

.long        __irq_svc                        @  3 (SVC_26 / SVC_32)

 

 ----》__irq_usr定義如下:

__irq_usr:

usr_entry

kuser_cmpxchg_check

irq_handler

get_thread_infotsk

mov        why,#0

b        ret_to_user_from_irq

 UNWIND(.fnend                )

ENDPROC(__irq_usr)

 

          -----》irq_handler定義如下:

.macro        irq_handler

#ifdefCONFIG_MULTI_IRQ_HANDLER

ldr        r1,=handle_arch_irq

mov        r0,sp

adr        lr,BSYM(9997f)

ldr        pc,[r1]

#else

arch_irq_handler_default

#endif

 

----》arm/include/asm/entry-macro-multi.S:6:@        .macro        arch_irq_handler_default

.macro        arch_irq_handler_default

get_irqnr_preambler6, lr

1:        get_irqnr_and_base r0, r2, r6, lr

#get_irqnr_and_base函數完成獲取IRQ中斷號(irq number,依賴不同的soc的中斷控制器

movne        r1,sp

@

@ routine calledwith r0 = irq number, r1 = struct pt_regs *

@

adrne        lr,BSYM(1b)

bne        asm_do_IRQ

 

/*get_irqnr_and_base實現是依賴具體的硬件的,對於pxa270 cpu,其實現如下:

.macro get_irqnr_and_base, irqnr, irqstat, base, tmp

mov /base,#io_p2v(0x40000000) @ IIR Ctl = 0x40d00000

add /base, /base,#0x00d00000

ldr /irqstat,[/base, #0] @ ICIP

ldr /irqnr,[/base, #4] @ ICMR

ands /irqstat,/irqstat, /irqnr

beq 1001f /* 沒找到中斷,跳轉*/

rsb /irqnr,/irqstat, #0

and /irqstat,/irqstat, /irqnr

clz /irqnr,/irqstat

rsb /irqnr,/irqnr, #(31 - PXA_IRQ_SKIP)

#ifdefCONFIG_CPU_BULVERDE

b 1002f

#endif

1001:

1002:

.endm

.macroirq_prio_table

.endm

*/

 

接着---》asm_do_IRQ:-->handle_IRQ()------>執行request_irq()注冊的中斷。

 

補充,EINT 是共享一個IRQ,所以要到對應的IRQ handle里面,再處理不同的EINT handler,,如MTK

void mt_eint_registration(unsignedint eint_num, unsigned int flag,#eint注冊IRQ:MT_EINT_IRQ_ID

              void(EINT_FUNC_PTR) (void), unsigned int is_auto_umask)

{

。。。。。。

       EINT_FUNC.eint_func[eint_num] = EINT_FUNC_PTR;

       spin_unlock(&eint_lock);

       EINT_FUNC.eint_auto_umask[eint_num] = is_auto_umask;

       mt_eint_ack(eint_num);

 

mt_eint_isr(){

...

...        

if(EINT_FUNC.eint_func[index]) {

                   EINT_FUNC.eint_func[index] ();

 

版權聲明:本文為博主原創文章,未經博主允許不得轉載。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM