在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;
}
}
}