u-boot学习笔记(四)

news2025/5/10 8:56:36

文章目录

  • cmd/
    • sub_cmd/
      • exit.c
        • do_exit()
        • exit.c可提供的命令及使用方式:
      • ext2.c
        • do_ext2ls()
        • do_ext2load()
        • ext2.c可提供的命令及使用方式:
      • ext4.c
        • do_ext4_size()
        • do_ext4_load()
        • do_ext4_ls()
        • do_ext4_write()
        • ext4.c可提供的命令及使用方式:
      • fastboot.c
        • do_fastboot_udp()
        • do_fastboot_usb()
        • get_serialnumber_from_env()
        • do_fastboot()
        • fastboot.c可提供的命令及使用方式:
      • help.c
        • do_help()
        • help.c可提供的命令及使用方式:
      • nvedit.c
        • _do_env_set()
        • env_set()
        • env_set_hex()
      • version.c
        • do_version()
        • version.c可提供的命令及使用方式:
  • common/
    • sub_common/
      • command.c
        • _do_help()
        • find_cmd_tbl()
        • find_cmd()
        • cmd_usage()
        • var_complete()
        • dollar_complete()
        • complete_subcmdv()
        • complete_cmdv()
        • make_argv()
        • print_argv()
        • find_common_prefix()
        • cmd_auto_complete()
        • cmd_get_data_size()
        • fixup_cmdtable()
        • cmd_always_repeatable()
        • cmd_never_repeatable()
        • cmd_discard_repeatable()
        • cmd_call()
        • cmd_process()
        • cmd_process_error()
      • bootm.c
        • do_bootm_states()
  • env/
      • common.c
        • env_complete()
      • flags.c
        • env_flags_validate()
  • include/
    • sub_include/
      • command.h
        • struct cmd_tbl_s
      • linker_lists.h
        • llsym(_type, _name, _list)
        • ll_entry_declare(_type, _name, _list)
        • ll_entry_declare_list(_type, _name, _list)
        • ll_entry_start(_type, _list)
        • ll_entry_end(_type, _list)
        • ll_entry_count(_type, _list)
        • ll_entry_get(_type, _name, _list)
        • ll_start(_type)
        • ll_end(_type)
  • lib
    • sub_lib/
      • hashtable.c
        • hmatch_r()
        • _compare_and_overwrite_entry()
        • hsearch_r()
        • _hdelete()
        • hdelete_r()

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2372174.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AGV通信第3期|AGV集群智能应急响应系统:从故障感知到快速恢复

随着智慧工厂物流系统复杂度的提升&#xff0c;AGV运行过程中的异常处理能力已成为保障生产连续性的关键指标。面对突发障碍、设备故障等意外状况&#xff0c;传统依赖人工干预的响应模式已无法满足现代智能制造对时效性的严苛要求。 一、AGV异常应急体系面临的挑战 响应时效瓶…

军事目标无人机视角坦克检测数据集VOC+YOLO格式4003张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4003 标注数量(xml文件个数)&#xff1a;4003 标注数量(txt文件个数)&#xff1a;4003 …

软件安全(二)优化shellcode

我们在上一节课中所写的shellcode&#xff0c;其中使用到的相关的API是通过写入其内存地址来实现调用。这种方法具有局限性&#xff0c;如切换其他的操作系统API的内存地址就会发生变化&#xff0c;从而无法正常调用。 所谓的shellcode不过是在目标程序中加一个区段使得程序可…

RabbitMQ-运维

文章目录 前言运维-集群介绍多机多节点单机多节点 多机多节点下载配置hosts⽂件配置Erlang Cookie启动节点构建集群查看集群状态 单机多节点安装启动两个节点再启动两个节点验证RabbitMQ启动成功搭建集群把rabbit2, rabbit3添加到集群 宕机演示仲裁队列介绍raft算法协议 raft基…

深度学习基础--目标检测常见算法简介(R-CNN、Fast R-CNN、Faster R-CNN、Mask R-CNN、SSD、YOLO)

博主简介&#xff1a;努力学习的22级本科生一枚 &#x1f31f;​&#xff1b;探索AI算法&#xff0c;C&#xff0c;go语言的世界&#xff1b;在迷茫中寻找光芒​&#x1f338;​ 博客主页&#xff1a;羊小猪~~-CSDN博客 内容简介&#xff1a;常见目标检测算法简介​&#x1f…

LINUX CFS算法解析

文章目录 1. Linux调度器的发展历程2. CFS设计思想3. CFS核心数据结构3.1 调度实体(sched_entity)3.2 CFS运行队列(cfs_rq)3.3 任务结构体中的调度相关字段 4. 优先级与权重4.1 优先级范围4.2 权重映射表 (prio_to_weight[])优先级计算4.3.1. static_prio (静态优先级)4.3.2. n…

软考-软件设计师中级备考 14、刷题 算法

一、考点归纳 1&#xff09;排序 2、查找 3、复杂度 4、经典问题 0 - 1 背包动态规划0 - 1 背包问题具有最优子结构性质和重叠子问题性质。通过动态规划可以利用一个二维数组来记录子问题的解&#xff0c;避免重复计算&#xff0c;从而高效地求解出背包能装下的最大价值。分…

Baklib实战企业内容与中台管理差异解析

