從單片機到系統之--uboot啟動arm linux


UBOOT官網下載地址:http://ftp.denx.de/pub/u-boot/

很詳細的UBOOT解釋: https://www.crifan.com/files/doc/docbook/uboot_starts_analysis/release/html/uboot_starts_analysis.html

U-BOOT主要作用和執行流程

①一句話描述

  U-BOOT對硬件進行前期的初始化並准備堆棧,之后載入內核並向內核傳遞必要的參數,便於內核啟動。

②執行流程概況

  u-boot載入芯片后nor flash從0地址開始執行,nand flash會將前4K考入芯片的內存然后從0地址開始執行,程序最還是從_start標簽開始(lds文件定義,start.s有具體實現)。

  u-boot一般啟動分為兩階段:1.硬件相關的用匯編實現,初始化及重定位代碼完成后交給第二階段 2.第二階段較為通用,使用c代碼編寫。

1. UBoot內存划分, lds文件

  內存划分存在於對應芯片架構中的u-boot.lds文件中,這個文件是用於連接器連接時對程序各個段空間進行划分之用。

 
         

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)      //程序開始標號,既程序從標號為_start的位置開始執行,這個標號在對應CPU(如arm920t)的start.s文件中,接下來程序分析將分析這個文件

SECTIONS { . = 0x00000000; //程序最開始的地址為0 . = ALIGN(4);    //4字節地址對齊 .text :        //文本段開始位置 { __image_copy_start = .;  //拷貝image開始的地方(其實也是0) CPUDIR/start.o (.text)    //保證start.s的text段放在最前面 *(.text)            //u-boot中所有程序的文本段都會放到這個位置 } . = ALIGN(4);      //4字節對齊 .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }    //只讀數據從這里開始存放 . = ALIGN(4); .data : { *(.data)        //所有文件的數據段 } . = ALIGN(4); . = .;    //不i知道,可能重定位一下定位器的位置比較好玩兒吧 __u_boot_cmd_start = .;  //同上,定義了一個宏,保存當前位置信息 .u_boot_cmd : { *(.u_boot_cmd) }    //存儲u_boot_cmd __u_boot_cmd_end = .; . = ALIGN(4); __image_copy_end = .;     .rel.dyn : { __rel_dyn_start = .; *(.rel*) __rel_dyn_end = .; } .dynsym : { __dynsym_start = .; *(.dynsym) } _end = .; /* * Deprecated: this MMU section is used by pxa at present but * should not be used by new boards/CPUs. */ . = ALIGN(4096); .mmutable : { *(.mmutable) } .bss __rel_dyn_start (OVERLAY) : { __bss_start = .; *(.bss) . = ALIGN(4); __bss_end__ = .; } /DISCARD/ : { *(.dynstr*) } /DISCARD/ : { *(.dynamic*) } /DISCARD/ : { *(.plt*) } /DISCARD/ : { *(.interp*) } /DISCARD/ : { *(.gnu*) } }

  

2. 按代碼執行流程分析各個函數

  2.1 主要功能

  1. 設置CPU模式
  2. 關閉看門狗
  3. 關閉中斷
  4. 設置堆棧sp指針
  5. 清除bss段
  6. 異常中斷處理

  2.2 主要函數

函數入口:

.globl _start    //全局聲明,在lds中定義為entry
_start:    b    start_code      //主要執行函數,進入代碼后直接跳轉到start_code
    ldr    pc, _undefined_instruction  //ldr,將地址導入寄存器,pc為程序指針。導入pc后會進行調用跳轉
    ldr    pc, _software_interrupt
    ldr    pc, _prefetch_abort
    ldr    pc, _data_abort
    ldr    pc, _not_used
    ldr    pc, _irq
    ldr    pc, _fiq

 初始化函數:start_code

start_code: /* * set the cpu to SVC32 mode */ mrs r0, cpsr      //cspr值存入寄存器,方便修改
    bic    r0, r0, #0x1f    //將低5位【4:0】清零
    orr    r0, r0, #0xd3    //或操作,進行置位
    msr    cpsr, r0        //修改后的cspr放入寄存器 /*以上為設置cspr值的代碼段,使系統進入SVC方式*/
#if    defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK)
    /* * relocate exception table */ ldr r0, =_start  //標號地址放入r0,重載時確定起始位置
    ldr    r1, =0x0    //地址0x00放入r1, 重載后為0地址
    mov    r2, #16     //循環次數計數, 16*4 存前64字節?
copyex: subs r2, r2, #1  //減數做循環
    ldr    r3, [r0], #4  //r0地址存入r3,並且r9加4
    str    r3, [r1], #4  //r3地址數據(start開始的地方),放入r1的地址(0地址),r1加4
    bne    copyex    //r2不為0則循環寫數據,因為r2做了減法,cspr狀態值改變只會因為r2的操作產生
#endif #ifdef CONFIG_S3C24X0 /* turn off the watchdog */ # if defined(CONFIG_S3C2400) # define pWTCON 0x15300000 # define INTMSK 0x14400008    /* Interrupt-Controller base addresses */ # define CLKDIVN 0x14800014    /* clock divisor register */
#else # define pWTCON 0x53000000 # define INTMSK 0x4A000008    /* Interrupt-Controller base addresses */ # define INTSUBMSK 0x4A00001C # define CLKDIVN 0x4C000014    /* clock divisor register */ # endif ldr r0, =pWTCON    //看門狗地址存入r0,查datasheet
    mov    r1, #0x0      //0存入r1
    str    r1, [r0]      //看門狗地址存入0,既關看門狗

    /* * mask all IRQs by setting all bits in the INTMR - default */ mov r1, #0xffffffff ldr r0, =INTMSK str r1, [r0]    //中斷地址存入全1,禁止中斷
# if defined(CONFIG_S3C2410) ldr r1, =0x3ff ldr r0, =INTSUBMSK str r1, [r0] # endif /* FCLK:HCLK:PCLK = 1:2:4 */
    /* default FCLK is 120 MHz ! */ ldr r0, =CLKDIVN mov r1, #3 str r1, [r0]  //修改時鍾參數
#endif    /* CONFIG_S3C24X0 */

    /* * we do sys-critical inits only at reboot, * not when booting from ram! */ #ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_crit    //非從ram啟動時會調用這個函數
#endif

/* Set stackpointer in internal RAM to call board_init_f */ call_board_init_f:    //設置了lowlevel初始化從這里初始化板子
    ldr    sp, =(CONFIG_SYS_INIT_SP_ADDR)    //設置棧頂指針,宏在config.mk
    bic    sp, sp, #7 /* 8-byte alignment for ABI compliance */ //清除后四位,8字節對齊
    ldr    r0,=0x00000000 bl board_init_f    //調用函數,bl相對於b調用區別:會填充R4(lr)寄存器,調用結束會返回,函數在board.c中。返回后重新執行下面的函數,正好對應c中的relocate_code
                 //
cpu_init_crit最終也會調用這個函數  
 

 

非從RAM啟動的情況會執行這個函數:cpu_init_crit

cpu_init_crit: /* * flush v4 I/D caches */ mov r0, #0
    mcr    p15, 0, r0, c7, c7, 0    //關閉ICaches(指令緩存,關閉是為了降低MMU查表帶來的開銷)和DCaches(數據緩存,DCaches使用的是虛擬地址,開啟MMU之前必須關閉)
    mcr    p15, 0, r0, c8, c7, 0    //使無效整個數據TLB和指令TLB(TLB就是負責將虛擬內存地址翻譯成實際的物理內存地址
    /* * disable MMU stuff and caches */
  mrc    p15, 0, r0, c1, c0, 0
  bic    r0, r0, #0x00002300    @ clear bits 13, 9:8 (--V- --RS) //bit8:系統不保護,bit9:ROM不保護,bit13:設置中斷向量表的位置為0x0~0x1c,即異常模式基地址為0X0
  bic    r0, r0, #0x00000087    @ clear bits 7, 2:0 (B--- -CAM) //bit0~2:禁止MMU,禁止地址對齊檢查,禁止數據Cache.bit7:設為小端模式
  orr    r0, r0, #0x00000002    @ set bit 2 (A) Align //bit2:開啟數據Cache
  orr    r0, r0, #0x00001000    @ set bit 12 (I) I-Cache //bit12:開啟指令Cache
  mcr    p15, 0, r0, c1, c0, 0
    /* * before relocating, we have to setup RAM timing * because memory timing is board-dependend, you will * find a lowlevel_init.S in your board directory. */ mov ip, lr    //保存lr的值,用於返回調用cpu_init_crit的函數處,用於調用board_init_f
bl lowlevel_init //函數在對應的board文件夾中,第二階段啟動代碼,主要作用是初始化兩個重要數據結構,對SDRAM的內存分配設置,對各種需要用到的外設進行初始化,最后循環跳入main_loop()函數
mov lr, ip
mov pc, lr

 board_init_f函數

涉及到兩個重要的數據結構:1)bd_t結構體,關於開發板信息(波特率,ip, 平台號,啟動參數)。2)gd_t結構體成員主要是一些全局的系統初始化參數。需要用到時,用宏定義DECLARD_GLOBAL_DATA_PTT,指定占用寄存器r8,具體定義如下:

