u-boot啓動過程之STEP2

在STEP1的最後,代碼通過調用void start_armboot (void)進入了u-boot啓動的第二階段

1. void start_armboot (void)主要是對硬件的初始化,代碼詳解

void start_armboot (void)
{
    init_fnc_t **init_fnc_ptr;
    char *s;

    /* Pointer is writable since we allocated a register for it */
    // 初始化gd,使其指向global data區的基地址
    gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
    /* compiler optimization barrier needed for GCC >= 3.4 */
    //1. __asm__用於指示編譯器在此插入彙編語句
    //2. __volatile__用於告訴編譯器,嚴禁將此處的彙編語句與其它的語句重組合優化。即:原原本本按原來的樣子處理這這裏的彙編。
    //3. memory強制gcc編譯器假設RAM所有內存單元均被彙編指令修改,這樣cpu中的registers和cache中已緩存的內存單元中的數據將作廢。
    // cpu將不得不在需要的時候重新讀取內存中的數據。這就阻止了cpu又將registers,cache中的數據用於去優化指令,而避免去訪問內存。
    //4. "":::表示這是個空指令。
    __asm__ __volatile__("": : :"memory");

    //清零global data區
    memset ((void*)gd, 0, sizeof (gd_t));
    //初始化gd->bd,使其指向bd_t的基地址
    gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
    //清零bd_t區
    memset (gd->bd, 0, sizeof (bd_t));

    // u-boot代碼的大小。根據u-boot.lds可知,monitor_flash_len是.text + .rodata + .data + .got + .u_boot_cmd 的總長度。
    monitor_flash_len = _bss_start - _armboot_start;

    //init_sequence是初始化函數數組的起始地址,在這個循環中,會依次去執行初始化函數,若出現故障,則進入死循環。初始化函數包括
    //1. cpu_init,      /* basic cpu dependent setup, 分配IRQ, FIQ的棧地址 */
    //2. board_init,        /* basic board dependent setup, 初始化IO口 */
    //3. interrupt_init,        /* set up exceptions, 初始化時鐘 */
    //4. env_init,      /* initialize environment, 初始化環境變量 */
    //5. init_baudrate,     /* initialze baudrate settings, 初始化波特率 */
    //6. serial_init,       /* serial communications setup , 初始化串口*/
    //7. console_init_f,        /* stage 1 init of console, 初始化終端 */
    //8. display_banner,        /* say that we are here, 打印一些信息到終端 */
    //9. dram_init,     /* configure available RAM banks, 初始化SDRAM的內存起始地址和大小。 */
    //10. display_dram_config, 打印SDRAM的大小。
    for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
        if ((*init_fnc_ptr)() != 0) {
            hang ();
        }
    }

    /* configure available FLASH banks */
    // 初始化NOR FLASH,配置每個sector的size等。
    size = flash_init ();
    // 打印出NOR FLASH 的整個SIZE信息
    display_flash_config (size);

    /* armboot_start is defined in the board-specific linker script */
    // 初始化malloc區,並全部清零
    mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);

#if (CONFIG_COMMANDS & CFG_CMD_NAND)
    //初始化NAND FLASH相關的寄存器。
    puts ("NAND:  ");
    nand_init();        /* go init the NAND */
#endif

    /* initialize environment */
    //初始化環境變量
    env_relocate ();

    /* IP Address */
    // 獲取配置的IP地址
    gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");

    /* MAC Address */
    // 獲取配置的MAC值,並將其存儲到gd->bd->bi_enetaddr中。
    {
        int i;
        ulong reg;
        char *s, *e;
        char tmp[64];

        i = getenv_r ("ethaddr", tmp, sizeof (tmp));
        s = (i > 0) ? tmp : NULL;

        for (reg = 0; reg < 6; ++reg) {
            gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
            if (s)
                s = (*e) ? e + 1 : e;
        }
    }

    // 初始化並註冊外圍設備,串口就是在這裏初始化並註冊的。
    devices_init ();    /* get the devices list going. */

    // 初始化gd中的jump table中的函數,如get_version,malloc,getenv
    jumptable_init ();

    // 查找是否有可用的輸入輸出設備,若有,將其作爲標準的輸入輸出設備:
    // 輸入: getc, tstc
    // 輸出: putc,puts,printf
    console_init_r ();  /* fully init console as a device */

    // 初始化ARM的IO口。
    Port_Init();

    // PreLoadedONRAM這個變量在start.S中定義的。
    //.globl PreLoadedONRAM
    //PreLoadedONRAM:
    //.word 0
    if (!PreLoadedONRAM) {
        /* enable exceptions */
        enable_interrupts ();
        /* add by www.100ask.net */
        // 初始化usb口
        usb_init();
    }

    /* main_loop() can return to retry autoboot, if so just run it again. */
    // 經過一系列初始化後,進入主循環。
    for (;;) {
        main_loop ();
    }
}   

2. void main_loop (void)用於對FLASH進行分區、調用kernel,執行u-boot中輸入的命令,其被void start_armboot (void)調用。

void main_loop (void)
{
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
    //初始化bootdelay相關的變量
    char *s;
    int bootdelay;
#endif

#ifdef CONFIG_JFFS2_CMDLINE
    // 初始化分區表,對FLASH進行分區
    //mtdparts=mtdparts=nandflash0:256k@0(bootloader),128k(params),2m(kernel),-(root)
    extern int mtdparts_init(void);
    if (!getenv("mtdparts"))
    {
        run_command("mtdparts default", 0);
    }
    else
    {
        mtdparts_init();
    }
#endif

#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
    // 獲取 bootdelay的值: bootdelay=2
    s = getenv ("bootdelay");   
    // 轉換成整數
    bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
    // 獲取bootcmd的參數: bootcmd=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0
    s = getenv ("bootcmd");

    // 倒計時開始,若倒計時結束,沒有按下任何按鍵,則該條件成立
    if (bootdelay >= 0 && s && !abortboot (bootdelay)) 
    {
        // 打印Booting Linux ...
        printf("Booting Linux ...\n");            
        // 執行bootcmd的參數,nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0
        run_command (s, 0);
    }
#endif  /* CONFIG_BOOTDELAY */

    // 執行 menu 命令
    run_command("menu", 0);

    // 進入死循環,讀取命令並執行之。
    for (;;) 
    {
        len = readline (CFG_PROMPT);

        flag = 0;   /* assume no special flags for now */
        if (len > 0)
            strcpy (lastcommand, console_buffer);
        else if (len == 0)
            flag |= CMD_FLAG_REPEAT;

        if (len == -1)
            puts ("<INTERRUPT>\n");
        else
            rc = run_command (lastcommand, flag);

        if (rc <= 0) {
            /* invalid command or not repeatable, forget it */
            lastcommand[0] = 0;
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章