ti-sdk-evm-am335x-05.07 uboot分析(MLO跳轉到u-boot之前)


--------------------------------------------------------------------------------
                         ROM CODE怎樣從MMC啟動
--------------------------------------------------------------------------------



ROM code將boot parameters的結構體指針通過R0寄存器傳遞給MLO,在start.s的reset部分,第一句指令就是:
bl save_boot_params.
5.07版本號中相關代碼比較復雜。而7.00中就相對簡單:
就是將R0的數據存儲到r1所指的地址中,只存儲了一個指針就返回了。


-------------------------------------------------------------------------
                             Start.s  Start
-------------------------------------------------------------------------
位於arch/arm/cpu/armv7/start.s

上電直接跳到reset異常處理例程

調用save_boot_params,保存boot參數。不詳述。

continue...


cpy_clk_code是將DPLL的代碼復制到SRAM中,暫不述。
cpu_init_crit僅僅在SPL階段會被調用,用於setup important registers and memory timing.

接着調用lowlevel_init函數,位於arch/arm/cpu/armv7/omap-common/lowlevel_init.s文件。
從名字能夠看出,該文件用於底層的初始化。


--------------------------------------------------------------------------------
                    lowlevel_init Start(lowlevel_init.s)
--------------------------------------------------------------------------------


這里首先建立一個暫時的棧,用於執行接下來c程序s_init。

當中LOW_LEVEL_SRAM_STACK定義在asm/arch/omap.h文件中面。


而0x4030B7FC位於MPU的L3 OCMC0區域。

調用的函數s_init。位於board/ti/am335x/evm.c下。

在s_init中有幾步非常關鍵

1、  preloader_console_init()用於初始化串口,調用serial_init(),該函數位於common/serial.c文件中面。

serial_init()函數首先檢查當前有沒有串口,顯然沒有,就僅僅能用 默認串口 default_serial_console結構。該結構定義在drivers/serial/serial.c中。


當中CONFIG_CONS_INDEX宏定義在include/configs/am335x_evm.h,定義為1表示使用UART0口。
這塊是移植時候須要注意的地方。否則調試u-boot時候在默認的串口上不會信息打印。

2、 enable_i2c0_pin_mux() i2c_init()以及read_eeprom(),這三個函數主要適用於TI公司AM335x相關的板子。
     這一系列板子上面都有EEPROM而且存儲了板子的信息。read_eeprom用於讀取相關信息。
s_init Continue...

定義變量is_ddr3用於選擇初始化的對象,這也是移植時候須要注意的地方。
--------------------------------------------------------------------------------
                     lowlevel_init End(lowlevel_init.s)
--------------------------------------------------------------------------------


--------------------------------------------------------------------------------
                     call_board_init_f Start(start.s)
--------------------------------------------------------------------------------
Back to start.s
調用完cpu_init_crit后,start.s會繼續。接着會調用board_init_f(),在調用之前會在內部SRAM中設立棧指針,
這樣才干調用函數。由於函數調用須要入棧的操作。

1、棧指針值CONFIG_SYS_INIT_SP_ADDR
start.s又包括 config.h 文件(位於am335x/include/config.h,am335x是編譯時候生成的,Makefile將與目標板相關的文件集中在此處)
config.h文件內容:

在configs/am335x_evm.h中定義了CONFIG_SYS_INIT_SP_ADDR:

即CONFIG_SYS_INIT_SP_ADDR = SRAM0_START + SRAM0_SIZE - GENERATED_GBL_DATA_SIZE
當中 SRAM0_START定義在hardware.h中,進入后可知 SRAM0_START = 0x402F0400,正是Memory Map中SRAM internel的首地址。



SRAM0_SIZE定義在cpu.h中。進入后可知 SRAM0_SIZE = 0x1B400,這個SIZE一直將SRAM延伸到OCMC0里面,結束地址為0x4030B800。



GENERATED_GBL_DATA_SIZE定義在generic-asm-offset.h(asm-offset.h包括此頭文件)中。值為128。
這個size的空間是用於存儲global_data結構的數據,存儲空間以16字節對齊(這個並未證明,由於代碼里面沒有在不論什么地方提到將全局變量指針指向此地)。