typedef struct bd_info {
                 int                  bi_baudrate; /* serial console baudrate串口波特率 */
                 unsigned long                  bi_ip_addr; /* IP Address IP 地址*/
                 ulong                  bi_arch_number; /* unique id for this board 板子的id */
                 ulong                  bi_boot_params; /* where this board expects params 啟動參數*/
                 struct                  /* RAM configuration RAM 配置*/
                 { 
                 ulong start;
                 ulong size;
                 }                  bi_dram[CONFIG_NR_DRAM_BANKS];
         } bd_t;
Gd_t結構體定義,如下:
        typedef struct global_data {
                bd_t *bd;
                unsigned long flags; //指示標志,如設備已經初始化標志等
                unsigned long baudrate; //串行口通信速率
                unsigned long have_console; /* serial_init() was called */
          #ifdef CONFIG_PRE_CONSOLE_BUFFER
                unsigned long precon_buf_idx; /* Pre-Console buffer index */
          #endif
                unsigned long env_addr; /* Address of Environment struct 環境參數地址 */
                     unsigned long env_valid; /* Checksum of Environment valid? 環境參數CRC檢驗有效標志*/
                unsigned long fb_base; /* base address of frame buffer 幀緩沖區基地址*/
         ……
         } gd_t;

 

c函數,計算重定位后地址分配,填充bd和gd數據:board_init_f
void board_init_f(ulong bootflag)
{
    bd_t *bd;
    init_fnc_t **init_fnc_ptr;
    gd_t *id;
    ulong addr, addr_sp;
#ifdef CONFIG_PRAM
    ulong reg;
#endif

    bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_F, "board_init_f");

    /* Pointer is writable since we allocated a register for it */
    gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07);
    /* compiler optimization barrier needed for GCC >= 3.4 */
    __asm__ __volatile__("": : :"memory");   /*內存屏障,防止編譯器優化賦值順序*/

    memset((void *)gd, 0, sizeof(gd_t));

    gd->mon_len = _bss_end_ofs;   /*_bss_end_ofs的定義在start.S中: .globl _bss_end_ofs,這里計算出來了U-BOOT的大小,用於后面做減法計算重定位的起始地址*/
#ifdef CONFIG_OF_EMBED
    /* Get a pointer to the FDT */
    gd->fdt_blob = _binary_dt_dtb_start;
#elif defined CONFIG_OF_SEPARATE
    /* FDT is at end of image */
    gd->fdt_blob = (void *)(_end_ofs + _TEXT_BASE);
#endif
    /* Allow the early environment to override the fdt address */
    gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16,
                        (uintptr_t)gd->fdt_blob);

    for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {  /*初始化函數的數組,會逐個調用數組中的初始化函數*/
        if ((*init_fnc_ptr)() != 0) {
            hang ();
        }
    }

#ifdef CONFIG_OF_CONTROL
    /* For now, put this check after the console is ready */
    if (fdtdec_prepare_fdt()) {
        panic("** CONFIG_OF_CONTROL defined but no FDT - please see "
            "doc/README.fdt-control");
    }
#endif

    debug("monitor len: %08lX\n", gd->mon_len);
    /*
     * Ram is setup, size stored in gd !!
     */
    debug("ramsize: %08lX\n", gd->ram_size);
#if defined(CONFIG_SYS_MEM_TOP_HIDE)
    /*
     * Subtract specified amount of memory to hide so that it won't
     * get "touched" at all by U-Boot. By fixing up gd->ram_size
     * the Linux kernel should now get passed the now "corrected"
     * memory size and won't touch it either. This should work
     * for arch/ppc and arch/powerpc. Only Linux board ports in
     * arch/powerpc with bootwrapper support, that recalculate the
     * memory size from the SDRAM controller setup will have to
     * get fixed.
     */
    gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
