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\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"); }
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] == '\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\n", 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\n", 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\"\n", 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\n", len); return 1; } for (i = 2, s = value; i < argc; ++i) { char *v = argv[i]; while ((*s++ = *v++) != '\0') ; printf("s: [%s]\n", s); *(s - 1) = ' '; } if (s != value) *--s = '\0'; 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\n", 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\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; }
函数cli_process_fdt,在这里一直是false,所以不会回执行cli_secure_boot_cmd函数

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

void autoboot_command(const char *s)
{
    debug("### main_loop: bootcmd=\"%s\"\n", s ? s : ""); 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\n"); #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 : '\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("\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__ */ }
上面的do-while循环执行并解释uboot的命令,也就是我们看到的命令行模式

参考链接: https://blog.csdn.net/u011565090/article/details/39778419
https://www.cnblogs.com/fah936861121/articles/6929858.html
http://blog.chinaunix.net/uid-8867796-id-358806.html
https://blog.csdn.net/shiyongyue/article/details/73332185
http://blog.chinaunix.net/uid-25605754-id-1629872.html
https://www.cnblogs.com/CZM-/p/5069920.html

你可能感兴趣的:(uboot main_loop函数分析)