U-Boot源码之命令

U-Boot支持很多命令,通过命令我们可以实现很多操作。这里介绍U-Boot命令的实现。

一、U-Boot命令的操作

1.进入命令

       U-Boot 自带命令行接口,在 U-Boot 启动期间,按任意键(如空格键)可进入 U-Boot Shell 命令行,在Shell 界面可输入 U-Boot 支持的命令,使用 U-Boot 提供的各种功能:

U-Boot 2009.08-dirty ( 5 月  18 2015 - 10:22:36)
Freescale i.MX28 family
CPU:    454 MHz
BUS:    151 MHz
EMI:    205 MHz
GPMI:    24 MHz
DRAM:   128 MB
NAND:   proton id:   c2 f1 80 1d c2 f1

.......

MX28 U-Boot > 

2.查看命令列表

在 MX28 U-Boot >提示符下,输入?或者 help 可以查看 U-Boot 所支持的全部命令以及介绍。一个具体的 U-Boot 支持的命令的多寡,由编译时候的配置决定。事实上,由于硬件
差异,一个 U-Boot 不可能同时使用 U-Boot 所支持的全部命令。各命令的功能如下:

MX28 U-Boot > ?
?          - alias for 'help'
base       - print or set address offset
bdinfo     - print Board Info structure
bootm      - boot application image from memory
bootp      - boot image via network using BOOTP/TFTP protocol
chpart     - change active partition
cmp        - memory compare
cp         - memory copy
crc32      - checksum calculation
dhcp       - boot image via network using DHCP/TFTP protocol
echo       - echo args to console
go         - start application at address 'addr'
help       - print command description/usage
i2c        - I2C sub-system
loadb      - load binary file over serial line (kermit mode)
loads      - load S-Record file over serial line
loady      - load binary file over serial line (ymodem mode)
loop       - infinite loop on address range
md         - memory display
mm       - memory modify (auto-incrementing address)
mtdparts  - define flash/nand partitions
mtest      - simple RAM read/write test
mw       - memory write (fill)
nand       - NAND sub-system
nboot      - boot from NAND device
nm         - memory modify (constant address)
ping       - send ICMP ECHO_REQUEST to network host
printenv  - print environment variables 

rarpboot  - boot image via network using RARP/TFTP protocol
reset      - Perform RESET of the CPU
run        - run commands in an environment variable
saveenv    - save environment variables to persistent storage
saves      - save S-Record file over serial line
setenv     - set environment variables
tftpboot  - boot image via network using TFTP protocol
ubi        - ubi commands
ubifsload  - load file from an UBIFS filesystem
ubifsls    - list files in a directory
ubifsmount  - mount UBIFS volume
version    - print monitor version
       输入“?  命令”或者“help  命令”能够获取相应命令的使用方法。

3.命令子类

U-Boot 命令中,有一些命令归类在某一个子类中,如 I2C、NAND FLASH、UBI 等,直接输入命令类名称,将会显示该类下面的子命令及使用说明,这类命令的使用方法是“类
+子命令”。以 ubi 子系统为例,输入 ubi 将会得到:

MX28 U-Boot > ubi
ubi - ubi commands
Usage:
ubi part [part] [offset]
- Show or set current partition (with optional VID header offset)
ubi info [l[ayout]] - Display volume and ubi layout information
ubi create[vol] volume [size] [type] - create volume name with size
ubi write[vol] address volume size - Write volume from address with size
ubi read[vol] address volume [size] - Read volume to address with size
ubi remove[vol] volume - Remove volume
[Legends]
volume: character name
size: specified in bytes
type: s[tatic] or d[ynamic] (default=dynamic)

二、U-Boot命令的实现

下面以u-boot-2009.08版本介绍U-Boot命令的实现。

1、相关文件

(1)include/command.h

       定义了表示命令的数据结构struct cmd_tbl_s等。

(2)common/main.c

       函数run_command。

(3)common/cmd.xxx.c

       各个命令的实现。

2、U-Boot命令的配置

(1)CONFIG_SYS_MAXARGS

       定义命令参数的最大数量。

(2)CONFIG_SYS_LONGHELP

       如果定义了宏CONFIG_SYS_LONGHELP,则表示命令支持帮助说明。

(3)CONFIG_AUTO_COMPLETE

       如果定义了宏CONFIG_AUTO_COMPLETE,则表示命令支持自动补全功能。

(4)CONFIG_CMDLINE_EDITING

       如果定义了宏CONFIG_CMDLINE_EDITING,则表示命令支持命令历史功能。

3、数据结构

