uboot环境变量

摘于一篇别人的文章,不明出处,对作者表示感谢!

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; 


关于以上结构的说明: 


crc 是u-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; 


<include/asm-arm/Global_data.h> 


gd_t.env_addr  即指向 env_ptr->data。 uboot环境变量_第1张图片

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_REDUND,env_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, write。nand_write/erase 使用的是u-boot  的nand 驱动框架,我在做开发的过程中使用的是 


nand_legacy  驱动,所以可以把 nand_erase  和 nand_write             改成 nand_legacy_erase  和 


nand_legacy_rw 就可实现nand_legacy 驱动的保存环境变量版本。 


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


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


U-Boot 环境变量 


U-Boot通过环境变量(env)为用户提供一定程度的可配置性,这些环境变量包括串口终端所使用的波特 


率(baudrate)、启动操作系统内核的参数(bootargs)、本地IP 地址(ipaddr)、网卡MAC地址(ethaddr) 


等等。环境变量可以固化到非易失性存储介质中,使用printenv / saveenv 命令来查看和修改。本例中, 


环境变量固化到Flash 中(AM29LV160DB,2MB)。 


可配置性意味着环境变量中的项目是可以被添加、删除和修改的,即环境变量的内容可能会频繁变化。为 


了不让这种变化对U-Boot 的代码和数据造成破坏,通常的选择是在Flash 中准备一个专用的sector 来 


存储环境变量。简化的ROM 分配模型如下图所示,monitor 占用 Flash 前256KB,env 置于其后,Flash 


的最后一部分用来存放压缩的操作系统内核。 

由于U-Boot 代码通常达到100KB左右,且必须从地址0 处开始,按照这样的分配方式,我们将不得不 


为env 分配一块64KB 的sector,而实际中使用到的可能只是其中的几百字节!U-Boot 还会为env 在 


RAM 中保持一块同样大小的空间,这就造成ROM 和RAM 空间不必要的浪费。 


为了尽可能地减少资源浪费,同时保证系统的健壮性,我们可以把env 放置在Flash 中容量最小的sector 


里。这样,env 嵌入(embed)到U-Boot 的代码段。在common/environment.h 中会比较env 和 


monitor 的范围,如果确定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                 1  /* 使用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); 

你可能感兴趣的:(数据结构,框架,struct,Flash,存储,linker)