uboot被加载到内存中运行后,在启动过程的第二阶段,uboot会去flash中将环境变量复制到ddr中,如果uboot中的环境变量校验不通过,则使用默认的环境变量值初始化环境变量,如果用户从命令终端修改了环境变量的值,那么需要使用saveenv命令才会将环境变量同步到flash中保存,因为在命令终端运行时,uboot是运行在内存中,修改的是DDR内存中的环境变量。本文也就是分析uboot是如何实现这些功能的。
uboot启动分析可以查看博客《uboot启动第二阶段start_armboot函数分析》,其它的Uboot内容可以参考博客《序言与启动》
环境变量初始化接口env_relocate是定义在./u-boot-2010.06/common/env_common.c
void env_relocate (void)
{
#ifndef CONFIG_RELOC_FIXUP_WORKS
DEBUGF ("%s[%d] offset = 0x%lx\n", __FUNCTION__,__LINE__,
gd->reloc_off);
#endif
#ifdef CONFIG_AMIGAONEG3SE
enable_nvram();
#endif
#ifdef ENV_IS_EMBEDDED
/*
* The environment buffer is embedded with the text segment,
* just relocate the environment pointer
*/
#ifndef CONFIG_RELOC_FIXUP_WORKS
env_ptr = (env_t *)((ulong)env_ptr + gd->reloc_off);
#endif
DEBUGF ("%s[%d] embedded ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
#else
/*
* We must allocate a buffer for the environment
*/
env_ptr = (env_t *)malloc (CONFIG_ENV_SIZE); //①
DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
#endif
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 (); //③
}
gd->env_addr = (ulong)&(env_ptr->data); //④
#ifdef CONFIG_AMIGAONEG3SE
disable_nvram();
#endif
}
(A)分配堆空间
- CONFIG_RELOC_FIXUP_WORKS,CONFIG_AMIGAONEG3SE,ENV_IS_EMBEDDED在海思hi3521a中都没有定义,所以进来后就直接运行到了①;
- ①在3521a中CONFIG_ENV_SIZE=0x40000(256KB),定义在./u-boot-2010.06/include/configs/hi3521a.h中,这里是在堆中分配256K的空间来存放环境变量
- ②gd->env_valid 这个全局变量在进行spi flash 初始化的时候默认将它强制赋值为1,./u-boot-2010.06/common/env_sf.c中的sf_env_init函数
int sf_env_init(void)
{
/* SPI flash isn't usable before relocation */
gd->env_addr = (ulong)&default_environment[0];
gd->env_valid = 1;
return 0;
}
(B)环境变量重定向
- ③这里它会去读取flash中的环境变量,然后对它的数据进行有效性分析。对于hi3521a设备,它是在./u-boot-2010.06/common/env_sf.c的sf_env_relocate_spec函数中实现。
void sf_env_relocate_spec(void)
{
int ret;
env_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS,
CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE);
if (!env_flash)
goto err_probe;
ret = spi_flash_read(env_flash, CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, env_ptr); //(1)
if (ret)
goto err_read;
if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc) //(2)
goto err_crc;
gd->env_valid = 1; //(3)
return;
err_read:
spi_flash_free(env_flash);
env_flash = NULL;
err_probe:
err_crc:
puts("*** Warning - bad CRC, using default environment\n\n");
set_default_env();
}
- (1)这个是从flash中的CONFIG_ENV_OFFSET这个位置读取CONFIG_ENV_SIZE大小的值到env_ptr这个指针指向的地址。在海思hi3521a中CONFIG_ENV_OFFSET=0x80000=512KB,CONFIG_ENV_SIZE=0x40000=256KB。
- (2)这里我们看env_t这个结构体
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;
(C)默认环境变量
- 环境变量的开始4字节是CRC校验码,在(2)这里是对环境变量进行CRC32计算并且跟环境变量中保存的CRC做对比,如果不相等,表示flash中没有环境变量或是环境变量数据异常,然后去设置默认的环境变量。默认环境变量定义如下
uchar default_environment[] = {
#ifdef CONFIG_BOOTARGS
"bootargs=" CONFIG_BOOTARGS "\0"
#endif
#ifdef CONFIG_SLAVE_BOOTARGS
"slave_bootargs=" CONFIG_SLAVE_BOOTARGS "\0"
#endif
#ifdef CONFIG_BOOTCOMMAND
"bootcmd=" CONFIG_BOOTCOMMAND "\0"
#endif
#ifdef CONFIG_SLAVE_BOOTCMD
"slave_bootcmd=" CONFIG_SLAVE_BOOTCMD "\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_USE_MDIO
"use_mdio=" CONFIG_USE_MDIO "\0"
#endif
#ifdef CONFIG_MDIO_INTF
"mdio_intf=" CONFIG_MDIO_INTF "\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_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"
};
如果要添加默认环境变量,也可以直接修改这里。
- (3)如果在(2)中判断flash中读取出来的环境变量校验正确,那么就将gd->env_valid的值初始化为1,表示flash中的环境变量是有效的。
- ④将环境变量的地址赋值给Uboot运行的一个全局变量,以便其它地方的使用。
uboot 中的其它相关内容可以参考博客《序言与目录》