天天看點

ARM 中斷狀态和SVC狀态的堆棧切換 (異常)【轉】

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

ARM 中斷狀态和SVC狀态的堆棧切換 (異常)

基礎知識:

Arm的寄存器使用規則以及尋址指令:

R13  Sp     堆棧寄存器

R14  Lr     連接配接寄存器

R15  PC     程式計數器

多寄存器尋址:

LDMIA R0!,{R1-R4}

執行以後的效果

R1  <——[R0]

R2  <——[R0+4]

R3  <——[R0+8]

R4  <——[R0+12]

堆棧尋址:

STMFD入棧指令,相當于STMDB

STMFD SP!,{R2-R4} 注意這個“!”的使用,在使用和不使用的情況下會有不一樣的效果,在後面的代碼中具體分析。

[SP-4]  <­——R4

[SP-8]  <——R3 

[SP-12] <——R2   

LDMFD出棧指令,相當于LDMIA

LDMFD SP!,{R6-R8}

R6  <——[SP]

R7  <——[SP+4]

R8  <——[SP+8]

補充說明:

LDMIA / STMIA Increment After (先操作,後增加)

LDMIB / STMIB Increment Before(先增加,後操作)

LDMDA / STMDA Decrement After (先操作,後遞減)

LDMDB / STMDB Decrement Before(先遞減,後操作)

•STMFD (Push) 塊存儲- Full Descending stack [STMDB]

•LDMFD (Pop)  塊裝載- Full Descending stack [LDMIA]

這些使用規則以及預設的表達方法是給編譯器使用。但是在開發底層語言的同時,有必要知道這個麼命名的規則和使用方法。初始化代碼和部分關鍵代碼是靠彙編實作。這些代碼的了解不免和彙編打交道。是以了解一下基本的彙編規則還是很有幫助。

 Arm的工作模式:

Arm的工作模式以及相關寄存器設定:

1,使用者模式(usr)      [10000]:ARM處理器正常的程式執行狀态

2,快速中斷模式(fiq)  [10001]:用于高速資料傳輸或通道處理

3,外部中斷模式(irq)  [10010]:用于通用的中斷處理

4,管理模式(svc)      [10011]:作業系統使用的保護模式

5,中止模式(abt)          [10111]:當資料或指令預取終止時進入該模式,用于虛拟存

儲及存儲保護

6,未定義指令模式(und)[11011]:當未定義的指令執行時進入該模式,用于支援硬體

協處理器的軟體仿真

7,系統模式(sys)      [11111]:運作具有特權模式的作業系統任務

設定方法:

MRS R14,CPSR       讀取

MSR CPSR_c, R14     寫入

以上幾種模式存在的意義在于不同模式下特殊的幾個寄存器使用是有差別的。再svc模式下堆棧指針為sp svc中斷模式下sp指針為 sp irq等。同樣lr連接配接寄存器的内容也是有不同的含義。再不同模式切換中,lr寄存器儲存的位址是由硬體完成,但是表示的是不同模式下的下一條指令,既返模式切換後的傳回位址。

ARM 中斷狀态和SVC狀态的堆棧切換 (異常)【轉】

每一種模式對應不同的bank register。中文官方翻譯不詳。但是每一種模式要擁有自己獨立的寄存器組。并且每一種模式使用和可見寄存器的數量也是不相同的。

模式切換過程中其實隻針對spsr進行操作,而未涉及cpsr是的操作。這個過程之是以這樣主要是參考ARM cortex A8的TRM。其中這樣描述離開異常的情況:

Typically the return instruction is an arithmetic orlogical operation with the S bit set to

1 and rd = r15, so the core copies the SPSR back to theCPSR.

也就是說離開異常,從異常情況傳回以後會自動把spsr的内同拷貝到cpsr中。是以在執行BL Lr指令之前使用的堆棧其實并位切換。

Linux中初始化:

1,  Svc模式的堆棧初始化:

堆棧的概念是給C 語言編譯以後的代碼使用,是以從head.S一直到C語言的執行,就是start_kernel。

__mmap_switched:

    @注釋 1:

    adr r3, __switch_data + 4

    ldmia   r3!, {r4, r5, r6, r7}

    cmp r4, r5              @ Copy datasegment if needed

1:  cmpne   r5, r6

    ldrne   fp, [r4], #4

    strne   fp, [r5], #4

    bne 1b

    mov fp, #0              @ Clear BSS(and zero fp)

1:  cmp r6, r7

    strcc   fp, [r6],#4

    bcc 1b

    @注釋 2:

    ldmia   r3, {r4, r5, r6, r7, sp}

    str r9, [r4]            @ Saveprocessor ID

    str r1, [r5]            @ Savemachine type

    str r2, [r6]            @ Saveatags pointer

    bic r4, r0, #CR_A           @ Clear'A' bit

    stmia   r7, {r0, r4}            @Save control register values

    @注釋 3:

    b   start_kernel

