首页 > 其他分享 >从头理清uboot(3)-main_loop 及 CMD实现

从头理清uboot(3)-main_loop 及 CMD实现

时间:2024-07-28 22:55:55浏览次数:19  
标签:CMD uboot boot cmd list char bootdelay main

从头理清uboot(3)-main_loop 及 CMD实现

目录

1. main—loop 函数

上篇引导启动的分析最后会调用run_main_loop,在其中会循环调用main_loop()函数。见下方:

static int run_main_loop(void)
{
	for (;;)
		main_loop();
	return 0;
}

而在main_loop中,执行的语句如下:

void main_loop(void)
{
	const char *s;
	bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop"); //打印启动进度
	cli_init();							/*初始化 hash shell 相关  */
	run_preboot_environment_command();	/*获取 preboot 环境变量  */
	s = bootdelay_process();			/*读取环境变量 bootdelay和bootcmd 的内容*/
	if (cli_process_fdt(&s))			/* 此次uboot 直接返回uboot */
		cli_secure_boot_cmd(s);			
	autoboot_command(s);/* 如果延时到了,没有打断就执行默认的boot-arg */
	cli_loop();							/* 命令执行函数 */
}
  • bootdelay_process 函数解析:初始化好了bootdelay的参数,并且返回的命令是bootcmd 环境变量。

    const char *bootdelay_process(void)
    {
    	char *s;
    	int bootdelay;
        s = getenv("bootdelay");
    	bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
    
    #if !defined(CONFIG_FSL_FASTBOOT) && defined(is_boot_from_usb)
    	if (is_boot_from_usb()) {
    		disconnect_from_pc();
    		printf("Boot from USB for mfgtools\n");
    		bootdelay = 0;
    		set_default_env("Use default environment for \
    				 mfgtools\n");
    	} else {
    		printf("Normal Boot\n");
    	}
    #endif
        bootretry_init_cmd_timeout();
        s = getenv("bootcmd");
    #if !defined(CONFIG_FSL_FASTBOOT) && defined(is_boot_from_usb)
    	if (is_boot_from_usb()) {
    		s = getenv("bootcmd_mfg");
    		printf("Run bootcmd_mfg: %s\n", s);
    	}
    #endif    
       	process_fdt_options(gd->fdt_blob);
    	stored_bootdelay = bootdelay;
    	return s;
    } 
    

    其中的get_env函数:可见这个函数根据命令的不同,从哈希表遍历两种方式,去现有得环境变量中查找对应命令。

    char *getenv(const char *name)
    {
         /* after import into hashtable */
    	if (gd->flags & GD_FLG_ENV_READY) {
    		ENTRY e, *ep;
    
    		WATCHDOG_RESET();
    
    		e.key	= name;
    		e.data	= NULL;
    		hsearch_r(e, FIND, &ep, &env_htab, 0);
    
    		return ep ? ep->data : NULL;
    	}
    	/* restricted capabilities before import */
    	if (getenv_f(name, (char *)(gd->env_buf), sizeof(gd->env_buf)) > 0)
    		return (char *)(gd->env_buf);
    	return NULL;
    }
    
  • autoboot_command的函数代码精简如下,可见有三个参数,stored_bootdelay是刚刚初始化好的bootdelay环境变量的值,S 是bootcmd的值,这两个都条件成立。

    void autoboot_command(const char *s)
    {
      debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
        if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {
    		run_command_list(s, -1, 0); 
    	}
    }
    
    • 进一步分析abortboot(stored_bootdelay)发现里面会每次delay 1000ms 就会把传入的stored_bootdelay参数减一,所以当stored_bootdelay 递减到0的时候,会执行run_command_list(s, -1, 0)

    • 但是其中有个函数是tstc()有效的话,就会导致提前break ,不再执行run_command_list(s, -1, 0)。进入指令处理流程。

    	while ((bootdelay > 0) && (!abort)) {
    		--bootdelay;
    		/* delay 1000 ms */
    		ts = get_timer(0);
    		do {
    			if (tstc()) {	/* we got a key press	*/
    				abort  = 1;	/* don't auto boot	*/
    				bootdelay = 0;	/* no more delay	*/
    				(void) getc();  /* consume input	*/
    				break;
    			}
    			udelay(10000);
    		} while (!abort && get_timer(ts) < 1000);
    
    		printf("\b\b\b%2d ", bootdelay);
    	}
    
  • 正常的autoboot流程-- run_command_list 函数分析:

    会调用:rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON);
    	进而调用:rcode = parse_stream_outer(&input, flag);
    
  • 有按键输入,执行解析命令-cli_loop函数:

    parse_file_outer();
    	->调用:parse_stream_outer(&input, FLAG_PARSE_SEMICOLON);
    
  • 所以这种方式都会把接收到的命令交给parse_stream_outer函数执行。

    -> 调用: rcode = parse_stream(&temp, &ctx, inp, flag & FLAG_CONT_ON_NEWLINE ? -1 : '\n'); /* 解析输入的命令 */
    -> 调用: code = run_list(ctx.list_head); /* 执行命令*/
    	->run_list_real(pi);
    		->run_pipe_real(pi);
    			->cmd_process(flag, child->argc, child->argv,
    				   &flag_repeat, NULL); /* 实际的处理流程*/
    