#endif

    addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;   /*這樣addr是內存的高地址,0x30000000+256M, 關於gd->ram_size,在初始化函數dram_init中,已經給過值了*/ 
#ifdef CONFIG_LOGBUFFER
#ifndef CONFIG_ALT_LB_ADDR
    /* reserve kernel log buffer */
    addr -= (LOGBUFF_RESERVE);
    debug("Reserving %dk for kernel logbuffer at %08lx\n", LOGBUFF_LEN,
        addr);
#endif
#endif

#ifdef CONFIG_PRAM
    /*
     * reserve protected RAM
     */
    reg = getenv_ulong("pram", 10, CONFIG_PRAM);    /*從環境變量獲取代碼地址,pram*/
    addr -= (reg << 10);        /* size is in kB */
    debug("Reserving %ldk for protected RAM at %08lx\n", reg, addr);
#endif /* CONFIG_PRAM */

#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
    /* reserve TLB table */
    addr -= (4096 * 4);

    /* round down to next 64 kB limit */
    addr &= ~(0x10000 - 1);   //64K對齊

    gd->tlb_addr = addr;
    debug("TLB table at: %08lx\n", addr);
#endif

    /* round down to next 4 kB limit */
    addr &= ~(4096 - 1);  // K對齊,此處前面已經64K對齊了,就不需改動
    debug("Top of RAM usable for U-Boot at: %08lx\n", addr);

#ifdef CONFIG_LCD
#ifdef CONFIG_FB_ADDR
    gd->fb_base = CONFIG_FB_ADDR;
#else
    /* reserve memory for LCD display (always full pages) */
    addr = lcd_setmem(addr);
    gd->fb_base = addr;
#endif /* CONFIG_FB_ADDR */
#endif /* CONFIG_LCD */

    /*
     * reserve memory for U-Boot code, data & bss
     * round down to next 4 kB limit
     */
    addr -= gd->mon_len;  /*重定位地址再減去U-BOOT的大小,此時就是重定位時代碼要拷貝到的目的位置*/
    addr &= ~(4096 - 1);

    debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr);

#ifndef CONFIG_SPL_BUILD
    /*
     * reserve memory for malloc() arena
     */
    addr_sp = addr - TOTAL_MALLOC_LEN;   /*當前地址減去預留給堆的空間后,作為棧的初始地址*/
    debug("Reserving %dk for malloc() at: %08lx\n",
            TOTAL_MALLOC_LEN >> 10, addr_sp);
    /*
     * (permanently) allocate a Board Info struct
     * and a permanent copy of the "global" data
     */
    addr_sp -= sizeof (bd_t);  /*棧中用於放bd_t的數據,預留出足夠的空間*/
    bd = (bd_t *) addr_sp;
    gd->bd = bd;   /*bd指向新分配的地址,代碼重定位把數據copy過去后可以直接使用地址指針*/
    debug("Reserving %zu Bytes for Board Info at: %08lx\n",
            sizeof (bd_t), addr_sp);

#ifdef CONFIG_MACH_TYPE
    gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */
#endif

    addr_sp -= sizeof (gd_t);  /*棧頂指針減法,預留出gd_t的空間,參考bd_t*/
    id = (gd_t *) addr_sp;
    debug("Reserving %zu Bytes for Global Data at: %08lx\n",
            sizeof (gd_t), addr_sp);

    /* setup stackpointer for exeptions */
    gd->irq_sp = addr_sp;  /*此時棧頂的地址放入gd結構體,作為ird中斷使用的棧頂地址*/
#ifdef CONFIG_USE_IRQ
    addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
    debug("Reserving %zu Bytes for IRQ stack at: %08lx\n",
        CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp);
#endif
    /* leave 3 words for abort-stack    */
    addr_sp -= 12;  /*為中斷預留的棧空間*/ /* 8-byte alignment for ABI compliance */
    addr_sp &= ~0x07;
