天天看點

arm926t 異常向量的映射,(如何從0位址到0x20000000位址)

ARM 的 啟動代碼 是非常重要的代碼,直接關系到系統的穩定性和可靠性(這裡主要讨論 arm 7, arm9;cortex系列的會在後續的文章中讨論)。上次我們通過兩則文章讨論了ARM啟動代碼的過程,

ARM的啟動代碼(1):介紹

ARM的啟動代碼(2):AT91SAM9260啟動詳解

 這次我們聊聊ARM的代碼的具體編寫。那麼什麼樣的代碼會涉及到這些問題呢?

1.Bootloader或者位于啟動序列上進行加載其他應用程式的程式;

2.單獨的二進制鏡像,直接可以在ARM處理器上直接執行。

這兩種代碼都需要對 ARM 的啟動過程有深入了解。說深入了解,其實隻有一條,鬧鬧記住,ARM7,ARM9的異常向量表從位址0開始。這是鐵打不能改變的事實。這樣一來,所有的程式都要用0位址存儲自己的向量表,這豈不是成了稀缺資源。是以不同家的ARM晶片都提供了一些辦法解決這種問題。

對于arm7,很多晶片使用片内的flash。如at91sam7x256。為了友善,經常需要bootloader + 應用程式的方式。At91sam7x256提供了一個叫boot Memory的位址,1Mbytes, 從0x0~0x000F FFFF。可以映射成為内部的Flash和内部的SRAM。這個映射是:

Boot Memory: 0x0~0x000F FFFF, 1Mbytes

Internal Flash: 0x0010 0000~0x001F FFFF, 1Mbytes

Internal SRAM:0x0020 0000~0x002F FFFF, 1Mbytes

當Boot Memory映射成Internal Flash,flash的位址仍然從0x0010 0000開始,但是從0x0通路,等同于通路0x0010 0000;當映射成為 Internal SRAM,SRAM位址不變,通路0x0位址,等同于通路0x0020 0000。其實硬體做起來很簡單,就是将位址線用邏輯電路稍微處理一下。

由于代碼都要存放在FLASH裡,否則,沒電以後,啥都沒有了,也無法再次啟動。是以,異常向量要從0x0開始,那麼自然也要把向量放在這個位置,7x256上電以後預設boot memory 映射從flash開始。也就是說,把向量放在0x0010 0000即可解決這樣的問題。

當0x0010 0000被占用以後,bootloader 的向量問題解決了,那使用者代碼的中斷向量怎麼辦呢?不可能把bootloader的向量擦了,把使用者自己的向量寫入,那豈不是bootloader也完了?這裡有個小技巧,如果使用者程式從0x0010 1000開始,向量依然從這個位置開始。隻不過,在打開中斷,向量真正起作用前,将0x0010 1000這個地方向量到SRAM的首位址上,然後切換Boot Memory映射SRAM。那麼向量依然從0x0開始。這樣的話,無論多少級boot代碼,都可以完美的解決該問題。

ARM9除了以上的方法,還有個終極的利器,那就是MMU,你高興放哪就放哪,在向量起作用之前,用MMU将其位址變換為0即可。一點技術含量都沒有。

道理總是簡單的,實作起來總是有點點彎彎繞。我們看看實際的實作吧。這是7x256的向量代碼:

__vector:

    LDR     PC, [PC,#24]    ; Absolute jump can reach 4 GByte

    LDR     PC, [PC,#24]    ; Branch to undef_handler

    LDR     PC, [PC,#24]    ; Branch to swi_handler

    LDR     PC, [PC,#24]    ; Branch to prefetch_handler

    LDR     PC, [PC,#24]    ; Branch to data_handler

    DC32    0               ; Reserved

    LDR     PC, [PC,#24] ; Branch to irq_handler

    LDR     PC, [PC,#24] ; Branch to fiq_handler

    DC32    _program_start

    DC32    ARM_ExceptUndefInstrHndlr

    DC32    ARM_ExceptSwiHndlr

    DC32    ARM_ExceptPrefetchAbortHndlr

    DC32    ARM_ExceptDataAbortHndlr

    DC32    0

    DC32    ARM_ExceptIrqHndlr

DC32    ARM_ExceptFiqHndlr

這裡相對比較簡單,對這個指令做一下解釋。LDR     PC, [PC,#24]是将目前PC+24的位址的值載入到PC寄存器中。由于ARM流水線的問題,目前執行的指令,位址已經是後面兩條了。是以,是+24并不是+32。也就是把_program_start加載入PC指針裡。這段代碼已經消除了指令目前位置對跳轉位置的影響,可以随意的拷貝到任意的地方去執行。這段代碼放在0x20 0000地方,可以正常執行;放在0x10 0000地方也可以正常執行。

RTEMS的ARM9(CSB337)啟動向量:

vector_block:

        ldr     pc, Reset_Handler

        ldr     pc, Undefined_Handler

        ldr     pc, SWI_Handler

        ldr     pc, Prefetch_Handler

        ldr     pc, Abort_Handler

        nop

        ldr     pc, IRQ_Handler

        ldr     pc, FIQ_Handler

Reset_Handler:           b       bsp_reset

Undefined_Handler:      b       Undefined_Handler

SWI_Handler:             b       SWI_Handler

Prefetch_Handler:       b       Prefetch_Handler

Abort_Handler:           b       Abort_Handler

                        nop

IRQ_Handler:            b       IRQ_Handler

FIQ_Handler:            b       FIQ_Handler

Rtems是個複雜的作業系統,在彙編代碼裡安裝的隻是一個簡單的複位向量。其它向量都隻是簡單的死循環。作業系統運作起來以後,還要再次安裝向量的。向量指向作業系統的複雜的處理函數。但不管這些,向量存儲的位址是沒有改變的。Link腳本上可以看到向量被放在内部的SRAM的首位址上。(CSB337)

SECTIONS

{

    .base :

    {

        _sram_base = .;

arm_exception_table = .;    

. += 64;

連接配接器雖然把位置空出來了,但連接配接器依然不知道将vector_block放到什麼位置。怎麼辦?這裡的代碼解釋了一切。

        ldr     r0, =mem_map

        bl      mmu_init

        mov     r0, #0

        adr     r1, vector_block

        ldmia   r1!, {r2-r9}

        stmia   r0!, {r2-r9}

        ldmia   r1!, {r2-r9}

        stmia   r0!, {r2-r9}

Gnu的工具鍊并不針對某一個具體的平台。是以解決方案從某種意義上說,更具有普遍意義。先調用mmu_init,這是幹什麼,實際上是将MMU初始化,将我們定義的.base位址放到0x0位置去。然後緊接着下面的幾行代碼,是将上面的中斷向量到0x0位置去。一共64個位元組,實作4GB内的位址絕對跳轉。

關鍵字:ARM  啟動代碼  異常向量

iot

繼續閱讀