2. cmd_process 函数分析

​ 在看函数处理之前,可以先了解uboot cmd 结构体的组成,其中参数见下图注释

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;		/*简短提示信息*/
};

​ cmd_process 的处理流程如下所示, 可见主要流程为:查找命令->判断参数数量->回调函数调用。

enum command_ret_t cmd_process(int flag, int argc, char * const argv[],
			       int *repeatable, ulong *ticks)
{
	enum command_ret_t rc = CMD_RET_SUCCESS;
	cmd_tbl_t *cmdtp;

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

	/* found - check max args */
	if (argc > cmdtp->maxargs)
		rc = CMD_RET_USAGE;

#if defined(CONFIG_CMD_BOOTD)
	/* avoid "bootd" recursion */
	else if (cmdtp->cmd == do_bootd) {
		if (flag & CMD_FLAG_BOOTD) {
			puts("'bootd' recursion detected\n");
			rc = CMD_RET_FAILURE;
		} else {
			flag |= CMD_FLAG_BOOTD;
		}
	}
#endif
	/* If OK so far, then do the command */
	if (!rc) {
		if (ticks)
			*ticks = get_timer(0);
		rc = cmd_call(cmdtp, flag, argc, argv);/*利用回调函数执行*/
		if (ticks)
			*ticks = get_timer(*ticks);
		*repeatable &= cmdtp->repeatable;
	}
	if (rc == CMD_RET_USAGE)
		rc = cmd_usage(cmdtp);
	return rc;
}

3. cmd 定义流程

  • uboot 命令分析,在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) }

#define U_BOOT_CMD_MKENT(_name, _maxargs, _rep, _cmd, _usage, _help)	\
	U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,		\
					_usage, _help, NULL)

#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);

#define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help)		\
	U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, NULL)