#else
    addr_sp += 128;    /* leave 32 words for abort-stack   */
    gd->irq_sp = addr_sp;
#endif

    debug("New Stack Pointer is: %08lx\n", addr_sp);

#ifdef CONFIG_POST
    post_bootmode_init();
    post_run(NULL, POST_ROM | post_bootmode_get(0));
#endif

    gd->bd->bi_baudrate = gd->baudrate;
    /* Ram ist board specific, so move it to board code ... */
    dram_init_banksize();
    display_dram_config();    /* and display it */

    gd->relocaddr = addr;    /*u-boot重新搬運后的起始地址。*/
    gd->start_addr_sp = addr_sp;
    gd->reloc_off = addr - _TEXT_BASE;
    debug("relocation Offset is: %08lx\n", gd->reloc_off);
    memcpy(id, (void *)gd, sizeof(gd_t));

    relocate_code(addr_sp, id, addr);  /*relocate_code(addr_sp, id, addr);在start.S中定義,C又回到了匯編*/

    /* NOTREACHED - relocate_code() does not return */
}

 

 

被c函數調用,用於重定位代碼,存在於start.s中:relocate_code
    .globl    relocate_code
relocate_code:
    mov    r4, r0    /* save addr_sp */
    mov    r5, r1    /* save addr of gd */
    mov    r6, r2    /* save addr of destination */
  /*以上為保存傳入的3個參數*/
    /* Set up the stack                            */
stack_setup:
    mov    sp, r4

    adr    r0, _start
    cmp    r0, r6
    beq    clear_bss        /* skip relocation */
    mov    r1, r6            /* r1 <- scratch for copy_loop */
    ldr    r3, _bss_start_ofs
    add    r2, r0, r3        /* r2 <- source end address        */

copy_loop:
    ldmia    r0!, {r9-r10}        /* copy from source address [r0]    */
    stmia    r1!, {r9-r10}        /* copy to   target address [r1]    */
    cmp    r0, r2            /* until source end address [r2]    */
    blo    copy_loop
/*上述代碼將U-BOOT拷貝到了新的重定位后的位置,即上面片段計算出來的addr的地址*/
#ifndef CONFIG_SPL_BUILD
    /*
     * fix .rel.dyn relocations
     */
