/****************************************************************************/
//Main_loop:与平台无关,初始化启动次数限制,modem功能,设置软件版本号,启动延时,读取及命令解析。
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 //Modem功能通常供一些远程控制的系统使用
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 //设置U-Boot的版本号,初始化命令自动完成功能等
{
//将版本号写入flash
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
// 关闭Crtl+C组合键
int prev = disable_ctrlc(1); /* disable Control C checking */
# endif
run_command(p, 0); //命令执行
# ifdef CONFIG_AUTOBOOT_KEYED // 恢复Ctrl+C组合键
disable_ctrlc(prev); /* restore Control C checking */
# endif
}
#endif /* CONFIG_PREBOOT */
#if defined(CONFIG_UPDATE_TFTP) // 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"); //获得cmd错误
}
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>");
//在启动命令大于0前,判断是否有按键按下,如果有进入命令行模式,没有就进入操作系统
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();
/* 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);
// 读取命令,读取到的命令存储在全局变量console_buffer 中
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*/
}
/******************************************************/
// run_command(); //解析并运行命令
int run_command(const char *cmd, int flag)
{
#ifndef CONFIG_SYS_HUSH_PARSER
/*
* builtin_run_command can return 0 or 1 for success, so clean up
* its result.
*/
if (builtin_run_command(cmd, flag) == -1) //命令解析函数
return 1;
return 0;
#else
return parse_string_outer(cmd,
FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP);
#endif
}
/**********************************************************************************/
//builtin_run_command 命令解析函数
static int builtin_run_command(const char *cmd, int flag)
{
char cmdbuf[CONFIG_SYS_CBSIZE]; /* working copy of cmd */
char *token; /* start of token in cmdbuf */
char *sep; /* end of token (separator) in cmdbuf */
char finaltoken[CONFIG_SYS_CBSIZE];
char *str = cmdbuf;
char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */
int argc, inquotes;
int repeatable = 1;
int rc = 0;
clear_ctrlc(); /* forget any previous Control C */
if (!cmd || !*cmd) { //先对字符串命令cmd的有效性进行检测,判断该命令是否为空
return -1; /* empty command */
}
//判断字符串命令的长度是否在CFG_CBSIZE(256)范围之内
if (strlen(cmd) >= CONFIG_SYS_CBSIZE) {
puts ("## Command too long!\n");
return -1;
}
strcpy (cmdbuf, cmd); //备份入口命令cmd到cmdbuf
/* Process separators and check for invalid
* repeatable commands
*/
while (*str) { //str指向cmdbuf备份命令,循环判断字符串命令有几个
/*
* Find separator, or string end
* Allow simple escape of ';' by writing "\;"
*/
//sep指向当前命令str的开头,一直for寻找当前命令结尾处
for (inquotes = 0, sep = str; *sep; sep++) {
if ((*sep=='\'') &&
(*(sep-1) != '\\'))
inquotes=!inquotes;
if (!inquotes &&
(*sep == ';') && /* separator */
( sep != str) && /* past string start */
(*(sep-1) != '\\')) /* and NOT escaped */
break;
}
/*
* Limit the token to data between separators
*/
token = str;
if (*sep) {
str = sep + 1; /* start of command for next pass */
*sep = '\0';
}
else
str = sep; /* no more commands for next pass */
/* find macros in this token and replace them */
process_macros (token, finaltoken); //token=当前命令开头,将命令中的宏替换
//parse_line函数:解析当前命令用argv数组保存并返回当前命令参数个数
/* Extract arguments */
if ((argc = parse_line (finaltoken, argv)) == 0) {
rc = -1; /* no command at all */
continue;
}
if (cmd_process(flag, argc, argv, &repeatable)) //find_cmd函数
rc = -1;
/* Did the user stop this? */
if (had_ctrlc ()) //检查是否有ctrl+c按键按下,如果有的话,取消当前命令执行。
return -1; /* if stopped then not repeatable */
}
return rc ? rc : repeatable;
}
/********************************************************************************/
//cmd_process 函数实际调用find_cmd
enum command_ret_t cmd_process(int flag, int argc, char * const argv[],
int *repeatable)
{
enum command_ret_t rc = CMD_RET_SUCCESS;
cmd_tbl_t *cmdtp;
/* Look up command in command table */
//查找.u_boot_cmd段中是否有这个命令argv[0]名字,若有的话返回这个命令的结构体,否则返回NULL。
cmdtp = find_cmd(argv[0]);
if (cmdtp == NULL) {
printf("Unknown command '%s' - try 'help'\n", argv[0]);
return 1;
}
/* found - check max args */
if (argc > cmdtp->maxargs)
rc = CMD_RET_USAGE;
#if defined(CONFIG_CMD_BOOTD)
/* avoid "bootd" recursion */
else if (cmdtp->cmd == do_bootd) {
if (flag & CMD_FLAG_BOOTD) {
puts("'bootd' recursion detected\n");
rc = CMD_RET_FAILURE;
} else {
flag |= CMD_FLAG_BOOTD;
}
}
#endif
/* If OK so far, then do the command */
if (!rc) {
rc = cmd_call(cmdtp, flag, argc, argv);
*repeatable &= cmdtp->repeatable;
}
if (rc == CMD_RET_USAGE)
rc = cmd_usage(cmdtp);
return rc;
}
cmd_tbl_t *find_cmd (const char *cmd)
{
//__u_boot_cmd_start和__u_boot_cmd_end 在.lds连接脚本里已定义.
int len = &__u_boot_cmd_end - &__u_boot_cmd_start;
return find_cmd_tbl(cmd, &__u_boot_cmd_start, len);
}
cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len)
{
cmd_tbl_t *cmdtp;
cmd_tbl_t *cmdtp_temp = table; /*Init value */
const char *p;
int len;
int n_found = 0;
if (!cmd)
return NULL;
/*
* Some commands allow length modifiers (like "cp.b");
* compare command name only until first dot.
*/
//查找'.'字符,用来获得当前字符串命令长度len
len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);
//for循环是将*cmd入口参数从所有命令起始段找到命令结束段
for (cmdtp = table;
cmdtp != table + table_len;
cmdtp++) {
//比较len长度的*cmd和cmdtp->name,若相等表示找到一样的实际命令。
if (strncmp (cmd, cmdtp->name, len) == 0) {
//再次获取实际命令的长度,判断是否和当前命令的长度一样
if (len == strlen (cmdtp->name))
return cmdtp; /* full match */
cmdtp_temp = cmdtp; /* abbreviated command ? */
n_found++;
}
}
if (n_found == 1) { /* exactly one match */
return cmdtp_temp;
}
return NULL; /* not found or ambiguous command */
}
//命令解析
bootcmd=nboot.e 0xc0700000 0 0x120000; bootm
使用的命令是bootm,调用的应该是/common/cmd_bootm.c中的命令
/*******************************************************************/
/* bootm - boot application image from image in memory */
/*******************************************************************/
int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
if (bootm_start(cmdtp, flag, argc, argv))
return 1;
ret = bootm_load_os(images.os, &load_end, 1);
bootstage_mark(BOOTSTAGE_ID_CHECK_BOOT_OS);
}
//支持nand启动的机器
最后会调用common/cmd_nand.c 下的 do_nand
int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
{
int i, ret = 0;
ulong addr;
loff_t off, size;
char *cmd, *s;
nand_info_t *nand;
#ifdef CONFIG_SYS_NAND_QUIET
int quiet = CONFIG_SYS_NAND_QUIET;
#else
int quiet = 0;
#endif
const char *quiet_str = getenv("quiet");
int dev = nand_curr_device;
int repeat = flag & CMD_FLAG_REPEAT;
/* at least two arguments please */
if (argc < 2)
goto usage;
if (quiet_str)
quiet = simple_strtoul(quiet_str, NULL, 0) != 0;
cmd = argv[1];
/* Only "dump" is repeatable. */
if (repeat && strcmp(cmd, "dump"))
return 0;
if (strcmp(cmd, "info") == 0) {
putc('\n');
for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) {
if (nand_info[i].name)
nand_print_and_set_info(i);
}
return 0;
}
if (strcmp(cmd, "device") == 0) {
if (argc < 3) {
putc('\n');
if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE)
puts("no devices available\n");
else
nand_print_and_set_info(dev);
return 0;
}
dev = (int)simple_strtoul(argv[2], NULL, 10);
set_dev(dev);
return 0;
}
#ifdef CONFIG_ENV_OFFSET_OOB
/* this command operates only on the first nand device */
if (strcmp(cmd, "env.oob") == 0)
return do_nand_env_oob(cmdtp, argc - 1, argv + 1);
#endif
/* The following commands operate on the current device, unless
* overridden by a partition specifier. Note that if somehow the
* current device is invalid, it will have to be changed to a valid
* one before these commands can run, even if a partition specifier
* for another device is to be used.
*/
if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE ||
!nand_info[dev].name) {
puts("\nno devices available\n");
return 1;
}
nand = &nand_info[dev];
if (strcmp(cmd, "bad") == 0) {
printf("\nDevice %d bad blocks:\n", dev);
for (off = 0; off < nand->size; off += nand->erasesize)
if (nand_block_isbad(nand, off))
printf(" %08llx\n", (unsigned long long)off);
return 0;
}
/*
* Syntax is:
* 0 1 2 3 4
* nand erase [clean] [off size]
*/
if (strncmp(cmd, "erase", 5) == 0 || strncmp(cmd, "scrub", 5) == 0) {
nand_erase_options_t opts;
/* "clean" at index 2 means request to write cleanmarker */
int clean = argc > 2 && !strcmp("clean", argv[2]);
int scrub_yes = argc > 2 && !strcmp("-y", argv[2]);
int o = (clean || scrub_yes) ? 3 : 2;
int scrub = !strncmp(cmd, "scrub", 5);
int spread = 0;
int args = 2;
const char *scrub_warn =
"Warning: "
"scrub option will erase all factory set bad blocks!\n"
" "
"There is no reliable way to recover them.\n"
" "
"Use this command only for testing purposes if you\n"
" "
"are sure of what you are doing!\n"
"\nReally scrub this NAND flash? <y/N>\n";
if (cmd[5] != 0) {
if (!strcmp(&cmd[5], ".spread")) {
spread = 1;
} else if (!strcmp(&cmd[5], ".part")) {
args = 1;
} else if (!strcmp(&cmd[5], ".chip")) {
args = 0;
} else {
goto usage;
}
}
/*
* Don't allow missing arguments to cause full chip/partition
* erases -- easy to do accidentally, e.g. with a misspelled
* variable name.
*/
if (argc != o + args)
goto usage;
printf("\nNAND %s: ", cmd);
/* skip first two or three arguments, look for offset and size */
if (arg_off_size(argc - o, argv + o, &dev, &off, &size) != 0)
return 1;
nand = &nand_info[dev];
memset(&opts, 0, sizeof(opts));
opts.offset = off;
opts.length = size;
opts.jffs2 = clean;
opts.quiet = quiet;
opts.spread = spread;
if (scrub) {
if (!scrub_yes)
puts(scrub_warn);
if (scrub_yes)
opts.scrub = 1;
else if (getc() == 'y') {
puts("y");
if (getc() == '\r')
opts.scrub = 1;
else {
puts("scrub aborted\n");
return -1;
}
} else {
puts("scrub aborted\n");
return -1;
}
}
ret = nand_erase_opts(nand, &opts);
printf("%s\n", ret ? "ERROR" : "OK");
return ret == 0 ? 0 : 1;
}
if (strncmp(cmd, "dump", 4) == 0) {
if (argc < 3)
goto usage;
off = (int)simple_strtoul(argv[2], NULL, 16);
ret = nand_dump(nand, off, !strcmp(&cmd[4], ".oob"), repeat);
return ret == 0 ? 1 : 0;
}
if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {
size_t rwsize;
int read;
if (argc < 4)
goto usage;
addr = (ulong)simple_strtoul(argv[2], NULL, 16);
read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */
printf("\nNAND %s: ", read ? "read" : "write");
if (arg_off_size(argc - 3, argv + 3, &dev, &off, &size) != 0)
return 1;
nand = &nand_info[dev];
rwsize = size;
s = strchr(cmd, '.');
if (!s || !strcmp(s, ".jffs2") ||
!strcmp(s, ".e") || !strcmp(s, ".i")) {
if (read)
ret = nand_read_skip_bad(nand, off, &rwsize,
(u_char *)addr);
else
ret = nand_write_skip_bad(nand, off, &rwsize,
(u_char *)addr, 0);
#ifdef CONFIG_CMD_NAND_TRIMFFS
} else if (!strcmp(s, ".trimffs")) {
if (read) {
printf("Unknown nand command suffix '%s'\n", s);
return 1;
}
ret = nand_write_skip_bad(nand, off, &rwsize,
(u_char *)addr,
WITH_DROP_FFS);
#endif
#ifdef CONFIG_CMD_NAND_YAFFS
} else if (!strcmp(s, ".yaffs")) {
if (read) {
printf("Unknown nand command suffix '%s'.\n", s);
return 1;
}
ret = nand_write_skip_bad(nand, off, &rwsize,
(u_char *)addr, WITH_YAFFS_OOB);
#endif
} else if (!strcmp(s, ".oob")) {
/* out-of-band data */
mtd_oob_ops_t ops = {
.oobbuf = (u8 *)addr,
.ooblen = rwsize,
.mode = MTD_OOB_RAW
};
if (read)
ret = nand->read_oob(nand, off, &ops);
else
ret = nand->write_oob(nand, off, &ops);
} else if (!strcmp(s, ".raw")) {
/* Raw access */
mtd_oob_ops_t ops = {
.datbuf = (u8 *)addr,
.oobbuf = ((u8 *)addr) + nand->writesize,
.len = nand->writesize,
.ooblen = nand->oobsize,
.mode = MTD_OOB_RAW
};
rwsize = nand->writesize + nand->oobsize;
if (read)
ret = nand->read_oob(nand, off, &ops);
else
ret = nand->write_oob(nand, off, &ops);
} else {
printf("Unknown nand command suffix '%s'.\n", s);
return 1;
}
printf(" %zu bytes %s: %s\n", rwsize,
read ? "read" : "written", ret ? "ERROR" : "OK");
return ret == 0 ? 0 : 1;
}
if (strcmp(cmd, "markbad") == 0) {
argc -= 2;
argv += 2;
if (argc <= 0)
goto usage;
while (argc > 0) {
addr = simple_strtoul(*argv, NULL, 16);
if (nand->block_markbad(nand, addr)) {
printf("block 0x%08lx NOT marked "
"as bad! ERROR %d\n",
addr, ret);
ret = 1;
} else {
printf("block 0x%08lx successfully "
"marked as bad\n",
addr);
}
--argc;
++argv;
}
return ret;
}
if (strcmp(cmd, "biterr") == 0) {
/* todo */
return 1;
}
#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK
if (strcmp(cmd, "lock") == 0) {
int tight = 0;
int status = 0;
if (argc == 3) {
if (!strcmp("tight", argv[2]))
tight = 1;
if (!strcmp("status", argv[2]))
status = 1;
}
if (status) {
do_nand_status(nand);
} else {
if (!nand_lock(nand, tight)) {
puts("NAND flash successfully locked\n");
} else {
puts("Error locking NAND flash\n");
return 1;
}
}
return 0;
}
if (strcmp(cmd, "unlock") == 0) {
if (arg_off_size(argc - 2, argv + 2, &dev, &off, &size) < 0)
return 1;
if (!nand_unlock(&nand_info[dev], off, size)) {
puts("NAND flash successfully unlocked\n");
} else {
puts("Error unlocking NAND flash, "
"write and erase will probably fail\n");
return 1;
}
return 0;
}
#endif
usage:
return CMD_RET_USAGE;
}