uboot启动流程-run_main_loop 到 cmd_process处理说明二

本文详细解析了U-Boot启动过程中的run_main_loop和cmd_process函数,探讨了uboot命令的定义与执行机制,以及如何通过U_BOOT_CMD宏定义和初始化命令表cmd_tbl_t。

一.   uboot启动流程

上一篇文章大体分析了run_main_loop 到 cmd_process处理过程。地址如下:

uboot启动流程-run_main_loop 到 cmd_process处理说明一_凌肖战的博客-优快云博客

本文具体分析涉及的几个函数执行过程。

二.  cmd_process处理用到的 uboot命令

uboot 启动以后会进入 3 秒倒计时,如果在 3 秒倒计时结束之前按下按下回车键,就 会进入 uboot 的命令模式,如果倒计时结束以后都没有按下回车键,那么就会自动启动 Linux 核 , 这个功能 就 是 由 run_main_loop 函 数 来 完 成 的。

run_main_loop 函数主要是调用了 main_loop 函数

main_loop函数做了很多事,主要调用了几个函数:

bootdelay_process 函数:此函数会读取环境变量 bootdelay 和 bootcmd 的内容, 然后将 bootdelay 的值赋值给全局变量 stored_bootdelay,返回值为环境变量 bootcmd 的值。

autoboot_command 函数:此函数就是检查倒计时是否结束?倒计时结束之前有没有被打断?

 cli_loop 函数:是 uboot 的命令行处理函数,我们在 uboot 中输入各种命令,进行各种操作就是有 cli_loop 函数来处理的。该函数最终调用  cmd_process函数。

1.  uboot中命令是如何定义?

在学习 cmd_process 之前先看一下 uboot 中命令是如何定义的。 uboot 使用宏 U_BOOT_CMD 来定义命令,宏 U_BOOT_CMD 定义在文件 include/command.h 中,定义如下:
#define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help)		\
	U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, NULL)

可 以 看 出:U_BOOT_CMD U_BOOT_CMD_COMPLETE 的 特 例 , 将 U_BOOT_CMD_COMPLETE 的最后一个参数设置成 NULL 就 是 U_BOOT_CMD。U_BOOT_CMD_COMPLETE 如下:
#define U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, _comp) \
	ll_entry_declare(cmd_tbl_t, _name, cmd) =			\
		U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,	\
						_usage, _help, _comp);

宏 U_BOOT_CMD_COMPLETE 又 用 到了 :
ll_entry_declare U_BOOT_CMD_MKENT_COMPLETE
U_BOOT_CMD_MKENT_COMPLETE 定义在文件 include/command.h 中,内容如下:
#define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd, \
 _usage, _help, _comp) \
 { #_name, _maxargs, _rep, _cmd, _usage, \
 _CMD_HELP(_help) _CMD_COMPLETE(_comp) }