總結一下 。到這一步,在MPU的內部OCMC0區設置棧底SP = 0x4030B800 - 128 =  0x4030B780 ,並在棧底上面存儲分配128 Bytes空間用於存儲全局數據。
2、MLO重定位
board_init_f函數位於arch/arm/cpu/armv7/omap-common/spl.c。該函數唯一做的事情就是向relocate_code
函數傳入三個參數。然后程序流程又回到start.s文件。

在board_init_f()函數調用start.s中的relocate_code函數。這里實現重定位(實現機理能夠百度之),
重定位后。后面的puts不會執行了。由於relocate_code不會返回。見下圖。


--------------------------------------------------------------------------------
                              relocate_code Start(start.s) 
--------------------------------------------------------------------------------
relocate_code的代碼例如以下:
/*
*
 void relocate_code (addr_sp, gd, addr_moni)
*
* This "function" does not return, instead it continues in RAM
* after relocating the monitor code.
*
*/
     .globl     relocate_code
relocate_code:
     mov     r4, r0     /* save addr_sp */
     mov     r5, r1     /* save addr of gd */
     mov     r6, r2     /* save addr of destination */

     /* Set up the stack                                  */
stack_setup:
     mov     sp, r4

     adr     r0, _start
     cmp     r0, r6
     moveq     r9, #0          /* no relocation. relocation offset(r9) = 0 */
     beq     clear_bss          /* skip relocation */
     mov     r1, r6               /* r1 <- scratch for copy_loop */
     ldr     r3, _image_copy_end_ofs
     add     r2, r0, r3          /* r2 <- source end address         */

copy_loop:
     ldmia     r0!, {r9-r10}          /* copy from source address [r0]    */
     stmia     r1!, {r9-r10}          /* copy to   target address [r1]    */
     cmp     r0, r2               /* until source end address [r2]    */
     blo     copy_loop

#ifndef CONFIG_SPL_BUILD
     /*
     * fix .rel.dyn relocations
     */
     ldr     r0, _TEXT_BASE          /* r0 <- Text base */
     sub     r9, r6, r0          /* r9 <- relocation offset */
     ldr     r10, _dynsym_start_ofs     /* r10 <- sym table ofs */
     add     r10, r10, r0          /* r10 <- sym table in FLASH */
     ldr     r2, _rel_dyn_start_ofs     /* r2 <- rel dyn start ofs */
     add     r2, r2, r0          /* r2 <- rel dyn start in FLASH */
     ldr     r3, _rel_dyn_end_ofs     /* r3 <- rel dyn end ofs */
     add     r3, r3, r0          /* r3 <- rel dyn end in FLASH */
