天天看點

uboot分析

本人用的android平台用的bootloader用的是uboot,貌似大多數手持裝置平台都不用這個,因為功能過于強大用不上,反而顯得太複雜了。不知道這個平台開發者是怎麼想的。既然用了那就來分析一下,順便修改一下其中的幾個小問題,以符合我們的要求。

  uboot等同于其他所有的bootloader程式,從根本上講是一個稍複雜的裸機程式,是最底層的東西,要分析裸機程式我們要從它的連接配接檔案開始。連接配接檔案(.lds檔案)定義了程式編譯之後整個連接配接過程,這樣我們就可以找到這個程式的第一句彙編代碼,進而來下一步分析。uboot的連結檔案代碼在android\bootable\bootloader\uboot-imx\u-boot.lds

檢視文本 列印 ?

  1. OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")  //檔案輸出格式  
  2. OUTPUT_ARCH(arm)  
  3. ENTRY(_start)       //首位址标示符  
  4. SECTIONS  
  5. {  
  6.  . = 0x00000000;    //其實位址0  
  7.  . = ALIGN(4);      //4位元組對齊  
  8.  .text :        //代碼段  
  9.  {  
  10.    board/freescale/mx6q_sabresd/flash_header.o (.text.flasheader)   //第一個檔案是board/freescale/mx6q_sabresd/flash_header.o  
  11.    cpu/arm_cortexa8/start.o         //第二個cpu/arm_cortexa8/start.o   
  12.    board/freescale/mx6q_sabresd/libmx6q_sabresd.a (.text)  
  13.    lib_arm/libarm.a (.text)  
  14.    net/libnet.a (.text)  
  15.    drivers/mtd/libmtd.a (.text)  
  16.    drivers/mmc/libmmc.a (.text)  
  17.    . = DEFINED(env_offset) ? env_offset : .;  
  18.    common/env_embedded.o(.text)  
  19.    *(.text)             //剩餘的所有代碼  
  20.  }  
  21.  . = ALIGN(4);  
  22.  .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } //readonly data 段  
  23.  . = ALIGN(4);  
  24.  .data : { *(.data) }       //所有的readonly data  
  25.  . = ALIGN(4);  
  26.  .got : { *(.got) }  
  27.  . = .;  
  28.  __u_boot_cmd_start = .;        //u_boot_cmd段,裡面是所有uboot指令的一個清單  
  29.  .u_boot_cmd : { *(.u_boot_cmd) }  
  30.  __u_boot_cmd_end = .;  
  31.  . = ALIGN(4);  
  32.  _end_of_copy = .;  
  33.  __bss_start = .;           //bss段 就是記憶體資料段  
  34.  .bss : { *(.bss) }  
  35.  _end = .;  
  36. }  

從上面的代碼可以看出我們編譯生成的二進制應用程式組成是:代碼段->rodata段->uboot指令清單->bss段。我們 啟動 這個應用程式時候是從,0位址開始的,是以我們來看

board/freescale/mx6q_sabresd/flash_header.s這個檔案。

  這個檔案中除了配置設定記憶體和宏定義的僞彙編指令以外,真正執行的指令有一條

檢視文本 列印 ?

  1. .section ".text.flasheader", "x"  
  2.     b   _start  
  3.     .org    CONFIG_FLASH_HEADER_OFFSET  

也就是說,這個檔案一執行就直接跳到_start 位置處。_start 在android\bootable\bootloader\uboot-imx\cpu\arm_cortexa8\ start.S中,是以我們來看這個檔案代碼

檢視文本 列印 ?

  1. .globl _start  
  2. _start: b   reset         

這裡直接跳轉的reset中接下來看

檢視文本 列印 ?

  1. reset:  
  2.     mrs r0, cpsr  
  3.     bic r0, r0, #0x1f  
  4.     orr r0, r0, #0xd3  
  5.     msr cpsr,r0  
  6. #if (CONFIG_OMAP34XX)   //因為我們的cpu不是ompa的 是以這段不會編譯  
  7. .............................  
  8. #endif  
  9. #ifndef CONFIG_SKIP_LOWLEVEL_INIT  
  10.     bl  cpu_init_crit         
  11. #endif  

