閱讀115 返回首頁    go 阿裏雲 go 技術社區[雲棲]


U-Boot啟動過程

開發板上電後,執行U-Boot的第一條指令,然後順序執行U-Boot啟動函數。看一下board/smdk2410/u-boot.lds這個鏈接腳本,可以知道目標程序的各部分鏈接順序。第一個要鏈接的是cpu/arm920t/start.o,那麼U-Boot的入口指令一定位於這個程序中。下麵分兩階段介紹啟動流程:

                  

第一階段

1cpu/arm920t/start.S

這個匯編程序是U-Boot的入口程序,開頭就是複位向量的代碼。

_start: b       reset        //複位向量

       ldr   pc, _undefined_instruction

       ldr   pc, _software_interrupt

       ldr   pc, _prefetch_abort

       ldr   pc, _data_abort

       ldr   pc, _not_used

       ldr   pc, _irq      //中斷向量

       ldr   pc, _fiq      //中斷向量

 /* the actual reset code  */

reset:          //複位啟動子程序

       /* 設置CPUSVC32模式 */

       mrs   r0,cpsr

       bic   r0,r0,#0x1f

       orr   r0,r0,#0xd3

       msr   cpsr,r0

 

/* 關閉看門狗 */

……      ……

relocate:                  /* U-Boot重新定位到RAM */

adr   r0, _start          /* r0是代碼的當前位置 */

ldr   r1, _TEXT_BASE      /*_TEXT_BASERAM中的地址 */

cmp     r0, r1          /* 比較r0r1,判斷當前是從Flash啟動,還是RAM */

beq     stack_setup  /* 如果r0等於r1,跳過重定位代碼 */

 

/* 準備重新定位代碼 */

ldr   r2, _armboot_start

ldr   r3, _bss_start

sub   r2, r3, r2       /* r2 得到armboot的大小   */

add   r2, r0, r2      /* r2 得到要複製代碼的末尾地址 */

copy_loop: /* 重新定位代碼 */

ldmia r0!, {r3-r10}   /*從源地址[r0]複製 */

stmia r1!, {r3-r10}   /* 複製到目的地址[r1] */

cmp   r0, r2    /* 複製數據塊直到源數據末尾地址[r2] */

ble   copy_loop

 

/* 初始化堆棧等    */

stack_setup:

ldr   r0, _TEXT_BASE  /* 上麵是128 KiB重定位的u-boot */

sub   r0, r0, #CFG_MALLOC_LEN  /* 向下是內存分配空間 */

sub   r0, r0, #CFG_GBL_DATA_SIZE /* 然後是bdinfo結構體地址空間  */

#ifdef CONFIG_USE_IRQ

sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)

#endif

sub   sp, r0, #12     /* abort-stack預留3個字 */

clear_bss:

ldr   r0, _bss_start      /* 找到bss段起始地址 */

ldr   r1, _bss_end        /*  bss段末尾地址   */

mov   r2, #0x00000000     /* 清零 */

clbss_l:str r2, [r0]  

/* bss段地址空間清零循環...  */

       add   r0, r0, #4

       cmp   r0, r1

bne   clbss_l

 

/* 跳轉到start_armboot函數入口,_start_armboot字保存函數入口指針 */

ldr   pc, _start_armboot

_start_armboot: .word start_armboot     //start_armboot函數在lib_arm/board.c中實現

 

第二階段

2lib_arm/board.c

start_armbootU-Boot執行的第一個C語言函數,完成係統初始化工作,進入主循環,處理用戶輸入的命令。

 

3init_sequence[]

init_sequence[]數組保存著基本的初始化函數指針。

 

init_fnc_t *init_sequence[] = {

cpu_init,   /* 基本的處理器相關配置 -- cpu/arm920t/cpu.c */

board_init, /* 基本的板級相關配置 -- board/smdk2410/smdk2410.c */

interrupt_init,  /* 初始化中斷處理 -- cpu/arm920t/s3c24x0/interrupt.c */

env_init,      /* 初始化環境變量 -- common/cmd_flash.c */

init_baudrate,  /* 初始化波特率設置 -- lib_arm/board.c */

serial_init,  /* 串口通訊設置 -- cpu/arm920t/s3c24x0/serial.c */

console_init_f,       /* 控製台初始化階段1 -- common/console.c */

display_banner,       /* 打印u-boot信息 -- lib_arm/board.c */

dram_init,     /* 配置可用的RAM -- board/smdk2410/smdk2410.c */

display_dram_config,  /* 顯示RAM的配置大小 -- lib_arm/board.c */

NULL,

};

void start_armboot (void)

{

/* 順序執行init_sequence數組中的初始化函數 */

       for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {

              if ((*init_fnc_ptr)() != 0) {

                      hang ();

              }

       }

/*配置可用的Flash */

       size = flash_init ();

       display_flash_config (size);

       /* _armboot_start u-boot.lds鏈接腳本中定義 */

       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 */

}

 

命令實現

U-Boot作為Bootloader,具備多種引導內核啟動的方式。常用的gobootm命令可以直接引導內核映像啟動。U-Boot與內核的關係主要是內核啟動過程中參數的傳遞。

 

1go命令的實現

/* common/cmd_boot.c  */

int do_go (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])

{

       ulong addr, rc;

       int     rcode = 0;

       if (argc < 2) {

              printf ("Usage:\n%s\n", cmdtp->usage);

              return 1;

       }

       addr = simple_strtoul(argv[1], NULL, 16);

       printf ("## Starting application at 0x%08lX ...\n", addr);

       rc = ((ulong (*)(int, char []))addr) (--argc, &argv[1]);    /* 運行程序 */

    

if (rc != 0) rcode = 1;

       printf ("## Application terminated, rc = 0x%lX\n", rc);  /*如果是運行linux,這條指令是否能運行?*/

       return rcode;

}

 

go命令調用do_go()函數,跳轉到某個地址執行的。如果在這個地址準備好了自引導的內核映像,就可以啟動了。盡管go命令可以帶變參,實際使用時不用來傳遞參數。

 

2bootm命令的實現

/* common/cmd_bootm.c */

int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])

{

……        ……

/* 檢查頭部 */

if (crc32 (0, (uchar *)data, len) != checksum) {

       puts ("Bad Header Checksum\n");

       SHOW_BOOT_PROGRESS (-2);

       return 1;

  }

……          ……

/*解壓縮*/

  switch (hdr->ih_comp) {

  case IH_COMP_NONE:

       if(ntohl(hdr->ih_load) == addr) {

            printf ("   XIP %s ... ", name);

       } else {

#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)

            size_t l = len;

            void *to = (void *)ntohl(hdr->ih_load);

            void *from = (void *)data;

 

            printf ("   Loading %s ... ", name);

 

            while (l > 0) {

                size_t tail = (l > CHUNKSZ) ? CHUNKSZ : l;

                WATCHDOG_RESET();

                memmove (to, from, tail);

                to += tail;

                from += tail;

                l -= tail;

            }

#else   /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */

            memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);

#endif  /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */

       }

       break;

  case IH_COMP_GZIP:

       printf ("   Uncompressing %s ... ", name);

       if (gunzip ((void *)ntohl(hdr->ih_load), unc_len,

                (uchar *)data, &len) != 0) {

            puts ("GUNZIP ERROR - must RESET board to recover\n");

            SHOW_BOOT_PROGRESS (-6);

            do_reset (cmdtp, flag, argc, argv);

       }

       break;

#ifdef CONFIG_BZIP2

  case IH_COMP_BZIP2:

       printf ("   Uncompressing %s ... ", name);

       /*

        * If we've got less than 4 MB of malloc() space,

        * use slower decompression algorithm which requires

        * at most 2300 KB of memory.

        */

       i = BZ2_bzBuffToBuffDecompress ((char*)ntohl(hdr->ih_load),

                          &unc_len, (char *)data, len,

                          CFG_MALLOC_LEN < (4096 * 1024), 0);

       if (i != BZ_OK) {

            printf ("BUNZIP2 ERROR %d - must RESET board to recover\n", i);

            SHOW_BOOT_PROGRESS (-6);

            udelay(100000);

            do_reset (cmdtp, flag, argc, argv);

       }

       break;

#endif /* CONFIG_BZIP2 */

  default:

       if (iflag)

            enable_interrupts();

       printf ("Unimplemented compression type %d\n", hdr->ih_comp);

       SHOW_BOOT_PROGRESS (-7);

       return 1;

  }

}

……           ……             ……

 

switch (hdr->ih_os) {

     default:           /* handled by (original) Linux case */

     case IH_OS_LINUX:

         do_bootm_linux  (cmdtp, flag, argc, argv,

                   addr, len_ptr, verify);

         break;

     case IH_OS_NETBSD:

         do_bootm_netbsd (cmdtp, flag, argc, argv,

                   addr, len_ptr, verify);

         break;

     case IH_OS_RTEMS:

         do_bootm_rtems (cmdtp, flag, argc, argv,

                   addr, len_ptr, verify);

         break;

     case IH_OS_VXWORKS:

         do_bootm_vxworks (cmdtp, flag, argc, argv,

                    addr, len_ptr, verify);

         break;

     case IH_OS_QNX:

         do_bootm_qnxelf (cmdtp, flag, argc, argv,

                    addr, len_ptr, verify);

         break;

     }

bootm命令調用do_bootm函數。這個函數專門用來引導各種操作係統映像,可以支持引導LinuxvxWorksQNX等操作係統。引導Linux的時候,調用do_bootm_linux()函數。

 

3do_bootm_linux函數的實現

/* lib_arm/armlinux.c */

void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],

                   ulong addr, ulong *len_ptr, int verify)

{

theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);

… …                 … …

      /* we assume that the kernel is in place */

      printf ("\nStarting kernel ...\n\n");

… …                 … …

      theKernel (0, bd->bi_arch_number, bd->bi_boot_params);  /*啟動內核,傳遞啟動參數*/

}

 

do_bootm_linux()函數是專門引導Linux映像的函數,它還可以處理ramdisk文件係統的映像。這裏引導的內核映像和ramdisk映像,必須是U-Boot格式的。U-Boot格式的映像可以通過mkimage工具來轉換,其中包含了U-Boot可以識別的符號。

最後更新:2017-04-03 12:53:47

  上一篇:go AJAX - 基本知識
  下一篇:go Maven發布項目