嵌入式linux开发uboot移植(五)——uboot命令体系

嵌入式linux开发uboot移植(五)——uboot命令体系

本文将根据SMDKV210开发板的三星官方uboot源码分析uboot的命令体系。内容 包括uboot的命令体系的实现机制,uboot命令是如何执行的,以及如何在uboot中添加一个自定义的命令。

一、uboot命令体系简介

uboot命令体系代码放在uboot/common中,包括cmd_xxx.c、command.c 、main.c源码文件。uboot实现命令体系的方法是每一个uboot命令对应一个函数,与shell的实现是一致的。

uboot命令体系没有采用数组、链表来实现,而是每一个命令对应一个cmd_tbl_t命令类型结构体,通过对cmd_tbl_t命令类型结构体的段属性设置,将命令集存储在了程序中的自定义段.u_boot_cmd中,程序在链接阶段会将命令集分配在程序中的自定义段。链接脚本命令集自定义段如下:

__u_boot_cmd_start = .;//命令集段起始地址

.u_boot_cmd : { *(.u_boot_cmd) }//命令集中的命令

__u_boot_cmd_end = .;//命令集段的结束地址

cmd_tbl_t命令类型结构体的段属性设置如下:

#define Struct_Section  __attribute__ ((unused,section (".u_boot_cmd")))
#ifdef  CFG_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}
#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}
#endif/* CFG_LONGHELP */

U_BOOT_CMD宏实际上定义了一个cmd_tbl_t类型命令结构体,U_BOOT_CMD宏的六个参数就是cmd_tbl_t类型命令结构体对应的六个成员。

使用实例如下:

U_BOOT_CMD(hello, 0, 0, do_hello, "hello world help info");

宏展开后为:

cmd_tbl_t __u_boot_cmd_hello __attribute___((unused, section(".u_boot_cmd"))) = {"hello", 0, 0, do_hello, "hello world help info"}

通过将每个命令的cmd_tbl_t命令类型结构体的段属性的设置为.u_boot_cmd,可以确保uboot命令集中的所有命令在链接阶段都会链接分配到.u_boot_cmd自定义段,当然命令在.u_boot_cmd自定义段内是随机排序的。

uboot命令集中的每个命令对应一个cmd_tbl_t类型变量,用户输入一个命令时,uboot命令体系会到命令集中查找输入的命令,如果找到就执行,没有找到就提示命令没有找到信息。

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 CFG_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
};
typedef struct cmd_tbl_scmd_tbl_t;

二、uboot命令的解析

uboot在启动进入BL2阶段后最终执行在main_loop函数,如果在自动倒计时时没有按下字符键,uboot将自动启动kernel;如果按下了字符键,uboot将进入人机交互命令行的主循环,执行读取、解析、执行命令。

main_loop函数中会先执行getenv ("bootcmd"),如果bootcmd环境变量设置的是启动kenel的命令,则在自动倒计时结束后如果没有字符输入,则uboot会自动执行bootcmd的命令,默认即执行启动kernel。如果自动倒计时结束前有字符输入,则进入命令行提示符状态阻塞等待用户输入命令。readline函数读取用户输入命令,进而通过run_command函数解析、运行命令。run_command函数会将接收的命令用parse_line函数解析,主要是将接收的命令字符串根据空格、分号分割成几部分,利用find_cmd函数遍历查找命令集,看uboot中是否有输入的命令,如果没有输入的命令,打印提示符。如果有当前输入的命令,调用当前输入命令的命令结构体的函数指针成员cmd执行命令对应的函数。

run_command函数源码如下:

int run_command (const char *cmd, int flag)
{
.......................
while (*str) {
//对命令字符串进行简单分割
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 */
#ifdef DEBUG_PARSER
printf ("token: \"%s\"\n", token);
#endif
/* find macros in this token and replace them */
process_macros (token, finaltoken);
//解析命令字符串
if ((argc = parse_line (finaltoken, argv)) == 0) {
rc = -1;/* no command at all */
continue;
}
//遍历查找命令集中是否有当前输入命令
if ((cmdtp = find_cmd(argv[0])) == NULL) {
printf ("Unknown command ‘%s‘ - try ‘help‘\n", argv[0]);
rc = -1;/* give up after bad command */
continue;
}
 
/* found - check max args */
if (argc > cmdtp->maxargs) {
printf ("Usage:\n%s\n", cmdtp->usage);
rc = -1;
continue;
}
#if defined(CONFIG_CMD_BOOTD)
/* avoid "bootd" recursion */
if (cmdtp->cmd == do_bootd) {
#ifdef DEBUG_PARSER
printf ("[%s]\n", finaltoken);
#endif
if (flag & CMD_FLAG_BOOTD) {
puts ("‘bootd‘ recursion detected\n");
rc = -1;
continue;
} else {
flag |= CMD_FLAG_BOOTD;
}
}
#endif
 
//调用命令结构体的成员函数指针cmd对应的命令函数
if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
rc = -1;
}
repeatable &= cmdtp->repeatable;
/* Did the user stop this? */
if (had_ctrlc ())
return -1;/* if stopped then not repeatable */
}
return rc ? rc : repeatable;
}

