您的位置:首页 > 其它

uboot-tiny4412启动流程(下)----如何将自己的裸板测试程序加入uboot中启动测试

2016-12-16 00:15 344 查看
      今天在工作上搞了一天高通的芯片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中,因为它是一个死循环,因此会周而复始的进行。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: