新聞中心

EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > ARM 中斷狀態(tài)和SVC狀態(tài)的堆棧切換 (異常)

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

作者: 時(shí)間:2016-11-20 來(lái)源:網(wǎng)絡(luò) 收藏
基礎(chǔ)知識(shí):

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

本文引用地址:http://2s4d.com/article/201611/318975.htm

R13 Sp 堆棧寄存器

R14 Lr 連接寄存器

R15 PC 程序計(jì)數(shù)器

寄存器尋址

LDMIA R0!,{R1-R4}

執(zhí)行以后的效果

R1 <——[R0]

R2 <——[R0+4]

R3 <——[R0+8]

R4 <——[R0+12]

堆棧尋址:

STMFD入棧指令,相當(dāng)于STMDB

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

[SP-4] <­——R4

[SP-8] <——R3

[SP-12] <——R2

LDMFD出棧指令,相當(dāng)于LDMIA

LDMFD SP!,{R6-R8}

R6 <——[SP]

R7 <——[SP+4]

R8 <——[SP+8]

補(bǔ)充說(shuō)明:

LDMIA/STMIAIncrementAfter (先操作,后增加)

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

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

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

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

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

這些使用規(guī)則以及默認(rèn)的表達(dá)方法是給編譯器使用。但是在開(kāi)發(fā)底層語(yǔ)言的同時(shí),有必要知道這個(gè)么命名的規(guī)則和使用方法。初始化代碼和部分關(guān)鍵代碼是靠匯編實(shí)現(xiàn)。這些代碼的理解不免和匯編打交道。因此了解一下基本的匯編規(guī)則還是很有幫助。

Arm的工作模式:

Arm的工作模式以及相關(guān)寄存器設(shè)置:

1,用戶模式(usr)[10000]:ARM處理器正常的程序執(zhí)行狀態(tài)

2,快速中斷模式(fiq)[10001]:用于高速數(shù)據(jù)傳輸或通道處理

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

4,管理模式(svc)[10011]:操作系統(tǒng)使用的保護(hù)模式

5,中止模式(abt)[10111]:當(dāng)數(shù)據(jù)或指令預(yù)取終止時(shí)進(jìn)入該模式,用于虛擬存

儲(chǔ)及存儲(chǔ)保護(hù)

6,未定義指令模式(und)[11011]:當(dāng)未定義的指令執(zhí)行時(shí)進(jìn)入該模式,用于支持硬件

協(xié)處理器的軟件仿真

7,系統(tǒng)模式(sys)[11111]:運(yùn)行具有特權(quán)模式的操作系統(tǒng)任務(wù)

設(shè)置方法:

MRS R14,CPSR 讀取

MSR CPSR_c, R14 寫(xiě)入

以上幾種模式存在的意義在于不同模式下特殊的幾個(gè)寄存器使用是有區(qū)別的。再svc模式下堆棧指針為sp svc中斷模式下sp指針為 sp irq等。同樣lr連接寄存器的內(nèi)容也是有不同的含義。再不同模式切換中,lr寄存器保存的地址是由硬件完成,但是表示的是不同模式下的下一條指令,既返模式切換后的返回地址。

每一種模式對(duì)應(yīng)不同的bank register。中文官方翻譯不詳。但是每一種模式要擁有自己獨(dú)立的寄存器組。并且每一種模式使用和可見(jiàn)寄存器的數(shù)量也是不相同的。

模式切換過(guò)程中其實(shí)只針對(duì)spsr進(jìn)行操作,而未涉及cpsr是的操作。這個(gè)過(guò)程之所以這樣主要是參考ARM cortex A8的TRM。其中這樣描述離開(kāi)異常的情況:

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.

也就是說(shuō)離開(kāi)異常,從異常情況返回以后會(huì)自動(dòng)把spsr的內(nèi)同拷貝到cpsr中。所以在執(zhí)行BL Lr指令之前使用的堆棧其實(shí)并位切換。

Linux中初始化:

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

堆棧的概念是給C 語(yǔ)言編譯以后的代碼使用,因此從head.S一直到C語(yǔ)言的執(zhí)行,就是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這是以個(gè)地址。Linker會(huì)安排這個(gè)地址具體的數(shù)值。打開(kāi)Sysmap可以發(fā)現(xiàn)這個(gè)數(shù)值為:c0008123 t __switch_data

注釋 2:

將r3所指的內(nèi)容依次裝入{r4– r6,sp},這個(gè)時(shí)候sp指針就有了具體的數(shù)值了。

注釋 3:

跳轉(zhuǎn)指令,指向C函數(shù)的start_kernel。這時(shí)候棧針開(kāi)始起效。因?yàn)镃語(yǔ)言編譯出來(lái)的代碼參數(shù)傳遞,調(diào)用變量保存等都使用sp指針。這個(gè)指針僅僅是給初始化代碼所使用。在進(jìn)程的概念中還有進(jìn)程堆棧的概念。這時(shí)候的sp具體指向的是描述進(jìn)程結(jié)構(gòu)的結(jié)構(gòu)體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");

函數(shù):cpu_init()文件:setup.c

通過(guò)msr設(shè)置了cpsr寄存器。然后通過(guò)mov指令把具體的參數(shù)地址寫(xiě)入sp寄存器。

其中offsetof(struct stack, irq[0])這個(gè)表達(dá)式表示的是偏移量。既是在結(jié)構(gòu)體中的偏移量。

其實(shí)在這個(gè)函數(shù)中初始化的irq堆棧只有4 bytes x 3。這么小的堆??臻g是否可以滿足中端的需求。答案是:可以。在中端進(jìn)入的函數(shù)中其實(shí)并沒(méi)有完全使用irq模式下sp_irq指向的堆??臻g。在進(jìn)入函數(shù)中馬上有利用了msr指令進(jìn)行了模式切換,切換到了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) 進(jìn)入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大小的空間。在后面的代碼中還會(huì)看到str lr, [sp, #8]保存了最后一個(gè)參數(shù)到sp_irq的空間中。這里要注意:stmia sp, {r0, lr}這條指令。沒(méi)有使用“!”號(hào)。這樣一來(lái)盡管指令執(zhí)行后sp指針指向的地址不會(huì)自加。因此在正式切換到SVC模式之前sp_irq所指向的地址并沒(méi)有變化。這樣再次進(jìn)入中斷模式時(shí)候,sp_irq不需要調(diào)整,可以重復(fù)使用。

注釋 2:

參照ARM 的芯片設(shè)計(jì)手冊(cè)可以發(fā)現(xiàn),離開(kāi)異常,從異常情況返回以后會(huì)自動(dòng)把spsr的內(nèi)同拷貝到cpsr中。所以在執(zhí)行BL Lr指令之前使用的堆棧其實(shí)并位切換。

這樣一來(lái)盡管是中斷的模式進(jìn)入系統(tǒng),但是由中斷模式切換至SVC模式。在SVC模式中完成了中斷的后續(xù)相應(yīng)和操作。

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

如果文章中有什么不對(duì)的地方,還請(qǐng)高手指正。



評(píng)論


技術(shù)專(zhuān)區(qū)

關(guān)閉