[IMX6Q]u-boot环境变量原理分析


u-boot版本: v2009.08

一些重要参数如串口波特率,bootmcmd,loadaddr等参数,可能需要
动态修改调试,u-boot提供了环境变量env用于保存这些信息到永久性存储
介质如SD或者RAM中。RAM会丢失,而存于SD则下次开机依然存在。
u-boot命令列表中,提供了setenv, saveenv两个命令,前者用于临时保存到
RAM中,后者会保存到SD中。

几个概念:
a. 存储介质

对于不同的存储介质,它的读写操作方式也不同,本例使用SD。

uboot-imx/include/configs/mx6q_sabresd.h

#define CONFIG_FSL_ENV_IN_MMC
uboot-imx/common/Makefile

COBJS-$(CONFIG_ENV_IS_IN_MMC) += env_mmc.o
所以本例对应的是env_mmc.c

b. 事实上env即可在开机后从RAM中中分配一个数据结构临时保存,也可以直接将env放在u-boot的text段从而

节省RAM空间,本例描述的是前者,后者后面文档会描述如何实现。

c. environment_s结构

uboot-imx/include/environment.h

typedef	struct environment_s {
	uint32_t	crc;		/* CRC32 over data bytes	*/
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
	unsigned char	flags;		/* active/obsolete flags	*/
#endif
	unsigned char	data[ENV_SIZE]; /* Environment data		*/
} env_t;
crc: 读取环境变量时先会校验,其中当第一次读取的时候,因为flash没有环境变量保存,所以会失败,这是正常的。
data: 存放环境变量。

d. 全局数据访问env
typedef	struct	global_data {
	bd_t		*bd;
	unsigned long	flags;
	unsigned long	baudrate;
	unsigned long	have_console;	/* serial_init() was called */
	unsigned long	reloc_off;	/* Relocation Offset */
	unsigned long	env_addr;	/* Address  of Environment struct */
	unsigned long	env_valid;	/* Checksum of Environment valid? */
	unsigned long	fb_base;	/* base address of frame buffer */
......
} gd_t;

reloc_off: env一开始是保存在sd中的,开机之后为了快速读取,它会被重定位到RAM中。

env_add: 上面env_t的地址,一开始是指向SD中的,后来是指向RAM中的拷贝数据。

env_valid: 如果crc检验成功,则此变量为1.


流程分析:

u-boot和env相关的启动流程有:

start_armboot -> init_sequence -> env_init -> env_relocate

env_init:

uboot-imx/common/env_mmc.c

int env_init(void)
{
	/* use default */
	/*初始化为默认值以及env有效*/
	gd->env_addr = (ulong)&default_environment[0];
	gd->env_valid = 1;

#ifdef CONFIG_DYNAMIC_MMC_DEVNO
	/*读取当前使用sd的mmc number*/
	extern int get_mmc_env_devno(void);
	mmc_env_devno = get_mmc_env_devno();
#else
	mmc_env_devno = CONFIG_SYS_MMC_ENV_DEV;
#endif

	return 0;
}
default_environment:

uboot-imx/common/env_common.c

uchar default_environment[] = {
#ifdef	CONFIG_BOOTARGS
	"bootargs="	CONFIG_BOOTARGS			"\0"
#endif
#ifdef	CONFIG_BOOTCOMMAND
	"bootcmd="	CONFIG_BOOTCOMMAND		"\0"
#endif
#ifdef	CONFIG_RAMBOOTCOMMAND
	"ramboot="	CONFIG_RAMBOOTCOMMAND		"\0"
#endif
#ifdef	CONFIG_NFSBOOTCOMMAND
	"nfsboot="	CONFIG_NFSBOOTCOMMAND		"\0"
#endif
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
	"bootdelay="	MK_STR(CONFIG_BOOTDELAY)	"\0"
#endif
#if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)
	"baudrate="	MK_STR(CONFIG_BAUDRATE)		"\0"
#endif
#ifdef	CONFIG_LOADS_ECHO
	"loads_echo="	MK_STR(CONFIG_LOADS_ECHO)	"\0"
#endif
#ifdef	CONFIG_ETHADDR
	"ethaddr="	MK_STR(CONFIG_ETHADDR)		"\0"
#endif
#ifdef	CONFIG_ETH1ADDR
	"eth1addr="	MK_STR(CONFIG_ETH1ADDR)		"\0"
#endif
#ifdef	CONFIG_ETH2ADDR
	"eth2addr="	MK_STR(CONFIG_ETH2ADDR)		"\0"
#endif
#ifdef	CONFIG_ETH3ADDR
	"eth3addr="	MK_STR(CONFIG_ETH3ADDR)		"\0"
#endif
#ifdef	CONFIG_ETH4ADDR
	"eth4addr="	MK_STR(CONFIG_ETH4ADDR)		"\0"
#endif
#ifdef	CONFIG_ETH5ADDR
	"eth5addr="	MK_STR(CONFIG_ETH5ADDR)		"\0"
#endif
#ifdef	CONFIG_IPADDR
	"ipaddr="	MK_STR(CONFIG_IPADDR)		"\0"
#endif
#ifdef	CONFIG_SERVERIP
	"serverip="	MK_STR(CONFIG_SERVERIP)		"\0"
#endif
#ifdef	CONFIG_SYS_AUTOLOAD
	"autoload="	CONFIG_SYS_AUTOLOAD			"\0"
#endif
#ifdef	CONFIG_PREBOOT
	"preboot="	CONFIG_PREBOOT			"\0"
#endif
#ifdef	CONFIG_ROOTPATH
	"rootpath="	MK_STR(CONFIG_ROOTPATH)		"\0"
#endif
#ifdef	CONFIG_GATEWAYIP
	"gatewayip="	MK_STR(CONFIG_GATEWAYIP)	"\0"
#endif
#ifdef	CONFIG_NETMASK
	"netmask="	MK_STR(CONFIG_NETMASK)		"\0"
#endif
#ifdef	CONFIG_HOSTNAME
	"hostname="	MK_STR(CONFIG_HOSTNAME)		"\0"
#endif
#ifdef	CONFIG_BOOTFILE
	"bootfile="	MK_STR(CONFIG_BOOTFILE)		"\0"
#endif
#ifdef	CONFIG_LOADADDR
	"loadaddr="	MK_STR(CONFIG_LOADADDR)		"\0"
#endif
#ifdef CONFIG_RD_LOADADDR
	"rd_loadaddr="  MK_STR(CONFIG_RD_LOADADDR)	"\0"
#endif
#ifdef  CONFIG_CLOCKS_IN_MHZ
	"clocks_in_mhz=1\0"
#endif
#if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)
	"pcidelay="	MK_STR(CONFIG_PCI_BOOTDELAY)	"\0"
#endif
#ifdef  CONFIG_EXTRA_ENV_SETTINGS
	CONFIG_EXTRA_ENV_SETTINGS
#endif
	"\0"
};
本例定义了:
CONFIG_BOOTDELAY,CONFIG_BAUDRATE, CONFIG_LOADADDR, CONFIG_RD_LOADADDR, CONFIG_EXTRA_ENV_SETTINGS。