fixloop:
     ldr     r0, [r2]          /* r0 <- location to fix up, IN FLASH! */
     add     r0, r0, r9          /* r0 <- location to fix up in RAM */
     ldr     r1, [r2, #4]
     and     r7, r1, #0xff
     cmp     r7, #23               /* relative fixup?

*/
     beq     fixrel
     cmp     r7, #2               /* absolute fixup?

*/
     beq     fixabs
     /* ignore unknown type of fixup */
     b     fixnext
fixabs:
     /* absolute fix: set location to (offset) symbol value */
     mov     r1, r1, LSR #4          /* r1 <- symbol index in .dynsym */
     add     r1, r10, r1          /* r1 <- address of symbol in table */
     ldr     r1, [r1, #4]          /* r1 <- symbol value */
     add     r1, r1, r9          /* r1 <- relocated sym addr */
     b     fixnext
fixrel:
     /* relative fix: increase location by offset */
     ldr     r1, [r0]
     add     r1, r1, r9
fixnext:
     str     r1, [r0]
     add     r2, r2, #8          /* each rel.dyn entry is 8 bytes */
     cmp     r2, r3
     blo     fixloop
     b     clear_bss
_rel_dyn_start_ofs:
     .word __rel_dyn_start - _start
_rel_dyn_end_ofs:
     .word __rel_dyn_end - _start
_dynsym_start_ofs:
     .word __dynsym_start - _start

#endif     /* #ifndef CONFIG_SPL_BUILD */

clear_bss:
#ifdef CONFIG_SPL_BUILD
     /* No relocation for SPL */
     ldr     r0, =__bss_start
     ldr     r1, =__bss_end__
#else
     ldr     r0, _bss_start_ofs
     ldr     r1, _bss_end_ofs
     mov     r4, r6               /* reloc addr */
     add     r0, r0, r4
     add     r1, r1, r4
#endif
     mov     r2, #0x00000000          /* clear                   */

clbss_l:str     r2, [r0]          /* clear loop...              */
     add     r0, r0, #4
     cmp     r0, r1
     bne     clbss_l

/*
* We are done. Do not return, instead branch to second part of board
* initialization, now running from RAM.
*/
jump_2_ram:
/*
* If I-cache is enabled invalidate it
*/
#ifndef CONFIG_SYS_ICACHE_OFF
     mcr     p15, 0, r0, c7, c5, 0     @ invalidate icache
     mcr     p15, 0, r0, c7, c10, 4     @ DSB
     mcr     p15, 0, r0, c7, c5, 4     @ ISB
#endif
     ldr     r0, _board_init_r_ofs
     adr     r1, _start
     add     lr, r0, r1
     add     lr, lr, r9
     /* setup parameters for board_init_r */
     mov     r0, r5          /* gd_t */
     mov     r1, r6          /* dest_addr */
     /* jump to it ... */
     mov     pc, lr

_board_init_r_ofs:
     .word board_init_r - _start


從代碼的最后能夠看出來,最后mov pc,lr,是跳到重定位后的 board_init_r函數處。

代碼還是那段代碼,僅僅是在內存中的位置變化了。


再回到board_init_f中調用relocate_code函數。傳入三個參數:
     relocate_code(CONFIG_SPL_STACK, &g_data, CONFIG_SPL_TEXT_BASE);
第一個參數是棧指針:CONFIG_SPL_STACK
第二個參數是全局數據結構指針:&g_data
第三個參數是重定位的目的地址:CONFIG_SPL_TEX_BASE
relocate_code的代碼是用匯編書寫的。這里要提到的是C參數怎樣傳遞給匯編語言的。這里必需要提到ABI和EABI。
ABI是Application Binary Interface的縮寫,跟API非常類似。但ABI是比API更貼近硬件的一層接口。它規定的
是二進制代碼之間的調用規則。

我們知道API是與機器硬件平台無關的接口,如熟悉的printf函數,不同的庫的實現可能是不同的。可是它們的接口名稱
與參數類型都是一樣的。

ABI比API的要求更嚴格。它要求在寄存器級別遵守相同的接口規范。如函數調用的參數傳遞規則,

寄存器、堆棧的使用方式等。編譯器被要求遵守相同的ABI規范,這樣不同編譯器編譯出來的庫(相同硬件)才干被別的
編譯器使用。
EABI是使用與嵌入式場合的應用二進制接口。這里就簡單提一下:函數調用時,傳遞的參數假設都小於32bit則使用
R0-R3傳遞參數。參數個數超出4個則使用STACK傳遞。因此為了代碼的效率。程序猿應該盡量保證將函數的參數個數限制
在4個下面。

對於CONFIG_SPL_STACK,是LOW_LEVEL_SRAM_STACK的宏定義。

另外am335x_evm.h還包括hardware.h

hardware.h包括omap.h

在omap.h中有LOW_LEVEL_SRAM_STACK的宏定義,這個宏定義和前文提到調用s_init函數前的建立棧底是一樣的。


因為棧生長的方向是向下的,所以棧底必須設置為高地址處。這個地址已經快接近OCMC0的底部。


對於 CONFIG_SPL_TEXT_BASE。正是MPU內部SRAM的起始地址。

--------------------------------------------------------------------------------
                              relocate_code End (start.s)
--------------------------------------------------------------------------------
總結一下:從信息打印看,我們的板子的stack生長方向是遞減的。
有一點非常疑惑的是:在調用board_init_f之前,SP設置為0x4030B780。進入board_init_f后調用
relocate_code,傳入的SP地址參數為0x4030B7FC,SP往上面移動了。這是為什呢?由於重定位后是不會從
relocate_code函數返回的,所以board_init_f的入棧參數會被抹掉但無所謂,可是能不能將這兩次的SP設置成一樣的呢?
(PS: TI-SDK-AM335X-EVM-07.00中堆棧的設置沒有讓人confused的地方了)

--------------------------------------------------------------------------------
                     call_board_init_f End(start.s)
--------------------------------------------------------------------------------

board_init_f函數調用relocate_code。該函數實現代碼的搬遷。

從以下的圖中能夠看到:


_start處於內存位置:0x402F0400
在relocate_code中,
relocate_code:
     mov     r4, r0     /* save addr_sp */
     mov     r5, r1     /* save addr of gd */
     mov     r6, r2     /* save addr of destination */
stack_setup:
     mov     sp, r4

     adr     r0, _start
     cmp     r0, r6
     moveq     r9, #0          /* no relocation. relocation offset(r9) = 0 */
     beq     clear_bss          /* skip relocation */
     mov     r1, r6               /* r1 <- scratch for copy_loop */
     ldr     r3, _image_copy_end_ofs
     add     r2, r0, r3          /* r2 <- source end address         */
將_start載入到r0中,與r6比較。而r6是由r2傳遞過來的目標地址CONFIG_SPL_TEXT_BASE=0x402F0400。
因此會運行beq clear_bss處,即copy_loop的代碼不會運行。

在代碼里面增加宏開關。又一次編譯后依舊能運行。證實了這個想法:
MLO階段不會將自己copy到SRAM中,copy動作應該是由ROM Code實現的。
另外一個證明就是,ti-sdk-07.00版本號的u-boot代碼中明白了沒有在MLO階段調用relocate_code,只在u-boot階段調用。

-------------------------------------------------------------------------
                             Start.s  End
-------------------------------------------------------------------------

--------------------------------------------------------------------------------
                     board_init_r Start(start.s)
--------------------------------------------------------------------------------
relocate_code結束的時候。跳到board_init_r函數處。該函數位於arch/arm/cpu/armv7/omap-common/spl.c。

該函數主要初始化內存、各個外設:
     mem_malloc_init()
     timer_init()
     i2c_init()
     spl_board_init()
然后選擇從那個外設載入image。我們調試的時候從SD卡載入u-boot的image,所以關注spl_mmc_load_image函數。
--------------------------------------------------------------------------------
                     spl_mmc_load_image Start(board_init_r) 
--------------------------------------------------------------------------------
首先取得boot_device的序號。這個序號是定義在boot_params結構體的第三個成員項

omap_boot_device函數定義在arch/arm/cpu/armv7/omap-common/boot-common.c中。

結構體omap_boot_parameters定義在 ,這個定義與最開始
時候的Booting Parameters Structure是一致的。

將boot_device和boot_params的地址都打印出來。例如以下圖所看到的:

能夠看到boot device是8,相應的是MMC/SD port 0設備。boot_params結構體的地址是0x40303990。位於L3 0CMC0存儲區,非常顯然這個地址是由連接器分配的。位於“Downloaded Image”區域。
而在spl_mmc_load_image中最關鍵的部分是,

將boot_mode打印出來,2相應MMCSD_MODE_FAT模式。

另外。因為代碼的添加。這兒能夠看到boot_params結構體的地址變成了0x403039B8。

而mmc_load_image_fat中最重要的代碼就是解析header和讀出image。

spl_parse_image_header將header的中信息解析出來放在結構體spl_image里面,file_fat_read函數將image讀到spl_image.load_addr所指向的緩沖區。
--------------------------------------------------------------------------------
                     spl_mmc_load_image End(board_init_r) 
--------------------------------------------------------------------------------

spl_image是非常重要的一個結構體。將其打印出來看看有什么。


spl_image的地址已經到了0x8000,0000。這已經是Externel SDRAM的首地址了。那么誰安排spl_image的地址的?
搜索一番,發現spl_image處於.bss區域,即未初始化區域,map里面將其放在0x8000000地址。

回過頭來查看arch/arm/cpu/armv7/omap-common/u-boot-spl.lds鏈接腳本文件,能夠看到:
也就是說.bss段被指向sdram存儲區域。map文件則顯示鏈接器將spl_image安排在第一個。 不知道這樣的安排是否是有意為之?(后來從7.00上面證明並不是刻意安排。而是ld的行為) 從map看。似乎是誰離lds文件更近誰出如今map的最前面。這樣的安排是有些道理的。一定程度上能解釋為什么spl_image在map上處於第一個。

從spl_image.os的數值能夠得出,下一步就是運行jump_to_image_no_args()函數。

該函數首先typedef一個新類型,該類型是入參和返回值均為void的函數指針類型,而且用這個類型定義一個變量image_entry,將spl_image.entry_point賦值給它,最后運行這個函數,從匯編的角度看,就是將spl_image.entry_point賦值給PC,下一步就是跳轉到SDRAM的0x80100000運行。

--------------------------------------------------------------------------------
                     board_init_r End(start.s)
--------------------------------------------------------------------------------

MLO階段結束。




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM