Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2161121
  • 博文数量: 361
  • 博客积分: 10828
  • 博客等级: 上将
  • 技术积分: 4161
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-20 14:34
文章分类

全部博文(361)

文章存档

2011年(132)

2010年(229)

分类: LINUX

2010-02-21 14:45:51

U-BOOT环境变量实现

(基于smdk2410)

1.相关文件

common/env_common.c

u-boot调用的通用函数接口,它们隐藏了env的不同实现方式,比如dataflash, epprom, flash

 

common/env_dataflash.c

env 存储在dataflash中的实现

 

common/env_epprom.c

env 存储在epprom中的实现

 

common/env_flash.c

env 存储在flash中的实现

 

common/env_nand.c

env 存储在nand中的实现

 

common/env_nvedit.c

实现u-boot对环境变量的操作命令

 

environment.c

环境变量以及一些宏定义

 

env如果存储在Flash中还需要Flash的支持。

2.数据结构

env u-boot 中通常有两种存在方式,在永久性存储介质中( Flash NVRAM )在SDRAM,可以配置不使用 env 的永久存储方式,但这不常用。u-boot 在启动的时候会将存储在永久性存储介质中的 env 重新定位到 RAM 中,这样可以快速访问,同时可以通过saveenv RAM 中的 env 保存到永久性存储介质中。

 

include/environment.h中定义了表示env的数据结构

 

typedef struct environment_s

{

       unsigned long crc;   /* CRC32 over data bytes */

#ifdef CFG_REDUNDAND_ENVIRONMENT

       unsigned char flags;  /* active/obsolete flags */

#endif

       unsigned char data[ENV_SIZE]; /* Environment data */

} env_t;

关于以上结构的说明:

crcu-boot在保存env 的时候加上去的校验头,在第一次启动时一般 crc校验会出错,这很正常,因为这时 Flash中的数据无效。

data字段保存实际的环境变量。u-boot env name=value”\0”的方式存储,在所有env的最后以”\0\0”表示整个 env 的结束。新的name=value对总是被添加到 env 数据块的末尾,当删除一个name=value对时,后面的环境变量将前移,对一个已经存在的环境变量的修改实际上先删除再插入。

env 可以保存在 u-boot TEXT 段中,这样 env 就可以同 u-boot 一同加载入RAM中,这种方法没有测试过。

       上文提到u-boot会将 env flash 等存储设备重定位到 RAM 中,在 env 的不同实现版本( env_xxx.c )中定义了 env_ptr, 它指向 env RAM中的位置。u-boot在重定位 env后对环境变量的操作都是针对 env_ptr

       env_t 中除了数据之外还包含校验头,u-boot env_t 的数据指针有保存在了另外一个地方,这就是 gd_t 结构( 不同平台有不同的 gd_t 结构 ,这里以ARM为例仅列出和 env 相关的部分

typedef struct global_data

{

      

       unsigned long env_off;         /* Relocation Offset */

       unsigned long env_addr;        /* Address of Environment struct ??? */

       unsigned long env_valid        /* Checksum of Environment valid */

      

} gd_t;

gd_t.env_addr 即指向 env_ptr->data

 

 

 

 

 

3.ENV 的初始化

start_armboot : lib_arm/board.c

*env_init : env_xxx.c xxx = nand | flash | epprom …

env_relocate : env_common.c

*env_relocate_spec : env_xxx.c xxx=nand | flash | eporom…

3.1env_init

实现 env 的第一次初始化,对于nand env (非embedded方式):

Env_nand.c : env_init

gd->env_addr = (ulong)&default_environment[0]; //先使gd->env_addr指向默认的环境变量

gd->env_valid = 1;// env 有效位置1

3.2 env_relocate

#ifdefine ENV_IS_EMBEDDED

(略)

#else

env_ptr = (env_t *)malloc (CFG_ENV_SIZE);

#endif

if( gd->env_valid == 0) // Env_annd.c : env_init 中已经将 gd->env_valid 1

{

      

}

else

       env_relocate_spec ();// 调用具体的 env_relocate_spec 函数

gd->env_addr = (ulong)&(env_ptr->data);// 最终完成将环境变量搬移到内存

这里涉及到两个和环境变量有关的宏

ENV_IS_EMBEDDED : env 是否存在于 u-boot TEXT 段中

CFG_ENV_SIZE : env 块的大小

实际上还需要几个宏来控制u-boot 对环境变量的处理

CFG_ENV_IS_IN_NAND : env 块是否存在于Nand Flash

CFG_ENV_OFFSET : env 块在 Flash 中偏移地址

 

3.3*env_relocate_spec

这里仅分析 Nand Flash env_relocate_spec 实现

如果未设置 CFG_ENV_OFFSET_REDUNDenv_relocate_spec的实现如下 :

void env_relocate_spec (void)

{

#if !defined(ENV_IS_EMBEDDED)

       ulong total;

       int ret;

 

       total = CFG_ENV_SIZE;

       ret = nand_read(&nand_info[0], CFG_ENV_OFFSET, &total, (u_char*)env_ptr);

      if (ret || total != CFG_ENV_SIZE)

              return use_default();

 

       if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc)

              return use_default();

#endif /* ! ENV_IS_EMBEDDED */

}