env_relocate

uboot-imx/common/env_common.c

void env_relocate (void)
{
......
/*未定义*/
#ifdef ENV_IS_EMBEDDED
	/*
	 * The environment buffer is embedded with the text segment,
	 * just relocate the environment pointer
	 */
	env_ptr = (env_t *)((ulong)env_ptr + gd->reloc_off);
	DEBUGF ("%s[%d] embedded ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
#else
	/*
	 * We must allocate a buffer for the environment
	 */
	/*从RAM中新分配一个struct env_t*/
	env_ptr = (env_t *)malloc (CONFIG_ENV_SIZE);
	DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
#endif
	/*env_init()初始化为1了*/
	if (gd->env_valid == 0) {
#if defined(CONFIG_GTH)	|| defined(CONFIG_ENV_IS_NOWHERE)	/* Environment not changable */
		puts ("Using default environment\n\n");
#else
		puts ("*** Warning - bad CRC, using default environment\n\n");
		show_boot_progress (-60);
#endif
		set_default_env();
	}
	else {
		env_relocate_spec ();
	}
	/*替换掉默认env,重新指向从SD读取的新env*/
	gd->env_addr = (ulong)&(env_ptr->data);
......
}

env_relocate_spec

void env_relocate_spec(void)
{
#if !defined(ENV_IS_EMBEDDED)
	/*获取sd信息*/
	struct mmc *mmc = find_mmc_device(mmc_env_devno);
	/*初始化sd*/
	if (init_mmc_for_env(mmc))
		return;
	/*从sd的CONFIG_ENV_OFFSET处读取CONFIG_ENV_SIZE大小数据,也就是读取env*/
	if (read_env(mmc, CONFIG_ENV_SIZE, CONFIG_ENV_OFFSET, env_ptr))
		return use_default();
	/*读取的env进行crc校验,由于第一次开机没有env保存到SD,所以会失败。*/
	if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc)
		return use_default();

	gd->env_valid = 1;
#endif
}
#if !defined(ENV_IS_EMBEDDED)
static void use_default()
{
	puts ("*** Warning - bad CRC or MMC, using default environment\n\n");
	set_default_env();
}
#endif
void set_default_env(void)
{
	if (sizeof(default_environment) > ENV_SIZE) {
		puts ("*** Error - default environment is too large\n\n");
		return;
	}

	memset(env_ptr, 0, sizeof(env_t));
	/*使用默认的env*/
	memcpy(env_ptr->data, default_environment,
	       sizeof(default_environment));
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
	env_ptr->flags = 0xFF;
#endif
	/*再做crc校验*/
	env_crc_update ();
	/*当前env有效*/
	gd->env_valid = 1;
}

因此开机之后,env是使用default_environment里的参数,而它又是临时保存在RAM中的。


setenv和saveenv命令:

两者对应的函数分别是:do_setenv()和saveenv().

uboot-imx/common/env_nvedit.c

setenv:

U_BOOT_CMD(
	setenv, CONFIG_SYS_MAXARGS, 0,	do_setenv,
	"set environment variables",
	"name value ...\n"
	"    - set environment variable 'name' to 'value ...'\n"
	"setenv name\n"
	"    - delete environment variable 'name'"
);

do_setenv -> _do_setenv  -> env_get_addr

uchar *env_get_addr (int index)
{
	/*env 有效*/
	if (gd->env_valid) {
		/*返回之前保存在内存中env地址*/
		return ( ((uchar *)(gd->env_addr + index)) );
	} else {
		return (&default_environment[index]);
	}
}
saveenv:

U_BOOT_CMD(
	saveenv, 1, 0,	do_saveenv,
	"save environment variables to persistent storage",
	""
);
do_saveenv -> saveenv

int saveenv(void)
{
	struct mmc *mmc = find_mmc_device(mmc_env_devno);

	if (init_mmc_for_env(mmc))
		return 1;

	printf("Writing to MMC(%d)... ", mmc_env_devno);
	/*写到SD中永久保存*/
	if (write_env(mmc, CONFIG_ENV_SIZE, CONFIG_ENV_OFFSET, env_ptr)) {
		puts("failed\n");
		return 1;
	}

	puts("done\n");
	return 0;
}





你可能感兴趣的:([IMX6Q]u-boot环境变量原理分析)