這裡接下來執行cpu_init_crit

檢視文本 列印 ?

  1. cpu_init_crit:  
  2.     mov r0, #0          @ set up for MCR  
  3.     mcr p15, 0, r0, c8, c7, 0   @ invalidate TLBs  
  4.     mcr p15, 0, r0, c7, c5, 0   @ invalidate icache  
  5.     mrc p15, 0, r0, c1, c0, 0  
  6.     bic r0, r0, #0x00002000 @ clear bits 13 (--V-)  
  7.     bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)  
  8.     orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align  
  9.     orr r0, r0, #0x00000800 @ set bit 12 (Z---) BTB  
  10.     mcr p15, 0, r0, c1, c0, 0  
  11.     mov ip, lr      @ persevere link reg across call  
  12.     bl  lowlevel_init   @ go setup pll,mux,memory//執行lowlevel_init這個函數代碼在  
  13.                             @\bootloader\uboot-imx\board\freescale\mx6q_sabresd\lowlevel_init.S中  
  14.                             @主要對時鐘,外部ram,rom等進行了初始化代碼不貼了。  
  15.     mov lr, ip      @ restore link  
  16.     mov pc, lr      @ back to my caller  

初始化完成後,接下來執行

檢視文本 列印 ?

  1. #ifndef CONFIG_SKIP_RELOCATE_UBOOT  
  2. relocate:               @ relocate U-Boot to RAM    将uboot重新定位到記憶體中  
  3.     adr r0, _start      @ r0 <- current position of code  
  4.     ldr r1, _TEXT_BASE      @ test if we run from flash or RAM   
  5.     cmp r0, r1          @ don't reloc during debug測試目前代碼是否已經在記憶體中  
  6.     beq stack_setup     @如果在的話就直接跳轉到stack_setup  
  7.     ldr r2, _armboot_start  @如果不在的話,加載_armboot_start位址到r2中。_armboot_start是uboot執行的主體c函數。  
  8.     ldr r3, _bss_start  
  9.     sub r2, r3, r2      @ r2 <- size of armboot計算bss_start-armboot_start 儲存到R2中,也就是uboot的總大小  
  10.     add r2, r0, r2      @ r2 <- source end address 計算出uboot代碼和rodata位址  
  11. copy_loop:              @ copy 32 bytes at a time   //開始拷貝  
  12.     ldmia   r0!, {r3 - r10}     @ copy from source address [r0]  
  13.     stmia   r1!, {r3 - r10}     @ copy to   target address [r1]  
  14.     cmp r0, r2          @ until source end addreee [r2]  
  15.     ble copy_loop  
  16. #endif    
  17. stack_setup:  
  18.     ldr r0, _TEXT_BASE      @ upper 128 KiB: relocated uboot  
  19.     sub r0, r0, #CONFIG_SYS_MALLOC_LEN @ malloc area//為c語言malloc函數配置設定記憶體  
  20.     sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE @ bdinfo  
  21. #ifdef CONFIG_USE_IRQ  
  22.     sub r0, r0, #(CONFIG_STACKSIZE_IRQ + CONFIG_STACKSIZE_FIQ)  
  23. #endif  
  24.     sub sp, r0, #12     @ leave 3 words for abort-stack//配置設定c語言堆棧  
  25.     and sp, sp, #~7     @ 8 byte alinged for (ldr/str)d  
  26. clear_bss:  
  27.     ldr r0, _bss_start      @ find start of bss segment //清除bss段  
  28.     ldr r1, _bss_end        @ stop here  
  29.     mov r2, #0x00000000     @ clear value  
  30. clbss_l:  
  31.     str r2, [r0]        @ clear BSS location  
  32.     cmp r0, r1          @ are we at the end yet  
  33.     add r0, r0, #4      @ increment clear index pointer  
  34.     bne clbss_l         @ keep clearing till at end  
  35. #ifdef CONFIG_ARCH_MMU  
  36.     bl board_mmu_init   //初始化mmu  
  37. #endif  
  38.     ldr pc, _start_armboot  @ jump to C code以上所有的初始化就已經完成了,接下類正式執行c語言代碼了。這才是我們的重點  
  39. _start_armboot: .word start_armboot  