在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 *[]);
	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 *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};

       name是指向命令名字的的指针。

       maxargs是命令支持的命令参数的最大个数。

       repeatable指示命令是否允许自动重复执行。

       cmd是指向命令具体实现的函数指针。命令实现函数有4个参数:cmdtp - 指向cmd_tbl_s结构体的指针,flag - 标志(很多命令函数可能用不上),argc - 命令参数个数,argv - 命令参数值(注意,argv[0]是命令名字,argv[1]等是命令参数。argc实际是命令参数加1)。命令实现函数定义在common目录下的cmd_xxx文件的do_xxx函数中,比如:common/cmd_eeprom.c的do_eeprom函数。

       usage是指向命令的短帮助信息,即对命令的简单描述的指针。

       help是指向命令的长帮助信息,即细节的帮助信息的指针。

       complete是指向自动补全参数函数的指针。

4、命令的执行过程

       U-Boot的运行过程是上电执行start.s后跳转至函数start_armboot,函数start_armboot完成一些初始化工作后在一个死循环main_loop中不断从控制台接收命令并进行解释执行。      

void start_armboot (void)
{
    ......

    /* main_loop() can return to retry autoboot, if so just run it again. */
	for (;;) {
		main_loop ();
	}
    
}

       main_loop函数中会先执行getenv(“bootcmd”),如果bootcmd环境变量设置的是启动kenel的命令,则在自动倒计时结束后如果没有字符输入,则uboot会自动执行bootcmd的命令,默认即执行启动kernel。如果自动倒计时结束前有字符输入,则进入命令行提示符状态阻塞等待用户输入命令。

		s = getenv ("bootcmd");

	debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");

	if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
# ifdef CONFIG_AUTOBOOT_KEYED
		int prev = disable_ctrlc(1);	/* disable Control C checking */
# endif

# ifndef CONFIG_SYS_HUSH_PARSER
		run_command (s, 0);
# else
		parse_string_outer(s, FLAG_PARSE_SEMICOLON |
				    FLAG_EXIT_FROM_LOOP);
# endif
 

       然后,函数main_loop不断从控制台接收命令并进行解释执行。函数main_loop有两种方式去接收命令并进行解释执行。

	/*
	 * Main Loop for Monitor Command Processing
	 */
#ifdef CONFIG_SYS_HUSH_PARSER
	parse_file_outer();
	/* This point is never reached */
	for (;;);
#else
	for (;;) {
#ifdef CONFIG_BOOT_RETRY_TIME
		if (rc >= 0) {
			/* Saw enough of a valid command to
			 * restart the timeout.
			 */
			reset_cmd_timeout();
		}
#endif
		len = readline (CONFIG_SYS_PROMPT);

		flag = 0;	/* assume no special flags for now */
		if (len > 0)
			strcpy (lastcommand, console_buffer);
		else if (len == 0)
			flag |= CMD_FLAG_REPEAT;
#ifdef CONFIG_BOOT_RETRY_TIME
		else if (len == -2) {
			/* -2 means timed out, retry autoboot
			 */
			puts ("\nTimed out waiting for command\n");
# ifdef CONFIG_RESET_TO_RETRY
			/* Reinit board to run initialization code again */
			do_reset (NULL, 0, 0, NULL);
# else
			return;		/* retry autoboot */
# endif
		}
#endif

		if (len == -1)
			puts ("<INTERRUPT>\n");
		else
			rc = run_command (lastcommand, flag);

		if (rc <= 0) {
			/* invalid command or not repeatable, forget it */
			lastcommand[0] = 0;
		}
	}

(1)一般循环方式(未定义宏CONFIG_SYS_HUSH_PARSER)

       run_command函数负责命令的解析和执行。run_command函数位于common/main.c。

       run_command函数的执行过程是,分离多个命令 ----> 扩展宏定义 ----> 分析命令及其参数 ----> 查找命令 ----> 执行命令。

       以组合命令upkernel为例,upkernel命令能够完成从tftp服务器加载uImage内核文件,并完成相应NAND Flash擦除、烧写内核以及设置内核参数的工作,upkernel命令相当于以下命令(tftp命令、nand erase命令、nand write命令)的组合:

  tftp $(loadaddr) $(serverip):$(kernel);nand erase clean $(kerneladdr) $(kernelsize);nand write.jffs2 $(loadaddr) $(kerneladdr) $(kernelsize);

       其中各个命令是以;分割开的。

       run_command函数首先要做的是把各个命令分离开来。

		/*
		 * Find separator, or string end
		 * Allow simple escape of ';' by writing "\;"
		 */
		for (inquotes = 0, sep = str; *sep; sep++) {
			if ((*sep=='\'') &&
			    (*(sep-1) != '\\'))
				inquotes=!inquotes;

			if (!inquotes &&
			    (*sep == ';') &&	/* separator		*/
			    ( sep != str) &&	/* past string start	*/
			    (*(sep-1) != '\\'))	/* and NOT escaped	*/
				break;
		}

		/*
		 * Limit the token to data between separators
		 */
		token = str;
		if (*sep) {
			str = sep + 1;	/* start of command for next pass */
			*sep = '\0';
		}
		else
			str = sep;	/* no more commands for next pass */