/*使用 .rel.dyn字段進行重定位*/
ldr r0, _TEXT_BASE /* r0 <- Text base */ sub r9, r6, r0 /* r9 <- relocation offset */ ldr r10, _dynsym_start_ofs /* r10 <- sym table ofs */ add r10, r10, r0 /* r10 <- sym table in FLASH */ ldr r2, _rel_dyn_start_ofs /* r2 <- rel dyn start ofs */ add r2, r2, r0 /* r2 <- rel dyn start in FLASH */ ldr r3, _rel_dyn_end_ofs /* r3 <- rel dyn end ofs */ add r3, r3, r0 /* r3 <- rel dyn end in FLASH */ fixloop: ldr r0, [r2] /* r0 <- location to fix up, IN FLASH! */ add r0, r0, r9 /* r0 <- location to fix up in RAM */ ldr r1, [r2, #4] and r7, r1, #0xff cmp r7, #23 /* relative fixup? */ beq fixrel cmp r7, #2 /* absolute fixup? */ beq fixabs /* ignore unknown type of fixup */ b fixnext fixabs: /* absolute fix: set location to (offset) symbol value */ mov r1, r1, LSR #4 /* r1 <- symbol index in .dynsym */ add r1, r10, r1 /* r1 <- address of symbol in table */ ldr r1, [r1, #4] /* r1 <- symbol value */ add r1, r1, r9 /* r1 <- relocated sym addr */ b fixnext fixrel: /* relative fix: increase location by offset */ ldr r1, [r0] add r1, r1, r9 fixnext: str r1, [r0] add r2, r2, #8 /* each rel.dyn entry is 8 bytes */ cmp r2, r3 blo fixloop #endif clear_bss: #ifndef CONFIG_SPL_BUILD ldr r0, _bss_start_ofs ldr r1, _bss_end_ofs mov r4, r6 /* reloc addr */ add r0, r0, r4 add r1, r1, r4 mov r2, #0x00000000 /* clear */ clbss_l:str r2, [r0] /* clear loop... */ add r0, r0, #4 cmp r0, r1 bne clbss_l bl coloured_LED_init bl red_led_on #endif /* * We are done. Do not return, instead branch to second part of board * initialization, now running from RAM. */ #ifdef CONFIG_NAND_SPL ldr r0, _nand_boot_ofs mov pc, r0 _nand_boot_ofs: .word nand_boot      //nand啟動 #else ldr r0, _board_init_r_ofs adr r1, _start add lr, r0, r1 add lr, lr, r9 /* setup parameters for board_init_r */ mov r0, r5 /* gd_t */ mov r1, r6 /* dest_addr */ /* jump to it ... */ mov pc, lr    //這里lr為board_init_r地址 _board_init_r_ofs: .word board_init_r - _start #endif _rel_dyn_start_ofs: .word __rel_dyn_start - _start _rel_dyn_end_ofs: .word __rel_dyn_end - _start _dynsym_start_ofs: .word __dynsym_start - _start

 

第二階段:board_init_r最終調用main_loop解析參數啟動內核
void board_init_r(gd_t *id, ulong dest_addr)
{
    ulong malloc_start;
#if !defined(CONFIG_SYS_NO_FLASH)
    ulong flash_size;
#endif

    gd = id;

    gd->flags |= GD_FLG_RELOC;    /* tell others: relocation done */
    bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_R, "board_init_r");

    monitor_flash_len = _end_ofs;

    /* Enable caches */
    enable_caches();

    debug("monitor flash len: %08lX\n", monitor_flash_len);
    board_init();    /* Setup chipselects */  /*設置板級的初始化*/
    /*
     * TODO: printing of the clock inforamtion of the board is now
     * implemented as part of bdinfo command. Currently only support for
     * davinci SOC's is added. Remove this check once all the board
     * implement this.
     */
#ifdef CONFIG_CLOCKS
    set_cpu_clk_info(); /* Setup clock information */
#endif
#ifdef CONFIG_SERIAL_MULTI
    serial_initialize();
#endif

    debug("Now running in RAM - U-Boot at: %08lx\n", dest_addr);

#ifdef CONFIG_LOGBUFFER
    logbuff_init_ptrs();
#endif
#ifdef CONFIG_POST
    post_output_backlog();
#endif

    /* The Malloc area is immediately below the monitor copy in DRAM */
    malloc_start = dest_addr - TOTAL_MALLOC_LEN;  /*剩余出堆內存的空間*/
    mem_malloc_init (malloc_start, TOTAL_MALLOC_LEN);

#if !defined(CONFIG_SYS_NO_FLASH)
    puts("Flash: ");

    flash_size = flash_init();
    if (flash_size > 0) {
# ifdef CONFIG_SYS_FLASH_CHECKSUM
        char *s = getenv("flashchecksum");

        print_size(flash_size, "");
        /*
         * Compute and print flash CRC if flashchecksum is set to 'y'
         *
         * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX
         */
        if (s && (*s == 'y')) {
            printf("  CRC: %08X", crc32(0,
                (const unsigned char *) CONFIG_SYS_FLASH_BASE,
                flash_size));
        }
        putc('\n');
# else    /* !CONFIG_SYS_FLASH_CHECKSUM */
        print_size(flash_size, "\n");
# endif /* CONFIG_SYS_FLASH_CHECKSUM */
    } else {
        puts(failed);
        hang();
    }
#endif

#if defined(CONFIG_CMD_NAND)
    puts("NAND:  ");
    nand_init();        /* go init the NAND */
#endif

#if defined(CONFIG_CMD_ONENAND)
    onenand_init();
#endif

#ifdef CONFIG_GENERIC_MMC
       puts("MMC:   ");
       mmc_initialize(gd->bd);
#endif

#ifdef CONFIG_HAS_DATAFLASH
    AT91F_DataflashInit();
    dataflash_print_info();
#endif

    /* initialize environment */
    env_relocate();

#if defined(CONFIG_CMD_PCI) || defined(CONFIG_PCI)
    arm_pci_init();
#endif

    /* IP Address */
    gd->bd->bi_ip_addr = getenv_IPaddr("ipaddr");

    stdio_init();    /* get the devices list going. */

    jumptable_init();

#if defined(CONFIG_API)
    /* Initialize API */
    api_init();
#endif

    console_init_r();    /* fully init console as a device */

#if defined(CONFIG_ARCH_MISC_INIT)
    /* miscellaneous arch dependent initialisations */
    arch_misc_init();
#endif
#if defined(CONFIG_MISC_INIT_R)
    /* miscellaneous platform dependent initialisations */
    misc_init_r();
#endif

     /* set up exceptions */
    interrupt_init();
    /* enable exceptions */
    enable_interrupts();

    /* Perform network card initialisation if necessary */
#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)
    /* XXX: this needs to be moved to board init */
    if (getenv("ethaddr")) {
        uchar enetaddr[6];
        eth_getenv_enetaddr("ethaddr", enetaddr);
        smc_set_mac_addr(enetaddr);
    }
#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */

    /* Initialize from environment */
    load_addr = getenv_ulong("loadaddr", 16, load_addr);
#if defined(CONFIG_CMD_NET)
    {
        char *s = getenv("bootfile");

        if (s != NULL)
            copy_filename(BootFile, s, sizeof(BootFile));
    }
#endif

#ifdef CONFIG_BOARD_LATE_INIT
    board_late_init();
#endif

#ifdef CONFIG_BITBANGMII
    bb_miiphy_init();
#endif
#if defined(CONFIG_CMD_NET)
    puts("Net:   ");
    eth_initialize(gd->bd);
#if defined(CONFIG_RESET_PHY_R)
    debug("Reset Ethernet PHY\n");
    reset_phy();
#endif
#endif

#ifdef CONFIG_POST
    post_run(NULL, POST_RAM | post_bootmode_get(0));
#endif

#if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER)
    /*
     * Export available size of memory for Linux,
     * taking into account the protected RAM at top of memory
     */
    {
        ulong pram = 0;
        uchar memsz[32];

#ifdef CONFIG_PRAM
        pram = getenv_ulong("pram", 10, CONFIG_PRAM);
#endif
#ifdef CONFIG_LOGBUFFER
#ifndef CONFIG_ALT_LB_ADDR
        /* Also take the logbuffer into account (pram is in kB) */
        pram += (LOGBUFF_LEN + LOGBUFF_OVERHEAD) / 1024;
#endif
#endif
        sprintf((char *)memsz, "%ldk", (gd->ram_size / 1024) - pram);
        setenv("mem", (char *)memsz);
    }
#endif

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

    /* NOTREACHED - no way out of command loop except booting */
}

 main.c中的main_loop函數

