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;
}