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 主要功能
- 設置CPU模式
- 關閉看門狗
- 關閉中斷
- 設置堆棧sp指針
- 清除bss段
- 異常中斷處理
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*/ }