基礎(chǔ)知識:Arm的寄存器使用規(guī)則以及尋址指令:
本文引用地址:http://m.butianyuan.cn/article/201611/318975.htmR13 Sp 堆棧寄存器
R14 Lr 連接寄存器
R15 PC 程序計數(shù)器
多寄存器尋址:
LDMIA R0!,{R1-R4}
執(zhí)行以后的效果
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]
這些使用規(guī)則以及默認的表達方法是給編譯器使用。但是在開發(fā)底層語言的同時,有必要知道這個么命名的規(guī)則和使用方法。初始化代碼和部分關(guān)鍵代碼是靠匯編實現(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)使用的保護模式
5,中止模式(abt)[10111]:當數(shù)據(jù)或指令預(yù)取終止時進入該模式,用于虛擬存
儲及存儲保護
6,未定義指令模式(und)[11011]:當未定義的指令執(zhí)行時進入該模式,用于支持硬件
協(xié)處理器的軟件仿真
7,系統(tǒng)模式(sys)[11111]:運行具有特權(quán)模式的操作系統(tǒng)任務(wù)
設(shè)置方法:
MRS R14,CPSR 讀取
MSR CPSR_c, R14 寫入
以上幾種模式存在的意義在于不同模式下特殊的幾個寄存器使用是有區(qū)別的。再svc模式下堆棧指針為sp svc中斷模式下sp指針為 sp irq等。同樣lr連接寄存器的內(nèi)容也是有不同的含義。再不同模式切換中,lr寄存器保存的地址是由硬件完成,但是表示的是不同模式下的下一條指令,既返模式切換后的返回地址。
每一種模式對應(yīng)不同的bank register。中文官方翻譯不詳。但是每一種模式要擁有自己獨立的寄存器組。并且每一種模式使用和可見寄存器的數(shù)量也是不相同的。
模式切換過程中其實只針對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的內(nèi)同拷貝到cpsr中。所以在執(zhí)行BL Lr指令之前使用的堆棧其實并位切換。
Linux中初始化:
1, Svc模式的堆棧初始化:
堆棧的概念是給C 語言編譯以后的代碼使用,因此從head.S一直到C語言的執(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這是以個地址。Linker會安排這個地址具體的數(shù)值。打開Sysmap可以發(fā)現(xiàn)這個數(shù)值為:c0008123 t __switch_data
注釋 2:
將r3所指的內(nèi)容依次裝入{r4– r6,sp},這個時候sp指針就有了具體的數(shù)值了。
注釋 3:
跳轉(zhuǎn)指令,指向C函數(shù)的start_kernel。這時候棧針開始起效。因為C語言編譯出來的代碼參數(shù)傳遞,調(diào)用變量保存等都使用sp指針。這個指針僅僅是給初始化代碼所使用。在進程的概念中還有進程堆棧的概念。這時候的sp具體指向的是描述進程結(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
通過msr設(shè)置了cpsr寄存器。然后通過mov指令把具體的參數(shù)地址寫入sp寄存器。
其中offsetof(struct stack, irq[0])這個表達式表示的是偏移量。既是在結(jié)構(gòu)體中的偏移量。
其實在這個函數(shù)中初始化的irq堆棧只有4 bytes x 3。這么小的堆??臻g是否可以滿足中端的需求。答案是:可以。在中端進入的函數(shù)中其實并沒有完全使用irq模式下sp_irq指向的堆??臻g。在進入函數(shù)中馬上有利用了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]保存了最后一個參數(shù)到sp_irq的空間中。這里要注意:stmia sp, {r0, lr}這條指令。沒有使用“!”號。這樣一來盡管指令執(zhí)行后sp指針指向的地址不會自加。因此在正式切換到SVC模式之前sp_irq所指向的地址并沒有變化。這樣再次進入中斷模式時候,sp_irq不需要調(diào)整,可以重復(fù)使用。
注釋 2:
參照ARM 的芯片設(shè)計手冊可以發(fā)現(xiàn),離開異常,從異常情況返回以后會自動把spsr的內(nèi)同拷貝到cpsr中。所以在執(zhí)行BL Lr指令之前使用的堆棧其實并位切換。
這樣一來盡管是中斷的模式進入系統(tǒng),但是由中斷模式切換至SVC模式。在SVC模式中完成了中斷的后續(xù)相應(yīng)和操作。
文章只是做了學(xué)習(xí)筆記已被后用,把這些思路羅列出來也給自己以后再回憶查找提供方便。
如果文章中有什么不對的地方,還請高手指正。
評論