可以看出有以下几点:

  1. U_BOOT_CMD就是U_BOOT_CMD_COMPLETE的宏参数最后一个为NULL

  2. U_BOOT_CMD_COMPLETE会继续调用ll_entry_declareU_BOOT_CMD_MKENT_COMPLETE函数。分别显示如下

    • #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)))
      
    • #define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,\
      				_usage, _help, _comp)			\
      		{ #_name, _maxargs, _rep, _cmd, _usage,			\
      			_CMD_HELP(_help) _CMD_COMPLETE(_comp) }
      
  3. 其中再次调用了_CMD_HELP(_help) _CMD_COMPLETE(_comp)这两部分定义如下(相关宏已经定义了):

    #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
    
  • 利用一个CMD来分析

    U_BOOT_CMD(
    spibootldr, 2, 0, do_spibootldr,
    "boot ldr image from spi",
    "[offset]\n"
    "    - boot ldr image stored at offset into spi\n");
    

    经过上述的分析,此命令可以被解析为:

    --ll_entry_declare(_type, _name, _list)
    ->  cmd_tbl_t _u_boot_list_2_spibootldr_2_spibootldr __aligned(4)		\
    		__attribute__((unused,				\
    		section(".u_boot_list_2_do_spibootldr_2_spibootldr)))
    
    --{ "spibootldr", 2, 0, do_spibootldr," boot ldr image from spi",			\
    		"[offset]\n""- boot ldr image stored at offset into spi\n", 	NULL, }               
    

    ## 连接符:表示后面直接链接

    # 字符串化:表示将传来的参数字符串化。

    所以最后实现的语句如下所示:

          cmd_tbl_t _u_boot_list_2_spibootldr_2_spibootldr __aligned(4)	\ __attribute__((unused, section(".u_boot_list_2_do_spibootldr_2_spibootldr"))) 
                                     = { "spibootldr", 2, 0, do_spibootldr,
                                        " boot ldr image from spi",
                                        "[offset]\n""- boot ldr image stored at offset into spi\n",
                                        NULL, } 
    

​ 4字节对齐定义了一个cmd_tbl_t的结构体变量_u_boot_list_2_spibootldr_2_spibootldr,且被划分到了u_boot_list_2_do_spibootldr_2_spibootldr section。

  • 其中对于cmd_tbl_t 有以下定义:

      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
      };
    
  • section 在内存中的存储位置如下所示:四字节对齐之后存储在rodata 段之后。

       .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
       . = ALIGN(4);
       .data : {
        *(.data*)
       }
       . = ALIGN(4);
       . = .;
       . = ALIGN(4);
       .u_boot_list : {
        KEEP(*(SORT(.u_boot_list*)));
       }
    

注意:这里有一次SORT排序,结合下面的代码,就会发现能够获取板子的整个命令段。函数内部静态定义也会存在全局变量中。

  • #define ll_entry_start(_type, _list)\
    ({\
        static char start[0] __aligned(4) __attribute__((unused,	\
            section(".u_boot_list_2_"#_list"_1")));			\
        (_type *)&start;						\
    })
    #define ll_entry_end(_type, _list)\
    ({									\
        static char end[0] __aligned(4) __attribute__((unused,		\
            section(".u_boot_list_2_"#_list"_3")));			\
        (_type *)&end;							\
    })
    

标签:CMD,uboot,boot,cmd,list,char,bootdelay,main
From: https://www.cnblogs.com/satellite98/p/18329099

相关文章

  • 小白必看的cmd简单代码!(图片看不到的可复制 粘贴到Typroa进行观看)
    打卡cmd的方法直接window加r输入cmd在下方菜单找到window标志,打开输入命令提示符更高级的cmd权限使用:右键命令提示符,点击"以管理员身份运行"一些简单的dos命令(均需英文输入法)(回车步骤省略)1.盘符切换:打开cmd后输入想要切换的磁盘再加上:即可![](C:\Users\直実\Pictures......
  • 智谱GLM Api接口适配langchain OpenAI llamaindex的openAI接口
    动机OpenAI充值比较麻烦,且访问不是那么方便。因此想用国内的api的去调试和测试一个任务。但是很多教程都是以openAI的接口为例子的,因此学习起来就不那么方便。本文参考了hugggingface中迁移OpenAI的博客,chatGLMcookbook关于接口的迁移文档,llamindexOpenAIlike的示例,终于调......
  • Temperatures()函数中用const创建温度转换中使用的变量.在main()函数中使用一个循环让
    /编写一个程序,要求用户输入一个华氏温度。程序应读取double类型的值作为温度值,并把该值作为参数传递该一个用户自定义的函数Temperatures()。该函数计算摄氏温度和开氏温度,并以小数点后面两位数字的精度显示3种温度。要求使用不同的温度标签来表示这3个温度值。下面是华氏温度转......
  • 2023.7.2-3-4Mssql xp_cmdshell提权
    1.概念Mssql和SQLsever的一个产品的不同名称。都属于微软公司旗下。而上述Mssqlxp_cmdshell提权也属于数据库提权的一种。主要依赖于sqlserver自带的存储过程。1.1xp_cmdshell提权扩展存储过程中xp_cmdshell是一个开放接口,可以让sqlsever调用cmd命令。此过程在SQLsever......
  • 【Android驱动05】通过U-Boot来设置启动参数cmdline将数据传递给应用层的方法
    在Android系统中,通过U-Boot(通常称为uboot)来设置启动参数(如cmdline)或尝试直接将数据传递给应用层(通过系统属性property)是一个较为间接的过程,因为U-Boot主要负责硬件初始化和引导Linux内核,而Android系统属性则是由Android系统服务(如init进程和property服务)管理的。不过,我们可......
  • QT mainwindow UI界面添加工具栏
    1.在mainwindowUI设计器界面右上角右键mainwindow 弹出如下菜单图1可以看到添加工具栏,移除状态栏等相关操作都在菜单中2.新建action相关菜单项图2在红框中的ActionEdit中,第一行菜单栏按钮(分别是新建,复制,粘贴,删除,修改)点击以进行创建鼠......
  • 为什么当我在 cmd 中输入 python - -version 时,除了空行之外什么都没有出现?
    我已经下载了python3.9。但是,在通过命令行检查版本时,我确实得到了一个空行而不是版本。我在cmd上使用了以下命令:python--version有几个原因可能会导致在cmd中输入python--version时只出现空行。以下是一些可能的解决方法:1.Python没有添加到......
  • JAVA编译和运行的CMD命令
    JAVA编译和运行的CMD命令编译JAVA程序编译Java程序是将源代码文件(.java)转换为字节码文件(.class)的过程。在CMD中,我们可以使用javac命令来进行编译。命令格式:javac[选项]文件名.java运行JAVA程序编译完成后,你可以使用java命令来运行生成的字节码文件。命令格式:java[选项]......
  • java中的涉及到的cmd命令
    Java中的常见CMD命令在Java中,可以使用以下一些常见的命令行(cmd)命令:java:用于运行Java程序的命令。javac:用于编译Java源代码文件的命令。java-jar:用于创建和管理Java归档文件(JAR文件)的命令javap:用于翻译java字节码文件,可以看到class文件的内部结构不常用的命令:javadoc:用......
  • Windows常用的cmd命令
    在Windows操作系统中,CMD(命令提示符)是一种用于执行命令行操作的工具。以下是一些常用的CMD命令:1.dir -显示当前目录下的文件和文件夹。2.cd -更改当前目录。3.cd.. -返回上一级目录。4.mkdir -创建新目录。5.rmdir -删除空目录。6.del -删除文件。7.d......