• uboot启动流程源码分析(二)


    上一篇找到了我的关键点–main_loop函数,这一篇来好好看一下。

    uboot中的main_loop函数是怎么工作的。

    1、main_loop函数是做什么的?

    start_armboot最后进入死循环调用了main_loop 函数;

    uboot的目的是启动内核,那么main_loop一定会有设置启动参数启动内核的实现;

    main_loop()函数做的都是与具体平台无关的工作,主要包括初始化启动次数限制机制、设置软件版本号、打印启动信息、解析命令等。

    2、main_loop()函数内容

    void main_loop(void)
    {
        const char *s;
    
        bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");
    
        if (IS_ENABLED(CONFIG_VERSION_VARIABLE))
            env_set("ver", version_string);  /* set version variable */
    
        cli_init();
    
        if (IS_ENABLED(CONFIG_USE_PREBOOT))
            run_preboot_environment_command();
    
        if (IS_ENABLED(CONFIG_UPDATE_TFTP))
            update_tftp(0UL, NULL, NULL);
    
        s = bootdelay_process();
        if (cli_process_fdt(&s))
            cli_secure_boot_cmd(s);
    
        autoboot_command(s);
    
        cli_loop();
        panic("No CLI available");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    env_set:设置环境变量,两个参数分别为name和value

    cli_init:用于初始化hash shell的一些变量

    run_preboot_environment_command:执行预定义的环境变量的命令

    bootdelay_process:加载延时处理,一般用于Uboot启动后,有几秒的倒计时,用于进入命令行模式。

    cli_loop:命令行模式,主要作用于Uboot的命令行交互。

    bootdelay_process

    const char *bootdelay_process(void)
    {
        char *s;
        int bootdelay;
    
        bootcount_inc();
    
        s = env_get("bootdelay");                               //先判断是否有bootdelay环境变量,如果没有,就使用menuconfig中配置的CONFIG_BOOTDELAY时间
        bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
    
        if (IS_ENABLED(CONFIG_OF_CONTROL))                      //是否使用设备树进行配置
            bootdelay = fdtdec_get_config_int(gd->fdt_blob, "bootdelay",
                              bootdelay);
    
        debug("### main_loop entered: bootdelay=%d\n\n", bootdelay);
    
        if (IS_ENABLED(CONFIG_AUTOBOOT_MENU_SHOW))
            bootdelay = menu_show(bootdelay);
        bootretry_init_cmd_timeout();
    
    #ifdef CONFIG_POST
        if (gd->flags & GD_FLG_POSTFAIL) {
            s = env_get("failbootcmd");
        } else
    #endif /* CONFIG_POST */
        if (bootcount_error())
            s = env_get("altbootcmd");
        else
            s = env_get("bootcmd");                             //获取bootcmd环境变量,用于后续的命令执行
    
        if (IS_ENABLED(CONFIG_OF_CONTROL))
            process_fdt_options(gd->fdt_blob);
        stored_bootdelay = bootdelay;
    
        return s;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    autoboot_command

    void autoboot_command(const char *s)
    {
        debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "");
    
        if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {
            bool lock;
            int prev;
    
            lock = IS_ENABLED(CONFIG_AUTOBOOT_KEYED) &&
                !IS_ENABLED(CONFIG_AUTOBOOT_KEYED_CTRLC);
            if (lock)
                prev = disable_ctrlc(1); /* disable Ctrl-C checking */
    
            run_command_list(s, -1, 0);
    
            if (lock)
                disable_ctrlc(prev);    /* restore Ctrl-C checking */
        }
    
        if (IS_ENABLED(CONFIG_USE_AUTOBOOT_MENUKEY) &&
            menukey == AUTOBOOT_MENUKEY) {
            s = env_get("menucmd");
            if (s)
                run_command_list(s, -1, 0);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    我们看一下判断条件stored_bootdelay != -1 && s && !abortboot(stored_bootdelay

    • stored_bootdelay:为环境变量的值,或者menuconfig设置的值

    • s:为环境变量bootcmd的值,为后续运行的指令

    • abortboot(stored_bootdelay):主要用于判断是否有按键按下。如果按下,则不执行bootcmd命令,进入cli_loop命令行模式;如果不按下,则执行bootcmd命令,跳转到加载Linux启动。

    cli_loop

    void cli_loop(void)
    {
        bootstage_mark(BOOTSTAGE_ID_ENTER_CLI_LOOP);
    #ifdef CONFIG_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_HUSH_PARSER*/
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    如上代码,程序只执行parse_file_outer来处理用户的输入、输出信息。

    最后付一个关于main_loop()较为丰富的函数,去掉了宏定义控制的代码

    void main_loop (void)
    {
    
    static char lastcommand[CFG_CBSIZE] = {
     0, };
    int len;
    int rc = 1;
    int flag;
    #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) //是否有bootdelay
    char *s;
    int bootdelay;
    #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);//将启动次数加1再写回去保存起来
    sprintf (bcs_set, "%lu", bootcount);
    setenv ("bootcount", bcs_set); //设置已经启动的次数到环境变量bootcount
    bcs = getenv ("bootlimit");//从环境变量获取启动次数的上限,此时返回的是字符串还需要转换成整数
    bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0;
    #endif /* CONFIG_BOOTCOUNT_LIMIT */
    #ifdef CONFIG_VERSION_VARIABLE //设置ver环境变量,里面保存的是uboot的版本
    {
    
    extern char version_string[];
    setenv ("ver", version_string); /* set version variable */
    }
    #endif /* CONFIG_VERSION_VARIABLE */
    #ifdef CONFIG_AUTO_COMPLETE //命令的自动补全功能
    install_auto_complete();
    #endif
    #ifdef CONFIG_FASTBOOT//支持fastboot刷机
    if (fastboot_preboot())
    run_command("fastboot", 0);
    #endif
    /* 下面就是实现uboot启动延时机制bootdelay的代码 */
    #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);
    /* 检查启动次数是否超过上限*/
    #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 : "");
    /*abortboot函数是检测在bootdelay时间内是否有人按键:如果有人按键则返回1; 超过bootdelay的时间没有人按键则返回0,if条件满足则启动内核*/
    if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
    
    #ifdef CONFIG_AUTOBOOT_KEYED
    int prev = disable_ctrlc(1); /* 禁止 ctrl+c 功能 */
    #endif
    run_command (s, 0); //启动内核
    #ifdef CONFIG_AUTOBOOT_KEYED
    disable_ctrlc(prev); /* 恢复 ctrl+c 功能 */
    #endif
    }
    #endif /* CONFIG_BOOTDELAY */
    /* * 下面是一个死循环,不停的从控制台读取命令解析,直到执行bootm命令去启动内核 */
    for (;;) {
    
    len = readline (CFG_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;
    if (len == -1)
    puts ("\n");
    else
    rc = run_command (lastcommand, flag); //解析并运行读取到的指令
    if (rc <= 0) {
    
    /* invalid command or not repeatable, forget it */
    lastcommand[0] = 0;
    }
    }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
  • 相关阅读:
    Kotlin File.reader BufferedReader readLine
    SpringBoot基础知识和运行
    Spark 的主要组件及任务分工
    2022年中级经济师《工商管理》考试大纲
    ubuntn azkaban4.0.0链接mysql(5.1/8.0)数据库配置
    rsync—远程同步
    苹果U盘格式化了怎么恢复
    捆绑保险的同时,平安汽车金融还站着把利息收了?
    2022年全球及中国鼻梁条行业头部企业市场占有率及排名调研报告
    虹科示波器 | 汽车免拆检修 | 2010款奥迪A5车怠速时发动机偶尔自动熄火
  • 原文地址:https://blog.csdn.net/weixin_45264425/article/details/127400109