企业内容管理中台本质差异 企业内容管理系统&#xff08;CMS&#xff09;与内容中台的核心差异在于战略定位与技术路径的本质性区隔。传统CMS聚焦于内容存储与审批流程的线性管理&#xff0c;而内容中台则构建起全域数据服务中枢&#xff0c;通过API接口实现跨系统内容资产调用…

通用外设驱动模型(四步法)

举例&#xff1a;GPIO配置步骤 1、使能时钟 __HAL_RCC_GPIOx_CLK_ENABLE()2、设置工作模式 HAL_GPIO_Init()3、设置输出状态&#xff08;可选&#xff09; HAL_GPIO_WritePin() HAL_GPIO_TogglePin()4、读取输入状态&#xff08;可选&#xff09; HAL_GPIO_ReadPin()模块…

IoT无线组网模块,万物互联的底层通信基石

随着物联网&#xff08;IoT&#xff09;技术在“快车道”上持续飞驰&#xff0c;一场“交互革命”正在人们的日常出行与工作学习等生活场景中加速爆发。从智能家居到智慧城市&#xff0c;从智慧交通到工业自动化&#xff0c;物联网&#xff08;IoT&#xff09;技术凭借着万物互…

learning ray之ray强化学习/超参调优和数据处理

之前我们掌握了Ray Core的基本编程&#xff0c;我们已经学会了如何使用Ray API。现在&#xff0c;让我们将这些知识应用到一个更实际的场景中——构建一个强化学习项目&#xff0c;并且利用Ray来加速它。 我们的目标是&#xff0c;通过Ray的任务和Actor&#xff0c;将一个简单…

【Linux】深入拆解Ext文件系统:从磁盘物理结构到Linux文件管理

目录 1、理解硬件 &#xff08;1&#xff09;磁盘 &#xff08;2&#xff09;磁盘的物理结构 &#xff08;3&#xff09;磁盘的存储结构 &#xff08;4&#xff09;磁盘的逻辑结构 &#xff08;5&#xff09;CHS && LBA地址 2、引入文件系统 &#xff08;1&…

基于 Ubuntu 24.04 部署 WebDAV

无域名&#xff0c;HTTP 1. 简介 WebDAV&#xff08;Web Distributed Authoring and Versioning&#xff09;是一种基于 HTTP 的协议&#xff0c;允许用户通过网络直接编辑和管理服务器上的文件。本教程介绍如何在 Ubuntu 24.04 上使用 Apache2 搭建 WebDAV 服务&#xff0c;无…

tauri-plugin-store 这个插件将数据存在本地电脑哪个位置

tauri-plugin-store 插件用于在 Tauri 应用中以键值对形式持久化存储数据。它将数据存储在用户本地电脑的一个 JSON 文件中&#xff0c;具体路径取决于操作系统&#xff0c;并且通常位于操作系统的应用数据目录中。 默认存储位置 以默认配置为例&#xff08;使用 default sto…

一场陟遐自迩的 SwiftUI + CoreData 性能优化之旅(下)

概述 自从 SwiftUI 诞生那天起&#xff0c;我们秃头码农们就仿佛打开了一个全新的撸码世界&#xff0c;再辅以 CoreData 框架的鼎力相助&#xff0c;打造一款持久存储支持的 App 就像探囊取物般的 Easy。 话虽如此&#xff0c;不过 CoreData 虽好&#xff0c;稍不留神也可能会…

数字人驱动/动画方向最新顶会期刊论文收集整理 | AAAI 2025

会议官方论文列表&#xff1a;https://ojs.aaai.org/index.php/AAAI/issue/view/624 以下论文部分会开源代码&#xff0c;若开源&#xff0c;会在论文原文的摘要下方给出链接。 语音驱动头部动画/其他 EchoMimic: Lifelike Audio-Driven Portrait Animations through Editabl…

数据结构 集合类与复杂度

文章目录 &#x1f4d5;1. 集合类&#x1f4d5;2. 时间复杂度✏️2.1 时间复杂度✏️2.2 大O渐进表示法✏️2.3 常见的时间复杂度量级✏️2.4 常见时间复杂度计算举例 &#x1f4d5;3. 空间复杂度 &#x1f4d5;1. 集合类 Java 集合框架&#xff08;Java Collection Framework…

Python学习笔记--Django的安装和简单使用(一)

一.简介 Django 是一个用于构建 Web 应用程序的高级 Python Web 框架。Django 提供了一套强大的工具和约定&#xff0c;使得开发者能够快速构建功能齐全且易于维护的网站。Django 遵守 BSD 版权&#xff0c;初次发布于 2005 年 7 月, 并于 2008 年 9 月发布了第一个正式版本 1…

SecureCRT网络穿透/代理

场景 公司的办公VPN软件只有Windows系统版本&#xff0c;没有Macos系统版本&#xff0c;而日常开发过程中需要先登录VPN后&#xff0c;然后才能登录应用服务器。 目的&#xff1a;Macos系统在使用SecureCRT时&#xff0c;登录服务器&#xff0c;需要走Parallels Desktop进行网络…

视频添加字幕脚本分享

脚本简介 这是一个给视频添加字幕的脚本&#xff0c;可以方便的在指定的位置给视频添加不同大小、字体、颜色的文本字幕&#xff0c;添加方式可以直接修改脚本中的文本信息&#xff0c;或者可以提前编辑好.srt字幕文件。脚本执行环境&#xff1a;windowsmingwffmpeg。本方法仅…