ARM的啟動代碼(3):異常向量
ARM的啟動代碼(1):介紹
本文引用地址:http://m.butianyuan.cn/article/201611/317543.htmARM的啟動代碼(2):AT91SAM9260啟動詳解
這次我們聊聊ARM的代碼的具體編寫。那么什么樣的代碼會涉及到這些問題呢?
1.Bootloader或者位于啟動序列上進行加載其他應用程序的程序;
2.單獨的二進制鏡像,直接可以在ARM處理器上直接執(zhí)行。
這兩種代碼都需要對ARM的啟動過程有深入理解。說深入理解,其實只有一條,鬧鬧記住,ARM7,ARM9的異常向量表從地址0開始。這是鐵打不能改變的事實。這樣一來,所有的程序都要用0地址存儲自己的向量表,這豈不是成了稀缺資源。所以不同家的ARM芯片都提供了一些辦法解決這種問題。
對于arm7,很多芯片使用片內(nèi)的flash。如at91sam7x256。為了方便,經(jīng)常需要bootloader+應用程序的方式。At91sam7x256提供了一個叫bootMemory的地址,1Mbytes,從0x0~0x000FFFFF??梢杂成涑蔀閮?nèi)部的Flash和內(nèi)部的SRAM。這個映射是:
BootMemory:0x0~0x000FFFFF,1Mbytes
InternalFlash:0x00100000~0x001FFFFF,1Mbytes
InternalSRAM:0x00200000~0x002FFFFF,1Mbytes
當BootMemory映射成InternalFlash,flash的地址仍然從0x00100000開始,但是從0x0訪問,等同于訪問0x00100000;當映射成為InternalSRAM,SRAM地址不變,訪問0x0地址,等同于訪問0x00200000。其實硬件做起來很簡單,就是將地址線用邏輯電路稍微處理一下。
由于代碼都要存放在FLASH里,否則,沒電以后,啥都沒有了,也無法再次啟動。所以,異常向量要從0x0開始,那么自然也要把向量放在這個位置,7x256上電以后默認bootmemory映射從flash開始。也就是說,把向量放在0x00100000即可解決這樣的問題。
當0x00100000被占用以后,bootloader的向量問題解決了,那用戶代碼的中斷向量怎么辦呢?不可能把bootloader的向量擦了,把用戶自己的向量寫入,那豈不是bootloader也完了?這里有個小技巧,如果用戶程序從0x00101000開始,向量依然從這個位置開始。只不過,在打開中斷,向量真正起作用前,將0x00101000這個地方向量到SRAM的首地址上,然后切換BootMemory映射SRAM。那么向量依然從0x0開始。這樣的話,無論多少級boot代碼,都可以完美的解決該問題。
ARM9除了以上的方法,還有個終極的利器,那就是MMU,你高興放哪就放哪,在向量起作用之前,用MMU將其地址變換為0即可。一點技術含量都沒有。
道理總是簡單的,實現(xiàn)起來總是有點點彎彎繞。我們看看實際的實現(xiàn)吧。這是7x256的向量代碼:
__vector:
LDR PC,[PC,#24];Absolutejumpcanreach4GByte
LDRPC,[PC,#24];Branchtoundef_handler
LDRPC,[PC,#24];Branchtoswi_handler
LDRPC,[PC,#24];Branchtoprefetch_handler
LDRPC,[PC,#24];Branchtodata_handler
DC320;Reserved
LDR PC,[PC,#24] ;Branchtoirq_handler
LDR PC,[PC,#24] ;Branchtofiq_handler
DC32_program_start
DC32ARM_ExceptUndefInstrHndlr
DC32ARM_ExceptSwiHndlr
DC32ARM_ExceptPrefetchAbortHndlr
DC32ARM_ExceptDataAbortHndlr
DC320
DC32ARM_ExceptIrqHndlr
DC32ARM_ExceptFiqHndlr
這里相對比較簡單,對這個指令做一下解釋。LDR PC,[PC,#24]是將當前PC+24的地址的值載入到PC寄存器中。由于ARM流水線的問題,當前執(zhí)行的指令,地址已經(jīng)是后面兩條了。所以,是+24并不是+32。也就是把_program_start加載入PC指針里。這段代碼已經(jīng)消除了指令當前位置對跳轉(zhuǎn)位置的影響,可以隨意的拷貝到任意的地方去執(zhí)行。這段代碼放在0x200000地方,可以正常執(zhí)行;放在0x100000地方也可以正常執(zhí)行。
RTEMS的ARM9(CSB337)啟動向量:
vector_block:
ldrpc,Reset_Handler
ldrpc,Undefined_Handler
ldrpc,SWI_Handler
ldrpc,Prefetch_Handler
ldrpc,Abort_Handler
nop
ldrpc,IRQ_Handler
ldrpc,FIQ_Handler
Reset_Handler:bbsp_reset
Undefined_Handler:bUndefined_Handler
SWI_Handler:bSWI_Handler
Prefetch_Handler:bPrefetch_Handler
Abort_Handler:bAbort_Handler
nop
IRQ_Handler:bIRQ_Handler
FIQ_Handler:bFIQ_Handler
Rtems是個復雜的操作系統(tǒng),在匯編代碼里安裝的只是一個簡單的復位向量。其它向量都只是簡單的死循環(huán)。操作系統(tǒng)運行起來以后,還要再次安裝向量的。向量指向操作系統(tǒng)的復雜的處理函數(shù)。但不管這些,向量存儲的地址是沒有改變的。Link腳本上可以看到向量被放在內(nèi)部的SRAM的首地址上。(CSB337)
SECTIONS
{
.base:
{
_sram_base=.;
/*reserveroomforthevectorsandfunctionpointers*/
arm_exception_table=.;
.+=64;
連接器雖然把位置空出來了,但連接器依然不知道將vector_block放到什么位置。怎么辦?這里的代碼解釋了一切。
/*
*InitializetheMMU.Afterwereturn,theMMUisenabled,
*andmemorymayberemapped.Ihopewedontremapthis
*memoryaway.
*/
ldrr0,=mem_map
blmmu_init
/*
*Initializetheexceptionvectors.Thisincludesthe
*exceptionsvectors(0x00000000-0x0000001c),andthe
*pointerstotheexceptionhandlers(0x00000020-0x0000003c).
*/
movr0,#0
adrr1,vector_block
ldmiar1!,{r2-r9}
stmiar0!,{r2-r9}
ldmiar1!,{r2-r9}
stmiar0!,{r2-r9}
Gnu的工具鏈并不針對某一個具體的平臺。所以解決方案從某種意義上說,更具有普遍意義。先調(diào)用mmu_init,這是干什么,實際上是將MMU初始化,將我們定義的.base地址放到0x0位置去。然后緊接著下面的幾行代碼,是將上面的中斷向量到0x0位置去。一共64個字節(jié),實現(xiàn)4GB內(nèi)的地址絕對跳轉(zhuǎn)。
評論