命令的遍历查找:

uboot命令集实际是分配在自定义段.u_boot_cmd中的,通过在uboot程序中声明引用自定义段.u_boot_cmd的开始地址__u_boot_cmd_start和结束地址__u_boot_cmd_end,find_cmd函数就可以通过指针访问命令集中的命令。

cmd_tbl_t *find_cmd (const char *cmd)
{
cmd_tbl_t *cmdtp;
cmd_tbl_t *cmdtp_temp = &__u_boot_cmd_start;//命令集的首地址
const char *p;
int len;
int n_found = 0;
len = ((p = strchr(cmd, ‘.‘)) == NULL) ? strlen (cmd) : (p - cmd);//计算主命令的长度
for (cmdtp = &__u_boot_cmd_start;//命令集的起始地址
     cmdtp != &__u_boot_cmd_end;//命令集的结束地址
     cmdtp++) {
if (strncmp (cmd, cmdtp->name, len) == 0) {//将当前命令在命令集中遍历查找
if (len == strlen (cmdtp->name))//如果当前命令长度与查找的命令长度相同,说明命令相同
return cmdtp;/* full match */
//如果当前命令长度与查找到的命令的长度不相同,则主命令相同,子命令继续查找
cmdtp_temp = cmdtp;/* abbreviated command ? */
n_found++;
}
}
if (n_found == 1) {/* exactly one match */
return cmdtp_temp;
}
return NULL;/* not found or ambiguous command */
}

三、uboot命令的执行

run_command函数中解析、遍历查找命令后,如果找到会通过调用命令结构体的成员cmd函数指针调用当前命令对应的命令函数do_xxxx。uboot命令的定义模板示例如下:

#if defined(CONFIG_CMD_ECHO)
int do_echo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
int i, putnl = 1;
for (i = 1; i < argc; i++) 
{
    char *p = argv[i], c;
    if (i > 1)
    putc(‘ ‘);
    while ((c = *p++) != ‘\0‘)
     {
        if (c == ‘\\‘ && *p == ‘c‘) 
        {
        putnl = 0;
        p++;
        } 
        else 
        {
        putc(c);
         }
           }
}
if (putnl)
putc(‘\n‘);
return 0;
}
U_BOOT_CMD(
echo,CFG_MAXARGS,1,do_echo,
"echo    - echo args to console\n",
"[args..]\n"
"    - echo args to console; \\c suppresses newline\n"
);
#endif

CONFIG_CMD_ECHO宏可以决定定义的命令是否编译进当前uboot中,一般需要在开发板头文件中定义。命令定义必须包括命令结构体的定义和命令函数的定义。U_BOOT_CMD宏定义了命令结构体,do_echo函数则是命令的具体执行函数。

四、uboot命令添加编程实践

uboot中添加命令可以在common/commmand.c中添加,也可以重新添加一个cmd_xxxx.c文件添加。

1、command.c文件中添加命令

int do_hello (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
printf ("\n%s\n", "hello world");
return 0;
}
U_BOOT_CMD(
hello,1,1,do_hello,
"hello - print hello world help info\n",
NULL
);

在command.c文件中添加命令会导致命令集混乱,因此不推荐。

2、添加cmd_xxxx.c文件添加命令

A、创建cmd_xxxx.c文件

B、添加头文件

#include <common.h>

#include <command.h>

C、添加do_xxx()函数和U_BOOT_CMD宏

int do_hello (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
printf ("\n%s\n", "hello world");
return 0;
}
U_BOOT_CMD(
hello,1,1,do_hello,
"hello - print hello world help info\n",
NULL
);

D、在common/Makefile中添加cmd_xxxx.o目标文件

COBJS-y+=cmd_xxxx.o

E、编译工程,测试hello命令

在common目录下添加cmd_xxx.c命令文件是比较规范的操作,便于uboot命令集的规范化,推荐方式。

uboot的命令体系本身较为复杂,但开发者在uboot中添加命令是很简单的,只需要添加cmd_xxx.c文件,修改相应Makefile文件就行。

时间: 2024-10-13 00:53:16

嵌入式linux开发uboot移植(五)——uboot命令体系的相关文章

嵌入式 Linux开发Kernel移植(三)——Kernel工程Makefile分析

嵌入式 Linux开发Kernel移植(三)--Kernel工程Makefile分析 本文选择三星发布的基于SMDKV210开发板的linux 2.6.35.7版本kernel. 一.Kernel Makefle体系简介 1.Kernel Makefile体系组成 Kernel Makefile体系包含Kconfig和Kbuild两个系统. Kconfig系统 Kconfig 对应的是内核配置阶段,make xxconfig就是在使用Kconfig系统.Kconfig由三部分组成: script

