系統移植之uboot源代碼簡要分析(2)

經過上一篇系統移植之uboot源代碼簡要分析(1)對uboo進行分析後,我們知道BootLoader的第二階段啓動(BL2)從start_armboot處開始執行,start_armboot函數定義在“lib_arm/board.c”中
U-Boot第二階段流程圖

void start_armboot (void)
{
    init_fnc_t **init_fnc_ptr;
    char *s;
    int mmc_exist = 0;
    ...
    for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
        if ((*init_fnc_ptr)() != 0) /*返回值必須爲0,否則初始化失敗,系統初始化終止*/
         {
            hang ();
        }
    }

init_fnc_t **init_fnc_ptr是一個二級指針,指向的內容爲以下的數組init_sequence,該數組存儲的是各類初始化函數的地址,start_armboot通過
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr)
循環逐個調用init_sequence數組中的初始化函數,完成對系統的初始化。

init_fnc_t *init_sequence[] = {
    cpu_init,       /* basic cpu dependent setup */
#if defined(CONFIG_SKIP_RELOCATE_UBOOT)
    reloc_init,     /* Set the relocation done flag, must
                   do this AFTER cpu_init(), but as soon
                   as possible */
#endif
    board_init,     /* basic board dependent setup */
    interrupt_init,     /* set up exceptions */
    env_init,       /* initialize environment */
    init_baudrate,      /* initialze baudrate settings */
    serial_init,        /* serial communications setup */
    console_init_f,     /* stage 1 init of console */
    display_banner,     /* say that we are here */
#if defined(CONFIG_DISPLAY_CPUINFO)
    print_cpuinfo,      /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
    checkboard,     /* display board info */
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
    init_func_i2c,
#endif
    dram_init,      /* configure available RAM banks */
    display_dram_config,
    NULL,
};

另外,我們在start_armboot函數中可以見到很多個“gd_t”類型,該類型是定義於“include/asm-arm/globle_data.h”,是uboot的全局變量類型,

typedef struct  global_data {
    bd_t        *bd;
    unsigned long   flags;
    unsigned long   baudrate;

    ...

    void        **jt;       /* jump table */
} gd_t;

在include/asm-arm/globle_data.h的最後,我們發現一段比較難懂的C語句

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

這句話的意思是指定R8保存變量gd的地址,並聲明該值是volatile(易變的),然後用“DECLARE_GLOBAL_DATA_PTR”表示它。
在gd_t類型結構體中,我們需要重點關注“bd_t”這個類型結構體(開發板信息結構體),它與我們的移植的關聯很大。這個結構體定義在“include/asm-arm/uboot.h”中↓

typedef struct bd_info {
    int         bi_baudrate;    /* serial console baudrate (串口控制檯波特率)*/
    unsigned long   bi_ip_addr; /* IP Address */
    unsigned char   bi_enetaddr[6]; /* Ethernet adress */
    struct environment_s           *bi_env;
    ulong           bi_arch_number; /* unique id for this board (機器碼,即開發板ID,當且僅當ID在內核中存在且正確時候,纔可進行啓動)*/
    ulong           bi_boot_params; /* where this board expects params  uboot傳遞給內核的參數的地址*/
    struct              /* RAM configuration DRAM的起始地址和大小*/
    {
    ulong start;
    ulong size;
    }bi_dram[CONFIG_NR_DRAM_BANKS];
            /*CONFIG_NR_DRAM_BANKS是允許的DRAM的數量*/
#ifdef CONFIG_HAS_ETH1
    /* second onboard ethernet port */
    unsigned char   bi_enet1addr[6];
#endif
} bd_t;/*開發板信息結構體*/

回到start_armboot函數

經過for循環調用各個初始化函數完成基礎硬件的初始化,以及SOC的其他初始化(此處不累贅,對我們理解uboot啓動流程沒有太大影響)之後,程序進入”main_loop()”
main_loop函數定義於:common/main.c

/* main_loop() can return to retry autoboot, if so just run it again. */
    for (;;) {
        main_loop ();
    }

main_loop的執行流程:

main_loop函數執行流程
我們知道BootLoader有兩種啓動流程
1. 啓動加載(boot loading)模式:
常用於已發佈的產品上,整個過程不需要用戶介入。
2. 下載(downloading)模式:
開發人員可以使用各種命令,通過串口連接或者網絡連接等通信手段從主機(host)下載文件。常用協議:x/y/z modem協議、TFTP、nfs協議、USB。