接下來正式看C代碼,也就是start_armboot這個函數代碼在android\bootable\bootloader\uboot-imx\lib_arm\board.c中

檢視文本 列印 ?

  1. void start_armboot (void)  
  2. {  
  3.     init_fnc_t **init_fnc_ptr;  
  4.     char *s;  
  5. #if defined(CONFIG_VFD) || defined(CONFIG_LCD)  
  6.     unsigned long addr;  
  7. #endif  
  8.     gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));  
  9.     //配置設定一段記憶體.在cpu存儲控制器初始化之前,是不能通路外部ram的,是以需要一小段  
  10.     //記憶體來運作最初的初始化函數,這段記憶體一般是cpu内部ram  
  11.     __asm__ __volatile__("": : :"memory");  
  12.     memset ((void*)gd, 0, sizeof (gd_t));  
  13.     gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));  
  14.     memset (gd->bd, 0, sizeof (bd_t));  
  15.     gd->flags |= GD_FLG_RELOC;  
  16.     monitor_flash_len = _bss_start - _armboot_start;  
  17.     for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {   
  18.         if ((*init_fnc_ptr)() != 0) {  
  19.             hang ();  
  20.         }  
  21.     }  

注意看這裡init_sequence的定義

檢視文本 列印 ?

  1. init_fnc_t *init_sequence[] = {  
  2. #if defined(CONFIG_ARCH_CPU_INIT)  
  3.     arch_cpu_init,        
  4. #endif  
  5.     board_init,       
  6. #if defined(CONFIG_USE_IRQ)  
  7.     interrupt_init,       
  8. #endif  
  9.     timer_init,       
  10.     env_init,         
  11.     init_baudrate,        
  12.     serial_init,          
  13.     console_init_f,       
  14.     display_banner,       
  15. #if defined(CONFIG_DISPLAY_CPUINFO)  
  16.     print_cpuinfo,        
  17. #endif  
  18. #if defined(CONFIG_DISPLAY_BOARDINFO)  
  19.     checkboard,       
  20. #endif  
  21. #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)  
  22.     init_func_i2c,  
  23. #endif  
  24.     dram_init,        
  25. #if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)  
  26.     arm_pci_init,  
  27. #endif  
  28.     display_dram_config,  
  29.     NULL,  
  30. };  

這個是一個函數指針的數組,涉及到cpu的最後的一些初始化。到了這裡cpu的所有初始化都完成了

我們繼續看闆子的其他配置

檢視文本 列印 ?

  1. #ifdef CONFIG_LCD   //lcd緩存設定  
  2.     if (!gd->fb_base) {  
  3. #       ifndef PAGE_SIZE  
  4. #         define PAGE_SIZE 4096  
  5. #       endif  
  6.         addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);  
  7.         lcd_setmem (addr);  
  8.         gd->fb_base = addr;  
  9.     }  
  10. #endif   
  11.     env_relocate ();//設定環境變量 也就是printenv 列印出來的那些  
  12. #ifdef CONFIG_VFD  
  13.     drv_vfd_init(); //空函數  
  14. #endif   
  15. #ifdef CONFIG_SERIAL_MULTI  
  16.     serial_initialize();//序列槽初始化  
  17. #endif  
  18.     gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");  
  19. #if defined CONFIG_SPLASH_SCREEN && defined CONFIG_VIDEO_MX5  
  20.     setup_splash_image();//lcd顯示log  
  21. #endif  
  22.     //重新定義stdio的位置,在本環境中被定義到了序列槽上  
  23.     stdio_init ();    
  24.     jumptable_init ();//把一些初始化函數的指針放到gd中,為以後調用  
  25. #if defined(CONFIG_API)  
  26.     api_init ();  
  27. #endif  
  28.     console_init_r ();    
  29. #if defined(CONFIG_ARCH_MISC_INIT)  
  30.     arch_misc_init ();//空函數  
  31. #endif  
  32. #if defined(CONFIG_MISC_INIT_R)  
  33.     misc_init_r ();//空函數  
  34. #endif  
  35.     enable_interrupts ();//使能中斷  
  36. #ifdef CONFIG_DRIVER_TI_EMAC      
  37. extern void davinci_eth_set_mac_addr (const u_int8_t *addr);//不編譯  
  38.     if (getenv ("ethaddr")) {  
  39.         uchar enetaddr[6];       
  40.         eth_getenv_enetaddr("ethaddr", enetaddr);  
  41.         davinci_eth_set_mac_addr(enetaddr);  
  42.     }  
  43. #endif  
  44. #ifdef CONFIG_DRIVER_CS8900  
  45.     cs8900_get_enetaddr ();//不編譯  
  46. #endif  
  47. #if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)  
  48.     if (getenv ("ethaddr")) {  
  49.         uchar enetaddr[6];  
  50.         eth_getenv_enetaddr("ethaddr", enetaddr);//不編譯  
  51.         smc_set_mac_addr(enetaddr);  
  52.     }  
  53. #endif   
  54. #if defined(CONFIG_ENC28J60_ETH) && !defined(CONFIG_ETHADDR)  
  55.     extern void enc_set_mac_addr (void);//不編譯  
  56.     enc_set_mac_addr ();  
  57. #endif   
  58.     if ((s = getenv ("loadaddr")) != NULL) {  
  59.         load_addr = simple_strtoul (s, NULL, 16);  
  60.     }  
  61. #if defined(CONFIG_CMD_NET)  
  62.     if ((s = getenv ("bootfile")) != NULL) {  
  63.         copy_filename (BootFile, s, sizeof (BootFile));  
  64.     }  
  65. #endif  
  66. #ifdef BOARD_LATE_INIT  
  67.     board_late_init (); //初始化i2c,pmic等  
  68. #endif  

