zoukankan      html  css  js  c++  java
  • Mini440之uboot移植之源码分析命令解析(五)

    看到这里的同学,恭喜你,马上就看完了u-boot的源码了。

    我们介绍到了init_sequence_r的最后一个函数run_main_loop,该函数位于common/board_r.c文件中。

    一、run_main_loop(common/board_r.c)

    static int run_main_loop(void)
    {
    #ifdef CONFIG_SANDBOX
        sandbox_main_loop_init();
    #endif
        /* main_loop() can return to retry autoboot, if so just run it again */
        for (;;)
            main_loop();
        return 0;
    }

    进入命令循环(即整个boot的工作循环),接受用户从串口输入的命令,然后进行相应工作。

    二、main_loop(common/main.c)

    main_loop函数位于common/main.c文件中。

    /* 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");
    
    #ifndef CONFIG_SYS_GENERIC_BOARD
        puts("Warning: Your board does not use generic board. Please read\n");
        puts("doc/README.generic-board and take action. Boards not\n");
        puts("upgraded by the late 2014 may break or be removed.\n");
    #endif
    
    #ifdef CONFIG_VERSION_VARIABLE
        setenv("ver", version_string);  /* set version variable */
    #endif /* CONFIG_VERSION_VARIABLE */
    
        cli_init();
    
        run_preboot_environment_command();   /*啥也没做 */
    
    #if defined(CONFIG_UPDATE_TFTP)
        update_tftp(0UL, NULL, NULL);
    #endif /* CONFIG_UPDATE_TFTP */
    
        s = bootdelay_process();
        if (cli_process_fdt(&s))
            cli_secure_boot_cmd(s);
    
        autoboot_command(s);
    
        cli_loop();
        panic("No CLI available");
    }

    该函数做的都是与平台无关的工作,主要包括:

    • cli_init:用来初始化hush shell使用的变量top_vars ;
    • bootdelay_process和autoboot_command:u-boot预启动相关函数,读取环境变量bootdelay和bootcmd的配置值,在u-boot启动延时计数期间内如无用户按键输入干预,那么将执行bootcmd配置中的命令,默认配置未启动linux内核;如果有按键按下或者倒计时结束,将会进入uboot命令行模式;
    • cli_llp:死循环,进入uboot命令行,解析命令,并执行对应函数;

    2.1 cli_init(common/cli.c)

    void cli_init(void)
    {
    #ifdef CONFIG_SYS_HUSH_PARSER
        u_boot_hush_start();
    #endif
    
    #if defined(CONFIG_HUSH_INIT_VAR)
        hush_init_var();
    #endif
    }

    CONFIG_SYS_HUSH_PARSER在CONFIG_SYS_HUSH_PARSER中定义。 u_boot_hush_start(common/cli_hush.c) 定义:

    int u_boot_hush_start(void)
    {
        if (top_vars == NULL) {
            top_vars = malloc(sizeof(struct variables));
            top_vars->name = "HUSH_VERSION";
            top_vars->value = "0.01";
            top_vars->next = NULL;
            top_vars->flg_export = 0;
            top_vars->flg_read_only = 1;
    #ifdef CONFIG_NEEDS_MANUAL_RELOC
            u_boot_hush_reloc();
    #endif
        }
        return 0;
    }

    其中top_vars定义:

    struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 };
    struct variables *top_vars = &shell_ver;

    从上可知:cli_init用来初始化hush shell使用的变量top_vars 

    2.2 run_preboot_environment_command(common/main.c)

    static void run_preboot_environment_command(void)
    {
    #ifdef CONFIG_PREBOOT
        char *p;
    
        p = getenv("preboot");
        if (p != NULL) {
    # ifdef CONFIG_AUTOBOOT_KEYED
            int prev = disable_ctrlc(1);    /* disable Control C checking */
    # endif
    
            run_command_list(p, -1, 0);
    
    # ifdef CONFIG_AUTOBOOT_KEYED
            disable_ctrlc(prev);    /* restore Control C checking */
    # endif
        }
    #endif /* CONFIG_PREBOOT */
    }

    如果定义了CONFIG_PREBOOT,该函数将会从环境变量获取preboot的定义,该变量包含了一些预启动命令,一般环境变量中不包含该项配置。

    由于CONFIG_PREBOOT宏未定义,所以这里均不执行。

    2.3 bootdelay_process(common/autoboot.c)

    const char *bootdelay_process(void)
    {
        char *s;
        int bootdelay;
    #ifdef CONFIG_BOOTCOUNT_LIMIT
        unsigned long bootcount = 0;
        unsigned long bootlimit = 0;
    #endif /* CONFIG_BOOTCOUNT_LIMIT */
    
    #ifdef CONFIG_BOOTCOUNT_LIMIT
        bootcount = bootcount_load();
        bootcount++;
        bootcount_store(bootcount);
        setenv_ulong("bootcount", bootcount);
        bootlimit = getenv_ulong("bootlimit", 10, 0);
    #endif /* CONFIG_BOOTCOUNT_LIMIT */
    
        s = getenv("bootdelay");
        bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
    
    #ifdef CONFIG_OF_CONTROL
        bootdelay = fdtdec_get_config_int(gd->fdt_blob, "bootdelay",
                bootdelay);
    #endif
    
        debug("### main_loop entered: bootdelay=%d\n\n", bootdelay);
    
    #if defined(CONFIG_MENU_SHOW)
        bootdelay = menu_show(bootdelay);
    #endif
        bootretry_init_cmd_timeout();
    
    #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");
    
        process_fdt_options(gd->fdt_blob);
        stored_bootdelay = bootdelay;
    
        return s;
    }

    bootdelay_process从环境变量获取bootdelay和bootcmd配置值,将提取的bootdelay配置值转换成整数,赋值给全局变量stored_bootdelay。最后返回bootcmd的配置值。

    u-boot在执行中,会输出如下调试信息:

    initcall: 0000f4e4 (relocated to 33f304e4)
    ### main_loop entered: bootdelay=5

    2.4 autoboot_command(common/autoboot.c)

    void autoboot_command(const char *s)
    {
        debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
    
        if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {
    #if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
            int prev = disable_ctrlc(1);    /* disable Control C checking */
    #endif
    
            run_command_list(s, -1, 0);
    
    #if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
            disable_ctrlc(prev);    /* restore Control C checking */
    #endif
        }
    
    #ifdef CONFIG_MENUKEY
        if (menukey == CONFIG_MENUKEY) {
            s = getenv("menucmd");
            if (s)
                run_command_list(s, -1, 0);
        }
    #endif /* CONFIG_MENUKEY */
    }

    stored-bootdelay为u-boot的启动延时计数值,如果倒计时正常结束,那么将执行run_command_list,此函数会执行参数s指定的一系列命令,也就是bootcmd中配置中的命令,bootcmd中保存着默认的启动命令,因此linux内核启动

    如果在倒计时结束前按下回车键,run_command_list就不会执行,autoboot_command相当于空函数,然后执行cli_loop函数,这个是命令行处理函数,负责接收处理输入命令。

    三、命令行实现

    由于cli_loop的实现比较复杂,这里单独介绍。cli_loop定义在common/cli.c文件中:

    void cli_loop(void)
    {
    #ifdef CONFIG_SYS_HUSH_PARSER
        parse_file_outer();
        /* This point is never reached */
        for (;;);
    #elif defined(CONFIG_CMDLINE)
        cli_simple_loop();
    #else
        printf("## U-Boot command line is disabled. Please enable CONFIG_CMDLINE\n");
    #endif /*CONFIG_SYS_HUSH_PARSER*/
    }

    这里进行了循环执行命令的代码:

    • 第一种形式是采用HUSH解析的方式;
    • 第二种形式是采用cli_simple_loop的方式;

    第二种调用比较简单。主要是直接从串口读取一行命令:

    len = cli_readline(CONFIG_SYS_PROMPT);

    然后调用如下函数开始执行:

    rc = run_command_repeatable(lastcommand, flag);->cli_simple_run_command

    然而smdk2410默认采用的HUSH解析的方式。下面我们重点介绍这个。

    3.1 parse_file_outer(common/cli_hush.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;
    }

    __U_BOOT__在common/cli_hush.c文件中定义。其中setup_file_in_str函数初始化了input结构参数:

    #ifndef __U_BOOT__
    static void setup_file_in_str(struct in_str *i, FILE *f)
    #else
    static void setup_file_in_str(struct in_str *i)
    #endif
    {
        i->peek = file_peek;
        i->get = file_get;
        i->__promptme=1;
        i->promptmode=1;
    #ifndef __U_BOOT__
        i->file = f;
    #endif
        i->p = NULL;
    }

    3.2 parse_stream_outer(common/cli_hush.c)

    /* 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;
    #ifdef __U_BOOT__
        int code = 1;
    #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,
                         flag & FLAG_CONT_ON_NEWLINE ? -1 : '\n');
    #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.\n");
                        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>\n");
                inp->__promptme = 1;
    #endif
                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)));
    #ifndef __U_BOOT__
        return 0;
    #else
        return (code != 0) ? 1 : 0;
    #endif /* __U_BOOT__ */
    }

    部分FLAG宏定义如下:

    #define FLAG_EXIT_FROM_LOOP 1
    #define FLAG_PARSE_SEMICOLON (1 << 1)      /* symbol ';' is special for parser */
    #define FLAG_REPARSING       (1 << 2)      /* >=2nd pass */
    #define FLAG_CONT_ON_NEWLINE (1 << 3)      /* continue when we see \n */

    parse_stream_outer函数就是 hush shell的命令解释器,使用do-while循环接收命令行输入,然后利用函数parse_stream函数解析,调用run_list函数在经过一系列函数调用cmd_process函数来处理命令。

    3.4 parse_stream(common/cli_hush.c)

    /* return code is 0 for normal exit, 1 for syntax error */
    static int parse_stream(o_string *dest, struct p_context *ctx,
                struct in_str *input, int end_trigger)
    {
        unsigned int ch, m;
    #ifndef __U_BOOT__
        int redir_fd;
        redir_type redir_style;
    #endif
        int next;
    
        /* Only double-quote state is handled in the state variable dest->quote.
         * A single-quote triggers a bypass of the main loop until its mate is
         * found.  When recursing, quote state is passed in via dest->quote. */
    
        debug_printf("parse_stream, end_trigger=%d\n",end_trigger);
        while ((ch=b_getch(input))!=EOF) {
            m = map[ch];
    #ifdef __U_BOOT__
            if (input->__promptme == 0) return 1;
    #endif
            next = (ch == '\n') ? 0 : b_peek(input);
    
            debug_printf("parse_stream: ch=%c (%d) m=%d quote=%d - %c\n",
                ch >= ' ' ? ch : '.', ch, m,
                dest->quote, ctx->stack == NULL ? '*' : '.');
    
            if (m==0 || ((m==1 || m==2) && dest->quote)) {
                b_addqchr(dest, ch, dest->quote);
            } else {
                if (m==2) {  /* unquoted IFS */
                    if (done_word(dest, ctx)) {
                        return 1;
                    }
                    /* If we aren't performing a substitution, treat a newline as a
                     * command separator.  */
                    if (end_trigger != '\0' && ch=='\n')
                        done_pipe(ctx,PIPE_SEQ);
                }
                if (ch == end_trigger && !dest->quote && ctx->w==RES_NONE) {
                    debug_printf("leaving parse_stream (triggered)\n");
                    return 0;
                }
    #if 0
                if (ch=='\n') {
                    /* Yahoo!  Time to run with it! */
                    done_pipe(ctx,PIPE_SEQ);
                    run_list(ctx->list_head);
                    initialize_context(ctx);
                }
    #endif
                if (m!=2) switch (ch) {
            case '#':
                if (dest->length == 0 && !dest->quote) {
                    while(ch=b_peek(input),ch!=EOF && ch!='\n') { b_getch(input); }
                } else {
                    b_addqchr(dest, ch, dest->quote);
                }
                break;
            case '\\':
                if (next == EOF) {
                    syntax();
                    return 1;
                }
                b_addqchr(dest, '\\', dest->quote);
                b_addqchr(dest, b_getch(input), dest->quote);
                break;
            case '$':
                if (handle_dollar(dest, ctx, input)!=0) return 1;
                break;
            case '\'':
                dest->nonnull = 1;
                while(ch=b_getch(input),ch!=EOF && ch!='\'') {
    #ifdef __U_BOOT__
                    if(input->__promptme == 0) return 1;
    #endif
                    b_addchr(dest,ch);
                }
                if (ch==EOF) {
                    syntax();
                    return 1;
                }
                break;
            case '"':
                dest->nonnull = 1;
                dest->quote = !dest->quote;
                break;
    #ifndef __U_BOOT__
            case '`':
                process_command_subs(dest, ctx, input, '`');
                break;
            case '>':
                redir_fd = redirect_opt_num(dest);
                done_word(dest, ctx);
                redir_style=REDIRECT_OVERWRITE;
                if (next == '>') {
                    redir_style=REDIRECT_APPEND;
                    b_getch(input);
                } else if (next == '(') {
                    syntax();   /* until we support >(list) Process Substitution */
                    return 1;
                }
                setup_redirect(ctx, redir_fd, redir_style, input);
                break;
            case '<':
                redir_fd = redirect_opt_num(dest);
                done_word(dest, ctx);
                redir_style=REDIRECT_INPUT;
                if (next == '<') {
                    redir_style=REDIRECT_HEREIS;
                    b_getch(input);
                } else if (next == '>') {
                    redir_style=REDIRECT_IO;
                    b_getch(input);
                } else if (next == '(') {
                    syntax();   /* until we support <(list) Process Substitution */
                    return 1;
                }
                setup_redirect(ctx, redir_fd, redir_style, input);
                break;
    #endif
            case ';':
                done_word(dest, ctx);
                done_pipe(ctx,PIPE_SEQ);
                break;
            case '&':
                done_word(dest, ctx);
                if (next=='&') {
                    b_getch(input);
                    done_pipe(ctx,PIPE_AND);
                } else {
    #ifndef __U_BOOT__
                    done_pipe(ctx,PIPE_BG);
    #else
                    syntax_err();
                    return 1;
    #endif
                }
                break;
            case '|':
                done_word(dest, ctx);
                if (next=='|') {
                    b_getch(input);
                    done_pipe(ctx,PIPE_OR);
                } else {
                    /* we could pick up a file descriptor choice here
                     * with redirect_opt_num(), but bash doesn't do it.
                     * "echo foo 2| cat" yields "foo 2". */
    #ifndef __U_BOOT__
                    done_command(ctx);
    #else
                    syntax_err();
                    return 1;
    #endif
                }
                break;
    #ifndef __U_BOOT__
            case '(':
            case '{':
                if (parse_group(dest, ctx, input, ch)!=0) return 1;
                break;
            case ')':
            case '}':
                syntax();   /* Proper use of this character caught by end_trigger */
                return 1;
                break;
    #endif
            case SUBSTED_VAR_SYMBOL:
                dest->nonnull = 1;
                while (ch = b_getch(input), ch != EOF &&
                    ch != SUBSTED_VAR_SYMBOL) {
                    debug_printf("subst, pass=%d\n", ch);
                    if (input->__promptme == 0)
                        return 1;
                    b_addchr(dest, ch);
                }
                debug_printf("subst, term=%d\n", ch);
                if (ch == EOF) {
                    syntax();
                    return 1;
                }
                break;
            default:
                syntax();   /* this is really an internal logic error */
                return 1;
                }
            }
        }
        /* complain if quote?  No, maybe we just finished a command substitution
         * that was quoted.  Example:
         * $ echo "`cat foo` plus more"
         * and we just got the EOF generated by the subshell that ran "cat foo"
         * The only real complaint is if we got an EOF when end_trigger != '\0',
         * that is, we were really supposed to get end_trigger, and never got
         * one before the EOF.  Can't use the standard "syntax error" return code,
         * so that parse_stream_outer can distinguish the EOF and exit smoothly. */
        debug_printf("leaving parse_stream (EOF)\n");
        if (end_trigger != '\0') return -1;
        return 0;
    }

    3.4 cmd_process函数

    在uboot中,命令是通过宏U-BOOT-CMD来定义,最终目的是为了定义一个cmd_tbl_t类型的变量,并初始化这个变量的各个成员。Uboot中的每个命令都存放在.uboot_boot_list段中,每个命令都有一个名为do_xxx(xxx为具体的命令名)的函数,这个do_xxx函数就是具体的命令处理函数。了解了uboot中命令的组成再来看一下cmd_process函数的处理过程。

    cmd_process在common/command.c中定义。通过调用find_cmd函数在命令表中找到指定的命令,命令表其实就是cmd_tlb_t结构体数组,在find_cmd函数中通过函数ll_entry_start得到数组的第一个元素,也就是命令表起始地址,通过ll_entry_count得到数组长度,也就是命令表的长度,最后通过find_cmd_tlb在命令表中找到所需的命令。

    在命令表中找到命令以后,就调用cmd_call函数来执行具体的命令

    参考文章

    [1]从零开始之uboot、移植uboot2017.01(八、命令解析与实现)

    [2]uboot启动流程关键函数的介绍(二)

  • 相关阅读:
    独一份秘籍 | 开发数字孪生3D可视化炫酷场景?还有MAC电脑大奖可拿!
    数字孪生城市,如何破旧立新?ThingJS
    官方示例(十六):3D场景中BIM剖切面参数化开发ThingJS
    官方案例(十五):3D开发构造器参数测量多边形面积 ThingJS
    如何在Spring Boot 中使用 HandlerMethodArgumentResolver
    python的基本数据类型
    canvas 隐藏 踩坑
    小程序canvas 圆角框带填充颜色
    小程序 canvas 文字加粗
    flex布局 一行4个元素 后面不够4个元素对齐
  • 原文地址:https://www.cnblogs.com/zyly/p/15612039.html
Copyright © 2011-2022 走看看