進入main_loop後,執行流程如上圖main_loop的執行流程所示。
函數首先讀取環境參數bootdelay和bootcmd,然後判斷bootdelay秒內串口是否有輸入,
1>有輸入,進入downloading模式
2>無輸入,進入boot loading模式,啓動加載內核;
~(啓動內核–>是通過環變量bootcmd中存放的內核相關啓動命令來啓動內核的,這些命令有以下一些:
print(printenv):打印環境變量;
set(setenv):設置環境變量
saveenv:保存環境變量;
tftp:通過TFTP服務下載TFTP服務器目錄下的文件到內存中
go:執行內存中的二進制代碼
bootm:啓動內存中的內核鏡像文件zImage


那麼這些內核啓動命令是如何定義的呢?↓


內核啓動命令定義於“include/command.h”中,類型定義方式如下

/* uboot命令結構體 */
struct cmd_tbl_s {
    char        *name;      /* Command Name 命令名     */
    int     maxargs;    /* maximum number of arguments命令最大參數個數  */
    int     repeatable; /* autorepeat allowed?  重複次數    */
                    /* Implementation function 命令處理函數   */
    int     (*cmd)(struct cmd_tbl_s *, int, int, char *[]);
    char        *usage;     /* Usage message    (short)簡單使用信息   */
#ifdef  CFG_LONGHELP
    char        *help;      /* Help  message    (long)詳細幫助信息    */
#endif
#ifdef CONFIG_AUTO_COMPLETE
    /* do auto completion on the arguments */
    int     (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
/*將uboot命令結構體聲明爲cmd_tbl_t*/
typedef struct cmd_tbl_s    cmd_tbl_t; 

好,接下來我們分析啓動命令具體是如何定義的

/*
 * Command Flags:
 */
#define CMD_FLAG_REPEAT 0x0001  /*repeat last command       */
#define CMD_FLAG_BOOTD  0x0002  /* command is from bootd    */

/* 
 *1.這段宏定義具體意思:如果Struct_Section沒被使用(unused),則
 *將Struct_Section連接到啓動命令段(".u_boot_cmd")中
 **.u_boot_cmd定義於鏈接腳本uboot.lds中*
 */
#define Struct_Section  __attribute__ ((unused,section (".u_boot_cmd")))


#ifdef  CFG_LONGHELP    /*如果定義了詳細幫助信息*/

/*
 *2.定義一個帶參宏U_BOOT_CMD,表示後面的定義
 *2.1)"##"連接符號,表示將__u_boot_cmd和name連接成爲__u_boot_cmd_name
 *2.2)Struct_Section 將“__u_boot_cmd_name”到“.u_boot_cmd”啓動代碼段
 */

#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
/**/

#else   /* no long help info 沒有詳細幫助信息 */

#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage}

#endif  /* CFG_LONGHELP */

到”common/command.c”中可以查看具體定義了哪些命令。


我們再來看看“bootm”命令具體是如何啓動內存中的內核鏡像文件zImage的。

bootm命令定義在“common/cmd_bootm.c”
因爲uboot是通用型BootLoader,可知uboot可以支持多種操作系統的啓動,因此bootm命令需要根據具體操作系統分支執行,

switch (os) {
    default:            /* handled by (original) Linux case */
    case IH_OS_LINUX:
#ifdef CONFIG_SILENT_CONSOLE
        fixup_silent_linux();
#endif
        do_bootm_linux (cmdtp, flag, argc, argv, &images);
        break;

...

#ifdef CONFIG_ARTOS
    case IH_OS_ARTOS:
        do_bootm_artos (cmdtp, flag, argc, argv, &images);
        break;
#endif
    }

我們使用是操作系統爲Linux,選擇分支爲“IH_OS_LINUX”,接下來調用“do_bootm_linux (cmdtp, flag, argc, argv, &images);”進行啓動。
do_bootm_linux函數定義於lib_arm/bootm.c 中

void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
             bootm_headers_t *images)
{
    ulong   initrd_start, initrd_end;
    ulong   ep = 0;
    bd_t    *bd = gd->bd;
    char    *s;
    int machid = bd->bi_arch_number;      /*獲取機器ID*/
    void    (*theKernel)(int zero, int arch, uint params);
    int ret;

    ...

    cleanup_before_linux ();

    /*
     *將參數:0,機器ID,uboot傳遞給內核的參數的地址
     *傳遞thekernel函數,進而啓動內核。
     */
    theKernel (0, machid, bd->bi_boot_params);
    /*
     *至此,uboot的全部工作完成,接下來由內核接管系統。
     */
    /* does not return */
    return;

error:
    do_reset (cmdtp, flag, argc, argv);
    return;
}

系統移植之uboot源代碼簡要分析如上,與各位道嵌友交流,若有謬誤還望指教,謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章