uboot跳轉RAM空間問題

該文章轉載於百度文庫中。

都知道 U-BOOT 分爲兩個階段,第一階段是(~/cpu/arm920t/start.S 中)在FLASH上運行(一般情況下),完成對硬件的初始化,包括看門狗,中斷緩存等,並且負責把代碼搬移到SDRAM 中(在搬移的時候檢查自身代碼是否在SDRAM 中),然後完成C 程序運行所需要環境的建立,包括堆棧的初始化等,最後執行一句跳轉指令:
             ldr pc, _start_armboot
             _start_armboot: .word start_armboot,
進入到/lib_arm/board.c 中的函數void start_armboot (void),從此就進入了第二階段。這是在很多資料上都有講述的,所以勿需多言了。現在對於第一階段有幾個問題,以前我一直是沒有搞明白的,既然在FLASH中的代碼是把自己拷貝到SDRAM 中,那麼在S3C2410 的內存地址空間,就有兩份的啓動代碼,第一份就是在FLASH 中,第二份就是在SDRAM 中。根據鏈接腳本文件  (~/board/smdk2410/u-boot.lds)
             OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
             /*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
             OUTPUT_ARCH(arm)
             ENTRY(_start)
            SECTIONS
            {
               . = 0x00000000; /* 後記:這個鏈接起始地址實際上被-Ttest $(TEST_BASE)更新了*/
               . = ALIGN(4);
              .text :
               {
                   cpu/arm920t/start.o (.text)
                  *(.text)
                }
              . = ALIGN(4);
              .rodata : { *(.rodata) }
              . = ALIGN(4);
              .data : { *(.data) }
              . = ALIGN(4);
              .got : { *(.got) }
              . = .;
              __u_boot_cmd_start = .;
            .u_boot_cmd : { *(.u_boot_cmd) }
            __u_boot_cmd_end = .;
            . = ALIGN(4);
           __bss_start = .;
           .bss : { *(.bss) }
           _end = .;
       }
           其中的鏈接命令 . = 0x00000000;表示地址計數器從0 地址開始計數,而且_start 是程序代碼段的入口,那麼*.text 中的所有地址標號(cpu/arm920t/start.S 中定義的)就應該從0 地址開始計數,那麼標號start_armboot(就是void start_armboot (void)函數的入口地址)應該在FLASH中才對啊,所以按照上邊的分析,
           

                ldr pc, _start_armboot
               _start_armboot: .word start_armboot
