u-boot 中的 shell
在 u-boot 的最后阶段,实际是通过一系列存储在环境变量中的命令来完成系统引导的工作。同时我们也可以在调试串口中,中断 u-boot 的引导进程,进入一个可以输入命令的界面。这二者的命令解析都是通过 u-boot 代码中嵌入的一个非常小巧的 shell 工具来实现的。
shell 的启动在 main_loop ,即在 init_sequence_r 初始化的最后一个函数调用中 run_main_loop :
/* We come here after U-Boot is initialised and ready to process commands */
void main_loop(void)
{
const char *s;
bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");
cli_init();
run_preboot_environment_command();
s = bootdelay_process();
if (cli_process_fdt(&s))
cli_secure_boot_cmd(s);
autoboot_command(s);
cli_loop();
panic("No CLI available");
}
从 cli_init 函数中可以看到 u-boot 启动的这个 shell 名称叫做 hush shell 。在 busybox ,也可以看到这个小巧的内嵌的 shell 身影,从代码量的大小来看,u-boot 中的 hush shell 相对 busybox 做了很大的瘦身工作。
run_preboot_environment_command 执行环境变量 preboot 所定义的命令,将命令字符串送入了 run_command_list 函数:(u-boot 代码中对条件编译的滥用,真是不堪入目)
#define FLAG_PARSE_SEMICOLON (1 << 1) /* symbol ';' is special for parser */
int run_command_list(const char *cmd, int len, int flag)
{
int rcode = 0;
rcode = parse_string_outer(cmd, FLAG_PARSE_SEMICOLON);
return rcode;
}
在使用 hush shell 时,run_command_list 只是对 hush shell 中的函数 parse_string_outer 进行了一层封装:
struct in_str {
const char *p;
char peek_buf[2];
int __promptme;
int promptmode;
int (*get) (struct in_str *);
int (*peek) (struct in_str *);
};
int parse_string_outer(const char *s, int flag)
{
struct in_str input;
char *p = NULL;
int rcode;
if (!s)
return 1;
if (!*s)
return 0;
if (!(p = strchr(s, '\n')) || *++p) {
p = xmalloc(strlen(s) + 2);
strcpy(p, s);
strcat(p, "\n");
setup_string_in_str(&input, p);
rcode = parse_stream_outer(&input, flag);
free(p);
return rcode;
} else {
setup_string_in_str(&input, s);
return parse_stream_outer(&input, flag);
}
}
setup_string_in_str 初始化了一个 struct in_str 类型的变量:
static void setup_string_in_str(struct in_str *i, const char *s)
{
i->peek = static_peek;
i->get = static_get;
i->__promptme = 1;
i->promptmode = 1;
i->p = s;
}
parse_stream_outer 函数定义如下:
/* This holds pointers to the various results of parsing */
struct p_context {
struct child_prog *child;
struct pipe *list_head;
struct pipe *pipe;
struct redir_struct *pending_redirect;
reserved_style w;
int old_flag; /* for figuring out valid reserved words */
struct p_context *stack;
int type; /* define type of parser : ";$" common or special symbol */
/* How about quoting status? */
};
static void initialize_context(struct p_context *ctx)
{
ctx->pipe = NULL;
ctx->child = NULL;
ctx->list_head = new_pipe();
ctx->pipe = ctx->list_head;
ctx->w = RES_NONE;
ctx->stack = NULL;
ctx->old_flag = 0;
done_command(ctx); /* creates the memory for working child */
}
/* most recursion does not come through here, the exeception is
* from builtin_source() */
static int parse_stream_outer(struct in_str *inp, int flag)
{
struct p_context ctx;
o_string temp = NULL_O_STRING;
int rcode;
int code = 1;
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,
flag & FLAG_CONT_ON_NEWLINE ? -1 : '\n');
if (rcode == 1)
flag_repeat = 0;
if (rcode != 1 && ctx.old_flag != 0) {
syntax();
flag_repeat = 0;
}
if (rcode != 1 && ctx.old_flag == 0) {
done_word(&temp, &ctx);
done_pipe(&ctx,PIPE_SEQ);
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.\n");
continue;
}
break;
}
if (code == -1)
flag_repeat = 0;
} else {
if (ctx.old_flag != 0) {
free(ctx.stack);
b_reset(&temp);
}
if (inp->__promptme == 0) printf("<INTERRUPT>\n");
inp->__promptme = 1;
temp.nonnull = 0;
temp.quote = 0;
inp->p = NULL;
free_pipe_list(ctx.list_head,0);
}
b_free(&temp);
/* loop on syntax errors, return on EOF */
} while (rcode != -1 && !(flag & FLAG_EXIT_FROM_LOOP) &&
(inp->peek != static_peek || b_peek(inp)));
return (code != 0) ? 1 : 0;
}