然后函数process_macros对$(xxx)进行扩展,即把$(xxx)替换为实际的值。

		/* find macros in this token and replace them */
		process_macros (token, finaltoken);

然后函数parse_line提取命令的名字和参数。

		/* Extract arguments */
		if ((argc = parse_line (finaltoken, argv)) == 0) {
			rc = -1;	/* no command at all */
			continue;
		}

然后通过函数find_cmd在命令表中查找命令结构体cmd_tbl_s。

		/* Look up command in command table */
		if ((cmdtp = find_cmd(argv[0])) == NULL) {
			printf ("Unknown command '%s' - try 'help'\n", argv[0]);
			rc = -1;	/* give up after bad command */
			continue;
		}

函数find_cmd通过命令名字,从存放命令结构体cmd_tbl_s的内存块中查找出相应的命令结构体cmd_tbl_s的地址。函数find_cmd的代码如下所示:

cmd_tbl_t *find_cmd (const char *cmd)
{
	int len = &__u_boot_cmd_end - &__u_boot_cmd_start;
	return find_cmd_tbl(cmd, &__u_boot_cmd_start, len);
}

其中__u_boot_cmd_start和__u_boot_cmd_end定义在u-boot.lds中,__u_boot_cmd_start表示存放命令结构体cmd_tbl_s的内存块的首地址,__u_boot_cmd_end表示存放命令结构体cmd_tbl_s的内存块的结束地址。

函数find_cmd实现的关键在于如何把各命令结构体cmd_tbl_s统一连续地放在一个内存块中。各命令结构体cmd_tbl_s统一连续地放在一个内存块中是在u-boot.lds文件中实现的。

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
     ...... 

	 __u_boot_cmd_start = .;
	 .u_boot_cmd : { *(.u_boot_cmd) } 
	 __u_boot_cmd_end = .;
 
     
     ......
 }

上面的语句.u_boot_cmd : { *(.u_boot_cmd) } 表示.u_boot_cmd段都放在.u_boot_cmd段(内存块)中。.u_boot_cmd段定义在include/command.h中。

#define Struct_Section  __attribute__ ((unused,section (".u_boot_cmd")))

凡是带有__attribute__ ((unused,section (".u_boot_cmd")))属性声明的变量都存放在.u_boot_cmd段中,并且即使该变量没有在代码中显示使用编译器也不会产生警告。

       这样只要将u-boot所有命令对应的cmd_tbl_s变量加上“.u_boot_cmd”声明,编译器就会自动将其放在“u_boot_cmd”段,查找cmd_tbl_s变量时只要在 __u_boot_cmd_start 与 __u_boot_cmd_end 之间查找就可以了。

       u-boot命令对应的cmd_tbl_s变量是通过宏U_BOOT_CMD实现“.u_boot_cmd”声明的。

#ifdef  CONFIG_SYS_LONGHELP

#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}