接下來涉及到了我們最關心的地方, 啟動模式 和 按鍵 響應

檢視文本 列印 ?

  1. #ifdef CONFIG_ANDROID_RECOVERY  
  2.     check_recovery_mode();  //檢測是否進入recovery  
  3. #endif  
  4. #if defined(CONFIG_CMD_NET)  
  5. #if defined(CONFIG_NET_MULTI)  
  6.     puts ("Net:   ");  
  7. #endif  
  8.     eth_initialize(gd->bd);  //根據gd的配置初始化以太網  
  9. #if defined(CONFIG_RESET_PHY_R)  
  10.     debug ("Reset Ethernet PHY\n");  
  11.     reset_phy();  
  12. #endif  
  13. #endif  
  14. #ifdef CONFIG_FASTBOOT  
  15.     check_fastboot_mode();  //檢測是否進入fastboot  
  16. #endif  

從代碼裡可以看出我們是首先檢測recovery,然後才檢測fastboot模式。

我們先來看原版是怎麼做的,首先是recovery

檢視文本 列印 ?

  1. void check_recovery_mode(void)  
  2. {  
  3.     if (check_key_pressing())  
  4.         setup_recovery_env();  
  5.     else if (check_recovery_cmd_file()) {  
  6.         puts("Recovery command file founded!\n");  
  7.         setup_recovery_env();  
  8.     }  
  9. }  

這裡首先檢測是否有合法的 按鍵 按下,如果有的話就配置環境變量進入recovery

沒有按鍵就檢測uboot指令檔案,看是不是主系統要求進入recovery

是以我們這裡的重點是check_key_pressing()這個函數,仔細研究發現這個函數用的是uboot

标準的gpio驅動,官方給我們移植的uboot裡面并沒有初始化這個驅動,而是自己另外寫的。也就是說

我們調用check_key_pressing()這個函數永遠都傳回0值而執行else if (check_recovery_cmd_file())這一句

我們來看 check_recovery_cmd_file()

檢視文本 列印 ?

  1. int check_recovery_cmd_file(void)  
  2. {  
  3.     int button_pressed = 0;  
  4.     int recovery_mode = 0;  
  5.     recovery_mode = check_and_clean_recovery_flag();//讀取kernel的recovery标志位,如果有的話就要進入recovery  
  6.     mxc_iomux_v3_setup_pad(MX6X_IOMUX(PAD_GPIO_5__GPIO_1_5));   //初始化vol down的gpio  
  7.     gpio_direction_input(GPIO_VOL_DN_KEY);  
  8.     if (gpio_get_value(GPIO_VOL_DN_KEY) == 0) { //如果vol down已經按下  
  9.         button_pressed = 1;  
  10.         printf("Recovery key pressed\n");  
  11.     }  
  12.     return recovery_mode || button_pressed; //傳回進入recovery  
  13. }  

