1、uboot环境变量结构
- 外部环境变量:外部环境变量是固化在存储介质上的,可断电保存,具体保存在什么介质上,通过开发板定义的CONFIG_ENV_IS_IN_XXX选项来指定,例如nand、flash、mmc等。如果不使用外部环境变量,可定义CONFIG_ENV_IS_NO_WHERE。saveenv命令就是存储在外部环境变量。外部环境变量的前四个字节存储的是crc校验码。
./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
- 内部环境变量:内部环境变量是由default_environment[]存储在uboot的镜像中。
./include/env_default.h
static char default_environment[] = {
#else
const uchar default_environment[] = {
#endif
#ifdef CONFIG_ENV_CALLBACK_LIST_DEFAULT
ENV_CALLBACK_VAR "=" CONFIG_ENV_CALLBACK_LIST_DEFAULT "\0"
#endif
#ifdef CONFIG_ENV_FLAGS_LIST_DEFAULT
ENV_FLAGS_VAR "=" CONFIG_ENV_FLAGS_LIST_DEFAULT "\0"
#endif
#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=" __stringify(CONFIG_BOOTDELAY) "\0"
#endif
#if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)
"baudrate=" __stringify(CONFIG_BAUDRATE) "\0"
#endif
#ifdef CONFIG_LOADS_ECHO
"loads_echo=" __stringify(CONFIG_LOADS_ECHO) "\0"
#endif
#ifdef CONFIG_ETHADDR
"ethaddr=" __stringify(CONFIG_ETHADDR) "\0"
#endif
#ifdef CONFIG_ETH1ADDR
"eth1addr=" __stringify(CONFIG_ETH1ADDR) "\0"
#endif
#ifdef CONFIG_ETH2ADDR
"eth2addr=" __stringify(CONFIG_ETH2ADDR) "\0"
#endif
#ifdef CONFIG_ETH3ADDR
"eth3addr=" __stringify(CONFIG_ETH3ADDR) "\0"
#endif
#ifdef CONFIG_ETH4ADDR
"eth4addr=" __stringify(CONFIG_ETH4ADDR) "\0"
#endif
#ifdef CONFIG_ETH5ADDR
"eth5addr=" __stringify(CONFIG_ETH5ADDR) "\0"
#endif
#ifdef CONFIG_ETHPRIME
"ethprime=" CONFIG_ETHPRIME "\0"
#endif
#ifdef CONFIG_IPADDR
"ipaddr=" __stringify(CONFIG_IPADDR) "\0"
#endif
#ifdef CONFIG_SERVERIP
"serverip=" __stringify(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=" CONFIG_ROOTPATH "\0"
#endif
#ifdef CONFIG_GATEWAYIP
"gatewayip=" __stringify(CONFIG_GATEWAYIP) "\0"
#endif
#ifdef CONFIG_NETMASK
"netmask=" __stringify(CONFIG_NETMASK) "\0"
#endif
#ifdef CONFIG_HOSTNAME
"hostname=" __stringify(CONFIG_HOSTNAME) "\0"
#endif
#ifdef CONFIG_BOOTFILE
"bootfile=" CONFIG_BOOTFILE "\0"
#endif
#ifdef CONFIG_LOADADDR
"loadaddr=" __stringify(CONFIG_LOADADDR) "\0"
#endif
#ifdef CONFIG_CLOCKS_IN_MHZ
"clocks_in_mhz=1\0"
#endif
#if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)
"pcidelay=" __stringify(CONFIG_PCI_BOOTDELAY)"\0"
#endif
#ifdef CONFIG_ENV_VARS_UBOOT_CONFIG
"arch=" CONFIG_SYS_ARCH "\0"
"cpu=" CONFIG_SYS_CPU "\0"
"board=" CONFIG_SYS_BOARD "\0"
"board_name=" CONFIG_SYS_BOARD "\0"
#ifdef CONFIG_SYS_VENDOR
"vendor=" CONFIG_SYS_VENDOR "\0"
#endif
#ifdef CONFIG_SYS_SOC
"soc=" CONFIG_SYS_SOC "\0"
#endif
#endif
#ifdef CONFIG_EXTRA_ENV_SETTINGS
CONFIG_EXTRA_ENV_SETTINGS
#endif
"\0"
#ifdef DEFAULT_ENV_INSTANCE_EMBEDDED
}
#endif
};
2、环境变量初始化
- 环境变量初始化过程主要涉及两个函数:board_init_f和board_init_r。
1、board_init_r
- 在board_init_f中调用 env_init,根据CONFIG_ENV_IS_IN_XXX,特定的./commom/env_xxx.c文件就会被编译,之后会调用特定.c中的env_init函数。对于env_flash.c与env_mmc.c中的env_init还不是完全一样。
./common/env_flash.c
int env_init(void)
{
if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) {
gd->env_addr = (ulong)&(env_ptr->data);
gd->env_valid = 1;
return 0;
}
gd->env_addr = (ulong)&default_environment[0];
gd->env_valid = 0;
return 0;
}
./common/env_mmc.c
int env_init(void)
{
/* use default */
gd->env_addr = (ulong)&default_environment[0];
gd->env_valid = 1;
return 0;
}
- 对于flash中env_init函数会进行外部环境变量的检测,通过env_ptr直接指向存储介质上的外部环境变量区域来访问,计算env_ptr->data的crc和env_ptr->crc是否相等,验证外部环境变量是否有效。若有效,设置gd使用外部环境变量。若无效,使用内部环境变量。
- 对于mmc,mmc需要初始化接口后才能访问,因此此时无法访问外部环境变量,因此直接使用内部环境变量。
- 未理解的是为什么flash使用内部环境变量时gd->env_valid = 0,而mmc使用内部环境变量时gd->valid = 1。
2、board_init_r
- 在board_init_r中调用initr_env。
./common/board_r.c
static int initr_env(void)
{
/* initialize environment */
if (should_load_env())
env_relocate();
else
set_default_env(NULL);
/* Initialize from environment */
load_addr = getenv_ulong("loadaddr", 16, load_addr);
return 0;
}
./common/env_common.c
void env_relocate(void)
{
#if defined(CONFIG_NEEDS_MANUAL_RELOC)
env_reloc();
env_htab.change_ok += gd->reloc_off;
#endif
if (gd->env_valid == 0) {
#if defined(CONFIG_ENV_IS_NOWHERE) || defined(CONFIG_SPL_BUILD)
/* Environment not changable */
set_default_env(NULL);
#else
bootstage_error(BOOTSTAGE_ID_NET_CHECKSUM);
set_default_env("!bad CRC");
#endif
} else {
env_relocate_spec();
}
}
- gd->env_valid在board_f阶段中的函数env_init调用中被赋值为1,这里将接着执行env_relocate_spec。env_relocate_spec针对不同的环境变量存储设备有多处实现,这里使用CONFIG_ENV_IS_IN_MMC宏,所以使用common/env_mmc.c中的定义(参见common/Makefile),由于没有定义CONFIG_ENV_OFFSET_REDUND,则
./common/env_mmc.c
void env_relocate_spec(void)
{
#if !defined(ENV_IS_EMBEDDED)
ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
struct mmc *mmc;
u32 offset;
int ret;
int dev = mmc_get_env_devno();
#ifdef CONFIG_SPL_BUILD
dev = 0;
#endif
mmc = find_mmc_device(dev);
if (init_mmc_for_env(mmc)) {
ret = 1;
goto err;
}
if (mmc_get_env_addr(mmc, 0, &offset)) {
ret = 1;
goto fini;
}
if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) {
ret = 1;
goto fini;
}
env_import(buf, 1);
ret = 0;
fini:
fini_mmc_for_env(mmc);
err:
if (ret)
set_default_env(NULL);
#endif
}
- 如果从mmc中读取环境变量成功,则调用函数env_import,该函数将对读取到的环境变量进行CRC校验,如校验失败,会执行set_default_env。否则接着调用himport_r执行hash表的初始化。
set_default_env函数用来配置默认的的环境变量。它在common/env_common.c中实现:
void set_default_env(const char *s)
{
int flags = 0;
if (sizeof(default_environment) > ENV_SIZE) {
puts("*** Error - default environment is too large\n\n");
return;
}
if (s) {
if (*s == '!') {
printf("*** Warning - %s, "
"using default environment\n\n",
s + 1);
} else {
flags = H_INTERACTIVE;
puts(s);
}
} else {
puts("Using default environment\n\n");
}
if (himport_r(&env_htab, (char *)default_environment,
sizeof(default_environment), '\0', flags, 0,
0, NULL) == 0)
error("Environment import failed: errno = %d\n", errno);
gd->flags |= GD_FLG_ENV_READY;
}

- 无论是使用默认的环境变量,还是使用从flash、mmc读取到的有效环境变量配置,完成环境变量的读取后,都将调用himport_r将获取到的环境变量导入到hash表中。