main_loop函數分析參考這里: https://blog.csdn.net/andy_wsj/article/details/8614905

執行到啟動內核的調用流程:parse_file_outer->parse_stream_outer->run_list->run_list_real->run_pipe_real->cmd_process->(通過注冊到u_boot_cmd結構的的do_bootm函數后向下繼續調用)->do_bootm->do_bootm_linux->【boot_prep_linux(填充a_tag),boot_jump_linux(控制權移交內核,內核入口從image的laodaddr獲取)】,到此u_boot壽終正寢,權限交給內核。

void main_loop (void)
{
#ifndef CONFIG_SYS_HUSH_PARSER
    static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };
    int len;
    int rc = 1;
    int flag;
#endif

#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
    char *s;
    int bootdelay;
#endif
#ifdef CONFIG_PREBOOT
    char *p;
#endif
#ifdef CONFIG_BOOTCOUNT_LIMIT
    unsigned long bootcount = 0;
    unsigned long bootlimit = 0;
    char *bcs;
    char bcs_set[16];
#endif /* CONFIG_BOOTCOUNT_LIMIT */

#ifdef CONFIG_BOOTCOUNT_LIMIT
    bootcount = bootcount_load();
    bootcount++;
    bootcount_store (bootcount);
    sprintf (bcs_set, "%lu", bootcount);
    setenv ("bootcount", bcs_set);
    bcs = getenv ("bootlimit");
    bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0;
#endif /* CONFIG_BOOTCOUNT_LIMIT */

