zoukankan      html  css  js  c++  java
  • uboot main_loop函数分析

    本文uboot代码版本: 2016.05
     
    跑完spl之后,如果选择了引导uboot启动,最后会进入board_init_r函数,该函数中的数组init_sequence_r中的最后一个元素run_main_loop函数包含我们要介绍的main_loop函数。
     
    位于common/board_r.c下的board_init_r函数, 会按顺序调用数组init_seqence_r里面的函数
     
    void board_init_r(gd_t *new_gd, ulong dest_addr)
    {
    #ifdef CONFIG_NEEDS_MANUAL_RELOC
        int i;
    #endif
    
    #ifdef CONFIG_AVR32
        mmu_init_r(dest_addr);
    #endif
    
    #if !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
        gd = new_gd;
    #endif
    
    #ifdef CONFIG_NEEDS_MANUAL_RELOC
        for (i = 0; i < ARRAY_SIZE(init_sequence_r); i++)
            init_sequence_r[i] += gd->reloc_off;
    #endif
    
        if (initcall_run_list(init_sequence_r))
            hang();
    
        /* NOTREACHED - run_main_loop() does not return */
        hang();
    }
    位于common/board_r.c 的数组 init_sequence_r,数组最后为run_main_loop函数
    init_fnc_t init_sequence_r[] = {
        initr_trace,
        initr_reloc,
        /* TODO: could x86/PPC have this also perhaps? */
    #ifdef CONFIG_ARM
        initr_caches,
        /* Note: For Freescale LS2 SoCs, new MMU table is created in DDR.
         *     A temporary mapping of IFC high region is since removed,
         *     so environmental variables in NOR flash is not availble
         *     until board_init() is called below to remap IFC to high
         *     region.
         */
    #endif
        initr_reloc_global_data,
    #if defined(CONFIG_SYS_INIT_RAM_LOCK) && defined(CONFIG_E500)
        initr_unlock_ram_in_cache,
    #endif
        initr_barrier,
        initr_malloc,
        initr_console_record,
    #ifdef CONFIG_SYS_NONCACHED_MEMORY
        initr_noncached,
    #endif
        bootstage_relocate,
    #ifdef CONFIG_DM
        initr_dm,
    #endif
        initr_bootstage,
    #if defined(CONFIG_ARM) || defined(CONFIG_NDS32)
        board_init,    /* Setup chipselects */
    #endif
        /*
         * TODO: printing of the clock inforamtion of the board is now
         * implemented as part of bdinfo command. Currently only support for
         * davinci SOC's is added. Remove this check once all the board
         * implement this.
         */
    #ifdef CONFIG_CLOCKS
        set_cpu_clk_info, /* Setup clock information */
    #endif
    #ifdef CONFIG_EFI_LOADER
        efi_memory_init,
    #endif
        stdio_init_tables,
        initr_serial,
        initr_announce,
        INIT_FUNC_WATCHDOG_RESET
    #ifdef CONFIG_NEEDS_MANUAL_RELOC
        initr_manual_reloc_cmdtable,
    #endif
    #if defined(CONFIG_PPC) || defined(CONFIG_M68K)
        initr_trap,
    #endif
    #ifdef CONFIG_ADDR_MAP
        initr_addr_map,
    #endif
    #if defined(CONFIG_BOARD_EARLY_INIT_R)
        board_early_init_r,
    #endif
        INIT_FUNC_WATCHDOG_RESET
    #ifdef CONFIG_LOGBUFFER
        initr_logbuffer,
    #endif
    #ifdef CONFIG_POST
        initr_post_backlog,
    #endif
        INIT_FUNC_WATCHDOG_RESET
    #ifdef CONFIG_SYS_DELAYED_ICACHE
        initr_icache_enable,
    #endif
    #if defined(CONFIG_PCI) && defined(CONFIG_SYS_EARLY_PCI_INIT)
        /*
         * Do early PCI configuration _before_ the flash gets initialised,
         * because PCU ressources are crucial for flash access on some boards.
         */
        initr_pci,
    #endif
    #ifdef CONFIG_WINBOND_83C553
        initr_w83c553f,
    #endif
    #ifdef CONFIG_ARCH_EARLY_INIT_R
        arch_early_init_r,
    #endif
        power_init_board,
    #ifndef CONFIG_SYS_NO_FLASH
        initr_flash,
    #endif
        INIT_FUNC_WATCHDOG_RESET
    #if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_X86) || 
        defined(CONFIG_SPARC)
        /* initialize higher level parts of CPU like time base and timers */
        cpu_init_r,
    #endif
    #ifdef CONFIG_PPC
        initr_spi,
    #endif
    #ifdef CONFIG_CMD_NAND
        initr_nand,
    #endif
    #ifdef CONFIG_CMD_ONENAND
        initr_onenand,
    #endif
    #ifdef CONFIG_GENERIC_MMC
        initr_mmc,
    #endif
    #ifdef CONFIG_HAS_DATAFLASH
        initr_dataflash,
    #endif
        initr_env,
    #ifdef CONFIG_SYS_BOOTPARAMS_LEN
        initr_malloc_bootparams,
    #endif
        INIT_FUNC_WATCHDOG_RESET
        initr_secondary_cpu,
    #if defined(CONFIG_ID_EEPROM) || defined(CONFIG_SYS_I2C_MAC_OFFSET)
        mac_read_from_eeprom,
    #endif
        INIT_FUNC_WATCHDOG_RESET
    #if defined(CONFIG_PCI) && !defined(CONFIG_SYS_EARLY_PCI_INIT)
        /*
         * Do pci configuration
         */
        initr_pci,
    #endif
        stdio_add_devices,
        initr_jumptable,
    #ifdef CONFIG_API
        initr_api,
    #endif
        console_init_r,        /* fully init console as a device */
    #ifdef CONFIG_DISPLAY_BOARDINFO_LATE
        show_board_info,
    #endif
    #ifdef CONFIG_ARCH_MISC_INIT
        arch_misc_init,        /* miscellaneous arch-dependent init */
    #endif
    #ifdef CONFIG_MISC_INIT_R
        misc_init_r,        /* miscellaneous platform-dependent init */
    #endif
        INIT_FUNC_WATCHDOG_RESET
    #ifdef CONFIG_CMD_KGDB
        initr_kgdb,
    #endif
        interrupt_init,
    #if defined(CONFIG_ARM) || defined(CONFIG_AVR32)
        initr_enable_interrupts,
    #endif
    #if defined(CONFIG_MICROBLAZE) || defined(CONFIG_AVR32) || defined(CONFIG_M68K)
        timer_init,        /* initialize timer */
    #endif
    #if defined(CONFIG_STATUS_LED)
        initr_status_led,
    #endif
        /* PPC has a udelay(20) here dating from 2002. Why? */
    #ifdef CONFIG_CMD_NET
        initr_ethaddr,
    #endif
    #ifdef CONFIG_BOARD_LATE_INIT
        board_late_init,
    #endif
    #if defined(CONFIG_CMD_AMBAPP)
        ambapp_init_reloc,
    #if defined(CONFIG_SYS_AMBAPP_PRINT_ON_STARTUP)
        initr_ambapp_print,
    #endif
    #endif
    #ifdef CONFIG_CMD_SCSI
        INIT_FUNC_WATCHDOG_RESET
        initr_scsi,
    #endif
    #ifdef CONFIG_CMD_DOC
        INIT_FUNC_WATCHDOG_RESET
        initr_doc,
    #endif
    #ifdef CONFIG_BITBANGMII
        initr_bbmii,
    #endif
    #ifdef CONFIG_CMD_NET
        INIT_FUNC_WATCHDOG_RESET
        initr_net,
    #endif
    #ifdef CONFIG_POST
        initr_post,
    #endif
    #if defined(CONFIG_CMD_PCMCIA) && !defined(CONFIG_CMD_IDE)
        initr_pcmcia,
    #endif
    #if defined(CONFIG_CMD_IDE)
        initr_ide,
    #endif
    #ifdef CONFIG_LAST_STAGE_INIT
        INIT_FUNC_WATCHDOG_RESET
        /*
         * Some parts can be only initialized if all others (like
         * Interrupts) are up and running (i.e. the PC-style ISA
         * keyboard).
         */
        last_stage_init,
    #endif
    #ifdef CONFIG_CMD_BEDBUG
        INIT_FUNC_WATCHDOG_RESET
        initr_bedbug,
    #endif
    #if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER)
        initr_mem,
    #endif
    #ifdef CONFIG_PS2KBD
        initr_kbd,
    #endif
    #if defined(CONFIG_SPARC)
        prom_init,
    #endif
        run_main_loop,
    };
    位于同一目录下的run_main_loop函数,可以看到在一个死循环里面执行main_loop函数,也就是说run_main_loop永远不会返回。
    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;
    }
    以下为位于common/main.c下面的main_loop函数,bootstage_mark_name函数最终调用了show_boot_progress函数,该函数为空函数

    /* 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
    ");
        puts("doc/README.generic-board and take action. Boards not
    ");
        puts("upgraded by the late 2014 may break or be removed.
    ");
    #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");
    }
    setenv函数如下所示,用于设置环境变量ver为version_string
    version_string在common/cmd_version.c定义为
    const char __weak version_string[] = U_BOOT_VERSION_STRING;
    U_BOOT_VERSION_STRING在include/version.h中定义为

    #define U_BOOT_VERSION_STRING U_BOOT_VERSION " (" U_BOOT_DATE " - " 
        U_BOOT_TIME " " U_BOOT_TZ ")" CONFIG_IDENT_STRING
    U_BOOT_VERSION, U_BOOT_DATE, U_BOOT_TIME, U_BOOT_TZ这几个宏在include/generated/version_autogenerated.h和include/generated/timestamp_autogenerated.h中被定义,CONFIG_IDENT_STRING在include/version.h中被定义为空("")

    //nclude/generated/version_autogenerated.h  这个头文件在编译的时候自动生成
    define PLAIN_VERSION "2016.05-00297-ge4b7ffa-dirty"
    #define U_BOOT_VERSION "U-Boot " PLAIN_VERSION
    #define CC_VERSION_STRING "arm-linux-gnueabihf-gcc (Linaro GCC 5.3-2016.02) 5.3.1 20160113"
    #define LD_VERSION_STRING "GNU ld (GNU Binutils) 2.25.0 Linaro 2015_10"
    //include/generated/timestamp_autogenerated.h 这个头文件在编译的时候自动生成
    #define U_BOOT_DATE "Sep 11 2019"
    #define U_BOOT_TIME "21:00:25"
    #define U_BOOT_TZ "+0800"
    #define U_BOOT_DMI_DATE "09/11/2019"
    uboot起来之后可以通过printenv命令查看当前所设置的环境变量,其中就有ver这个环境变量
    varname  =  "ver"
    varvalue = "U-Boot 2016.05-00297-ge4b7ffa-dirty (Sep 11 2019 - 19:24:32 +0800)"
    int setenv(const char *varname, const char *varvalue)
    {
        const char * const argv[4] = { "setenv", varname, varvalue, NULL };
    
        /* before import into hashtable */
        if (!(gd->flags & GD_FLG_ENV_READY))
            return 1;
    
        if (varvalue == NULL || varvalue[0] == '')
            return _do_env_set(0, 2, (char * const *)argv, H_PROGRAMMATIC);
        else
            return _do_env_set(0, 3, (char * const *)argv, H_PROGRAMMATIC);
    }
    varname和varvalue都不为空,所以接着执行 _do_env_set(0, 3, (char * const *)argv, H_PROGRAMMATIC);

    /*
     * Set a new environment variable,
     * or replace or delete an existing one.
     */
    static int _do_env_set(int flag, int argc, char * const argv[], int env_flag)
    {
        int   i, len;
        char  *name, *value, *s;
        ENTRY e, *ep;
    
        debug("Initial value for argc=%d
    ", argc);
        while (argc > 1 && **(argv + 1) == '-') { //argv[1] = "ver", 跳过此循环
            char *arg = *++argv;
            --argc;
            while (*++arg) {
                switch (*arg) {
                case 'f':        /* force */
                    env_flag |= H_FORCE;
                    break;
                default:
                    return CMD_RET_USAGE;
                }
            }
        }
        debug("Final value for argc=%d
    ", argc);
        name = argv[1];//"ver"
        value = argv[2];//"U-Boot 2016.05-00297-ge4b7ffa-dirty (Sep 11 2019 - 20:31:15 +0800)"
    
        if (strchr(name, '=')) {//判断name中是否有 “=”这个字符,如没有返回NULL,此处条件不成立
            printf("## Error: illegal character '='"
                   "in variable name "%s"
    ", name);
            return 1;
        }
    
        env_id++;
    
        /* Delete only ? */
        if (argc < 3 || argv[2] == NULL) {//如果第3个参数为NULL,就删除这变量
            int rc = hdelete_r(name, &env_htab, env_flag);
            return !rc;
        }
        /*
         * Insert / replace new value
         */
        for (i = 2, len = 0; i < argc; ++i)
            len += strlen(argv[i]) + 1; 
      
        value = malloc(len);
        if (value == NULL) {
            printf("## Can't malloc %d bytes
    ", len);
            return 1;
        }
    
        for (i = 2, s = value; i < argc; ++i) {
            char *v = argv[i];
    
            while ((*s++ = *v++) != '')
                ;
            printf("s: [%s]
    ", s);
            *(s - 1) = ' ';
        }
        if (s != value)
            *--s = '';
        e.key    = name;
        e.data    = value;
        hsearch_r(e, ENTER, &ep, &env_htab, env_flag);
        free(value);
        if (!ep) {
            printf("## Error inserting "%s" variable, errno=%d
    ",
                name, errno);
            return 1;
        }
    
        return 0;
    }
    接下来执行cli_init函数,在common/cli.c中对函数的定义如下,如果定义了CONFIG_SYS_HUSH_PARSER则支持hush shell,一个据说来自busybox的命令解析器

    void cli_init(void)
    {
    #ifdef CONFIG_SYS_HUSH_PARSER
        u_boot_hush_start();
    #endif
    
    #if defined(CONFIG_HUSH_INIT_VAR)
        hush_init_var();
    #endif
    }
    run_preboot_environment_command函数从环境变量中获取preboot的定义,该变量包含了一些预启动命令,一般环境变量中不包含该项配置。我目前所用的系统也没有定义该配置,因为没有定义CONFIG_UPDATE_TFTP,所以先不管update_tftp这个函数

    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 */
    }
    函数bootdelay_process在common/autoboot.c中定义,用于获取环境变量bootdelay的值并转成整数后保存在全局变量stored_bootdelay中,最后获取bootcmd的配置值作为函数的返回值。

    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
    
    ", 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.
    ",
                   (unsigned)bootlimit);
            s = getenv("altbootcmd");
        } else
    #endif /* CONFIG_BOOTCOUNT_LIMIT */
            s = getenv("bootcmd");
    
        process_fdt_options(gd->fdt_blob);
        stored_bootdelay = bootdelay;
    
        return s;
    }
    函数cli_process_fdt,在这里一直是false,所以不会回执行cli_secure_boot_cmd函数

    往下执行定义在common/autoboot.c的autoboot_command函数

    void autoboot_command(const char *s)
    {
        debug("### main_loop: bootcmd="%s"
    ", 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 */
    }
    上面第一个if中的函数abortboot函数如下,由于没有定义CONFIG_AUTOBOOT_KEYED,所以会调用abortboot_normal函数,在bootdelay时间内如果没有用户干预,没有按任何键盘键,则这个函数
    顺利执行完,往下执行run_command_list函数,执行环境变量bootcmd里面的一大堆命令,尝试启动并计入linux内核;
     
    假若在bootdelay时间按下了任何键,abortboot函数返回1。

    tatic int abortboot(int bootdelay)
    {
    #ifdef CONFIG_AUTOBOOT_KEYED
        return abortboot_keyed(bootdelay);
    #else
        return abortboot_normal(bootdelay);
    #endif
    }
    则继续往下执行cli_loop运行hush shell解释器:

    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
    ");
    #endif /*CONFIG_SYS_HUSH_PARSER*/
    }
    parse_file_outer进行必要的初始化之后,将调用parse_strea_outer函数;

    /* 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 : '
    ');
    #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.
    ");
                        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>
    ");
                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__ */
    }
    上面的do-while循环执行并解释uboot的命令,也就是我们看到的命令行模式

  • 相关阅读:
    利用闭包向post回调函数传参数
    在seajs中使用require加载静态文件的问题
    jqGrid标题行与第一行之间有很大空白的问题解决。
    关于bootstrapValidator提交问题的解决
    心得(一)
    使用心得(一)
    Dreamweaver CC 2018/64位下载地址及安装教程
    tf.test.is_gpu_available()报错false的解决方案
    Tensorflow——[Could not load dynamic library cudart64_101.dll]解决方案
    如何验证是否正确安装了CUDA
  • 原文地址:https://www.cnblogs.com/cheyihaosky/p/11509485.html
Copyright © 2011-2022 走看看