1. 程式人生 > >[IMX6Q]u-boot環境變數原理分析

[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;
}