#ifdef CONFIG_MODEM_SUPPORT
    debug ("DEBUG: main_loop:   do_mdm_init=%d\n", do_mdm_init);
    if (do_mdm_init) {
        char *str = strdup(getenv("mdm_cmd"));
        setenv ("preboot", str);  /* set or delete definition */
        if (str != NULL)
            free (str);
        mdm_init(); /* wait for modem connection */
    }
#endif  /* CONFIG_MODEM_SUPPORT */

#ifdef CONFIG_VERSION_VARIABLE
    {
        setenv ("ver", version_string);  /* set version variable */
    }
#endif /* CONFIG_VERSION_VARIABLE */

#ifdef CONFIG_SYS_HUSH_PARSER
    u_boot_hush_start ();
#endif

#if defined(CONFIG_HUSH_INIT_VAR)
    hush_init_var ();
#endif

#ifdef CONFIG_PREBOOT
    if ((p = getenv ("preboot")) != NULL) {
# ifdef CONFIG_AUTOBOOT_KEYED
        int prev = disable_ctrlc(1);    /* disable Control C checking */
# endif

        run_command(p, 0);

# ifdef CONFIG_AUTOBOOT_KEYED
        disable_ctrlc(prev);    /* restore Control C checking */
# endif
    }
#endif /* CONFIG_PREBOOT */

#if defined(CONFIG_UPDATE_TFTP)
    update_tftp (0UL);
#endif /* CONFIG_UPDATE_TFTP */

#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
    s = getenv ("bootdelay");
    bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;

    debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);

#if defined(CONFIG_MENU_SHOW)
    bootdelay = menu_show(bootdelay);
#endif
# ifdef CONFIG_BOOT_RETRY_TIME
    init_cmd_timeout ();
# endif    /* CONFIG_BOOT_RETRY_TIME */

#ifdef CONFIG_POST
    if (gd->flags & GD_FLG_POSTFAIL) {
        s = getenv("failbootcmd");
    }
    else
#endif /* CONFIG_POST */
#ifdef CONFIG_BOOTCOUNT_LIMIT
    if (bootlimit && (bootcount > bootlimit)) {
        printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
                (unsigned)bootlimit);
        s = getenv ("altbootcmd");
    }
    else
#endif /* CONFIG_BOOTCOUNT_LIMIT */
        s = getenv ("bootcmd");

    debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");

    if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
# ifdef CONFIG_AUTOBOOT_KEYED
        int prev = disable_ctrlc(1);    /* disable Control C checking */
# endif

        run_command(s, 0);

# ifdef CONFIG_AUTOBOOT_KEYED
        disable_ctrlc(prev);    /* restore Control C checking */
# endif
    }

# ifdef CONFIG_MENUKEY
    if (menukey == CONFIG_MENUKEY) {
        s = getenv("menucmd");
        if (s)
            run_command(s, 0);
    }
#endif /* CONFIG_MENUKEY */
#endif /* CONFIG_BOOTDELAY */

    /*
     * Main Loop for Monitor Command Processing
     */
#ifdef CONFIG_SYS_HUSH_PARSER
    parse_file_outer();    /*2410走這里,最終通過cmd_process根據命令行的入參調用內核啟動,啟動函數定義在U_BOOT_CMD結構體定義的結構中*/ /* This point is never reached */
    for (;;);
#else
    for (;;) {
#ifdef CONFIG_BOOT_RETRY_TIME
        if (rc >= 0) {
            /* Saw enough of a valid command to
             * restart the timeout.
             */
            reset_cmd_timeout();
        }
#endif
        len = readline (CONFIG_SYS_PROMPT);

        flag = 0;    /* assume no special flags for now */
        if (len > 0)
            strcpy (lastcommand, console_buffer);
        else if (len == 0)
            flag |= CMD_FLAG_REPEAT;
#ifdef CONFIG_BOOT_RETRY_TIME
        else if (len == -2) {
            /* -2 means timed out, retry autoboot
             */
            puts ("\nTimed out waiting for command\n");
# ifdef CONFIG_RESET_TO_RETRY
            /* Reinit board to run initialization code again */
            do_reset (NULL, 0, 0, NULL);
# else
            return;        /* retry autoboot */
# endif
        }
#endif

        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;
        }
    }
#endif /*CONFIG_SYS_HUSH_PARSER*/
}

 







 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM