周立功lpc21xx/lpc22xx系列ARM7啟動(dòng)代碼分析
所以還是做一個(gè)ARM7的啟動(dòng)代碼分析吧, 網(wǎng)上那一份相比,我這個(gè)主要關(guān)注startup.s文件.網(wǎng)上那個(gè)startup.s幾乎是一筆帶過(guò)的.
本文引用地址:http://butianyuan.cn/article/201611/317260.htm紅色標(biāo)記的是源碼.
SVC_STACK_LEGTH EQU 0
FIQ_STACK_LEGTH EQU 0
IRQ_STACK_LEGTH EQU 256
ABT_STACK_LEGTH EQU 0
UND_STACK_LEGTH EQU 0
NoInt EQU 0x80
USR32Mode EQU 0x10
SVC32Mode EQU 0x13
SYS32Mode EQU 0x1f
IRQ32Mode EQU 0x12
FIQ32Mode EQU 0x11
上面幾行代碼,不用過(guò)多分析, 定義幾個(gè)符號(hào)而已, 把EQU想像成C中的#define就可以了. 具體定義的數(shù)值,下面的代碼用到我再解釋.
IMP
上面這一句的作用是在代碼中禁用 semihosting 機(jī)制. 到底什么是semihostiong這里不多說(shuō), 網(wǎng)上有很多. 這里只說(shuō)明Semihosting主要用來(lái)調(diào)試, 在release版本的代碼中一般是要禁用的.
IMP
IMP
IMP
上面三行是把要引入的外部標(biāo)號(hào)聲明一下,以便下面使用.
EXPORT bottom_of_heap
EXPORT StackUsr
EXPORT Reset
EXPORT __user_initial_stackheap
上面四行是把要給其它文件使用的標(biāo)號(hào)聲明
AREA vectors,CO
ENTRY
上面這一行聲明匯編文件的入口, 整個(gè)文件是從這里開(kāi)始執(zhí)行的.
Reset
LDR PC, ResetAddr
LDR PC, UndefinedAddr
LDR PC, SWI_Addr
LDR PC, PrefetchAddr
LDR PC, DataAbortAddr
DCD 0xb9205f80
LDR PC, [PC, #-0xff0]
LDR PC, FIQ_Addr
上面幾行是配置中斷向量表. 中斷向量表的順序是不能變的,因?yàn)檫@是ARM7規(guī)定的,可以參考相關(guān)書(shū)籍. 這里有幾個(gè)問(wèn)題要說(shuō)明一下.
第一, 關(guān)于DCD 0xb9205f80, 按照ARM7的中斷向量表分布圖, 這個(gè)位置是個(gè)保留位. 但是究竟為什么要用0xb9205f80這個(gè)數(shù)值呢.
根據(jù)周立功的說(shuō)法, nxp系列的lpc21xx,lpc22xx片子要求"中斷向量表中所有數(shù)據(jù)32位累加和為0,否則程序不能脫機(jī)運(yùn)行", 我在AXD反匯編了一下(如下圖),把中斷向量表中的8個(gè)機(jī)器碼累加了一下:0xe59ff018*6+0xe51ffff0+0xb9205f80,沒(méi)錯(cuò), 結(jié)果是零. 但是我遇到一個(gè)問(wèn)題, 就是我在實(shí)驗(yàn)中,把0xb9205f80這個(gè)數(shù)值改成任何值,程序運(yùn)行都沒(méi)問(wèn)題. 頭大了, 這個(gè)問(wèn)題待解決中……(希望高手看到了可以指點(diǎn)一二).
第二,關(guān)于LDR PC, [PC, #-0xff0].這里本應(yīng)該放IRQ中斷的, 為什么是這么一句話. 其實(shí)在我blog的其中一篇文章里有提到過(guò)這一點(diǎn).
ARM7的三級(jí)流水線結(jié)構(gòu)導(dǎo)致了PC指向的是當(dāng)前指令的后8個(gè)字節(jié). 本來(lái)IRQ是應(yīng)該放在0x00000018處的. LDR PC, [PC, #-0xff0]這條語(yǔ)句執(zhí)行后, PC的當(dāng)前值就是0x00000018+8-0xff0. 很容易計(jì)算出它的結(jié)果是0xfffff030. 看一下lpc22xx的手冊(cè)就知道. 這個(gè)地址就是VICVectAddr. 也就是說(shuō)本來(lái)這個(gè)地址是應(yīng)該放IRQ服務(wù)程序的入口地址的,但是這個(gè)地址被放在了VICVectAddr 這個(gè)寄存器里. 英文手冊(cè)里有一段對(duì)VICVectAddr 描述. 看了之后就容易明白是怎么回事了: Vector Address Register. When an IRQ interrupt occurs, the IRQ service routine can read this register and jump to the value read
ResetAddr DCD ResetInit
UndefinedAddr DCD Undefined
SWI_Addr DCD SoftwareInterrupt
PrefetchAddr DCD PrefetchAbort
DataAbortAddr DCD DataAbort
Nouse DCD 0
IRQ_Addr DCD 0
FIQ_Addr DCD FIQ_Handler
這幾行是為上面中斷向量表中的中斷標(biāo)號(hào)分配內(nèi)存空間, 也就是它們的執(zhí)行地址. 一開(kāi)始我有個(gè)疑問(wèn), 為什么不直接用LDR PC, ResetInit,還要用DCD中轉(zhuǎn)一下, 后來(lái)上網(wǎng)查了一下,才恍然大悟, ldr指令中的地址必須為當(dāng)前指令地址是4KB范圍內(nèi), 用DCD中轉(zhuǎn)一下就可以在整個(gè)程序空間尋址.
Undefined
B Undefined
SoftwareInterrupt
B SoftwareInterrupt
PrefetchAbort
B PrefetchAbort
DataAbort
B DataAbort
FIQ_Handler
STMFD SP!, {R0-R3, LR}
BL FIQ_Exception
LDMFD SP!, {R0-R3, LR}
SUBS PC, LR, #4
這幾行不用過(guò)多解釋, 只是說(shuō)明上面幾個(gè)異常如何執(zhí)行.
InitStack
MOV R0, LR
;設(shè)置管理模式堆棧
MSR CPSR_c, #0xd3
LDR SP, StackSvc
;設(shè)置中斷模式堆棧
MSR CPSR_c, #0xd2
LDR SP, StackIrq
;設(shè)置快速中斷模式堆棧
MSR CPSR_c, #0xd1
LDR SP, StackFiq
;設(shè)置中止模式堆棧
MSR CPSR_c, #0xd7
LDR SP, StackAbt
;設(shè)置未定義模式堆棧
MSR CPSR_c, #0xdb
LDR SP, StackUnd
;設(shè)置系統(tǒng)模式堆棧
MSR CPSR_c, #0xdf
LDR SP, =StackUsr
MOV PC, R0
上面是一個(gè)子函數(shù), 函數(shù)名為InitStack. 顧名思意, 這個(gè)函數(shù)設(shè)置ARM七種工作模式下的堆棧. 關(guān)于這一段代碼有三點(diǎn)要說(shuō).
第一,MSR CPSR_c, #0xdf, 這一句把ARM的工作模式設(shè)置為系統(tǒng)模式,或者也可以說(shuō)是用戶模式, 因?yàn)橄到y(tǒng)模式與用戶模式是共享相同的寄存器組. 用0xdf對(duì)CPSR寄存器賦值,就把IRQ中斷關(guān)閉了(可以查一下CRSR的詳細(xì)說(shuō)明), 代碼正常執(zhí)行時(shí)處理器是處在用戶模式的,所以IRQ中斷是不會(huì)執(zhí)行的. 所以,如果用周立功的這個(gè)啟動(dòng)代碼,當(dāng)你的程序中需要中斷時(shí),要把0xdf改成0x5f. 之前看到很多人在網(wǎng)上說(shuō)用周立功的ADS工程模板,進(jìn)不了中斷,很多情況下是這個(gè)原因.
第二, 并不是每一種模式下的堆棧都用設(shè)置的, 比如說(shuō)如果你的程序中不會(huì)用到FIQ,就可以不用設(shè)置快速中斷下的堆棧.
第三, 注意LDR SP, =StackUsr這個(gè)語(yǔ)句, 其它都是沒(méi)有=號(hào)的, 為什么這個(gè)要用等號(hào)呢? 這就是LDR偽指令與LDR指令的區(qū)別了,LDR SP, =StackUsr是把StackUsr表示的地址裝載到sp,LDR SP, StackUnd是把StackUnd表示地址的內(nèi)容裝載到sp,注意下面幾句
StackSvc DCD SvcStackSpace + (SVC_STACK_LEGTH - 1)* 4
StackIrq DCD IrqStackSpace + (IRQ_STACK_LEGTH - 1)* 4
StackFiq DCD FiqStackSpace + (FIQ_STACK_LEGTH - 1)* 4
StackAbt DCD AbtStackSpace + (ABT_STACK_LEGTH - 1)* 4
StackUnd DCD UndtStackSpace + (UND_STACK_LEGTH - 1)* 4
可以看到,沒(méi)有”=”的標(biāo)號(hào)都已經(jīng)用DCD初始化了, 而StackUsr到底是什么呢, 它是由下面的語(yǔ)句決定的
(startup.s文件)
AREA Stacks, DA
StackUsr
(分散加載文件)
STACKS 0x40002000 UNINIT
{
Startup.o (Stacks)
}
這樣就明白了, StackUsr肯定是0x40000000~0x400020000之間的某個(gè)數(shù). 用戶模式下的堆??臻g就是它了.
ResetInit
BL InitStack
BL TargetResetInit
B __main
處理器上電復(fù)位后通過(guò)中斷向量表進(jìn)入該函數(shù),__main函數(shù)主要工作是初始化C的庫(kù)函數(shù), 并由它進(jìn)入C的main函數(shù).
__user_initial_stackheap
LDR r0,=bottom_of_heap
; LDR r1,=StackUsr
MOV pc,lr
__user_initial_stackheap函數(shù)是ADS的一個(gè)庫(kù)函數(shù), 如果程序中用到的分散加載文件, 這個(gè)函數(shù)必須要被實(shí)現(xiàn). 應(yīng)用程序的棧和heap是在C庫(kù)函數(shù)初始化過(guò)程中建立起來(lái)的??梢酝ㄟ^(guò)重定向?qū)?yīng)的子程序來(lái)改變堆棧和heap的位置. 堆棧的地址在分散加載文件里已經(jīng)指定好,本函數(shù)不應(yīng)該修改它們的值. 用r0,r1分別返回heap和stack的基址. 關(guān)于ADS的存儲(chǔ)器機(jī)制大家可以去網(wǎng)上查更詳細(xì)的資料.
StackSvc DCD SvcStackSpace + (SVC_STACK_LEGTH - 1)* 4
StackIrq DCD IrqStackSpace + (IRQ_STACK_LEGTH - 1)* 4
StackFiq DCD FiqStackSpace + (FIQ_STACK_LEGTH - 1)* 4
StackAbt DCD AbtStackSpace + (ABT_STACK_LEGTH - 1)* 4
StackUnd DCD UndtStackSpace + (UND_STACK_LEGTH - 1)* 4
AREA MyStacks, DA
SvcStackSpace SPACE SVC_STACK_LEGTH * 4 ;Stack spaces for Administration Mode
IrqStackSpace SPACE IRQ_STACK_LEGTH * 4 ;Stack spaces for Interrupt ReQuest Mode
FiqStackSpace SPACE FIQ_STACK_LEGTH * 4 ;Stack spaces for Fast Interrupt reQuest Mode
AbtStackSpace SPACE ABT_STACK_LEGTH * 4 ;Stack spaces for Suspend Mode
UndtStackSpace SPACE UND_STACK_LEGTH * 4 ;Stack spaces for Undefined Mode
上面幾行代碼是為各個(gè)模式下的堆棧分配空間. 其中MyStacksA的位置會(huì)在分散加載文件中指定.
IF :DEF: EN_CRP
IF . >= 0x1fc
INFO 1,"nThe da
ENDIF
CrpData
WHILE . < 0x1fc
NOP
WEND
CrpData1
DCD 0x87654321 ;/*When the Da
ENDIF
上面這幾行其實(shí)是加密芯片用的, lpc21xx和lpc22xx系列的ARM7,當(dāng)你的工程選擇RelInFlash時(shí), 代碼寫進(jìn)flash,芯片也同時(shí)被加密, 加密狀態(tài)下JTAG也讀不到芯片, 也不能單步調(diào)試, 要解密的話必須要用ISP完全擦除一下. 上面的代碼的意思就是在地址0x1fc處放數(shù)據(jù)0x87654321, 從而實(shí)現(xiàn)加密的功能, 但前提是IF :DEF: EN_CRP, 也就是定義了EN_CPP這個(gè)宏. 而這個(gè)宏是在當(dāng)選擇了RelInFlash時(shí)ADS自動(dòng)定義的. 然后,再說(shuō)一下0x87654321的問(wèn)題. LPC2100 系列ARM7微控制器是世界首款可加密的ARM芯片,對(duì)其加密的方法是通過(guò)用戶程序在指定地址上設(shè)置規(guī)定的數(shù)據(jù)。PHILIPS公司規(guī)定,對(duì)于 LPC2100芯片(除LPC2106/2105/2104外),當(dāng)片內(nèi)FLASH地址0x000001FC處的數(shù)據(jù)為0x87654321時(shí),芯片即被加密. 所以問(wèn)題搞定.
評(píng)論