嵌入式linux開發uboot啟動過程源碼分析(一)


 

一、uboot啟動流程簡介

    與大多數BootLoader一樣,uboot的啟動過程分為BL1和BL2兩個階段。BL1階段通常是開發板的配置等設備初始化代碼,需要依賴依賴於SoC體系結構,通常用匯編語言來實現;BL2階段主要是對外部設備如網卡、Flash等的初始化以及uboot命令集等的自身實現,通常用C語言來實現。

1、BL1階段

    uboot的BL1階段代碼通常放在start.s文件中,用匯編語言實現,其主要代碼功能如下:

  (1) 指定uboot的入口。在鏈接腳本uboot.lds中指定uboot的入口為start.S中的_start。

  (2)設置異常向量(exception vector)

  (3)關閉IRQ、FIQ,設置SVC模式

  (4)關閉L1 cache、設置L2 cache、關閉MMU

  (5)根據OM引腳確定啟動方式

  (6)在SoC內部SRAM中設置棧

  (7)lowlevel_init(主要初始化系統時鍾、SDRAM初始化、串口初始化等)

  (8)設置開發板供電鎖存

  (9)設置SDRAM中的棧

  (10)將uboot從SD卡拷貝到SDRAM中

  (11)設置並開啟MMU

  (12)通過對SDRAM整體使用規划,在SDRAM中合適的地方設置棧

  (13)清除bss段,遠跳轉到start_armboot執行,BL1階段執行完

2、BL2階段

    start_armboot函數位於lib_arm/board.c中,是C語言開始的函數,也是BL2階段代碼中C語言的主函數,同時還是整個u-boot(armboot)的主函數,BL2階段的主要功能如下:

  (1)規划uboot的內存使用

  (2)遍歷調用函數指針數組init_sequence中的初始化函數

  (3)初始化uboot的堆管理器mem_malloc_init

  (4)初始化SMDKV210開發板的SD/MMC控制器mmc_initialize

  (5)環境變量重定位env_relocate

  (6)將環境變量中網卡地址賦值給全局變量的開發板變量

  (7)開發板硬件設備的初始化devices_init

  (8)跳轉表jumptable_init

  (9)控制台初始化console_init_r

  (10)網卡芯片初始化eth_initialize

  (11)uboot進入主循環main_loop

二、uboot程序入口分析

1、link.lds鏈接腳本文件分析

u-boot.lds文件是uboot工程的鏈接腳本文件,位於board\samsung\smdkc110目錄下,對於工程項目編譯后期的鏈接階段非常重要,決定了uboot程序的組裝。

u-boot.lds鏈接文件中的ENTRY(_start)指定了uboot程序的入口地址為_start。

2、定位uboot程序入口地址

在SourceInsight建立uboot工程,利用索引功能查找_start,在搜索結果中找到與三星smdkv210開發板相關的代碼,最終鎖定cpu\s5pc11x\start.S文件,定位到文件中的_start標識符。

三、start.S文件分析

1、頭文件分析

start.S有四個頭文件:

#include <config.h>

    config.h頭文件在配置開發板時由mkconfig腳本創建的頭文件,頭文件內容即包含開發板的頭文件:#include <configs/smdkv210single.h>

#include <version.h>

    version.h頭文件的內容為包含自動生成的版本頭文件,頭文件內容為:#include "version_autogenerated.h",version_autogenerated.h頭文件定義了版本宏,宏定義為:#define U_BOOT_VERSION "U-Boot 1.3.4"。版本宏的值就是Makefile中定義的版本信息。

#include <asm/proc/domain.h>

    domain.h頭文件在定義了CONFIG_ENABLE_MMU宏時有效,為鏈接文件,實際指向的文件為include/asm-arm/proc-armv/domain.h。

#include <regs.h>

regs.h頭文件為鏈接文件,指向s5pc110.h頭文件,s5pc110.h文件內部使用宏定義了有關SoC內部寄存器的大量信息。

2、頭校驗信息的占位

#if defined(CONFIG_EVT1) && !defined(CONFIG_FUSED)

.word 0x2000

.word 0x0

.word 0x0

.word 0x0

#endif

定義uboot程序開頭的16字節校驗頭信息填充空間,頭校驗信息塊內的值需要在后面寫入。

3、異常向量表的構建

.globl _start

_start:

b reset

ldrpc, _undefined_instruction

ldrpc, _software_interrupt

ldrpc, _prefetch_abort

ldrpc, _data_abort

ldrpc, _not_used

ldrpc, _irq

ldrpc, _fiq

    uboot程序的入口點實際是定義了異常向量表,異常向量表由SoC硬件實現,因此uboot在開機上電復位時需要跳轉到reset執行。

4、復位reset分析

SoC上電復位后運行的第一段代碼就是reset。主要包括以下幾部分:

A、關閉IRQ、FIQ,並將處理器模式設置為SVC模式

B、CPU關鍵寄存器的初始化cpu_init_crit:

    關閉L2 cache

    初始化L2 cache

    開啟L2 cache

    關閉L1 cache

    關閉MMU

    讀取OM啟動引腳信息

    確定從啟動設備SD卡啟動

    設置SRAM中的棧為調用lowlevel_init做准備(lowlevel_init內部有嵌套調用)

    調用lowlevel_init(主要初始化系統時鍾、SDRAM初始化、串口初始化等)

    設置開發板供電鎖存

    設置SDRAM中的棧

    判斷當前代碼是否運行在SDRAM中,如果當前代碼運行在SDRAM中,則跳過代碼重定位。

    判斷啟動方式,選擇SD卡啟動設備,跳轉到mmcsd_boot

    SD卡啟動的准備工作,從SD卡拷貝uboot到SDRAM:movi_bl2_copy

 

C、設置MMU,開啟MMU

D、通過對SDRAM整體使用規划,在SDRAM中合適的地方設置棧

E、清除bss段,遠跳轉到start_armboot執行,BL1階段執行完

5、lowlevel_init分析

lowlevel_init位於\board\samsung\smdkc110\lowlevel_init.S中,主要功能如下:

    A、檢查復位狀態,判斷啟動的方式

根據復位狀態選擇復位啟動的方式,處於低功耗狀態時復位啟動可以跳過后續多個步驟。

    B、IO狀態恢復

    C、關閉看門狗

    D、外部SRAM的GPIO初始化、外部SROM初始化

    E、開發板供電鎖存設置

    F、判斷當前代碼是否運行在SDRAM,如果當前代碼運行在SDRAM,說明目前從低功耗狀態復位,可以跳過系統時鍾初始化、串口初始化、SDRAM初始化等

    G、初始化系統時鍾:system_clock_init

    H、初始化SDRAM內存:mem_ctrl_asm_init

    I、初始化串口,打印出’O’:uart_asm_init

    J、初始化trustzone:tzpc_init

    K、初始化nand或onenand

    L、檢查復位狀態

    M、關閉ABB

    N、串口打印出‘K’

說明:”OK”是打印出的調試信息,如果打印出’O’則說明在串口初始化uart_asm_init前的所有代碼是正確的。如果打印出”OK”則說明在開發板板級初始化lowlevel_init前的所有代碼是正常工作的。

system_clock_init、uart_asm_init、tzpc_init、nand_asm_init都位於lowlevel_init.S文件內,mem_ctrl_asm_init位於cpu\s5pc11x\s5pc110\cpu_init.S文件中。

四、board.c文件分析

uboot在執行完BL1階段后遠跳轉到start_armboot函數執行BL2,start_armboot函數位於lib_arm\board.c中。

1、重要變量的說明

typedef int (init_fnc_t) (void);函數類型

init_fnc_t **init_fnc_ptr;//二級函數指針

#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r8")

DECLARE_GLOBAL_DATA_PTR定義了一個存儲在寄存器r8中的指向gd_t類型全局變量的指針gd。

全局變量結構體的定義:

typedefstructglobal_data {

bd_t*bd;//boardinfo結構體信息,存放和開發板有關的信息

unsigned longflags;//標志位

unsigned longbaudrate;//串口通信波特率

unsigned longhave_console;//控制台/* serial_init() was called */

unsigned longreloc_off;//重定位偏移量/* Relocation Offset */

unsigned longenv_addr;//環境變量結構體的地址/* Address  of Environment struct */

unsigned longenv_valid;//環境變量使用標志/* Checksum of Environment valid? */

unsigned longfb_base;//fb基地址/* base address of frame buffer */

#ifdef CONFIG_VFD

unsigned charvfd_type;///* display type */

#endif

void**jt;//跳轉表/* jump table */

} gd_t;

開發板信息結構體變量的定義:

typedef struct bd_info {

    intbi_baudrate;//硬件串口波特率/* serial console baudrate */

    unsigned longbi_ip_addr;//開發板IP地址/* IP Address */

    unsigned charbi_enetaddr[6];//開發板網卡地址 /* Ethernet adress */

    struct environment_s       *bi_env;//環境變量指針

    ulong        bi_arch_number;//機器碼/* unique id for this board */

    ulong        bi_boot_params;//uboot啟動參數/* where this board expects params */

    struct/* RAM configuration */

    {

ulong start;

ulong size;

    }bi_dram[CONFIG_NR_DRAM_BANKS];//內存插條信息

#ifdef CONFIG_HAS_ETH1

    /* second onboard ethernet port */

    unsigned char   bi_enet1addr[6];//第二塊網卡的地址

#endif

} bd_t;

 

2、uboot的內存規划

wKioL1drRfTyRHOGAADgnjy9DiE579.jpg

    SDRAM_BASE被MMU映射在0xC0000000,CFG_UBOOT_BASE是0xC3E00000

    在BL1段運行時,uboot鏡像被拷貝到CFG_UBOOT_BASE開始的地址處。

  gd的地址:

gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);

  bd的地址:

gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));

3、start_armboot函數分析

    start_armboot函數的主要功能如下:

(1)、遍歷調用函數指針數組init_sequence中的初始化函數

依次遍歷調用函數指針數組init_sequence中的函數,如果有函數執行出錯,則執行hang函數,打印出”### ERROR ### Please RESET the board ###”,進入死循環。

(2)、初始化uboot的堆管理器mem_malloc_init

(3)、初始化SMDKV210的SD/MMC控制器mmc_initialize

(4)、環境變量重定位env_relocate

(5)、將環境變量中網卡地址賦值給全局變量的開發板變量

(6)、開發板硬件設備的初始化devices_init

(7)、跳轉表jumptable_init

(8)、控制台初始化console_init_r

(9)、網卡芯片初始化eth_initialize

(10)、uboot進入主循環main_loop

void start_armboot (void)
   {
   //全局數據變量指針gd占用r8。
   DECLARE_GLOBAL_DATA_PTR;       
   /* 給全局數據變量gd安排空間*/
   gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
   memset ((void*)gd, 0, sizeof (gd_t));
   /* 給板子數據變量gd->bd安排空間*/
   gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
   memset (gd->bd, 0, sizeof (bd_t));
   monitor_flash_len = _bss_start - _armboot_start;//u-boot長度。       
   /* 順序執行init_sequence數組中的初始化函數 */
   for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
       if ((*init_fnc_ptr)() != 0) {
           hang ();
                 }
          }
    /* 初始化堆空間 */
    mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
    /* 重新定位環境變量, */
     env_relocate ();
    /* 從環境變量中獲取IP地址 */
    gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
    /* 以太網接口MAC 地址 */
    devices_init ();      /* 設備初始化 */
    jumptable_init ();  //跳轉表初始化
    console_init_r ();    /* 完整地初始化控制台設備 */
    enable_interrupts (); /* 使能中斷處理 */
     /* 通過環境變量初始化 */
     if ((s = getenv ("loadaddr")) != NULL) {
        load_addr = simple_strtoul (s, NULL, 16);
     }
     /* main_loop()循環不斷執行 */
     for (;;) {
         main_loop ();      /* 主循環函數處理執行用戶命令 -- common/main.c */
          }
   }

4、函數指針數組init_sequence

函數指針數組init_sequence:

init_fnc_t *init_sequence[] = {

cpu_init,//CPU架構的初始化,為空cpu\s5pc11x\cpu.c

board_init,//開發板初始化board\samsung\smdkc110\smdkc110.c

interrupt_init,//定時器timer4初始化cpu\s5pc11x\interrupts.c

env_init,//環境變量初始化common\env_movi.c

init_baudrate,//波特率設置lib_arm\board.c

serial_init,//延時函數C,沒有再次初始化串口cpu\s5pc11x\serial.c

console_init_f,//控制台第一階段初始化,控制台未初始化好common\console.c

display_banner,//用串口發送uboot版本信息lib_arm\board.c

#if defined(CONFIG_DISPLAY_CPUINFO)

print_cpuinfo,//串口打印系統時鍾信息cpu\s5pc11x\s5pc110\speed.c

#endif

#if defined(CONFIG_DISPLAY_BOARDINFO)

checkboard,//打印開發板信Board:SMDKV210

//board\samsung\smdkc110\smdkc110.c

#endif

#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)

init_func_i2c,//SMDKV210未定義I2C,函數為空lib_arm\board.c

#endif

dram_init,//初始化gd->bd->bi_dram,開發板的SDRAM配置信息

board\samsung\smdkc110\smdkc110.c 

   display_dram_config,//串口打印出DRAM的大小信息,DRAM:xxxMB

lib_arm\board.c

NULL,

};

board_init函數:

dm9000_pre_init();//網卡初始化,GPIO和端口設置

gd->bd->bi_arch_number = MACH_TYPE;//開發板的機器碼,uboot的機器碼和linux的機器碼之間必須適配

gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100);//uboot給內核的傳參地址

display_banner函數:

打印uboot版本信息:uboot-1.3.4

print_cpuinfo函數:

打印CPU時鍾系統的時鍾信息

checkboard函數:

打印出開發板信息Board:   SMDKV210

display_dram_config函數:

打印出DRAM的大小信息,DRAM:xxxMB

打印出的信息可以作為調試使用,依次遍歷調用函數指針數組init_sequence中的函數,如果有函數執行出錯,則執行hang函數,打印出”### ERROR ### Please RESET the board ###”,進入死循環。


免責聲明!

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



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