ENDPROC(__mmap_switched)

注釋1:

    __switch_data這是以個位址。Linker會安排這個位址具體的數值。打開Sysmap可以發現這個數值為:c0008123 t __switch_data

注釋 2:

    将r3所指的内容依次裝入{r4– r6,sp},這個時候sp指針就有了具體的數值了。

注釋 3:

    跳轉指令,指向C函數的start_kernel。這時候棧針開始起效。因為C語言編譯出來的代碼參數傳遞,調用變量儲存等都使用sp指針。這個指針僅僅是給初始化代碼所使用。在程序的概念中還有程序堆棧的概念。這時候的sp具體指向的是描述程序結構的結構體task_info。

2,irq以及其他模式的初始化:

    /*

     * setup stacks for re-entrant exceptionhandlers

     */

    __asm__ (

    "msr    cpsr_c, %1\n\t"

    "add    r14, %0, %2\n\t"

    "mov    sp, r14\n\t"

    "msr    cpsr_c, %3\n\t"

    "add    r14, %0, %4\n\t"

    "msr    cpsr_c, %5\n\t"

    "add    r14, %0, %6\n\t"

    "msr    cpsr_c, %7"

        :

        : "r" (stk),

          PLC (PSR_F_BIT | PSR_I_BIT | IRQ_MODE),

          "I" (offsetof(struct stack,irq[0])),

          PLC (PSR_F_BIT | PSR_I_BIT | ABT_MODE),

          "I" (offsetof(struct stack,abt[0])),

          PLC (PSR_F_BIT | PSR_I_BIT | UND_MODE),

          "I" (offsetof(struct stack,und[0])),

          PLC (PSR_F_BIT | PSR_I_BIT | SVC_MODE)

        : "r14");

    函數:cpu_init()檔案:setup.c

    通過msr設定了cpsr寄存器。然後通過mov指令把具體的參數位址寫入sp寄存器。

其中offsetof(struct stack, irq[0])這個表達式表示的是偏移量。既是在結構體中的偏移量。

其實在這個函數中初始化的irq堆棧隻有4 bytes x 3。這麼小的堆棧空間是否可以滿足中端的需求。答案是:可以。在中端進入的函數中其實并沒有完全使用irq模式下sp_irq指向的堆棧空間。在進入函數中馬上有利用了msr指令進行了模式切換,切換到了svc模式。并且放棄了irq的模式。從中端傳回也是從svc模式傳回,而非irq模式。

代碼:

vector_\name:

    .if \correction

    sub lr, lr, #\correction

    .endif

    @

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

    @ (parent CPSR)

@ 注釋 1:

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

    mrs lr, spsr

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

    @ Prepare for SVC32 mode.  IRQs remain disabled.

    mrs r0, cpsr

    eor r0, r0, #(\mode ^ SVC_MODE) 進入SVC模式

    msr spsr_cxsf, r0

    @ the branch table mustimmediately follow this code

    and lr, lr, #0x0f

    mov r0, sp

    ldr lr, [pc, lr, lsl #2]

    movs    pc, lr         @ branch tohandler in SVC mode

    參照ARM的參考

ENDPROC(vector_\name)

注釋 1 :

儲存irq模式下的sp和lr指針到前面初始的sp_irq中。記住隻有4 bytes x 3大小的空間。在後面的代碼中還會看到str  lr, [sp, #8]儲存了最後一個參數到sp_irq的空間中。這裡要注意:stmia  sp, {r0, lr}這條指令。沒有使用“!”号。這樣一來盡管指令執行後sp指針指向的位址不會自加。是以在正式切換到SVC模式之前sp_irq所指向的位址并沒有變化。這樣再次進入中斷模式時候,sp_irq不需要調整,可以重複使用。

    參照ARM 的晶片設計手冊可以發現,離開異常,從異常情況傳回以後會自動把spsr的内同拷貝到cpsr中。是以在執行BL Lr指令之前使用的堆棧其實并位切換。

這樣一來盡管是中斷的模式進入系統,但是由中斷模式切換至SVC模式。在SVC模式中完成了中斷的後續相應和操作。

文章隻是做了學習筆記已被後用,把這些思路羅列出來也給自己以後再回憶查找提供友善。

如果文章中有什麼不對的地方,還請高手指正。

謝謝

edwardlu

本文轉自張昺華-sky部落格園部落格,原文連結:http://www.cnblogs.com/sky-heaven/p/5567443.html,如需轉載請自行聯系原作者

繼續閱讀