BL2的最后通过汇编调用了board_init_r函数,此时进入BL3的阶段,此时的主要工作:
这一阶段涉及的文件及任务如下
arch/arm/lib/board.c
1. board_init_r()是进入定制板目录的入口
common/main.c
2. main_loop()中关闭中断,执行命令以及加载引导内核
下面分析一下board_init_r函数:
/* ************************************************************************ * * This is the next part if the initialization sequence: we are now * running from RAM and have a "normal" C environment, i. e. global * data can be written, BSS has been cleared, the stack size in not * that critical any more, etc. * ************************************************************************ */ void board_init_r(gd_t *id, ulong dest_addr) { ... bd_t *bd; ... gd = id; bd = gd->bd; gd->flags |= GD_FLG_RELOC; /* tell others: relocation done */ monitor_flash_len = _end_ofs; ... debug("monitor flash len: %08lX ", monitor_flash_len); board_init(); /* Setup chipselects */
上述代码的作用是对gd和bd进行赋值,其中monitor_flash_len为整个U-Boot的长度。
malloc_start = dest_addr - TOTAL_MALLOC_LEN - sizeof(struct spare_boot_head_t); ... /* The Malloc area is immediately below the monitor copy in DRAM */ mem_malloc_init (malloc_start, TOTAL_MALLOC_LEN);
对SDRAM中的malloc空间进行清零初始化。
#if !defined(CONFIG_SYS_NO_FLASH) puts("Flash: "); flash_size = flash_init(); if (flash_size > 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(' '); # else /* !CONFIG_SYS_FLASH_CHECKSUM */ print_size(flash_size, " "); # endif /* CONFIG_SYS_FLASH_CHECKSUM */ } else { puts(failed); hang(); } #endif
上述代码的作用是计算FLASH的大小,并把它通过串口显示在控制台上。由于没有定义CONFIG_SYS_FLASH_CHECKSUM,所以没有执行CRC的校验和。其中flash_init函数是在drivers/mtd目录下的cfi_flash.c文件内(因为include/configs/smdk2410.h中定义了CONFIG_FLASH_CFI_DRIVER)。
/* set up exceptions */ interrupt_init(); /* enable exceptions */ enable_interrupts();
interrupt_init函数是建立IRQ中断堆栈,enable_interrupts函数是使能IRQ中断,它们都是在arch/arm/lib目录下的interrupts.c文件中定义的。
... /* initialize environment */ env_relocate();
初始化环境变量,由于gd->env_valid等于0,所以在这里设置的是缺省环境变量。env_relocate函数是在common目录下的env_common.c文件中定义的。
#if defined(CONFIG_CMD_PCI) || defined(CONFIG_PCI) arm_pci_init(); #endif
初始化PCI。
/* 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 */
上面代码的作用分别对应:
设置IP地址。
初始化各类外设,如IIC、LCD、键盘、USB等,当然只有在定义了这些外设的前提下,才对这些外设进行初始化。该函数是在common目录下的stdio.c文件中定义的。
初始化跳转表gd->jt,该跳转表是一个函数指针数组,它定义了U-Boot中基本的常用函数库。该函数是在common目录下的exports.c文件中定义的。
初始化API。
初始化控制台,即标准输入、标准输出和标准错误,在这里都是串口。该函数是在common目录下的console.c文件中定义的。
/* Initialize from environment */ s = getenv("loadaddr"); if (s != NULL) load_addr = simple_strtoul(s, NULL, 16);
从环境变量中获取loadaddr参数,得到需要加载的地址。
#if defined(CONFIG_CMD_NET) s = getenv("bootfile"); if (s != NULL) copy_filename(BootFile, s, sizeof(BootFile)); #endif
从环境变量中获取bootfile参数,得到通过TFTP加载的镜像文件名。
#if defined(CONFIG_CMD_NET) #if defined(CONFIG_NET_MULTI) puts("Net: "); #endif eth_initialize(gd->bd); #if defined(CONFIG_RESET_PHY_R) debug("Reset Ethernet PHY "); reset_phy(); #endif #endif
初始化以太网,其中eth_initialize函数是在net目录下的eth.c文件中定义的。
... /* main_loop() can return to retry autoboot, if so just run it again. */ for (;;) { main_loop(); } hang(); /* NOTREACHED - no way out of command loop except booting */ }
board_init_r函数的最后就是执行一个死循环,调用main_loop函数。该函数是在common目录下的main.c文件内定义的。
总结:
3.接下来分析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
声明一些hush参数。关于hush后面会讲到。
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) char *s; int bootdelay; #endif
声明启动延时需要的参数。
#ifdef CONFIG_SYS_HUSH_PARSER u_boot_hush_start (); #endif
初始化hush功能。稍后再说。
1 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) 2 s = getenv ("bootdelay"); 3 bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; 4 5 debug ("### main_loop entered: bootdelay=%d ", bootdelay); 6 7 # ifdef CONFIG_BOOT_RETRY_TIME 8 init_cmd_timeout (); 9 # endif /* CONFIG_BOOT_RETRY_TIME */ 10 11 #ifdef CONFIG_POST 12 if (gd->flags & GD_FLG_POSTFAIL) { 13 s = getenv("failbootcmd"); 14 } 15 else 16 #endif /* CONFIG_POST */ 17 #ifdef CONFIG_BOOTCOUNT_LIMIT 18 if (bootlimit && (bootcount > bootlimit)) { 19 printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd. ", 20 (unsigned)bootlimit); 21 s = getenv ("altbootcmd"); 22 } 23 else 24 #endif /* CONFIG_BOOTCOUNT_LIMIT */ 25 s = getenv ("bootcmd"); 26 27 debug ("### main_loop: bootcmd="%s" ", s ? s : "<UNDEFINED>"); 28 29 if (bootdelay >= 0 && s && !abortboot (bootdelay)) { 30 # ifdef CONFIG_AUTOBOOT_KEYED 31 int prev = disable_ctrlc(1); /* disable Control C checking */ 32 # endif 33 34 # ifndef CONFIG_SYS_HUSH_PARSER 35 run_command (s, 0); 36 # else 37 parse_string_outer(s, FLAG_PARSE_SEMICOLON | 38 FLAG_EXIT_FROM_LOOP); 39 # endif 40 41 # ifdef CONFIG_AUTOBOOT_KEYED 42 disable_ctrlc(prev); /* restore Control C checking */ 43 # endif 44 } 45 46 # ifdef CONFIG_MENUKEY 47 if (menukey == CONFIG_MENUKEY) { 48 s = getenv("menucmd"); 49 if (s) { 50 # ifndef CONFIG_SYS_HUSH_PARSER 51 run_command(s, 0); 52 # else 53 parse_string_outer(s, FLAG_PARSE_SEMICOLON | 54 FLAG_EXIT_FROM_LOOP); 55 # endif 56 } 57 } 58 #endif /* CONFIG_MENUKEY */ 59 #endif /* CONFIG_BOOTDELAY */
第2行和第3行的含义是从环境变量中获取bootdelay参数,得到自动启动缺省镜像文件的延时(单位是秒)。
其中bootdelay的作用是:uboot正常启动后,会调用main_loop(void)函数,进入main_loop()之后,如果在规定的时间(CONFIG_BOOTDELAY)内,没有检查到任何按键事件的发生,就会去加载OS,并启动系统。
第8行的含义是初始化命令行超时机制。
第25行的含义是从环境变量中获取bootcmd参数,得到在启动延时过程中自动执行的命令。当我们得到了bootcmd参数,bootdelay参数也是大于等于0,并且在启动延时过程中没有按下任意键时,执行第37行的parse_string_outer函数,该函数的作用是解释bootcmd参数
并执行,它是在common目录下的hush.c文件内定义的。
1 /* 2 * Main Loop for Monitor Command Processing 3 */ 4 #ifdef CONFIG_SYS_HUSH_PARSER 5 parse_file_outer(); 6 /* This point is never reached */ 7 for (;;); 8 #else 9 for (;;) { 10 #ifdef CONFIG_BOOT_RETRY_TIME 11 if (rc >= 0) { 12 /* Saw enough of a valid command to 13 * restart the timeout. 14 */ 15 reset_cmd_timeout(); 16 } 17 #endif 18 len = readline (CONFIG_SYS_PROMPT); 19 20 flag = 0; /* assume no special flags for now */ 21 if (len > 0) 22 strcpy (lastcommand, console_buffer); 23 else if (len == 0) 24 flag |= CMD_FLAG_REPEAT; 25 #ifdef CONFIG_BOOT_RETRY_TIME 26 else if (len == -2) { 27 /* -2 means timed out, retry autoboot 28 */ 29 puts (" Timed out waiting for command "); 30 # ifdef CONFIG_RESET_TO_RETRY 31 /* Reinit board to run initialization code again */ 32 do_reset (NULL, 0, 0, NULL); 33 # else 34 return; /* retry autoboot */ 35 # endif 36 } 37 #endif 38 39 if (len == -1) 40 puts ("<INTERRUPT> "); 41 else 42 rc = run_command (lastcommand, flag); 43 44 if (rc <= 0) { 45 /* invalid command or not repeatable, forget it */ 46 lastcommand[0] = 0; 47 } 48 } 49 #endif /*CONFIG_SYS_HUSH_PARSER*/ 50 }
由于在include/configs/smdk2410.h文件中定义了CONFIG_SYS_HUSH_PARSER,所以上面的代码仅仅执行的是第5行至第7行的内容。第5行的parse_file_outer函数是在common目录下的hush.c文件中定义的,它的含义是依次读取命令序列中的命令并执行之,其中在该
函数还调用了parse_stream_outer函数,这个函数体内有一个do-while循环,只有发生语法错误的时候才会跳出该循环,因此一般情况下永远也不会执行上面代码中的第7行内容,而是始终在那个do-while循环体内。
上面说到过如果在CONFIG_BOOTDELAY时间内,用户按下键盘上的任意一个按键,uboot就会进入与用户交互的状态。如果用户在配置文件中定义了CONFIG_SYS_HUSH_PARSER,就会通过parse_file_outer(),去接收并解析用户命令,否则进入一个for(;;)循环,
通过 readline (CONFIG_SYS_PROMPT)接收用户命令,然后调用run_command(cmd,flag)去解析并执行命令。
当在配置文件中定义了CONFIG_SYS_HUSH_PARSER,main_loop会调用parse_file_outer(),进入hush中。其中parse_file_outer()在u-bootcommonhush.c下。
#ifndef __U_BOOT__ static int parse_file_outer(FILE *f) #else int parse_file_outer(void) #endif { int rcode; struct in_str input; #ifndef __U_BOOT__ setup_file_in_str(&input, f); #else setup_file_in_str(&input); #endif rcode = parse_stream_outer(&input, FLAG_PARSE_SEMICOLON); return rcode; }
/* most recursion does not come through here, the exeception is * from builtin_source() */ int parse_stream_outer(struct in_str *inp, int flag) { struct p_context ctx; o_string temp=NULL_O_STRING; int rcode; #ifdef __U_BOOT__ int code = 0; #endif do { ctx.type = flag; initialize_context(&ctx); update_ifs_map(); if (!(flag & FLAG_PARSE_SEMICOLON) || (flag & FLAG_REPARSING)) mapset((uchar *)";$&|", 0); inp->promptmode=1; rcode = parse_stream(&temp, &ctx, inp, ' '); #ifdef __U_BOOT__ if (rcode == 1) flag_repeat = 0; #endif if (rcode != 1 && ctx.old_flag != 0) { syntax(); #ifdef __U_BOOT__ flag_repeat = 0; #endif } if (rcode != 1 && ctx.old_flag == 0) { done_word(&temp, &ctx); done_pipe(&ctx,PIPE_SEQ); #ifndef __U_BOOT__ run_list(ctx.list_head); #else code = run_list(ctx.list_head); if (code == -2) { /* exit */ b_free(&temp); code = 0; /* XXX hackish way to not allow exit from main loop */ if (inp->peek == file_peek) { printf("exit not allowed from main input shell. "); continue; } break; } if (code == -1) flag_repeat = 0; #endif } else { if (ctx.old_flag != 0) { free(ctx.stack); b_reset(&temp); } #ifdef __U_BOOT__ if (inp->__promptme == 0) printf("<INTERRUPT> "); inp->__promptme = 1; #endif temp.nonnull = 0; temp.quote = 0; inp->p = NULL; free_pipe_list(ctx.list_head,0); } b_free(&temp); } while (rcode != -1 && !(flag & FLAG_EXIT_FROM_LOOP)); /* loop on syntax errors, return on EOF */ #ifndef __U_BOOT__ return 0; #else return (code != 0) ? 1 : 0; #endif /* __U_BOOT__ */ }
hush是uboot中命令接收和解析的工具,与uboot原始的命令解析方法相比,该工具更加智能。
这里会在run_list中调用到hush中的run_pipe_real(struct pipe *pi),在该函数中经过一些列解析,最终会调用到对应的命令执行函数。
/* run_pipe_real() starts all the jobs, but doesn't wait for anything * to finish. See checkjobs(). * * return code is normally -1, when the caller has to wait for children * to finish to determine the exit status of the pipe. If the pipe * is a simple builtin command, however, the action is done by the * time run_pipe_real returns, and the exit code is provided as the * return value. * * The input of the pipe is always stdin, the output is always * stdout. The outpipe[] mechanism in BusyBox-0.48 lash is bogus, * because it tries to avoid running the command substitution in * subshell, when that is in fact necessary. The subshell process * now has its stdout directed to the input of the appropriate pipe, * so this routine is noticeably simpler. */ static int run_pipe_real(struct pipe *pi) { int i; #ifndef __U_BOOT__ int nextin, nextout; int pipefds[2]; /* pipefds[0] is for reading */ struct child_prog *child; struct built_in_command *x; char *p; # if __GNUC__ /* Avoid longjmp clobbering */ (void) &i; (void) &nextin; (void) &nextout; (void) &child; # endif #else int nextin; int flag = do_repeat ? CMD_FLAG_REPEAT : 0; struct child_prog *child; cmd_tbl_t *cmdtp; char *p; # if __GNUC__ /* Avoid longjmp clobbering */ (void) &i; (void) &nextin; (void) &child; # endif #endif /* __U_BOOT__ */ nextin = 0; #ifndef __U_BOOT__ pi->pgrp = -1; #endif /* Check if this is a simple builtin (not part of a pipe). * Builtins within pipes have to fork anyway, and are handled in * pseudo_exec. "echo foo | read bar" doesn't work on bash, either. */ if (pi->num_progs == 1) child = & (pi->progs[0]); #ifndef __U_BOOT__ if (pi->num_progs == 1 && child->group && child->subshell == 0) { int squirrel[] = {-1, -1, -1}; int rcode; debug_printf("non-subshell grouping "); setup_redirects(child, squirrel); /* XXX could we merge code with following builtin case, * by creating a pseudo builtin that calls run_list_real? */ rcode = run_list_real(child->group); restore_redirects(squirrel); #else if (pi->num_progs == 1 && child->group) { int rcode; debug_printf("non-subshell grouping "); rcode = run_list_real(child->group); #endif return rcode; } else if (pi->num_progs == 1 && pi->progs[0].argv != NULL) { for (i=0; is_assignment(child->argv[i]); i++) { /* nothing */ } if (i!=0 && child->argv[i]==NULL) { /* assignments, but no command: set the local environment */ for (i=0; child->argv[i]!=NULL; i++) { /* Ok, this case is tricky. We have to decide if this is a * local variable, or an already exported variable. If it is * already exported, we have to export the new value. If it is * not exported, we need only set this as a local variable. * This junk is all to decide whether or not to export this * variable. */ int export_me=0; char *name, *value; name = xstrdup(child->argv[i]); debug_printf("Local environment set: %s ", name); value = strchr(name, '='); if (value) *value=0; #ifndef __U_BOOT__ if ( get_local_var(name)) { export_me=1; } #endif free(name); p = insert_var_value(child->argv[i]); set_local_var(p, export_me); if (p != child->argv[i]) free(p); } return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */ } for (i = 0; is_assignment(child->argv[i]); i++) { p = insert_var_value(child->argv[i]); #ifndef __U_BOOT__ putenv(strdup(p)); #else set_local_var(p, 0); #endif if (p != child->argv[i]) { child->sp--; free(p); } } if (child->sp) { char * str = NULL; str = make_string((child->argv + i)); parse_string_outer(str, FLAG_EXIT_FROM_LOOP | FLAG_REPARSING); free(str); return last_return_code; } #ifndef __U_BOOT__ for (x = bltins; x->cmd; x++) { if (strcmp(child->argv[i], x->cmd) == 0 ) { int squirrel[] = {-1, -1, -1}; int rcode; if (x->function == builtin_exec && child->argv[i+1]==NULL) { debug_printf("magic exec "); setup_redirects(child,NULL); return EXIT_SUCCESS; } debug_printf("builtin inline %s ", child->argv[0]); /* XXX setup_redirects acts on file descriptors, not FILEs. * This is perfect for work that comes after exec(). * Is it really safe for inline use? Experimentally, * things seem to work with glibc. */ setup_redirects(child, squirrel); #else /* check ";", because ,example , argv consist from * "help;flinfo" must not execute */ if (strchr(child->argv[i], ';')) { printf ("Unknown command '%s' - try 'help' or use 'run' command ", child->argv[i]); return -1; } /* Look up command in command table */ if ((cmdtp = find_cmd(child->argv[i])) == NULL) { printf ("Unknown command '%s' - try 'help' ", child->argv[i]); return -1; /* give up after bad command */ } else { int rcode; #if defined(CONFIG_CMD_BOOTD) /* avoid "bootd" recursion */ if (cmdtp->cmd == do_bootd) { if (flag & CMD_FLAG_BOOTD) { printf ("'bootd' recursion detected "); return -1; } else flag |= CMD_FLAG_BOOTD; } #endif /* found - check max args */ if ((child->argc - i) > cmdtp->maxargs) return cmd_usage(cmdtp); #endif child->argv+=i; /* XXX horrible hack */ #ifndef __U_BOOT__ rcode = x->function(child); #else /* OK - call function to do the command */ rcode = (cmdtp->cmd) (cmdtp, flag,child->argc-i,&child->argv[i]); if ( !cmdtp->repeatable ) flag_repeat = 0; #endif child->argv-=i; /* XXX restore hack so free() can work right */ #ifndef __U_BOOT__ restore_redirects(squirrel); #endif return rcode; } } #ifndef __U_BOOT__ } for (i = 0; i < pi->num_progs; i++) { child = & (pi->progs[i]); /* pipes are inserted between pairs of commands */ if ((i + 1) < pi->num_progs) { if (pipe(pipefds)<0) perror_msg_and_die("pipe"); nextout = pipefds[1]; } else { nextout=1; pipefds[0] = -1; } /* XXX test for failed fork()? */ if (!(child->pid = fork())) { /* Set the handling for job control signals back to the default. */ signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGTERM, SIG_DFL); signal(SIGTSTP, SIG_DFL); signal(SIGTTIN, SIG_DFL); signal(SIGTTOU, SIG_DFL); signal(SIGCHLD, SIG_DFL); close_all(); if (nextin != 0) { dup2(nextin, 0); close(nextin); } if (nextout != 1) { dup2(nextout, 1); close(nextout); } if (pipefds[0]!=-1) { close(pipefds[0]); /* opposite end of our output pipe */ } /* Like bash, explicit redirects override pipes, * and the pipe fd is available for dup'ing. */ setup_redirects(child,NULL); if (interactive && pi->followup!=PIPE_BG) { /* If we (the child) win the race, put ourselves in the process * group whose leader is the first process in this pipe. */ if (pi->pgrp < 0) { pi->pgrp = getpid(); } if (setpgid(0, pi->pgrp) == 0) { tcsetpgrp(2, pi->pgrp); } } pseudo_exec(child); } /* put our child in the process group whose leader is the first process in this pipe */ if (pi->pgrp < 0) { pi->pgrp = child->pid; } /* Don't check for errors. The child may be dead already, * in which case setpgid returns error code EACCES. */ setpgid(child->pid, pi->pgrp); if (nextin != 0) close(nextin); if (nextout != 1) close(nextout); /* If there isn't another process, nextin is garbage but it doesn't matter */ nextin = pipefds[0]; } #endif return -1; }
上面关键是这段:
/* OK - call function to do the command */ rcode = (cmdtp->cmd) (cmdtp, flag,child->argc-i,&child->argv[i]); if ( !cmdtp->repeatable ) flag_repeat = 0;
通过这段代码调用对应的命令执行函数。cmd_tbl_t的结构如下:
struct cmd_tbl_s { char *name; /* Command Name */ int maxargs; /* maximum number of arguments */ int repeatable; /* autorepeat allowed? */ /* Implementation function */ int (*cmd)(struct cmd_tbl_s *, int, int, char * const []); char *usage; /* Usage message (short) */ #ifdef CONFIG_SYS_LONGHELP char *help; /* Help message (long) */ #endif #ifdef CONFIG_AUTO_COMPLETE /* do auto completion on the arguments */ int (*complete)(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]); #endif }; typedef struct cmd_tbl_s cmd_tbl_t;
对于uboot支持的每一个命令,是通过U_BOOT_CMD宏定义的,他定义了该命令对应的名称name,支持的最大参数rep,重复次数,实现函数cmd,以及输入help命令时,显示的帮助信息usage。
在执行函数cmd中,第一个参数对应该命令结构本身的指针,第二个参数对应flag标记,第三个参数对应参数数目,第四个参数是指针数组,里面存储的是对应参数的指针。