u-boot学习笔记(四)

发布于:2025-05-10 ⋅ 阅读:(17) ⋅ 点赞:(0)

cmd/

sub_cmd/

exit.c

do_exit()
static int do_exit(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
@cmdtp:命令的命令表项指针
@flag:标志
@argc:命令行参数个数
@argv:命令行参数
该函数用于退出shell,一般的命令函数执行失败,会返回-11,执行成功返回0。而do_exit在没有传入参数时,会返回-2,传入参数时返回-argv[1]-2,以此来告诉shell要退出。
exit.c可提供的命令及使用方式:
  • exit命令用于退出shell,可重复调用,该命令对应的函数为do_exit,支持argc为1-2个参数,使用方法为:exit 退出码,例如:exit 1。

ext2.c

do_ext2ls()
static int do_ext2ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
@cmdtp:命令的命令表项指针
@flag:标志
@argc:命令行参数个数
@argv:命令行参数
该函数用于列出ext2文件系统上的指定目录包含的内容,相当于linux中的ls,传入文件系统类型为FS_TYPE_EXT(即ext2/3/4文件系统)调用通用函数do_ls。
do_ext2load()
int do_ext2load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
@cmdtp:命令的命令表项指针
@flag:标志
@argc:命令行参数个数
@argv:命令行参数
该函数用于读取ext2文件系统上的指定文件并将其加载到内存中,传入文件系统类型为FS_TYPE_EXT(即ext2/3/4文件系统)调用通用函数do_load,会将环境变量"fileaddr"设置为目标内存地址,将环境变量"filesize"设置为读取的总字节数。
ext2.c可提供的命令及使用方式:
  • ext2ls命令用于列出ext2文件系统上的指定目录包含的内容,可重复调用,该命令对应的函数为do_ext2_ls,支持argc为2-4个参数,使用方法为:ext2ls 块设备类型 设备号和分区 目标目录,例如:ext2ls mmc 0:1 /boot,如果没有指定目标目录则会列出根目录/下的内容。
  • ext2load命令用于从存储设备(如MMC、USB、网络等)的文件系统中加载文件到内存,不可重复调用,该命令对应的函数为do_ext2_load,支持argc为2-6个参数,使用方法为:ext2load 块设备类型 设备号和分区 目标内存地址 源文件 读取长度,例如:ext2load mmc 0:1 0x82000000 /boot/zImage。

ext4.c

do_ext4_size()
int do_ext4_size(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
@cmdtp:命令的命令表项指针
@flag:标志
@argc:命令行参数个数
@argv:命令行参数
该函数用于查询ext4文件系统上指定文件的大小,传入文件系统类型为FS_TYPE_EXT(即ext2/3/4文件系统)调用通用函数do_size,会将环境变量"filesize"设置为查询的文件大小。
do_ext4_load()
int do_ext4_load(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
@cmdtp:命令的命令表项指针
@flag:标志
@argc:命令行参数个数
@argv:命令行参数
该函数用于读取ext4文件系统上的指定文件并将其加载到内存中,传入文件系统类型为FS_TYPE_EXT(即ext2/3/4文件系统)调用通用函数do_load,会将环境变量"fileaddr"设置为目标内存地址,将环境变量"filesize"设置为读取的总字节数。
do_ext4_ls()
int do_ext4_ls(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
@cmdtp:命令的命令表项指针
@flag:标志
@argc:命令行参数个数
@argv:命令行参数
该函数用于列出ext4文件系统上的指定目录包含的内容,相当于linux中的ls,传入文件系统类型为FS_TYPE_EXT(即ext2/3/4文件系统)调用通用函数do_ls。
do_ext4_write()
int do_ext4_write(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
@cmdtp:命令的命令表项指针
@flag:标志
@argc:命令行参数个数
@argv:命令行参数
该函数用于将内存中的数据写入到ext4文件系统上的指定文件,传入文件系统类型为FS_TYPE_EXT(即ext2/3/4文件系统)调用通用函数do_save。
ext4.c可提供的命令及使用方式:
  • ext4size命令用于用于获取 ext4 文件系统中某个文件的大小,不可重复调用,该命令对应的函数为do_ext4_size,支持argc为4个参数,使用方法为:ext4size 块设备类型 设备号和分区 目标文件,例如:ext4size mmc 0:1 /boot/kernel.img。
  • ext4ls命令用于列出ext4文件系统上的指定目录包含的内容,可重复调用,该命令对应的函数为do_ext4_ls,支持argc为2-4个参数,使用方法为:ext4ls 块设备类型 设备号和分区 目标目录,例如:ext4ls mmc 0:1 /boot,如果没有指定目标目录则会列出根目录/下的内容。
  • ext4load命令用于从存储设备(如MMC、USB、网络等)的文件系统中加载文件到内存,不可重复调用,该命令对应的函数为do_ext4_load,支持argc为2-7个参数,使用方法为:ext4load 块设备类型 设备号和分区 目标内存地址 源文件 读取长度 文件偏移量,例如:ext4load mmc 0:1 0x82000000 /boot/zImage。
  • ext4write命令用于将内存中的数据写入到ext4文件系统上的指定文件,可重复调用,该命令对应的函数为do_ext4_write,支持argc为6-7个参数,使用方法为:ext4write 块设备类型 设备号和分区 源数据内存地址 目标文件 写入长度 文件偏移量,例如:ext4write mmc 0:1 0x82000000 /boot/kernel.img 0x100000。该命令在CONFIG_CMD_EXT4_WRITE打开时才可以使用。

fastboot.c

do_fastboot_udp()
static int do_fastboot_udp(int argc, char *const argv[], uintptr_t buf_addr, size_t buf_size)
@argc:命令行参数个数
@argv:命令行参数
@buf_addr:内存缓冲区地址,用于存储传输的数据,在该函数中未被使用
@buf_size:缓冲区大小,在该函数中未被使用
该函数启动fastboot服务,通过UDP协议监听网络连接,以便接收来自外部设备(如PC上的fastboot客户端)的命令和数据。fastboot是一种用于快速刷写设备的协议,常用于开发和调试嵌入式系统。这里的buf_addr和buf_size并未被用上,fastboot的内存缓存地址存储在全局变量void *fastboot_buf_addr和u32 fastboot_buf_size中,这两个变量会在fastboot_init函数中被初始化,fastboot_init函数中被初始化,会在下面的do_fastboot函数中被调用
do_fastboot_usb()
static int do_fastboot_usb(int argc, char *const argv[], uintptr_t buf_addr, size_t buf_size)
@argc:命令行参数个数
@argv:命令行参数
@buf_addr:内存缓冲区地址,用于存储传输的数据,在该函数中未被使用
@buf_size:缓冲区大小,在该函数中未被使
该函数启动fastboot服务,通过USB连接与主机通信,实现设备固件的刷写、擦除等操作。该函数通过while循环持续处理USB通信,知道主机断开或者用户主动退出(按下Ctrl+C)。这个函数中传入的argc应为2,argv应形如usb n,其中n表示索引号。
get_serialnumber_from_env()
static int get_serialnumber_from_env(void)
该函数从uboot的环境变量中获取serialnumber的值,并将其设置为设备的序列号,如果该环境变量未定义,则使用默认值"BST"。serialnumber环境变量在uboot中的作用是提供一个唯一标识设备的方式。
do_fastboot()
static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
@cmdtp:命令的命令表项指针
@flag:标志
@argc:命令行参数个数
@argv:命令行参数
该函数是uboot中fastboot功能的统一入口函数,负责解析用户命令并分发到具体的实现(USB或UDP 式)。若传入的参数指定的是udp则会调用do_fastboot_udp函数,若指定的是usb则会调用get_serialnumber_from_env函数设置serialnumber的值并调用do_fastboot_usb函数。
fastboot.c可提供的命令及使用方式:
  • fastboot命令用于启动fastboot服务,设置fastboot所使用的传输方式(udp或者usb),设置fastboot数据传输缓存区地址和大小(如果传入的参数没有设置这两项,则使用默认的地址和大小:CONFIG_FASTBOOT_BUF_ADDR和CONFIG_FASTBOOT_BUF_SIZE),可重复调用,该命令对应的函数为do_fastboot,支持argc为CONFIG_SYS_MAXARGS个参数,使用方法为:fastboot -l 缓存区地址 -s 缓存区大小 传输方式 控制器编号(如果是传输方式为usb),例如:fastboot -l 0x80000000 usb 0。

help.c

do_help()
static int do_help(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
@cmdtp:命令的命令表项指针
@flag:标志
@argc:命令行参数个数
@argv:命令行参数
该函数用于打印命令的帮助信息,他会调用/common/command.c中的_do_help函数。
help.c可提供的命令及使用方式:
  • help命令(或者**?命令,在help.c中也注册了?**命令)用于获取命令的帮助信息,可重复调用,该命令对应的函数为do_help,支持argc为CONFIG_SYS_MAXARGS个参数,使用方法为:help 命令名 …,可以一次获取多个命令的帮助信息,也可以只使用help则会打印当前系统中的所有命令的帮助信息,可以用?代替help。例如:help fastboot 或? fastboot 。

nvedit.c

_do_env_set()
static int _do_env_set(int flag, int argc, char * const argv[], int env_flag)
@flag:标志,未使用
@argc:命令行参数个数
@argv:命令行参数,传入的命令行形式应为:{ "setenv",varname,varvalue1,varvalue2,...}
@env_flag:环境变量设置标志
该函数用于操作环境变量,对环境变量名为argv[1]即上面的varname进行操作,传入的参数个数argc<3或者argv[2]==NULL时表示删除目标环境变量,传入参数个数argc>=3时表示设置目标环境变量。函数执行成功返回0,失败返回1。这里对环境变量的操作是在全局环境hash表中操作的,所以作用效果仅限于内存,要想持久保存,需要调用相应函数写入到Flash。
env_set()
int env_set(const char *varname, const char *varvalue)
@varname:环境变量名
@varvalue:环境变量值
该函数用于操作环境变量,他会调用_do_env_set函数,若varvalue==NULL或varvalue[0]=='\0',就删除目标环境变量,否则设置目标环境变量的值为varvalue。函数执行成功返回0,失败返回1
env_set_hex()
int env_set_hex(const char *varname, ulong value)
@varname:环境变量名
@value:环境变量值
该函数用于设置环境变量,它将环境变量varname的值设置为value的16进制形式。函数执行成功返回0,失败返回1

version.c

do_version()
static int do_version(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
@cmdtp:命令的命令表项指针
@flag:标志
@argc:命令行参数个数
@argv:命令行参数
该函数用于打印当前uboot的版本信息,以及所使用的编译器版本、链接器版本等信息,形如:U-Boot 2019.04+2.1.1+ga744ef25.202309251631 (Apr 14 2025 - 09:57:13 +0000 ) Bst A1000
version.c可提供的命令及使用方式:
  • version命令用于获取uboot的版本信息,以及所使用的编译器版本、链接器版本等信息,可重复调用,该命令对应的函数为do_version,支持argc为1个参数,使用方法为:version。例如:version 。

common/

sub_common/

command.c

该文件是对buboot命令相关函数的定义,包括:

_do_help()
int _do_help(cmd_tbl_t *cmd_start, int cmd_items, cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
@cmd_start:命令表的首地址
@cmd_items:命令表的长度
@cmdtp:命令表项的指针
@flag:标志,函数中并未使用
@argc:命令行参数个数
@argv:命令行参数
该函数会打印命令行参数中所有目标命令的简短帮助信息,如果定义了CONFIG_SYS_LONGHELP还会打印详细帮助信息。如果传入的argc为1,即是输入了help,则会按照字母表顺序,依次打印系统中所有命令的简短帮助信息。失败返回1
find_cmd_tbl()
cmd_tbl_t *find_cmd_tbl(const char *cmd, cmd_tbl_t *table, int table_len)
@cmd:想要查找的命令的命令名
@table:命令表的地址
@table_len:命令表的长度
该函数从命令表中查找命令,命令名查找时只会截取.之前的部分(例如如果想要查找a.b只会查找a,在某些命令中,可能会有类似 .b、.w、.l 这样的修饰符,这些通常用于指定操作的字节长度),查找成功返回指向该命令的指针。由于传入的cmd可能只是一个命令前缀,仅当只有一个命令与该前缀匹配时才会返回查找到的命令指针(如果有多个匹配,但有一个是精确匹配也会成功返回这个精确匹配),否则返回NULL(未找到或有多个匹配时)。
find_cmd()
cmd_tbl_t *find_cmd(const char *cmd)
@cmd:想要查找的命令的命令名
该函数从命令所在的.u_boot_list_2_cmd_2段中查找命令,查找成功返回指向该命令的指针,失败返回NULL
cmd_usage()
int cmd_usage(const cmd_tbl_t *cmdtp)
@cmdtp:目标命令的命令表项指针
该函数打印目标命令的简短信息,如果定义了CONFIG_SYS_LONGHELP还会打印详细帮助信息。
var_complete()
int var_complete(int argc, char * const argv[], char last_char, int maxv, char *cmdv[])
@argc:命令行参数数量
@argv:命令行参数
@last_char:输入的命令行参数的最后一个字符
@maxv:最多可查找的前缀匹配的环境变量数量+2
@cmdv:存储指向所有前缀匹配的环境变量的指针
该函数用于命令行参数的环境变量部分的补全,当只输入命令程序名而没有输入其他参数时,会查找所有环境变量,输入了程序名并只输入一个参数(只输入一个环境变量前缀),会查找所有前缀匹配的环境变量,其他情况均查找失败。查找成功返回找到的前缀匹配的环境变量的数量,失败返回0
dollar_complete()
static int dollar_complete(int argc, char * const argv[], char last_char, int maxv, char *cmdv[])
@argc:命令行参数数量
@argv:命令行参数
@last_char:输入的命令行参数的最后一个字符
@maxv:最多可查找的前缀匹配的环境变量数量+2
@cmdv:存储指向所有前缀匹配的环境变量的指针
该函数用于给输入的命令行的最后一个参数进行补全,最后一个参数的形式必须是${待补全环境变量的前缀,否则补全失败。该函数会查找所有前缀匹配的环境变量,查找成功返回找到的前缀匹配的环境变量的数量,失败返回0
complete_subcmdv()
int complete_subcmdv(cmd_tbl_t *cmdtp, int count, int argc, char * const argv[], char last_char, int maxv, char *cmdv[])
@cmdtp:命令的命令表首地址
@count:命令的命令表的表项数
@argc:命令行参数数量
@argv:命令行参数
@last_char:输入的命令行参数的最后一个字符
@maxv:最多可查找的前缀匹配的命令数量+2
@cmdv:存储指向所有前缀匹配的命令的指针
该函数用于命令补全,如果argc为1且最后一个字符不是'\0'和空格,即命令只输入了一个前缀,则会在命令表中查找所有前缀匹配的命令(对于cp.b带有.形式的命令,会查找所有带有.前面前缀的即带有cp的命令),将找到的命令指针存储在cmdv中,返回找到的数量;如果argc为1而最后一个字符是'\0'或空格,或者argc>1,则会按照argv[0]在子命令表中查找命令,找到后执行对应命令的cmdtp->complete函数。
complete_cmdv()
static int complete_cmdv(int argc, char * const argv[], char last_char, int maxv, char *cmdv[])
@argc:命令行参数数量
@argv:命令行参数
@last_char:输入的命令行参数的最后一个字符
@maxv:最多可查找的前缀匹配的命令数量+2
@cmdv:存储指向所有前缀匹配的命令的指针
该函数用于命令补全,是通过调用complete_subcmdv函数实现的,查找范围为命令cmd所在段.u_boot_list_2_cmd_2中的所有命令。
make_argv()
static int make_argv(char *s, int argvsz, char *argv[])
@s:待提取参数的字符串
@argvsz:最多可提取的参数数量+1,argv最后一个数组元素要存储NULL
@argv:存储指向每个提取出来的参数的指针
该函数用于从字符串s中提取出参数,每个参数之间用'\0'分隔,返回提取到的参数数量。
print_argv()
static void print_argv(const char *banner, const char *leader, const char *sep, int linemax, char * const argv[])
@banner:打印的标题
@leader:每行的前缀
@sep:每个参数之间的分隔符
@linemax:每行的最大打印长度
@argv:待打印的参数列表
该函数用于打印命令行参数。
find_common_prefix()
static int find_common_prefix(char * const argv[])
@argv:存储待计算的参数序列
该函数查找argv中所有参数的公共前缀,返回公共前缀的长度。
cmd_auto_complete()
int cmd_auto_complete(const char *const prompt, char *buf, int *np, int *colp)
@prompt:命令提示符,命令提示符必须与系统中配置的一样,说明当前处于命令行模式,例如:#define CONFIG_SYS_PROMPT		"u-boot=> "
@buf:存储输入的命令行参数
@np:指向输入的命令行长度值的指针
@colp:指向当前所在列数值的指针
该函数用于命令行补全命令。对于环境变量${}的补全,只有待补全的环境变量为命令行最后一个参数时才可以补全。如果匹配的命令只有一个则直接在当前行输出。如果不止一个命令相匹配,而所有匹配的命令有公共前缀且大于当前已经输入的命令前缀长度,则直接在当前行输出,将命令补全到公共前缀长度。如果匹配的命令有多个,但是无法将当前已经输入的命令补全的更长,则直接在下面打印输出所有匹配的命令。该函数会更新np和colp指针处的值。
cmd_get_data_size()
int cmd_get_data_size(char* arg, int default_size)
该函数根据输入的字符串arg确定数据的大小,并返回相应的字节数,以 .b结尾返回1表示字节、.w结尾返回2表示字,.l结尾返回4表示字节长字等。如果字符串没有包含指定的大小后缀,则返回一个默认大小default_size。
fixup_cmdtable()
void fixup_cmdtable(cmd_tbl_t *cmdtp, int size)
@cmdtp:命令表的地址
@size:命令表的大小
该函数用于实现命令表中所有命令结构体中的指针在内存中的重定位,主要是将这些指针加上gd->reloc_off(gd->reloc_off是一个全局变量,是系统重定位偏移量)。这个函数中并没有改变cmdtp->cmd_rep函数指针的重定向。
cmd_always_repeatable()
int cmd_always_repeatable(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], int *repeatable)
@cmdtp:命令的命令表项指针
@flag:标志,函数中未使用
@argc:命令行参数数量
@argv:命令行参数
@repeatable:记录是否重复执行
该函数是命令结构体中cmd_rep函数指针的目标函数之一,在命令可被重复执行时cmd_rep指向cmd_always_repeatable。
cmd_never_repeatable()
int cmd_never_repeatable(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], int *repeatable)
@cmdtp:命令的命令表项指针
@flag:标志,函数中未使用
@argc:命令行参数数量
@argv:命令行参数
@repeatable:记录是否重复执行
该函数是命令结构体中cmd_rep函数指针的目标函数之一,在命令不可被重复执行时cmd_rep指向cmd_always_repeatable。
cmd_discard_repeatable()
int cmd_discard_repeatable(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
@cmdtp:命令的命令表项指针
@flag:标志,函数中未使用
@argc:命令行参数数量
@argv:命令行参数
该函数可作为命令结构体中cmd函数指针的目标函数,在具有子命令的主命令被初始化时会将cmd函数指针指向cmd_discard_repeatable,这个函数永远不会被执行。
cmd_call()
static int cmd_call(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], int *repeatable)
@cmdtp:命令的命令表项指针
@flag:标志,函数中未使用
@argc:命令行参数个数
@argv:命令行参数
@repeatable:记录是否重复执行
该函数用于执行目标命令,会调用cmdtp->cmd_rep()
cmd_process()
enum command_ret_t cmd_process(int flag, int argc, char * const argv[], int *repeatable, ulong *ticks)
@flag:标志
@argc:命令行参数个数
@argv:命令行参数
@repeatable:记录是否重复执行
@ticks:用于记录命令执行所使用的时间
该函数用于执行命令,会在命令cmd所在段.u_boot_list_2_cmd_2中查找argv[0]中的命令并执行,会在*ticks中记录命令执行的时间。u-boot命令行中输入的命令被识别胡都是通过调用该函数去执行对应命令的。
cmd_process_error()
int cmd_process_error(cmd_tbl_t *cmdtp, int err)
@cmdtp:命令的命令表项指针
@err:命令执行状态,应穿入enum command_ret_t类型的值
该函数根据传入的命令个err打印出错信息或者打印命令的usage简短帮助信息。

bootm.c

do_bootm_states()
int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], int states, bootm_headers_t *images, int boot_progress)
@cmdtp:命令的命令表项指针
@flag:标志
@argc:命令行参数个数
@argv:命令行参数
@states:状态掩码(BOOTM_STATE_*),指定要执行的步骤
@images:镜像信息结构体指针
@boot_progress:是否显示启动进度
该函数通过多阶段状态控制,依次完成内核镜像的加载验证、内存管理初始化、设备树和初始化内存盘的重定位等关键操作,最终完成硬件环境准备并跳转至操作系统入口点。作为bootm和bootz等命令的底层实现,它采用模块化设计支持多种镜像格式(如uImage、FIT),通过动态获取操作系统特定的启动函数来处理架构相关操作,同时提供伪启动调试机制和安全恢复功能,确保在内核接管前正确初始化硬件状态并释放资源,实现从引导加载程序到操作系统的平滑控制权移交。

env/

common.c

env_complete()
int env_complete(char *var, int maxv, char *cmdv[], int bufsz, char *buf, bool dollar_comp)
@var:需要查找的(前缀)环境变量名
@maxv:cmdv的大小,最多可以查找maxv-2个匹配的环境变量,因为如果匹配的环境变量过多,则最后两个数组指针分别指向"..."(或"${...}")和NULL
@cmdv:用来存储指向每个匹配的环境变量的指针
@bufsz:buf的大小
@buf:存储所有匹配的环境变量的全名
@dollar_comp:是否要匹配$符号,即在环境变量采用${环境变量前缀形式查找时开启(完整的形式应该是${环境变量},使用$时buf中按照${环境变量}形式存储)
该函数用于查找所有和var(前缀)匹配的环境变量,用于环境变量补全操作,最终cmdv中的指针成员会指向查找出来的所有环境变量,并按照字符串顺序排序。查找成功返回查找到的环境变量数量,失败返回0

flags.c

env_flags_validate()
int env_flags_validate(const ENTRY *item, const char *newval, enum env_op op, int flag)
@item:目标环境变量的哈希表条目指针
@newval:新值(创建或覆盖时使用,删除时为 NULL)
@op:操作类型(env_op_create/env_op_overwrite/env_op_delete)
@flag:标志位(如 H_FORCE 强制操作)
该函数用于验证环境变量操作:创建、覆盖、删除的合法性。新值newval的类型和item->flags不匹配时,类型验证失败返回-1。op操作权限未验证通过时失败返回1。验证成功返回0

include/

sub_include/

command.h

该头文件主要是对uboot中命令的定义,cmd结构体如下:

struct cmd_tbl_s
struct cmd_tbl_s {
	char	*name;//命令名字
	int		maxargs;//命令的最大参数数量
	//该函数指针指向的函数用于处理命令的可重复执行逻辑,按下Enter键是否重复
	int		(*cmd_rep)(struct cmd_tbl_s *cmd, int flags, int argc,
				   char * const argv[], int *repeatable);
	//该函数指针指向实现命令的函数
	int		(*cmd)(struct cmd_tbl_s *, int, int, char * const []);
	char	*usage;//命令简短说明,帮助信息
#ifdef	CONFIG_SYS_LONGHELP
	char	*help;//命令详细说明,帮助信息
#endif
#ifdef CONFIG_AUTO_COMPLETE
	//该函数指针指向实现命令参数自动补全的函数
	int		(*complete)(int argc, char * const argv[], char last_char, int 						maxv, char *cmdv[]);
#endif
};

头文件中有几个主要的宏定义:

CONFIG_CMDLINE:用于控制uboot是否启用命令行功能,未定义该宏时会关闭命令行功能,此时系统不会在内存中存储任何命令行的指令数据

CONFIG_AUTO_COMPLETE:控制是否有自动补全功能(似乎只有子命令的命令才可支持自动补全)

CONFIG_SYS_LONGHELP:控制命令是否有详细的帮助信息help

CONFIG_NEEDS_MANUAL_RELOC:控制子命令是否需要根据系统内存偏移调整命令结构体中指针的值,即重定位

U_BOOT_SUBCMDS_RELOC(_cmdname):定义了一个函数static void _cmdname##_subcmds_reloc(void),用于实现子命令结构体中指针的重定位
U_BOOT_SUBCMDS_DO_CMD(_cmdname):定义了一个函数static int do_##_cmdname(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], int *repeatable),用于匹配命令的子命令并执行,对于有子命令的命令,这些(主)命令结构体中的cmd_rep直接指向do_##_cmdname,而不会指向cmd_always_repeatable或cmd_never_repeatable,对于有子命令的命令,这些(主)命令结构体中的cmd指向cmd_discard_repeatable,这个cmd_discard_repeatable永远不会被执行,因为命令执行时是直接执行命令结构体中的cmd_rep所指向的函数而间接执行cmd指向的函数。
U_BOOT_SUBCMDS_COMPLETE(_cmdname):定义了一个函数static int complete_##_cmdname(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]),用于有子命令的(主)命令,实现命令的补全功能,只有在写出了(主)命令,并且子命令写出了前缀时(子命令未写全)才有用,因为在子命令写全时进行补全时会调用子命令结构体中的cmd_rep,但这个指针为NULL,子命令在创建时调用了宏U_BOOT_SUBCMD_MKENT,传入的cmd_rep固定为NULL。对于有子命令的命令,这些(主)命令结构体中的complete指向complete_##_cmdname。
#define U_BOOT_SUBCMDS(_cmdname, ...)					\
	static cmd_tbl_t _cmdname##_subcmds[] = { __VA_ARGS__ };	\
	U_BOOT_SUBCMDS_RELOC(_cmdname)					\
	U_BOOT_SUBCMDS_DO_CMD(_cmdname)					\
	U_BOOT_SUBCMDS_COMPLETE(_cmdname)
所有子命令的结构体存储在全局静态数组_cmdname##_subcmds[]中。
U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help)用于定义一个没有子命令的命令,参数依次为命令名,最大参数数量,是否可以重复执行,命令函数指针,简短帮助信息,详细帮助信息。对于没有子命令的命令,它结构体中的cmd_rep指向cmd_always_repeatable表示可以重复执行或cmd_never_repeatable表示不可以重复执行,complete指向NULL
U_BOOT_CMD_WITH_SUBCMDS(_name, _usage, _help, ...)用于定义一个带有子命令的命令,(主)命令结构体中的complete指向complete_##_name,cmd_rep指向do_##_name,cmd指向cmd_discard_repeatable。子命令的usage和help帮助信息均为空""

命令被存储到内存是通过下面几个宏定义实现的:

#define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,		\
				_usage, _help, _comp)			\
		{ #_name, _maxargs,					\
		 _rep ? cmd_always_repeatable : cmd_never_repeatable,	\
		 _cmd, _usage, _CMD_HELP(_help) _CMD_COMPLETE(_comp) }

#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);
ll_entry_declare(cmd_tbl_t, _name, cmd)也是一个宏定义:
    #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)))
通过上面几个宏定义,每一个命令的结构体会被存储在.u_boot_list_2_cmd_2_#_name段中,参考.lds文件可知所有.u_boot_list*段被合并到.u_boot_list段中,所以所有的命令的信息最终被存储在.u_boot_list段中。

linker_lists.h

该文件主要用于定义一些方便系统在内存查找元素的宏定义,uboot中的数据都存储在形如_u_boot_list_2_##_list##_2_##_name的段中:

llsym(_type, _name, _list)
#define llsym(_type, _name, _list) \
		((_type *)&_u_boot_list_2_##_list##_2_##_name)
获取段的首地址,并转换为_type *类型。
ll_entry_declare(_type, _name, _list)
#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)))
定义了一个名为_u_boot_list_2_##_list##_2_##_name的_type类型的数据,并存储在.u_boot_list_2_#_list_2_#_name段中。
ll_entry_declare_list(_type, _name, _list)
#define ll_entry_declare_list(_type, _name, _list)			\
	_type _u_boot_list_2_##_list##_2_##_name[] __aligned(4)		\
			__attribute__((unused,				\
			section(".u_boot_list_2_"#_list"_2_"#_name)))
定义了一个名为_u_boot_list_2_##_list##_2_##_name[]的_type类型的数组,并存储在.u_boot_list_2_#_list_2_#_name段中。
ll_entry_start(_type, _list)
#define ll_entry_start(_type, _list)					\
({									\
	static char start[0] __aligned(4) __attribute__((unused,	\
		section(".u_boot_list_2_"#_list"_1")));			\
	(_type *)&start;						\
})
获取指定存有数据的段的首地址start,因为static char start[0]定义的数组大小为0,所以实际上在.u_boot_list_2_#_list_1段中并没有存储任何数据,start符号只是作为一个内存地址被记录在符号表中,而该地址指向了一个空的段.u_boot_list_2_#_list_1段,又因为这些段在链接时被顺序存放,所以start中的值就是.u_boot_list_2_#_list_2段的首地址。
ll_entry_end(_type, _list)
#define ll_entry_end(_type, _list)					\
({									\
	static char end[0] __aligned(4) __attribute__((unused,		\
		section(".u_boot_list_2_"#_list"_3")));			\
	(_type *)&end;							\
})
获取指定存有数据的段的尾地址end,因为static char end[0]定义的数组大小为0,所以实际上在.u_boot_list_2_#_list_2段中并没有存储任何数据,end符号只是作为一个内存地址被记录在符号表中,而该地址指向了一个空的段.u_boot_list_2_#_list_3段,又因为这些段在链接时被顺序存放,所以end中的值就是.u_boot_list_2_#_list_2段的尾地址。
ll_entry_count(_type, _list)
#define ll_entry_count(_type, _list)					\
	({								\
		_type *start = ll_entry_start(_type, _list);		\
		_type *end = ll_entry_end(_type, _list);		\
		unsigned int _ll_result = end - start;			\
		_ll_result;						\
	})
统计.u_boot_list_2_#_list_2段中包含数据的数量。
ll_entry_get(_type, _name, _list)
#define ll_entry_get(_type, _name, _list)				\
	({								\
		extern _type _u_boot_list_2_##_list##_2_##_name;	\
		_type *_ll_result =					\
			&_u_boot_list_2_##_list##_2_##_name;		\
		_ll_result;						\
	})
获取.u_boot_list_2_#_list_2_#_name段的地址。
ll_start(_type)
#define ll_start(_type)							\
({									\
	static char start[0] __aligned(4) __attribute__((unused,	\
		section(".u_boot_list_1")));				\
	(_type *)&start;						\
})
获取.u_boot_list_2段的首地址。
ll_end(_type)
#define ll_end(_type)							\
({									\
	static char end[0] __aligned(4) __attribute__((unused,		\
		section(".u_boot_list_3")));				\
	(_type *)&end;							\
})
获取.u_boot_list_2段的尾地址。

lib

sub_lib/

hashtable.c

hmatch_r()
int hmatch_r(const char *match, int last_idx, ENTRY ** retval, struct hsearch_data *htab)
@match:需要查找的匹配字符串
@last_idx:上一次查找的索引,用来控制从哪开始继续查找,本次查找会从last_idx+1处开始
@retval:用于返回找到的匹配项的地址,如果找到了匹配项,retval会指向匹配项的条目
@htab:哈希表数据结构,包含了实际的哈希表数据及相关信息
该函数是一个哈希表查找函数,用于根据指定的匹配字符串match在哈希表中查找条目,并返回匹配的条目的索引,若未找到匹配项,则返回0并设置错误代码。
_compare_and_overwrite_entry()
static inline int _compare_and_overwrite_entry(ENTRY item, ACTION action, ENTRY **retval, struct hsearch_data *htab, int flag, unsigned int hval, unsigned int idx)
@item:目标环境变量的哈希表条目
@action:操作类型,可为覆盖写入ENTER或查找FIND
@retval:用于在写入或查找成功后记录目标哈希表条目
@htab:查找的哈希表
@flag:标志
@hval:哈希值
@idx:索引
该函数用于查找并处理已存在的哈希表条目,写入或查找成功返回对应哈希表条目的索引(大于0),失败返回0-1。如果是由于传入的索引对应的哈希表条目不匹配或者不可用,返回-1,如果是覆盖写入时传入的ENTRY中的哈希值和其类型不匹配或者回调函数调用失败返回0
hsearch_r()
int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval, struct hsearch_data *htab, int flag)
@item:目标环境变量的哈希表条目
@action:操作类型,可为覆盖写入ENTER或查找FIND
@retval:用于在写入或查找成功后记录目标哈希表条目
@htab:查找的哈希表
@flag:标志
该函数实现哈希查找并处理相应的哈希表条目,成功返回1,失败返回0。算法中采用开放寻址法中的线性探测解决哈希冲突,但必须保证哈希表的大小htab->size为质数。
_hdelete()
static void _hdelete(const char *key, struct hsearch_data *htab, ENTRY *ep, int idx)
@key:哈希键值
@htab:哈希表
@ep:待删除的哈希表项
@idx:待删除哈希表项的索引值

hdelete_r()
int hdelete_r(const char *key, struct hsearch_data *htab, int flag)
@key:哈希键值
@htab:哈希表
@flag:标志,未用到
该函数会在哈希表htab中查找具有键值key的哈希表项,找到后调用_hdelete函数将其删除,成功返回1,失败返回0

网站公告

今日签到

点亮在社区的每一天
去签到