#define U_BOOT_CMD_MKENT(name,maxargs,rep,cmd,usage,help) \
{#name, maxargs, rep, cmd, usage, help}

#else	/* no long help info */

#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage}

#define U_BOOT_CMD_MKENT(name,maxargs,rep,cmd,usage,help) \
{#name, maxargs, rep, cmd, usage}

#endif	/* CONFIG_SYS_LONGHELP */

每个命令对应的common/cmd_xxx.c文件中都用U_BOOT_CMD定义了一个与之相对应的cmd_tbl_s变量。 这里以reset命令为例。

U_BOOT_CMD(
	reset, 1, 0,	do_reset,
	"Perform RESET of the CPU",
	""
);

最后调用命令结构体cmd_tbl_s的cmd函数执行命令。

		/* OK - call function to do the command */
		if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
			rc = -1;
		}

(2)“hush”方式(定义宏CONFIG_SYS_HUSH_PARSER)

       如果定义了CONFIG_SYS_HUSH_PARSER,命令接收和解析讲采用busybox中的hush(对应common/hush.c)工具来实现,与uboot原始的命令解析方法相比,该工具更加智能。具体分析可以参看博文https://blog.csdn.net/andy_wsj/article/details/8614905。       

三、U-Boot自定义命令的添加

       要在U-Boot中添加自定义命令,可以参考博文:

https://blog.csdn.net/czg13548930186/article/details/76356019?utm_source=blogxgwz0

https://blog.csdn.net/HowieXue/article/details/79836382

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 编译mt7621配置的U-Boot需要进行以下步骤: 首先,确认已经准备好编译环境,包括安装好交叉编译工具链和相关依赖库。 然后,从U-Boot官方仓库中获取最新的源代码。可以使用git命令进行克隆,例如: git clone https://github.com/u-boot/u-boot.git 接下来,切换到代码目录并进入配置界面,执行以下命令: cd u-boot make menuconfig 在配置界面中,选择"MTK",然后选择"MTK_MT7621"作为目标平台。根据需要,可以进行其他选项的配置,如串口、网络等。 配置完成后,保存退出配置界面。然后,执行以下命令进行编译: make CROSS_COMPILE=<交叉编译工具链前缀> 编译过程可能需要一段时间,取决于计算机性能和代码规模。 编译完成后,可以得到u-boot.bin文件作为编译结果。该文件可以烧录到MT7621芯片的Flash中,用于启动设备。 以上是编译mt7621配置的U-Boot的大致步骤。根据具体需求,可能还需要进行其他配置和调整。在进行编译前,建议查阅U-Boot的官方文档和相关资料,以便更详细地了解配置和编译过程。 ### 回答2: 编译 U-Boot for MT7621 配置需要先准备编译环境和相关工具链。以下是编译 MT7621 配置的步骤: 1. 首先,确保你的系统上已经安装了交叉编译工具链。可以通过执行以下命令来检查是否已安装交叉编译工具链: ``` arm-openwrt-linux-gcc -v ``` 如果显示出工具链的版本信息,则表示已经安装成功。 2. 下载 U-Boot源码。你可以通过 Git 或者下载压缩包的方式获取源码。例如,使用 Git 命令来克隆 U-Boot 的仓库: ``` git clone https://github.com/u-boot/u-boot.git ``` 3. 进入源码目录,并创建编译配置文件。执行以下命令: ``` cd u-boot make mt7621_xxx_config ``` 其中,`mt7621_xxx_config` 是针对你的具体设备的配置文件,例如 `mt7621_rfb_config` 或 `mt7621_xxx_config` 等。你可以查阅相关文档来确定正确的配置文件。 4. 根据配置文件生成编译规则。执行以下命令: ``` make oldconfig ``` 根据你的需求来回答一些提示问题。默认选项通常是可以满足大多数需求的。 5. 开始编译。执行以下命令: ``` make ``` 编译时间会根据你的机器性能和代码大小而不同。最终编译产生的 U-Boot 可执行文件将位于生成文件夹中。 完成上述步骤后,你将获得一个适用于 MT7621 的 U-Boot 可执行文件。你可以将它用于你的项目中,并在启动过程中加载它。当然,在编译之前,你可能需要修改一些配置文件,以确保 U-Boot 适配你的具体硬件环境。 ### 回答3: 要编译 mt7621 的 u-boot 需要进行一些配置。 首先,你需要下载 u-boot 的源代码。可以从官方网站或者 GitHub 上找到 mt7621 相关的 u-boot 仓库,并下载代码至你的开发环境中。 接下来,你需要进入 u-boot 的源代码目录。使用终端命令行工具进入该目录。 在源代码目录中,你可以找到一个名为 `configs` 的文件夹,其中包含了不同平台的配置文件。你需要找到一个名为 `mt7621_bpir2_defconfig` 的文件,这是针对 mt7621 平台的默认配置文件。 如果你想进行自定义配置,可复制该文件,并在复制的文件上进行修改。你可以使用一个文本编辑器打开配置文件,找到需要修改的选项,并进行相应的修改。例如,你可以修改串口配置、内存配置、网络协议等选项,以适配你的具体需求。 完成修改后,你可以保存文件,并返回到源代码的根目录。 最后,你需要运行编译命令来生成可执行的 u-boot 映像文件。通常情况下,你可以通过运行 `make` 命令来进行编译。编译过程可能会耗费一些时间,请耐心等待。 当编译完成后,你可以在源代码目录下找到生成的 u-boot 映像文件。该文件一般以 `u-boot.bin` 或者 `u-boot.img` 的形式存在。 编译完成后,你可以将生成的 u-boot 映像文件烧录到 mt7621 平台的 Flash 存储器中,以替换原有的 u-boot 程序。 总而言之,编译 mt7621 的 u-boot 需要下载源代码、进行配置修改,并通过编译命令生成 u-boot 映像文件。最后,将生成的映像文件烧录到目标平台中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值