向嵌入式linux开发板移植openSSH

向嵌入式linux开发板移植openSSH 晚上没事干,想着能不能把高大上的openSSH移植到我现在开发的嵌入式linux开发板上,前不久刚弄出来dropbear,但遇到一些小问题,一会说,所以把openSSH搞上去还是可以的.开发板端为服务器端,由宿主机客户端访问. 2016-8-16 背景 开发板:freescale i.mx6q 宿主机操作系统:ubuntu 14.04 开发板操作系统:linux 3.0.35 基本情况 使用dropbear后,ssh算是在板子上成功跑起来了,参见我另一

嵌入式 Linux开发Kernel移植(二)——kernel内核配置和编译

嵌入式 Linux开发Kernel移植(二)--kernel内核配置和编译 本文选择linux 2.6.35.7版本kernel进行实践. 一.linux kernel源码目录分析 Kbuild,Kernel Build,管理内核编译的文件 Makefile,kernel工程的Makefile. arch,体系架构,arch目录下的子目录存放的是不同种类的架构 block,块设备,一般是存储设备,存放的块设备管理的相关代码 crypto,加密相关,存放加密算法实现代码 Documentation

嵌入式 Linux开发Kernel移植(一)——kernel内核简介

嵌入式 Linux开发Kernel移植(一)--kernel内核简介 一.Linux Kernel 1.linux kernel简介 Linux kernel是芬兰黑客 Linus Torvalds 1991年在英特尔x86架构上用C语言开发的自由免费的符合POSIX标准的类Unix操作系统. Linux kernel是一个一体化内核(monolithic kernel)系统,提供硬件抽象层.磁盘及文件系统控制.多任务等功能的系统软件,不是一套完整的操作系统. 内核空间与用户空间是程序执行的两种

嵌入式linux开发uboot移植(一)——uboot项目简介

嵌入式linux开发uboot移植(一)--uboot项目简介 一.uboot简介 U-Boot,全称 Universal Boot Loader,是遵循GPL条款的从FADSROM.8xxROM.PPCBOOT逐步发展演化而来的开放源码项目. 在操作系统方面,U-Boot不仅支持嵌入式Linux系统的引导,它还支持NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS, android嵌入式操作系统.目前支持的目标操作系统是OpenBSD, NetBSD, Fre

嵌入式linux开发uboot移植(七)——三星官方uboot的移植

嵌入式linux开发uboot移植(七)--三星官方uboot的移植 友善之臂Smart210开发板是基于三星SMDKV210评估板裁剪.调整而来的.因此三星官方发布的基于SMDKV210评估板的uboot是移植uboot到Smart210开发板的最合适uboot版本.本文将SMDKV210的uboot移植到Smart210开发板.Smart210开发板的配置如下: SoC:Samsung S5PV210 SDRAM:512MB DDR2 RAM FLASH存储:2G MLC NAND Flas

嵌入式linux开发uboot移植(三)——uboot启动过程源码分析

嵌入式linux开发uboot移植(三)--uboot启动过程源码分析 一.uboot启动流程简介 与大多数BootLoader一样,uboot的启动过程分为BL1和BL2两个阶段.BL1阶段通常是开发板的配置等设备初始化代码,需要依赖依赖于SoC体系结构,通常用汇编语言来实现:BL2阶段主要是对外部设备如网卡.Flash等的初始化以及uboot命令集等的自身实现,通常用C语言来实现. 1.BL1阶段 uboot的BL1阶段代码通常放在start.s文件中,用汇编语言实现,其主要代码功能如下:

嵌入式linux开发uboot移植(二)——uboot工程源码目录分析

嵌入式linux开发uboot移植(二)--uboot工程源码目录分析 本文分析的uboot为uboot_smdkv210,是三星官方发布的基于S5PV210评估开发板对应的uboot. 一.uboot源码目录结构解析 1.cpu 本文件夹下的子文件与处理器相关,每个文件夹代表一种CPU系列.每个子目录中都包括cpu.c.interrupts.c.start.S文件. cpu.c主要用于初始化CPU.设置指令Cache和数据Cache等 interrupt.c主要用于设置系统的各种中断和异常 s

嵌入式linux开发uboot移植(四)——uboot启动内核的机制

嵌入式linux开发uboot移植(四)--uboot启动内核的机制 一.嵌入式系统的分区 嵌入式系统部署在Flash设备上时,对于不同SoC和Flash设备,bootloader.kernel.rootfs的分区是不同的.三星S5PV210规定启动设备的分区方案如下: SD/MMC设备的分区方案: NandFlash设备的分区方案: 嵌入式系统在启动时,uboot.kernel.rootfs不能随意存放,必须存放在规划好的相应分区,在启动过程中uboot.kernel会到相应分区加载相应内容,