上面的代码很清楚的表明了 env_relocate_spec 的意图,调用 nand_read 将环境变量从 CFG_ENV_OFFSET 处读出,环境变量的大小为 CFG_ENV_SIZE 注意 CFG_ENV_OFFSET CFG_ENV_SIZE 要和 Nand Flash 的块/页边界对齐。读出数据后再调用crc32 env_ptr->data 进行校验并与保存在 env_ptr->crc 的校验码对比,看数据是否出错,从这里也可以看出在系统第一次启动时,Nand Flash 里面没有存储任何环境变量,crc校验肯定回出错,当我们保存环境变量后,接下来再启动板子u-boot就不会再报crc32出错了。

4. ENV 的保存

由上问的论述得知, env 将从永久性存储介质中搬到RAM里面,以后对env 的操作,比如修改环境变量的值,删除环境变量的值都是对这个 env RAM中的拷贝进行操作,由于RAM的特性,下次启动时所做的修改将全部消失,u-boot提供了将env 写回 永久性存储介质的命令支持 : saveenv,不同版本的 env nand flash, flash … )实现方式不同,以Nand Flash 的实现(未定义CFG_ENV_OFFSET_REDUND)为例

Env_nand.c : saveenv

int saveenv(void)

{

       ulong total;

       int ret = 0;

 

       puts ("Erasing Nand...");

       if (nand_erase(&nand_info[0], CFG_ENV_OFFSET, CFG_ENV_SIZE))

              return 1;

 

       puts ("Writing to Nand... ");

       total = CFG_ENV_SIZE;

       ret = nand_write(&nand_info[0], CFG_ENV_OFFSET, &total, (u_char*)env_ptr);

       if (ret || total != CFG_ENV_SIZE)

              return 1;

 

       puts ("done\n");

       return ret;

}

Nand Flash saveenv 命令实现很简单,调用nand_erase nand_write进行Nand Flash erase, writenand_write/erase使用的是u-boot nand驱动框架,我在做开发的过程中使用的是nand_legacy驱动,所以可以把nand_erasenand_write改成nand_legacy_erasenand_legacy_rw就可实现nand_legacy驱动的保存环境变量版本。

 

 

========================================================================

U-Boot环境变量

U-Boot通过环境变量(env)为用户提供一定程度的可配置性,这些环境变量包括串口终端所使用的波特率(baudrate)、启动操作系统内核的参数(bootargs)、本地IP地址(ipaddr)、网卡MAC地址(ethaddr)等等。环境变量可以固化到非易失性存储介质中,使用printenv / saveenv命令来查看和修改。本例中,环境变量固化到Flash中(AM29LV160DB2MB)。

可配置性意味着环境变量中的项目是可以被添加、删除和修改的,即环境变量的内容可能会频繁变化。为了不让这种变化对U-Boot的代码和数据造成破坏,通常的选择是在Flash中准备一个专用的sector来存储环境变量。简化的ROM分配模型如下图所示,monitor占用 Flash256KBenv置于其后,Flash的最后一部分用来存放压缩的操作系统内核。

 

 

AM29LV160DB分为35sector,地址范围分配如下:

Sector

Size ( KB )

Address Range ( Hex )

SA0

16

000000 ~ 003FFF

SA1

8

004000 ~ 005FFF

SA2

8

006000 ~ 007FFF

SA3

16

008000 ~ 00FFFF

SA4

32

010000 ~ 01FFFF

SA5

64

020000 ~ 02FFFF

...

64

...

SA34

64

1F0000 ~ 1FFFFF

 

由于U-Boot代码通常达到100KB左右,且必须从地址0处开始,按照这样的分配方式,我们将不得不为env分配一块64KBsector,而实际中使用到的可能只是其中的几百字节!U-Boot还会为envRAM中保持一块同样大小的空间,这就造成ROMRAM空间不必要的浪费。

为了尽可能地减少资源浪费,同时保证系统的健壮性,我们可以把env放置在Flash中容量最小的sector里。这样,env嵌入(embed)到U-Boot的代码段。在common/environment.h中会比较envmonitor的范围,如果确定env位于monitor内,则定义ENV_IS_EMBEDDED

 
# 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 

 

修改board/buf/EVB44B0/u-boot.lds

 
/*------------------------------------------------------------
 * Environment Variable setup
 */
#define CFG_ENV_IS_IN_FLASH          /* 使用Flash存储env     */ 
#define CFG_ENV_SIZE          0x2000   /* 容量8KB (SA1)       */
#define CFG_ENV_OFFSET        0x4000   /* 偏移地址 (SA1)        */  

 

$(LD)将一系列的obj文件连接成elf格式文件,其输出文件的内存布局由linker script决定。修改board/buf/EVB44B0/u-boot.lds

 
SECTIONS
{
  . = 0x00000000;
  . = ALIGN(4);

  .text      :
  {
    cpu/s3c44b0/start.o (.text)
    board/buf/EVB44B0/lowlevel_init.o (.text)
    lib_generic/string.o (.text)
    lib_generic/zlib.o (.text)

    . = env_offset;
    common/environment.o (.text)

    *(.text)
  }

  /* other sections ... */
} 

u-boot.map选择那些U-Boot运行必须的,且不易受CFG_*宏影响的obj文件,填充到start.o后面。可以参考board/trab/u-boot.lds

env_offset定义在common/environment.c中:

 
#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))
GEN_ABS(env_offset, CFG_ENV_OFFSET); 

阅读(2015) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~