此條語句後,並沒有跳轉到SDRAM 中的void start_armboot (void),而是跳轉到了FLASH 中的void start_armboot (void)中。
所以就出現了這樣的矛盾,在FLASH 中有一段代碼把自己拷貝到SDRAM 中,產生了兩份UBOOT 可執行的指令流,但是最後卻沒有跳轉到SDRAM 中去運行以提高指令執行的速度。


          產生以上的認識是基於以下幾個認識(肯定是錯誤的):
         1.*.text 中的所有地址標號(在鏈接時確定)是從0 地址開始生成的。實際上在arm-linux-ld 執行時,原來定義的0x0 地址被更新爲TEXT_BASE定義的地址。
        2.relocate: /* relocate U-Boot to RAM */
            adr r0, _start /* r0 <- current position of code */
            ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
            cmp r0, r1 /* don't reloc during  debug */
            beq stack_setup
            ldr r2, _armboot_start
           ldr r3, _bss_start
          sub r2, r3, r2 /* r2 <- size of armboot */
          add r2, r0, r2 /* r2 <- source end address */

         如果不是出於調試階段,這段搬移代碼中的r0 和r1 肯定不相等的,r0=#0,r1=#TEXT_BASE: 0x33F8000(在./board/smdk2410/config.mk 中),所以執行代碼的自身拷貝與搬移。
       注意:在GNU 中:adr r0, _start 作用是獲得 _start 的實際運行所在的地址值,而ldr r1, _TEXT_BASE 爲獲得地址_TEXT_BASE 中所存放的數據,其中adr r0,_start 翻譯成 add r0,(PC+#offset),offset 就是 adr r0, _start 指令到_start 的偏移量,在鏈接時確定,這個偏移量是地址無關的。

                而 ldr r1,_TEXT_BASE 指令表示以程序相對偏移的方式加載數據,是索引偏移加載的另外一種形式,

               等同於ldr r1,[PC+#offset],offset 是 ldr r1, _TEXT_BASE 到_TEXT_BASE 的偏移量。注意這種用法並不是僞指令,僞指令的特徵是 ldr r1,=expr/lable_expr。對於LDR 僞指令,ADS 的情況有些不一樣(細微差別),在ADS中的情況可以參考杜春雷<ARM 體系結構與編程>144 頁。
比較一下:
           add r0,(PC+#offset):(PC+#offset)是相對地址,表示把本指令上溯或下溯offset 處的地址加載到 r0;
           ldr r1,[PC+#offset]:[PC+#offset]也是相對地址,表示把偏移offset 處的地址上的數據加載到 r1;
現在繼續:
           剛纔分析所得到的矛盾,肯定是在認識上存在的偏差,經過把U-BOOT 進行make 後,從所生成的兩個.map 文件來看(~/u-boot.map 和Systen.map),所有的地址標號都是從0x33f80000 開始的,就是從SDRAM 的高地址開始,等於TEXT_BASE 的值,也就是說,鏈接器是從0x33F80000 開始來鏈接所編譯生成的目標文件的,而不是從0 地址開始,經過查看,start_armboot=0x33f80d9c,就是說void start_armboot (void)函數的入口地址在SDRAM 中(鏈接器決定),所
以執行
                  ldr pc, _start_armboot
                _start_armboot: .word start_armboot,
       PC 指針肯定就指向了SDRAM 中,換句話就是說進入到SDRAM 中了,對於ldr pc,_start_armboot,其仍然是GNU 中使用程序相對偏移的方式加載數據,翻譯一下就是ldr pc, [pc+pc 到_start_armboot 的偏移值],結果就把_start_armboot地址中的數start_armboot 放入pc 中完成了跳轉,而 start_armboot 的值(函數地址)是在鏈接時就確定了,是相對於 TEXT_BASE 的。因爲在整個UBOOT 的階段1 中所有的尋址都是相對位置的尋址(雖然鏈接器認爲是階段1 的代碼是從地址0x3ff80000 中開始鏈接的),把階段1 的代碼放在0 地址開始的FLASH 中也是可以正確的運行的,如果ARM 的復位向量是在0x00000001(假設),那麼把代碼燒寫到從0x00000004 處開始的地方,上電時也可以正確的運行(假設ARM 的復位向量是在0x00000004 成立),當然ARM 的復位向量不在這裏,只是以此假設來說明以上的對於階段1 的分析。
現在最後一個矛盾就是鏈接腳本(~/board/smdk2410/u-boot.lds)所描述的鏈接地址與實際的鏈接地址不相同的問題,因爲根據鏈接腳本,所有的地址標號應該從0 地址開始計數的,然而不是。經過查找Makefile 文件,在頂層的Makefile 文件中,在166 行中鏈接是的鏈接命令:
             $(LD) $(LDFLAGS) $$UNDEF_SYM $(OBJS) \,
      其中的LDFLAGS 在定義在頂層的config.mk 中的145 行:LDFLAGS += -Bstatic -T
       $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS),
       最關鍵的就是 -Ttext $(TEXT_BASE)命令了,他的含義就是說,起始地址在TEXT_BASE,而TEXT_BASE 在~/board/smdk2410/config.mk 中TEXT_BASE =0x3FF80000;到此就弄清楚爲什麼鏈接從0x3ff80000 開始的了,至於鏈接腳本,其主要作用是用來指明各個*.o 文件的順序,如入口地址標號(_start)等,以及使兩個地址標號得到當前的地址
             __u_boot_cmd_start = .; *.u_boot_cmd 段的起始地址
             .u_boot_cmd : { *(.u_boot_cmd) }
           __u_boot_cmd_end = .; *.u_boot_cmd 段的結束地址以供C 程序使用。 __u_boot_cmd_start 和__u_boot_cmd_end 可以作爲全局的一個常數使用。
總結:
             因爲-Ttext $(TEXT_BASE)命令的使用,鏈接器把UBOOT 從地址0x3ff80000 開始連接,在第一階段中,所有使用的目標地址尋址都是使用當前PC 值加減偏移量的方法,所以把UBOOT 燒寫到0 地址開始的
FLASH 中,不影響第一階段的正確執行。

發佈了51 篇原創文章 · 獲贊 65 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章