栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

uboot对环境变量和命令行的分析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

uboot对环境变量和命令行的分析

在了解了uboot的启动流程(uboot启动流程分析)之后,我们详细得来看uboot的环境变量在内存的组织方式和命令行是如何被执行的。

环境变量


我们知道在uboot的命令行输入pri命令可以打印出uboot的环境变量信息,这些环境变量是如何存储的?已及是怎么设置的?在内存中的组织方式是怎么样的?

是我们这篇要研究明白的。

环境变量是如何存储的?

在uboot 第一次烧入进开发板时,初始那些默认的环境变量是整合进uboot 程序里面的(存放在default_environment变量中)。

我们在命令行使用save命令后,会将环境变量保存进我们划定环境变量的存储区,这个存储区位于Flash中。(可以指定为Nor Flash或者是Nand Flash)

以s3c2440芯片的uboot为例,我们在配置文件中指定了内存区域的划分:

MTDPARTS_DEFAULT这个宏就指定了我们要划分的区域,我们在烧写uboot的使用就是按照这个划分进行烧写的。

实际上MTDPARTS_DEFAULT并没有真正指定区域的划分,只是定义了每个区域的名称,方便我们进行操作。而这里的划分,和我们用于实际划分Flash的宏定义是一样的,为了方便说明,我们直接用mtdparts来代替实际的各种宏定义。

Flash中紧接着在uboot区域后面的128K的存储空间被我们拿来存储环境变量。
如下图就是我们的jz2440开发板上的Nand Flash存储空间划分:

环境变量在内存中的组织方式 使用存储区中的环境变量

上面我们了解到了环境变量存储的位置。在uboot中通过一个env_t类型来管理环境变量的在内存的表示(由env_t这个数据结构来管理全部的环境变量所占的存储空间)。该类型定义如下:

typedef struct environment_s {
	uint32_t	crc;		
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
	unsigned char	flags;		
#endif
	unsigned char	data[ENV_SIZE]; 
} env_t;
#ifdef ENV_IS_EMBEDDED
extern env_t environment;
#endif 
  • crc 和flags 组成ENV_HEADER,环境变量的头部信息;
  • 而余下的环境变量信息全部存储在data空间中;

环境变量的存储空间由两部分组成头部(CRC、flags)和负载(data)。负载包含了所有环境变量数据,而头部由CRC校验码和一个flags参数组成。这两个部分会一起被写入到Flash中。
在env_init()函数中会将默认的环境变量(default_environment)的data存储在全局变量gd中:

env_t *env_ptr = &environment;
...
gd->env_addr = (ulong)env_ptr->data;
...

在我们调用save命令时,会使用相关的write函数将环境变量写到Flash中:

int saveenv(void)
{
	...
	puts("Erasing Nand...n");
	if (nand_erase_opts(&nand_info[0], &nand_erase_options))
		return 1;
	puts("Writing to Nand... ");
	if (writeenv(CONFIG_ENV_OFFSET, (u_char *)&env_new)) {
		puts("FAILED!n");
		return 1;
	}
	...
}

如上面对Nand Flash的操作中,先调用nand_erase_opts擦除一个block,然后再调用writeenv将环境变量写入到这个block中。这也就是为什么我们的环境变量存储区刚好为128K,其正好是我们使用的Nand Flash芯片的block大小。不同的芯片,其block的大小不同,环境变量的区域也不一定相同。

使用默认的环境变量

或者在uboot刚烧录进开发板时,这时候一般情况我们的Flash中还没有写入环境变量的相关信息。这时就需要使用默认的环境变量,也就是default_environment,这个变量定义在uboot内部,位于uboot的数据段中:

gd->env_addr	= (ulong)&default_environment[0];
gd->env_valid	= 1;

default_environment的定义如下方,在编译时期会根据宏开关,对default_environment数组进行填充:

const uchar default_environment[] = {
#ifdef	CONFIG_BOOTARGS
	"bootargs="	CONFIG_BOOTARGS			""
#endif
#ifdef	CONFIG_BOOTCOMMAND
	"bootcmd="	CONFIG_BOOTCOMMAND		""
#endif
#ifdef	CONFIG_RAMBOOTCOMMAND
	"ramboot="	CONFIG_RAMBOOTCOMMAND		""
#endif
#ifdef	CONFIG_NFSBOOTCOMMAND
	"nfsboot="	CONFIG_NFSBOOTCOMMAND		""
#endif
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
	"bootdelay="	MK_STR(CONFIG_BOOTDELAY)	""
#endif
#if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)
	"baudrate="	MK_STR(CONFIG_BAUDRATE)		""
#endif
#ifdef	CONFIG_LOADS_ECHO
	"loads_echo="	MK_STR(CONFIG_LOADS_ECHO)	""
#endif
#ifdef	CONFIG_ETHADDR
	"ethaddr="	MK_STR(CONFIG_ETHADDR)		""
#endif
#ifdef	CONFIG_ETH1ADDR
	"eth1addr="	MK_STR(CONFIG_ETH1ADDR)		""
#endif
#ifdef	CONFIG_ETH2ADDR
	"eth2addr="	MK_STR(CONFIG_ETH2ADDR)		""
#endif
#ifdef	CONFIG_ETH3ADDR
	"eth3addr="	MK_STR(CONFIG_ETH3ADDR)		""
#endif
#ifdef	CONFIG_ETH4ADDR
	"eth4addr="	MK_STR(CONFIG_ETH4ADDR)		""
#endif
#ifdef	CONFIG_ETH5ADDR
	"eth5addr="	MK_STR(CONFIG_ETH5ADDR)		""
#endif
#ifdef	CONFIG_IPADDR
	"ipaddr="	MK_STR(CONFIG_IPADDR)		""
#endif
#ifdef	CONFIG_SERVERIP
	"serverip="	MK_STR(CONFIG_SERVERIP)		""
#endif
#ifdef	CONFIG_SYS_AUTOLOAD
	"autoload="	CONFIG_SYS_AUTOLOAD		""
#endif
#ifdef	CONFIG_PREBOOT
	"preboot="	CONFIG_PREBOOT			""
#endif
#ifdef	CONFIG_ROOTPATH
	"rootpath="	CONFIG_ROOTPATH			""
#endif
#ifdef	CONFIG_GATEWAYIP
	"gatewayip="	MK_STR(CONFIG_GATEWAYIP)	""
#endif
#ifdef	CONFIG_NETMASK
	"netmask="	MK_STR(CONFIG_NETMASK)		""
#endif
#ifdef	CONFIG_HOSTNAME
	"hostname="	MK_STR(CONFIG_HOSTNAME)		""
#endif
#ifdef	CONFIG_BOOTFILE
	"bootfile="	CONFIG_BOOTFILE			""
#endif
#ifdef	CONFIG_LOADADDR
	"loadaddr="	MK_STR(CONFIG_LOADADDR)		""
#endif
#ifdef	CONFIG_CLOCKS_IN_MHZ
	"clocks_in_mhz=1"
#endif
#if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)
	"pcidelay="	MK_STR(CONFIG_PCI_BOOTDELAY)	""
#endif
#ifdef	CONFIG_EXTRA_ENV_SETTINGS
	CONFIG_EXTRA_ENV_SETTINGS
#endif
	""
};
环境变量的设置流程

其设置的流程图如下:

环境变量的设置分为两个阶段进行设置,在第一阶段也就是board_init_f阶段,会调用env_init()函数对环境变量进行初始化设置。这个阶段只能完成存储在Nor Flash的环境变量的读取操作。而Nand Flash的环境变量,由于此时Nand Flash还没有初始化(一般在第二阶段进行Nand Flash的初始化),因此读不出存储于Flash中的环境变量。

在uboot启动流程的第二阶段,也就是在board_init_r函数内,会调用env_relocate()函数,对Nand Flash该函数才真正完成了初始化的工作。env_relocate会调用himport_r函数将gd->env_addr也就是env_t中的data 填充到env_htab这个环境变量的哈希表中。

env_htab:是uboot用于存储环境变量的一张表格,其定义如下:

struct hsearch_data {
	struct _ENTRY *table;	
	unsigned int size;		
	unsigned int filled;	
};

typedef struct _ENTRY {
	int used;
	ENTRY entry;
} _ENTRY;

typedef struct entry {
	const char *key;		
	char *data;	
} ENTRY;

对于env_htab设置的思想如下:

  1. 首先会尝试从Flash(存储区)里读取出环境变量的信息(env_t),根据CRC校验,如果校验成功就会用存储区中的env_t设置env_htab;
  2. 如果校验失败就会使用uboot里面自带的default_environment来设置env_htab;

这么做很巧妙,如果我们第一次将uboot 烧录到开发板,那么正常情况下Flash中的校验码是不正确的,这时就会使用默认的default_environment设置我们的uboot环境变量。而当后面我们调用save将环境写入到Flash后,下次重启开发板时,重新读取Flash,这时CRC 校验就会是正确的,那么就会使用保存在Flash的env_t。
而我们后续对环境变量的修改都会被写入到Flash中,这样就实现了动态修改环境变量的功能。

命令行分析

在board.c 的第二阶段(board_init_r函数)最后会调用main_loop() 进入一个主循环。

这个主循环主要的工作有下面两点:

  1. 进入引导程序的倒计时,若在倒计时内没有按下任何键,则会自动引导启动内核;
  2. 如果按下了按键,则会进入uboot界面,输出命令行提示符,等待用户输入命令,然后根据命令进行一些列操作;


详细的流程如上图所示,main_loop()会调用abortboot()函数,在abortboot()中实现倒计时的功能,若在倒计时期间,有按键按下:

  • 有按键按下,函数返回值为“1”,这时就会进入左边的分支。调用readline()等待用户输入,用户输入的命令会被run_command()函数进行解析,然后调用相应的接口进行执行。
  • 无按键按下,函数返回值为“0”,这时就会进入右边的分支。调用getenv(“bootm”),获取启动内核的相关含操作,然后调用run_command运行bootm命令,由bootm启动内核;


如上图所示,在配置文件中我们定义了bootm的启动命令,宏CONFIG_BOOTCOMMAND;其中CONFIG_BOOTARGS 是传递给内核的参数,ttySAC0表示,使用的输出串口,root=/dev/mtdblock3 表示根文件系统所在的块。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1013381.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号