一、概述( ENV_IS_EMBEDDED的目的)
经典资料
认识
ENV_IS_EMBEDDED只有在CFG_ENV_IS_IN_FLASH或者CFG_ENV_IS_IN_NAND定义了才有效。也就是说,这种功能只有在使用了norflash或者nandflash时才有效。本文是以使用norflash为例来说明的。
上边贴的几张图片讲的内容主要是这个意思:由于uboot.bin是100多kb,而环境变量要占一个扇区。norflash前100Kb中才有空间比较小的扇区,例如第二个扇区才8Kb,而后边的扇区比较大,都是64Kb。程序运行后,通常需要将环境变量拷贝到RAM中,这样访问速度才快,因此RAM中也需要为环境变量区占用和flash中一样大的空间。
倘若,不使用 ENV_IS_EMBEDDED这种功能,将环境变量区放在所有代码段的最后边,环境变量区只能占用64Kb的扇区,而环境变量区实际上有可能不需要那么大的空间,就造成了flash和RAM空间的浪费。
倘若使用ENV_IS_EMBEDDED功能,将环境变量区嵌入到代码段中,例如占用第二个扇区的8Kb(环境变量区的前后都有代码,故曰"嵌入环境变量"),就可以节省lash和RAM空间。
此外,这种功能的好处不止此。重定位时,将代码拷贝到RAM中了,顺便也将环境变量区拷贝了进来,这样环境变量初始化过程中,就不需要在堆区开辟空间然后搬移,省了不少事。
二、程序分析(怎样实现这个目的的)
1、定义(include/environment.h)
#if defined(CFG_ENV_IS_IN_FLASH)
some other define
# if (CFG_ENV_ADDR >= CFG_MONITOR_BASE) && \
(CFG_ENV_ADDR+CFG_ENV_SIZE) <= (CFG_MONITOR_BASE + CFG_MONITOR_LEN)
# define ENV_IS_EMBEDDED 1
# endif
# if defined(CFG_ENV_ADDR_REDUND) || defined(CFG_ENV_OFFSET_REDUND)
# define CFG_REDUNDAND_ENVIRONMENT 1
# endif
#endif /* CFG_ENV_IS_IN_FLASH */
只有定义了CFG_ENV_IS_IN_FLASH,并且满足下边的条件才有可能定义这个宏,这个宏是在smdk2410.h中定义的。
# if (CFG_ENV_ADDR >= CFG_MONITOR_BASE) && \ (CFG_ENV_ADDR+CFG_ENV_SIZE) <= (CFG_MONITOR_BASE + CFG_MONITOR_LEN)
2、与ENV_IS_EMBEDDED相关的全局变量定义(common/environment.c)
#if defined(ENV_IS_EMBEDDED)
/* XXX - This only works with GNU C */
# define __PPCENV__ __attribute__ ((section(".text"))) //强制将environment转换为代码区
# define __PPCTEXT__ __attribute__ ((section(".text")))
/*
* Macros to generate global absolutes.
*/
#define GEN_SYMNAME(str) SYM_CHAR #str
#define GEN_VALUE(str) #str
#define GEN_ABS(name, value) \
asm (".globl " GEN_SYMNAME(name)); \
asm (GEN_SYMNAME(name) " = " GEN_VALUE(value))
/*
* Macros to transform values
* into environment strings.
*/
#define XMK_STR(x) #x
#define MK_STR(x) XMK_STR(x)
/*
* Check to see if we are building with a
* computed CRC. Otherwise define it as ~0.
*/
#if !defined(ENV_CRC)
# define ENV_CRC ~0
#endif
env_t environment __PPCENV__ = {
ENV_CRC, /* CRC Sum */
#ifdef CFG_REDUNDAND_ENVIRONMENT
1, /* Flags: valid */
#endif
{
#if defined(CONFIG_BOOTARGS)
"bootargs=" CONFIG_BOOTARGS "\0"
#endif
other defines
#ifdef CONFIG_EXTRA_ENV_SETTINGS
CONFIG_EXTRA_ENV_SETTINGS
#endif
"\0" /* Term. env_t.data with 2 NULs */
}
};
#ifdef CFG_ENV_ADDR_REDUND
env_t redundand_environment __PPCENV__ = {
0, /* CRC Sum: invalid */
0, /* Flags: invalid */
{
"\0"
}
};
#endif /* CFG_ENV_ADDR_REDUND */
/*
* These will end up in the .text section
* if the environment strings are embedded
* in the image. When this is used for
* tools/envcrc, they are placed in the
* .data/.sdata section.
*
*/
unsigned long env_size __PPCTEXT__ = sizeof(env_t);
/*
* Add in absolutes.
*/
GEN_ABS(env_offset, CFG_ENV_OFFSET);
#endif /* ENV_IS_EMBEDDED */
这部分程序就是建立嵌入环境变量区,并强制性转换为代码区。这个环境变量区是完整的,包含头部crc。嵌入环境变量区一开始也在程序中初始化了(用常数初始化),我们知道crc是环境变量(data)的校验码,也不知道编译器是怎么实现在编译的时候直接根据环境变量data区实现crc的赋值(有初始化了data区生成的crc)。
3、 ENV_IS_EMBEDDED所涉及的在(common/env_flash.c)中的定义
#ifdef ENV_IS_EMBEDDED extern uchar environment[]; env_t *env_ptr = (env_t *)(&environment[0]); //倘若定义了ENV_IS_EMBEDDED,那么指向嵌入环境区 #ifdef CMD_SAVEENV //如果使用了保存环境变量命令 /* static env_t *flash_addr = (env_t *)(&environment[0]);-broken on ARM-wd-*/ static env_t *flash_addr = (env_t *)CFG_ENV_ADDR; //保存环境变量区时的下载地址 #endif #else /* ! ENV_IS_EMBEDDED */ env_t *env_ptr = (env_t *)CFG_ENV_ADDR; // 定义flash中环境变量的地址 #ifdef CMD_SAVEENV static env_t *flash_addr = (env_t *)CFG_ENV_ADDR; #endif #endif /* ENV_IS_EMBEDDED */
4、env_init()函数(common/env_flash.c)
int env_init(void) { #ifdef CONFIG_OMAP2420H4 //没有定义 int flash_probe(void); if(flash_probe() == 0) goto bad_flash; #endif if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) { //如果校验成功 gd->env_addr = (ulong)&(env_ptr->data); //定位在flash中 gd->env_valid = 1; //代表flash中存在环境变量 return(0); } #ifdef CONFIG_OMAP2420H4 bad_flash: #endif gd->env_addr = (ulong)&default_environment[0]; //不成功,则使用系统默认的环境变量 //default_environment仅仅是环境变量的data区,不包含头部crc 、flags gd->env_valid = 0; //0代表使用默认的环境变量 return (0); }
注意:由于编译器在编译的时候,已经给利用data生成了crc,所以肯定能够校验通过。
5、env_relocate()函数(common/env_common.c)
void env_relocate (void) { DEBUGF ("%s[%d] offset = 0x%lx\n", __FUNCTION__,__LINE__, gd->reloc_off); //gd->reloc_off为0 #ifdef CONFIG_AMIGAONEG3SE enable_nvram(); #endif #ifdef ENV_IS_EMBEDDED //有定义,所以下边的有效 //倘若定义了 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); //重定位
//我还不知道reloc_off的作用哦 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 (CFG_ENV_SIZE); //为环境变量区分配空间
//在ENV_IS_EMBEDDED定义了的情况,不会为环境变量在堆区分配空间 DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr); #endif /* * After relocation to RAM, we can always use the "memory" functions */ env_get_char = env_get_char_memory; //定义env_get_char函数 if (gd->env_valid == 0) { //env_init中决定了env_valid的值,倘若flash中存在的环境变量校验错误 #if defined(CONFIG_GTH) || defined(CFG_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 (-1); #endif //#define ENV_SIZE (CFG_ENV_SIZE - ENV_HEADER_SIZE) ENV_SIZE在include/environment.h中定义 if (sizeof(default_environment) > ENV_SIZE) { puts ("*** Error - default environment is too large\n\n"); return; } memset (env_ptr, 0, sizeof(env_t)); //为环境变量存储区清零空间 memcpy (env_ptr->data, default_environment, sizeof(default_environment)); //将环境默认变量拷贝到RAM中的指定区域 #ifdef CFG_REDUNDAND_ENVIRONMENT //没有定义 env_ptr->flags = 0xFF; #endif env_crc_update (); //更新crc gd->env_valid = 1; //env_valid确认有效了 } else { //倘若flash中存在的环境变量校验成功 env_relocate_spec (); /*该函数实现真正的重定位功能,先从NAND flash中读取环境变量,如果读取成*/ } gd->env_addr = (ulong)&(env_ptr->data); //将环境变量的首地址(不含crc头部)赋给全局变量gd->env_addr #ifdef CONFIG_AMIGAONEG3SE disable_nvram(); #endif }
由于env_init()函数中,env_valid已经置为1,所以执行else分支,即 env_relocate_spec ()函数。
6、env_relocate_spec()函数(common/env_flash.c)
void env_relocate_spec (void) //此函数被env_relocate()函数调用 { #if !defined(ENV_IS_EMBEDDED) || defined(CFG_ENV_ADDR_REDUND) //这两个变量都没有定义 #ifdef CFG_ENV_ADDR_REDUND some process; #endif /* CFG_ENV_ADDR_REDUND */ memcpy (env_ptr, (void*)flash_addr, CFG_ENV_SIZE); //在env_relocate已经将env_ptr指向环境变量存储区 #endif /* ! ENV_IS_EMBEDDED || CFG_ENV_ADDR_REDUND */ //倘若ENV_IS_EMBED定义了,并且CFG_ENV_ADDR_REDUND没有定义,那么这将是一个空函数
}
这个函数不包含任何代码,即不进行环境变量的搬移工作。
三、实验与分析
1、修改include/configs/TQ2440.h
/*----------------------------------------------------------------------- * FLASH and environment organization */ #define CONFIG_AMD_LV800 1 /* uncomment this if you have a LV800 flash */ #if 0 #define CONFIG_AMD_LV400 1 /* uncomment this if you have a LV400 flash */ #endif #define CFG_MAX_FLASH_BANKS 1 /* max number of memory banks */ #ifdef CONFIG_AMD_LV800 #define PHYS_FLASH_SIZE 0x00200000 /* flash为2MB */ #define CFG_MAX_FLASH_SECT (35) /* 35个扇区*/ #define CFG_ENV_ADDR (CFG_FLASH_BASE + 0x04000) /* addr of environment */ #endif #ifdef CONFIG_AMD_LV400 #define PHYS_FLASH_SIZE 0x00080000 /* 512KB */ #define CFG_MAX_FLASH_SECT (11) /* max number of sectors on one chip */ #define CFG_ENV_ADDR (CFG_FLASH_BASE + 0x04000) //环境变量下载地址 #endif /* timeout values are in ticks */ #define CFG_FLASH_ERASE_TOUT (5*CFG_HZ) /* Timeout for Flash Erase */ #define CFG_FLASH_WRITE_TOUT (5*CFG_HZ) /* Timeout for Flash Write */ #define CFG_ENV_IS_IN_FLASH 1 #define CFG_ENV_SIZE 0x2000 //环境变量大小 /*定义下边两个参数使ENV_IS_EMBEDDED有效*/ #define CFG_MONITOR_BASE CFG_ENV_ADDR #define CFG_MONITOR_LEN 0x200000
实验现象
1> 编译后下载并启动开发板(第一次运行)
valid为1,环境变量在RAM中的0x33f926f0,在flash中的位置是0x33f926f0-0x33f80000=0x126f0,位于第五个扇区(SA4)。结合下边图片可以知道,上边第三个红框对应的行,正是下图中红色的框。也就是说ENV_IS_EMBEDDED确实生效了。
2> 保存环境变量
但是,环境变量却保存在第二个扇区(SA0)。
3> 重启(第二次运行)
遇到故障,重启不成功
4> 原因分析
0x33f926f0地址内容
0x33f84000内容
原因:环境变量区编译的地址不正确,所以保存环境变量后会把有效代码区(程序)破坏,所以重启不能正常执行。
2、修改board/TQ2440/u-boot.lds
. = ALIGN(4); .text : { cpu/arm920t/start.o (.text) . = env_offset; common/environment.o(.text) *(.text) }
实验现象
1> 编译、下载、上电(第一次运行)
2> 保存环境变量
3> 重启(第二次运行)
保存后的环境变量不能使用,说是bad CRC。
4> 原因分析
原因:第一次上电,crc是编译器产生的(正确的),所以校验通过了。保存环境变量是放在flash的0x4000位置处,当加载到内存中,就是0x33f84000。但是,0x33f80000放的是env_size,被破坏了。而且,程序运行后,把环境变量定位在0x33f84004,产生了错误。
3.修改common/environment.c
/* * These will end up in the .text section * if the environment strings are embedded * in the image. When this is used for * tools/envcrc, they are placed in the * .data/.sdata section. * */ //unsigned long env_size __PPCTEXT__ = sizeof(env_t); /* * Add in absolutes. */ GEN_ABS(env_offset, CFG_ENV_OFFSET);
env_size这个变量没什么用,所以注释掉。
实验现象
1> 编译、下载、上电(第一次运行)
正常
2> 保存环境变量
3> 重启(第二次运行)
可以看到保存的环境变量已经能够正常加载。
四、疑问
1、 怎样把本应放在数据段的environment放在了代码段?
见__attribute__ ((section(".text")))的测试
2、 编译器是怎样根据环境变量产生crc校验码的?
3、 连接的时候是怎样将environment连接在0x33f84000位置上的?