今天在工作上搞了一天高通的芯片uboot程序,目的是希望将一个裸板的程序移植到uboot中,并且开机让它运行。这个芯片是NXP4330,目前是高通的一个芯片,基于ARM-contexA9架构,那么就跟4412是一样的架构了,今天将uboot加载流程基本上算是搞明白了,也明白了uboot最后是通过一些手段,最终能够去加载kernel.img,最终启动内核,后面就是加载文件系统了。
心血来潮,所以,今天借这个机会在说明一下4412的uboot最后是怎么去获取kernel.img进而启动,其实都大同小异,搞明白一个其它的就类似的了。
以前有写过一篇文章,关于4412的uboot启动流程的汇编分析:http://blog.csdn.net/morixinguan
在这篇文章中,我们分析了4412 uboot的核心,最后通过以下代码调用了C中的函数:
这就算是完成了uboot启动的第一个阶段:
start_code -> cpu_init_crit -> call_board_init_f -> board_init_f
call_board_init_f: ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ ldr r0,=0x00000000 bl board_init_f从代码中我们可以看到,程序最终是bl board_init_f,跳转到这个函数去执行,我们跟到这个函数如下:
它的位置位于arch/arm/lib/board.c
void board_init_f(ulong bootflag) { bd_t *bd; init_fnc_t **init_fnc_ptr; gd_t *id; ulong addr, addr_sp; /* 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; for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr)() != 0) { hang(); } } 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; #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 */ i = getenv_r("pram", (char *)tmp, sizeof (tmp)); reg = (i > 0) ? simple_strtoul((const char *)tmp, NULL, 10) : CONFIG_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_NO_ICACHE) && defined(CONFIG_SYS_NO_DCACHE)) /* reserve TLB table */ addr -= (4096 * 4); /* round down to next 64 kB limit */ addr &= ~(0x10000 - 1); gd->tlb_addr = addr; debug ("TLB table at: %08lx\n", addr); #endif /* round down to next 4 kB limit */ addr &= ~(4096 - 1); debug ("Top of RAM usable for U-Boot at: %08lx\n", addr); #ifdef CONFIG_VFD # ifndef PAGE_SIZE # define PAGE_SIZE 4096 # endif /* * reserve memory for VFD display (always full pages) */ addr -= vfd_setmem(addr); gd->fb_base = addr; #endif /* CONFIG_VFD */ #ifdef CONFIG_LCD /* reserve memory for LCD display (always full pages) */ addr = lcd_setmem(addr); gd->fb_base = addr; #endif /* CONFIG_LCD */ /* * reserve memory for U-Boot code, data & bss * round down to next 4 kB limit */ addr -= gd->mon_len; addr &= ~(4096 - 1); #if defined(CONFIG_S5P) || defined(CONFIG_S5P6450) addr = CONFIG_SYS_LOAD_ADDR; #endif debug ("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr); #ifndef CONFIG_PRELOADER /* * 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 = (bd_t *) addr_sp; gd->bd = bd; debug ("Reserving %zu Bytes for Board Info at: %08lx\n", sizeof (bd_t), addr_sp); addr_sp -= sizeof (gd_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; #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 -= 3; /* 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; 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); /* NOTREACHED - relocate_code() does not return */ }第二阶段 的起始代码 arch/arm/lib/board.c 里的 void board_init_r(gd_t *id, ulong dest_addr) 函数
其中, id 是从 board_init_f 里面获取,dest_addr也就是链接u-boot的地址
void board_init_r(gd_t *id, ulong dest_addr)
{
char *s;
bd_t *bd;
ulong malloc_start;
#if !defined(CONFIG_SYS_NO_FLASH)
ulong flash_size;
#endif
gd = id;
bd = gd->bd;
gd->flags |= GD_FLG_RELOC; /* tell others: relocation done */
monitor_flash_len = _bss_start_ofs;
debug ("monitor flash len: %08lX\n", monitor_flash_len);
board_init(); /* Setup chipselects */
#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:\t");
if ((flash_size = flash_init()) > 0) {
# ifdef CONFIG_SYS_FLASH_CHECKSUM
print_size(flash_size, "");
/*
* Compute and print flash CRC if flashchecksum is set to 'y'
*
* NOTE: Maybe we should add some WATCHDOG_RESET()? XXX
*/
s = getenv("flashchecksum");
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:\t");
nand_init(); /* go init the NAND */
#endif
#if defined(CONFIG_CMD_ONENAND)
onenand_init();
#endif
#ifdef CONFIG_GENERIC_MMC
mmc_initialize(bd);
#endif
#ifdef CONFIG_HAS_DATAFLASH
AT91F_DataflashInit();
dataflash_print_info();
#endif
/* initialize environment */
env_relocate();
#ifdef CONFIG_VFD
/* must do this after the framebuffer is allocated */
drv_vfd_init();
#endif /* CONFIG_VFD */
/* 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 */
#if defined(CONFIG_DRIVER_DM9000)
/* XXX: this needs to be moved to board init */
if (getenv("ethaddr")) {
uchar enetaddr[6];
eth_getenv_enetaddr("ethaddr", enetaddr);
dm9000_set_mac_addr(enetaddr);
}
#endif
/* Initialize from environment */
if ((s = getenv("loadaddr")) != NULL) {
load_addr = simple_strtoul(s, NULL, 16);
}
#if defined(CONFIG_CMD_NET)
if ((s = getenv("bootfile")) != NULL) {
copy_filename(BootFile, s, sizeof (BootFile));
}
#endif
#ifdef BOARD_LATE_INIT
board_late_init();
#endif
#ifdef CONFIG_BITBANGMII
bb_miiphy_init();
#endif
#if defined(CONFIG_CMD_NET)
#if defined(CONFIG_NET_MULTI)
puts("Net:\t");
#endif
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;
uchar memsz[32];
#ifdef CONFIG_PRAM
char *s;
if ((s = getenv("pram")) != NULL) {
pram = simple_strtoul(s, NULL, 10);
} else {
pram = CONFIG_PRAM;
}
#else
pram=0;
#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", (bd->bi_memsize / 1024) - pram);
setenv("mem", (char *)memsz);
}
#endif
//从这里开始就进入了uboot的死循环,这里就是所谓的uboot main函数了
/* 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 */
}
由于上面代码过多,我们从代码的注释大致就可以看出,上面是对串口,内存,以及平台相关的东西进行了设置和初始化,到代码的最后我们看到了以下死循环:
for (;;) { main_loop(); }我们接着跟到main_loop();这个函数:
这个函数的位置位于:common/main.c
main_loop()函数做的都是与具体平台无关的工作,主要包括初始化启动次数限制机制、设置软件版本号、打印启动信息、解析命令等。
(1)设置启动次数有关参数。在进入main_loop()函数后,首先是根据配置加载已经保留的启动次数,并且根据配置判断是否超过启动次数。代码如下:
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 */ #if defined(CONFIG_VFD) && defined(VFD_TEST_LOGO) ulong bmp = 0; /* default bitmap */ extern int trab_vfd (ulong bitmap); #ifdef CONFIG_MODEM_SUPPORT if (do_mdm_init) bmp = 1; /* alternate bitmap */ #endif trab_vfd (bmp); #endif /* CONFIG_VFD && VFD_TEST_LOGO */ #ifdef CONFIG_BOOTCOUNT_LIMIT bootcount = bootcount_load(); // 加载保存的启动次数 bootcount++; // 启动次数加1 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"));// 获取Modem参数 setenv ("preboot", str); /* set or delete definition */ //设置环境变量 if (str != NULL) free (str); mdm_init(); /* wait for modem connection */ } #endif /* CONFIG_MODEM_SUPPORT */ //接下来设置U-Boot的版本号,初始化命令自动完成功能等。代码如下: #ifdef CONFIG_VERSION_VARIABLE { extern char version_string[]; setenv ("ver", version_string); /* set version variable */ // 设置版本号 } #endif /* CONFIG_VERSION_VARIABLE */ #ifdef CONFIG_SYS_HUSH_PARSER u_boot_hush_start ();//初始化hash功能 #endif #if defined(CONFIG_HUSH_INIT_VAR) hush_init_var (); #endif #ifdef CONFIG_AUTO_COMPLETE install_auto_complete(); // 初始化命令自动完成功能 #endif #ifdef CONFIG_PREBOOT if ((p = getenv ("preboot")) != NULL) { # ifdef CONFIG_AUTOBOOT_KEYED int prev = disable_ctrlc(1); /* disable Control C checking */// 关闭Crtl+C组合键 # endif # ifndef CONFIG_SYS_HUSH_PARSER run_command (p, 0);// 运行Boot参数 # else parse_string_outer(p, FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP); # endif # ifdef CONFIG_AUTOBOOT_KEYED disable_ctrlc(prev); /* restore Control C checking */ // 恢复Ctrl+C组合键 # endif } #endif /* CONFIG_PREBOOT */ #if defined(CONFIG_UPDATE_TFTP) update_tftp (); #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); # 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 # ifndef CONFIG_SYS_HUSH_PARSER run_command (s, 0); # else parse_string_outer(s, FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP); # endif # ifdef CONFIG_AUTOBOOT_KEYED disable_ctrlc(prev); /* restore Control C checking */ # endif } # ifdef CONFIG_MENUKEY if (menukey == CONFIG_MENUKEY) { // 检查是否支持菜单键 s = getenv("menucmd"); if (s) { # ifndef CONFIG_SYS_HUSH_PARSER run_command (s, 0); # else parse_string_outer(s, FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP); # endif } } #endif /* CONFIG_MENUKEY */ #endif /* CONFIG_BOOTDELAY */ /* * Main Loop for Monitor Command Processing */ #ifdef CONFIG_SYS_HUSH_PARSER parse_file_outer(); /* 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*/ }至此,uboot的加载流程大致就清楚了,如果我的硬件裸板测试程序要移植到uboot里面,然后将uboot重新启动,直接到裸板的测试程序,我们就可以利用上面这个机制,可以将所有的main_loop();函数的代码都去除,然后将硬件测试程序放在main_loop中,因为它是一个死循环,因此会周而复始的进行。