也就是說官方修改的uboot走了偷懶的方法,直接在check_recovery_cmd_file()增加了一個按鍵的盤定。很不正規

是以我們下一步要修改它,從官方的基礎上走,我們也不走标準uboot 按鍵驅動,而是自己寫。

修改之前先來看原版fastboot怎麼進入的

檢視文本 列印 ?

  1. void check_fastboot_mode(void)  
  2. {  
  3.     if (fastboot_check_and_clean_flag())  
  4.         do_fastboot(NULL, 0, 0, 0);  
  5. }  

這裡調用fastboot_check_and_clean_flag()來判定是否進入fastboot

檢視文本 列印 ?

  1. int fastboot_check_and_clean_flag(void)  
  2. {  
  3.     int flag_set = 0;  
  4.     u32 reg;  
  5.     reg = readl(SRC_BASE_ADDR + SRC_GPR10);  
  6.     flag_set = !!(reg & ANDROID_FASTBOOT_BOOT);  
  7.     if (flag_set) {  
  8.         reg &= ~ANDROID_FASTBOOT_BOOT;  
  9.         writel(reg, SRC_BASE_ADDR + SRC_GPR10);  
  10.     }  
  11.     return flag_set;  
  12. }  

從這裡看出,要進入進入fastboot,隻能檢測(SRC_BASE_ADDR + SRC_GPR10)寄存器的

ANDROID_FASTBOOT_BOOT位是否被kernel置位,并沒有按鍵,是以我們的闆子不可能靠

按鍵進入fastboot的實際情況也确實這樣。是以我們要修改這一塊,由于我們的cpu在power鍵按住5s

以後會強制關機。是以開機後我們必須松開power鍵,我們闆子檢測的按鍵隻能是1個。開機時vol up鍵進入

recovery,按住vol down進入fastboot模式。我們修改代碼如下 

建立個按鍵檢測函數check_key()

檢視文本 列印 ?

  1. int check_key(void)  
  2. {  
  3.     #define PRESSED_VOLUP 1  
  4.     #define PRESSED_VOLDOWN 2  
  5.     #define KEY_MASK  (PRESSED_VOLUP|PRESSED_VOLDOWN)  
  6.     #define RECOVERY_KEY_MASK (PRESSED_VOLUP)  
  7.     #define FASTBOOT_KEY_MASK (PRESSED_VOLDOWN)  
  8.     int state = 0;  
  9.     mxc_iomux_v3_setup_pad(MX6X_IOMUX(PAD_GPIO_5__GPIO_1_5));//vol down  
  10.     mxc_iomux_v3_setup_pad(MX6X_IOMUX(PAD_GPIO_5__GPIO_1_4));//vol up  
  11.     gpio_direction_input(GPIO_VOL_DN_KEY);  
  12.     gpio_direction_input(GPIO_VOL_UP_KEY);  
  13.     if (gpio_get_value(GPIO_VOL_UP_KEY) == 0)  
  14.         state |= PRESSED_VOLUP;  
  15.     if (gpio_get_value(GPIO_VOL_DN_KEY) == 0)  
  16.         state |= PRESSED_VOLDOWN;  
  17.     //如果摁下power+voldown就進入fastboot 這個的優先級要比recovery高。  
  18.     //就算同時按下power+volup+voldown三個鍵也要進入fastboot<strong>模式</strong>  
  19.     if ((state & KEY_MASK) == FASTBOOT_KEY_MASK)      
  20.         return 1;  
  21.     if(((state & KEY_MASK) == FASTBOOT_KEY_MASK))  
  22.         return 2;  
  23.     return 0;  
  24. }  

主函數判定的代碼段修改為

檢視文本 列印 ?

  1. if (check_key()==1)  
  2.     do_fastboot(NULL, 0, 0, 0);  
  3. if (check_key()==2)      
  4.     setup_recovery_env();  
  5. if (check_and_clean_recovery_flag()) {  
  6.     setup_recovery_env();  
  7. }  
  8. if (fastboot_check_and_clean_flag())  
  9.     do_fastboot(NULL, 0, 0, 0);  

這樣我們的啟動模式按鍵就修改完成了,編譯後測試成功。

下面我們還有代碼沒有分析完:uboot的主循環:main_loop()

代碼在:\bootable\bootloader\uboot-imx\common\main.c

檢視文本 列印 ?

  1. void main_loop (void)  
  2. {  
  3. #ifndef CONFIG_SYS_HUSH_PARSER  
  4.     static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };  
  5.     int len;  
  6.     int rc = 1;  
  7.     int flag;  
  8. #endif  
  9. #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)  
  10.     char *s;  
  11.     int bootdelay;  
  12. #endif  
  13. #ifdef CONFIG_PREBOOT  
  14.     char *p;  
  15. #endif  
  16. #ifdef CONFIG_BOOTCOUNT_LIMIT  
  17.     unsigned long bootcount = 0;  
  18.     unsigned long bootlimit = 0;  
  19.     char *bcs;  
  20.     char bcs_set[16];  
  21. #endif   
  22. #if defined(CONFIG_VFD) && defined(VFD_TEST_LOGO)  
  23.     ulong bmp = 0;        
  24.     extern int trab_vfd (ulong bitmap);  
  25. #ifdef CONFIG_MODEM_SUPPORT  
  26.     if (do_mdm_init)  
  27.         bmp = 1;      
  28. #endif  
  29.     trab_vfd (bmp);  
  30. #endif    
  31. #if defined(CONFIG_UPDATE_TFTP)  
  32.     update_tftp ();  
  33. #endif   
  34. #ifdef CONFIG_BOOTCOUNT_LIMIT  
  35.     bootcount = bootcount_load();  
  36.     bootcount++;  
  37.     bootcount_store (bootcount);  
  38.     sprintf (bcs_set, "%lu", bootcount);  
  39.     setenv ("bootcount", bcs_set);  
  40.     bcs = getenv ("bootlimit");  
  41.     bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0;  
  42. #endif   
  43. #ifdef CONFIG_MODEM_SUPPORT  
  44.     debug ("DEBUG: main_loop:   do_mdm_init=%d\n", do_mdm_init);  
  45.     if (do_mdm_init) {  
  46.         char *str = strdup(getenv("mdm_cmd"));  
  47.         setenv ("preboot", str);    
  48.         if (str != NULL)  
  49.             free (str);  
  50.         mdm_init();   
  51.     }  
  52. #endif    
  53. #ifdef CONFIG_VERSION_VARIABLE  
  54.     {  
  55.         extern char version_string[];  
  56.         setenv ("ver", version_string);    
  57.     }  
  58. #endif   
  59. #ifdef CONFIG_SYS_HUSH_PARSER  
  60.     u_boot_hush_start ();  
  61. #endif  
  62. #if defined(CONFIG_HUSH_INIT_VAR)  
  63.     hush_init_var ();  
  64. #endif  
  65. #ifdef CONFIG_AUTO_COMPLETE  
  66.     install_auto_complete();  
  67. #endif  
  68. #ifdef CONFIG_PREBOOT  
  69.     if ((p = getenv ("preboot")) != NULL) {  
  70. # ifdef CONFIG_AUTOBOOT_KEYED  
  71.         int prev = disable_ctrlc(1);      
  72. # endif  
  73. # ifndef CONFIG_SYS_HUSH_PARSER  
  74.         run_command (p, 0);  
  75. # else  
  76.         parse_string_outer(p, FLAG_PARSE_SEMICOLON |  
  77.                     FLAG_EXIT_FROM_LOOP);  
  78. # endif  
  79. # ifdef CONFIG_AUTOBOOT_KEYED  
  80.         disable_ctrlc(prev);      
  81. # endif  
  82.     }  
  83. #endif   
  84. #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)  
  85.     s = getenv ("bootdelay");  
  86.     bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;//計算bootdelay  
  87.     debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);  
  88. # ifdef CONFIG_BOOT_RETRY_TIME  
  89.     init_cmd_timeout ();  
  90. # endif   
  91. #ifdef CONFIG_POST  
  92.     if (gd->flags & GD_FLG_POSTFAIL) {  
  93.         s = getenv("failbootcmd");  
  94.     }  
  95.     else  
  96. #endif   
  97. #ifdef CONFIG_BOOTCOUNT_LIMIT  
  98.     if (bootlimit && (bootcount > bootlimit)) {  
  99.         printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",  
  100.                 (unsigned)bootlimit);  
  101.         s = getenv ("altbootcmd");  
  102.     }  
  103.     else  
  104. #endif   
  105.         s = getenv ("bootcmd");//得到bootcmd指令  
  106.     debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");  
  107. //每10ms從控制台讀取一個字元,并且顯示倒計時。如果讀取成功的話就繼續執行main_loop代碼,  
  108. //如果失敗的話就執行下面的run_command(s,0)  
  109.     if (bootdelay >= 0 && s && !abortboot (bootdelay)) {  
  110. # ifdef CONFIG_AUTOBOOT_KEYED  
  111.         int prev = disable_ctrlc(1);      
  112. # endif  
  113. # ifndef CONFIG_SYS_HUSH_PARSER  
  114.         run_command (s, 0);//執行 bootcmd指令  
  115. # else  
  116.         parse_string_outer(s, FLAG_PARSE_SEMICOLON |  
  117.                     FLAG_EXIT_FROM_LOOP);  
  118. # endif  
  119. # ifdef CONFIG_AUTOBOOT_KEYED  
  120.         disable_ctrlc(prev);      
  121. # endif  
  122.     }  
  123. # ifdef CONFIG_MENUKEY  
  124.     if (menukey == CONFIG_MENUKEY) {  
  125.         s = getenv("menucmd");  
  126.         if (s) {  
  127. # ifndef CONFIG_SYS_HUSH_PARSER  
  128.         run_command (s, 0);  
  129. # else  
  130.         parse_string_outer(s, FLAG_PARSE_SEMICOLON |  
  131.                     FLAG_EXIT_FROM_LOOP);  
  132. # endif  
  133.         }  
  134.     }  
  135. #endif   
  136. #endif    
  137. #ifdef CONFIG_AMIGAONEG3SE  
  138.     {  
  139.         extern void video_banner(void);  
  140.         video_banner();  
  141.     }  
  142. #endif  
  143. #ifdef CONFIG_SYS_HUSH_PARSER  
  144.     parse_file_outer();  
  145.     for (;;);  
  146. #else  
  147.     for (;;) {  //如果bootdelay時候有<strong>按鍵</strong> 就進入指令處理<strong>模式</strong>  
  148. #ifdef CONFIG_BOOT_RETRY_TIME  
  149.         if (rc >= 0) {  
  150.             reset_cmd_timeout();  
  151.         }  
  152. #endif  
  153.         len = readline (CONFIG_SYS_PROMPT);//從控制台讀取一行資料,以回車為标志  
  154.         flag = 0;     
  155.         if (len > 0)  
  156.             z (lastcommand, console_buffer);  
  157.         else if (len == 0)  
  158.             flag |= CMD_FLAG_REPEAT;  
  159. #ifdef CONFIG_BOOT_RETRY_TIME  
  160.         else if (len == -2) {  
  161.             puts ("\nTimed out waiting for command\n");  
  162. # ifdef CONFIG_RESET_TO_RETRY  
  163.             do_reset (NULL, 0, 0, NULL);  
  164. # else  
  165.             return;       
  166. # endif  
  167.         }  
  168. #endif  
  169.         if (len == -1)  
  170.             puts ("<INTERRUPT>\n");  
  171.         else  
  172.             rc = run_command (lastcommand, flag);//處理這條指令  
  173.         if (rc <= 0) {  
  174.             lastcommand[0] = 0;  
  175.         }  
  176.     }  
  177. #endif   
  178. }     

到了這裡整個的uboot流程已經走完了。從這裡可以知道,uboot正式運作以後,實作的所有功能都是通過指令實作的,要繼續分析的話,就要分析uboot的指令的實作了。

我們在下一篇文章裡面講述uboot指令是怎麼實作的,kernel是怎麼啟動的。

繼續閱讀