上 述 代 码 中 的 “ # ” 表 示 将 _name 传 递 过 来 的 值 字 符 串 化。
ll_entry_declar 定义在文件 include/linker_lists.h 中,定 义如下:
#define ll_entry_declare(_type, _name, _list) \
 _type _u_boot_list_2_##_list##_2_##_name __aligned(4) \
 __attribute__((unused, \
 section(".u_boot_list_2_"#_list"_2_"#_name)))
_type cmd_tbl_t ,因此 ll_entry_declare 就是定义了一个 cmd_tbl_t 变量,这里用到了 C
言中的“ ## ”连接符符。其中的“ ##_list ”表示用 _list 的值来替换,“ ##_name ”就是用 _name
值来替换。
U_BOOT_CMD_MKENT_COMPLETE 又用到了宏 _CMD_HELP _CMD_COMPLETE ,这两个
宏的定义如下:
#ifdef CONFIG_AUTO_COMPLETE
# define _CMD_COMPLETE(x) x,
#else
# define _CMD_COMPLETE(x)
#endif
#ifdef CONFIG_SYS_LONGHELP
# define _CMD_HELP(x) x,
#else
# define _CMD_HELP(x)
#endif

综上所述,看一下 U_BOOT_CMD 经过展开以后究竟是个什么模样的。

所有与命令有关的,即命令的实现,在 uboot根目录下的 cmd目录的文件下。

以命令 dhcp 为例,dhcp 为网络命令,所以在 uboot根目录下的/cmd/net.c文件中。dhcp 命令定义如下:
U_BOOT_CMD(
 dhcp, 3, 1, do_dhcp,
 "boot image via network using DHCP/TFTP protocol",
 "[loadAddress] [[hostIPaddr:]bootfilename]"
);
将其展开,结果如下:
1、将 U_BOOT_CMD 展开后为:
U_BOOT_CMD_COMPLETE(dhcp, 3, 1, do_dhcp,
 "boot image via network using DHCP/TFTP protocol",
 "[loadAddress] [[hostIPaddr:]bootfilename]",
 NULL)
 
2、将 U_BOOT_CMD_COMPLETE 展开后为:
ll_entry_declare(cmd_tbl_t, dhcp, cmd) = \
U_BOOT_CMD_MKENT_COMPLETE(dhcp, 3, 1, do_dhcp, \
 "boot image via network using DHCP/TFTP protocol", \
 "[loadAddress] [[hostIPaddr:]bootfilename]", \
 NULL);
 
3、将 ll_entry_declare 和 U_BOOT_CMD_MKENT_COMPLETE 展开后为: 
cmd_tbl_t _u_boot_list_2_cmd_2_dhcp __aligned(4) \
 __attribute__((unused,section(.u_boot_list_2_cmd_2_dhcp))) \ 
 { "dhcp", 3, 1, do_dhcp, \
 "boot image via network using DHCP/TFTP protocol", \
 "[loadAddress] [[hostIPaddr:]bootfilename]",\
 NULL}

dhcp 命令最终展开结果为:
cmd_tbl_t _u_boot_list_2_cmd_2_dhcp __aligned(4) \
 __attribute__((unused,section(.u_boot_list_2_cmd_2_dhcp))) \ 
 { "dhcp", 3, 1, do_dhcp, \
 "boot image via network using DHCP/TFTP protocol", \
 "[loadAddress] [[hostIPaddr:]bootfilename]",\
 NULL}

1 行定义了一个 cmd_tbl_t 类型的变量,变量名为 _u_boot_list_2_cmd_2_dhcp ,此变量 4字节对齐。
2 行,使用 __attribute__ 关 键 字 设 置 变 量 _u_boot_list_2_cmd_2_dhcp 存 储 .u_boot_list_2_cmd_2_dhcp 段中。第 2 行就是设置变量 _u_boot_list_2_cmd_2_dhcp 的存储位置。
u-boot.lds 链接脚本中有一个名为“ .u_boot_list ” 的段,所 .u_boot_list 开头的段都存放到 .u_boot.list 中。.u_boot.list 段如下:
. = ALIGN(4);
 . = .;
 . = ALIGN(4);
 .u_boot_list : {
  KEEP(*(SORT(.u_boot_list*)));
 }

3~6 行, cmd_tbl_t 是个结构体,因此第 3-6 行是初始化 cmd_tbl_t 这个结构体的各个成员变量。 cmd_tbl_t 结构体定义在文件 include/command.h 中,内容如下:
struct cmd_tbl_s {
	char		*name;		/* Command Name			*/
	int		maxargs;	/* maximum number of arguments	*/
	int		repeatable;	/* autorepeat allowed?		*/
					/* Implementation function	*/
	int		(*cmd)(struct cmd_tbl_s *, int, int, char * const []);
	char		*usage;		/* Usage message	(short)	*/
#ifdef	CONFIG_SYS_LONGHELP
	char		*help;		/* Help  message	(long)	*/
#endif
#ifdef CONFIG_AUTO_COMPLETE
	/* do auto completion on the arguments */
	int		(*complete)(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]);
#endif
};

综合上述,可以得出变量 _u_boot_list_2_cmd_2_dhcp 的各个成员的值如下所示:
_u_boot_list_2_cmd_2_dhcp.name = "dhcp"
_u_boot_list_2_cmd_2_dhcp.maxargs = 3
_u_boot_list_2_cmd_2_dhcp.repeatable = 1
_u_boot_list_2_cmd_2_dhcp.cmd = do_dhcp
_u_boot_list_2_cmd_2_dhcp.usage = "boot image via network using DHCP/TFTP protocol"
_u_boot_list_2_cmd_2_dhcp.help = "[loadAddress] [[hostIPaddr:]bootfilename]"
_u_boot_list_2_cmd_2_dhcp.complete = NULL

三.  总结

当我们在 uboot 的命令行中输入“ dhcp ”这个命令的时候,最终执行的是 do_dhcp 这个函数。
总结一下, uboot 中使用 U_BOOT_CMD 来定义一个命令,最终的目的就是为了定义一个
cmd_tbl_t 类型的变量,并初始化这个变量的各个成员。 uboot 中的每个命令都存储在 .u_boot_list
段中,每个命令都有一个名为 do_xxx(xxx 为具体的命令名 ) 的函数,这个 do_xxx 函数就是具体 的命令处理函数。

/* * (C) Copyright 2000 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * * SPDX-License-Identifier: GPL-2.0+ */ /* #define DEBUG */ #include <common.h> #include <autoboot.h> #include <cli.h> #include <version.h> #include <SsRecovery.h> #if defined(CONFIG_XZ) && defined(FACTORY_BOOT_COMPILE) #include <xz/xz.h> #include <xz/xz_config.h> #include <xz/xz_lzma2.h> #include <xz/xz_stream.h> #endif #ifdef FACTORY_BOOT_COMPILE #define BG_GREEN_FONT_WHITE "\033[42;37m" #define COLOR_NONE "\033[0m" #endif DECLARE_GLOBAL_DATA_PTR; /* * Board-specific Platform code can reimplement show_boot_progress () if needed */ __weak void show_boot_progress(int val) {} static void modem_init(void) { #ifdef CONFIG_MODEM_SUPPORT debug("DEBUG: main_loop: gd->do_mdm_init=%lu\n", gd->do_mdm_init); if (gd->do_mdm_init) { char *str = getenv("mdm_cmd"); setenv("preboot", str); /* set or delete definition */ mdm_init(); /* wait for modem connection */ } #endif /* CONFIG_MODEM_SUPPORT */ } 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 */ } #if defined(FACTORY_BOOT_COMPILE) && defined(FACTORY_BOOT_ONLY) /* 当只使用factory_boot时,要将main_loop()中uboot的引导功能编进factory_boot */ #undef FACTORY_BOOT_COMPILE #define U_BOOT_COMPILE #endif /* We come here after U-Boot is initialised and ready to process commands */ void main_loop(void) { #if defined(U_BOOT_COMPILE) || defined(DBG_U_BOOT_COMPILE) const char *s; #endif int ret = 0; 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 #if defined(CONFIG_TP_SERIAL_FORBIDDEN) && defined(U_BOOT_COMPILE) printf("SERIAL FORBIDDEN!\n"); u16 *p; p = 0x1f203d4c; *p = 0x3010; #endif modem_init(); #ifdef CONFIG_VERSION_VARIABLE setenv("ver", version_string); /* set version variable */ #endif /* CONFIG_VERSION_VARIABLE */ cli_init(); run_preboot_environment_command(); #ifdef CONFIG_AUTO_UPGRADE_SD run_command("sdupgrade", 0); #endif #ifdef CONFIG_SSTAR_AUTORUN_DSTAR run_command("sdstar", 0); #endif #if defined(CONFIG_UPDATE_TFTP) update_tftp(0UL); #endif /* CONFIG_UPDATE_TFTP */ #if defined(CONFIG_CMD_DFU) extern int update_dfu(void); update_dfu(); #endif /* CONFIG_CMD_DFU */ #ifdef CONFIG_MS_EMMC_RECOVERY RecoveryCheck(); #endif #if defined(U_BOOT_COMPILE) || defined(DBG_U_BOOT_COMPILE) s = bootdelay_process(); if (cli_process_fdt(&s)) cli_secure_boot_cmd(s); #ifdef CONFIG_TP_TAPO_SPMINIOS ret = validateFirmwareWithRecover(); if (!ret) { setenv("bootargs", CONFIG_SP_BOOTARGS); } else { setenv("bootargs", CONFIG_SPMINIOS_BOOTARGS); } #endif #ifdef CONFIG_TAPO_NAND_UPGRADE ret = validateFirmwareWithUpgrade(); #endif autoboot_command(s); #endif #ifdef FACTORY_BOOT_COMPILE bootdelay_process(); if (disable_boot()) { printf(BG_GREEN_FONT_WHITE"abort, init eth"COLOR_NONE"...\n"); run_command_list("estart", -1, 0); goto loop; } #ifdef CONFIG_TAPO_NAND_UPGRADE ret = processUboot(); #endif { printf("Firmware check pass!\n"); /* CONFIG_LOADADDR is 0x21000000, the addr for load normal boot image if ISP_IN_FLASH, normal boot image is stored in 0x40000 of flash; or it is stored in 0x30000 of flash. */ //ssc377, not use CONFIG_MS_SPINAND #ifdef CONFIG_MS_NAND_ONEBIN #ifdef BOOTLOADER_OFFSET char cmd_buf[257] = {0}; snprintf(cmd_buf, 256, "nand read.e 0x21000000 0x%0x 0x40000", BOOTLOADER_OFFSET); run_command_list(cmd_buf, -1, 0); #else run_command_list("nand read.e 0x21000000 0x600000 0x40000", -1, 0); #endif #else #ifdef ISP_IN_FLASH printf("IMG_RADIO_LEN defined, RADIO_LEN = 0x%0x\n", RADIO_LEN); char cmd_buf[128] = {0}; snprintf(cmd_buf, 512, "sf probe 0;sf read 0x21000000 0x%0x 0x%0x", BOOTLOADER_OFFSET, BOOTLOADER_LEN); run_command_list(cmd_buf, -1, 0); #else run_command_list("sf probe 0;sf read 0x21000000 0x30000 0x10000", -1, 0); #endif #endif struct xz_buf b; struct xz_dec *s; xz_crc32_init(); /* * Support up to 64 MiB dictionary. The actually needed memory * is allocated once the headers have been parsed. */ s = xz_dec_init(XZ_SINGLE, 16 * 1024); if (s == NULL) { printf("xz_dec_init ERROR!!\n"); } b.in = (unsigned char *)(CONFIG_LOADADDR + NORMAL_BOOT_IMAGE_OFFSET); // ignore 40 bytes image header b.in_pos = 0; b.in_size = NORMAL_BOOT_IMAGE_SIZE; b.out = (unsigned char *)(NORMAL_BOOT_LOADADDR); b.out_pos = 0; b.out_size = NORMAL_BOOT_MAX_SIZE; printf("XZ params: in_addr 0x%x, in_size 0x%x, out_addr 0x%x, out_size 0x%x\n", (unsigned int)b.in, (unsigned int)b.in_size, (unsigned int)b.out, (unsigned int)b.out_size); ret = xz_dec_run(s, &b); printf("XZ: uncompress ret %d, size = %d\n", ret, b.out_size); xz_dec_end(s); /*?a??ì?×aubootuboot???ˉòì3£?êìa£??óé?cache2ù×÷£??′ê1cache?′?aò2??ó°?ì*/ flush_dcache_all(); invalidate_icache_all(); /* NORMAL_BOOT_LOADADDR is 0x220A0000, the addr for running normal boot*/ char run_buf[128] = {0}; snprintf(run_buf, 128, "go 0x%0x", (unsigned char *)NORMAL_BOOT_LOADADDR); run_command(run_buf, 0); } loop: #endif cli_loop(); }
最新发布
10-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值