新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > ARM 中斷狀態和SVC狀態的堆棧切換 (異常)

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

        作者: 時間:2016-11-20 來源:網絡 收藏
        基礎知識:

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

        本文引用地址:http://www.104case.com/article/201611/318975.htm

        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/STMIAIncrementAfter (先操作,后增加)

        LDMIB/STMIBIncrementBefore(先增加,后操作)

        LDMDA/STMDADecrementAfter(先操作,后遞減)

        LDMDB/STMDBDecrementBefore(先遞減,后操作)

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

        •LDMFD(Pop) 塊裝載-FullDescending 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寄存器保存的地址是由硬件完成,但是表示的是不同模式下的下一條指令,既返模式切換后的返回地址。

        每一種模式對應不同的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 @ ClearA 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以及其他模式的初始化:

        __asm__ (

        "msr cpsr_c, %1nt"

        "add r14, %0, %2nt"

        "mov sp, r14nt"

        "msr cpsr_c, %3nt"

        "add r14, %0, %4nt"

        "mov sp, r14nt"

        "msr cpsr_c, %5nt"

        "add r14, %0, %6nt"

        "mov sp, r14nt"

        "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_(parent PC) and spsr_

        @ (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]

        @注釋 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不需要調整,可以重復使用。

        注釋 2:

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

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

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

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



        評論


        技術專區

        關閉
        主站蜘蛛池模板: 明星| 新竹市| 农安县| 册亨县| 南澳县| 静乐县| 闽侯县| 杭锦旗| 淮安市| 韶关市| 兴仁县| 淳化县| 晋宁县| 手机| 孟连| 黑水县| 上思县| 红原县| 弋阳县| 金华市| 营口市| 宁陕县| 蓬安县| 大丰市| 阿拉尔市| 永昌县| 孝昌县| 乌拉特后旗| 漳州市| 陇西县| 淳安县| 江北区| 内黄县| 祥云县| 通辽市| 湖北省| 通城县| 龙山县| 广州市| 聂荣县| 黄平县|