u-boot 分析之 默认环境变量配置

通过上节内容,我们大概了解了Uboot中DDR的配置相关知识,这节我们学习一下默认环境变量配置。在zynq开发的时候,在u-boot阶段,有的环境变量需要在烧录后再设置,

在烧录之后都会有一些默认的环境变量比如bootarg等,如果在编译u-boot之前就把自己需要的变量设置好,烧录的时候直接就得到自己想要的变量,这在量产中是很方便的。

在哪里配置呢?经过研习,终于找到了,在这里include/configs/<你的板子名称>.h,如include/configs/zynq_zturn.h,环境变量的部分配置如下:

[cpp]  view plain  copy
  1. #define CONFIG_EXTRA_ENV_SETTINGS        \  
  2.          "qboot_addr=0x000000\0"\  
  3.          "qbootenv_addr=0x080000\0"\  
  4.          "qbootenv_size=0x020000\0"\  
  5.          "qkernel_addr=0x500000\0"\  
  6.          "qdevtree_addr=0x980000\0"\  
  7.          "qramdisk_addr=0x990000\0"\  
  8.          "kernel_size=0x480000\0"      \  
  9.          "devicetree_size=0x010000\0"       \  
  10.          "ramdisk_size=0x600000\0"   \  
  11.          "boot_size=0x080000\0"         \  
  12.          "ethaddr=00:0a:35:00:01:22\0"     \  
  13.          "kernel_image=uImage\0"      \  
  14.          "kernel_load_address=0x2080000\0"\  
  15.          "ramdisk_image=uramdisk.image.gz\0"         \  
  16.          "ramdisk_load_address=0x4000000\0" \  
  17.          "devicetree_image=devicetree.dtb\0"   \  
  18.          "devicetree_load_address=0x2000000\0"     \  
  19.          "bitstream_image=system.bit.bin\0"     \  
  20.          "boot_image=BOOT.bin\0"     \  
  21.          "loadbit_addr=0x100000\0"   \  
  22.          "loadbootenv_addr=0x2000000\0"\  
  23.          "fdt_high=0x20000000\0"       \  
  24.          "initrd_high=0x20000000\0"  \  
  25.          "bootenv=uEnv.txt\0"\  
  26.          "loadbootenv=fatloadmmc 0 ${loadbootenv_addr} ${bootenv}\0" \  
  27.          "importbootenv=echoImporting environment from SD ...; " \  
  28.                    "envimport -t ${loadbootenv_addr} $filesize\0" \  
  29.          "mmc_loadbit_fat=echoLoading bitstream from SD/MMC/eMMC to RAM.. && " \  
  30.                    "get_bitstream_name&& mmcinfo && " \  
  31.                    "fatloadmmc 0 ${loadbit_addr} ${bitstream_image} && " \  
  32.                    "fpgaloadb 0 ${loadbit_addr} ${filesize}\0" \  
  33.          "norboot=echoCopying Linux from NOR flash to RAM... && " \  
  34.                    "cp.b0xE2100000 ${kernel_load_address} ${kernel_size} && " \  
  35.                    "cp.b0xE2600000 ${devicetree_load_address} ${devicetree_size} && " \  
  36.                    "echoCopying ramdisk... && " \  
  37.                    "cp.b0xE2620000 ${ramdisk_load_address} ${ramdisk_size} && " \  
  38.                    "bootm${kernel_load_address} ${ramdisk_load_address}${devicetree_load_address}\0" \  
  39.          "uenvboot="\  
  40.                    "ifrun loadbootenv; then " \  
  41.                             "echoLoaded environment from ${bootenv}; " \  
  42.                             "runimportbootenv; " \  
  43.                    "fi;" \  
  44.                    "iftest -n $uenvcmd; then " \  
  45.                             "echoRunning uenvcmd ...; " \  
  46.                             "runuenvcmd; " \  
  47.                    "fi\0"\  
  48.          "sdboot=ifmmcinfo; then " \  
  49.                             "runuenvboot; " \  
  50.                             "get_bitstream_name&& " \  
  51.                             "echo- load ${bitname} to PL... && " \  
  52.                             "fatloadmmc 0 0x200000 ${bitname} && " \  
  53.                             "fpgaloadb 0 0x200000 ${filesize} && " \  
  54.                             "echoCopying Linux from SD to RAM... && " \  
  55.                             "fatloadmmc 0 ${kernel_load_address} ${kernel_image} && " \  
  56.                             "fatloadmmc 0 ${devicetree_load_address} ${devicetree_image} && " \  
  57.                             "fatloadmmc 0 ${ramdisk_load_address} ${ramdisk_image} && " \  
  58.                             "bootm${kernel_load_address} ${ramdisk_load_address} ${devicetree_load_address};" \  
  59.                    "fi\0"\  
  60.          "usbboot=ifusb start; then " \  
  61.                             "runuenvboot; " \  
  62.                             "echoCopying Linux from USB to RAM... && " \  
  63.                             "fatloadusb 0 ${kernel_load_address} ${kernel_image} && " \  
  64.                             "fatloadusb 0 ${devicetree_load_address} ${devicetree_image} && " \  
  65.                             "fatloadusb 0 ${ramdisk_load_address} ${ramdisk_image} && " \  
  66.                             "bootm${kernel_load_address} ${ramdisk_load_address} ${devicetree_load_address};" \  
  67.                    "fi\0"\  
  68.          "nandboot=echoCopying Linux from NAND flash to RAM... && " \  
  69.                    "nandread ${kernel_load_address} 0x100000 ${kernel_size} && " \  
  70.                    "nandread ${devicetree_load_address} 0x600000 ${devicetree_size} && " \  
  71.                    "echoCopying ramdisk... && " \  
  72.                    "nandread ${ramdisk_load_address} 0x620000 ${ramdisk_size} && " \  
  73.                    "bootm${kernel_load_address} ${ramdisk_load_address}${devicetree_load_address}\0" \  
  74.          "jtagboot=echoTFTPing Linux to RAM... && " \  
  75.                    "tftpboot${kernel_load_address} ${kernel_image} && " \  
  76.                    "tftpboot${devicetree_load_address} ${devicetree_image} && " \  
  77.                    "tftpboot${ramdisk_load_address} ${ramdisk_image} && " \  
  78.                    "bootm${kernel_load_address} ${ramdisk_load_address}${devicetree_load_address}\0" \  
  79.          "rsa_norboot=echoCopying Image from NOR flash to RAM... && " \  
  80.                    "cp.b0xE2100000 0x100000 ${boot_size} && " \  
  81.                    "zynqrsa0x100000 && " \  
  82.                    "bootm${kernel_load_address} ${ramdisk_load_address}${devicetree_load_address}\0" \  
  83.          "rsa_nandboot=echoCopying Image from NAND flash to RAM... && " \  
  84.                    "nandread 0x100000 0x0 ${boot_size} && " \  
  85.                    "zynqrsa0x100000 && " \  
  86.                    "bootm${kernel_load_address} ${ramdisk_load_address}${devicetree_load_address}\0" \  
  87.          "rsa_qspiboot=echoCopying Image from QSPI flash to RAM... && " \  
  88.                    "sfprobe 0 0 0 && " \  
  89.                    "sfread 0x100000 0x0 ${boot_size} && " \  
  90.                    "zynqrsa0x100000 && " \  
  91.                    "bootm${kernel_load_address} ${ramdisk_load_address}${devicetree_load_address}\0" \  
  92.          "rsa_sdboot=echoCopying Image from SD to RAM... && " \  
  93.                    "fatloadmmc 0 0x100000 ${boot_image} && " \  
  94.                    "zynqrsa0x100000 && " \  
  95.                    "bootm${kernel_load_address} ${ramdisk_load_address}${devicetree_load_address}\0" \  
  96.          "rsa_jtagboot=echoTFTPing Image to RAM... && " \  
  97.                    "tftpboot0x100000 ${boot_image} && " \  
  98.                    "zynqrsa0x100000 && " \  
  99.                    "bootm${kernel_load_address} ${ramdisk_load_address}${devicetree_load_address}\0" \  
  100.          "qspiboot=echoCopying Linux from QSPI flash to RAM... && " \  
  101.                    "sfprobe 0 0 0 && " \  
  102.                    "qspi_get_bitsize0x0A0000 && " \  
  103.                    "sfread ${loadbit_addr} 0x0A0004 ${bitsize} && " \  
  104.                    "fpgaloadb 0 ${loadbit_addr} ${bitsize} && " \  
  105.                    "sfread ${kernel_load_address} ${qkernel_addr} ${kernel_size} && " \  
  106.                    "sfread ${devicetree_load_address} ${qdevtree_addr} ${devicetree_size} &&" \  
  107.                    "echoCopying ramdisk... && " \  
  108.                    "sfread ${ramdisk_load_address} ${qramdisk_addr} ${ramdisk_size} && "\  
  109.                    "bootm${kernel_load_address} ${ramdisk_load_address}${devicetree_load_address}\0" \  
  110.          "qspiupdate=echoUpdate qspi images from sd card... && " \  
  111.                    "echo- Init mmc... && mmc rescan && " \  
  112.                    "echo- Init qspi flash... && sf probe 0 0 0 && " \  
  113.                    "echo- Write boot.bin... && " \  
  114.                    "fatloadmmc 0 0x200000 boot.bin && " \  
  115.                    "sferase ${qboot_addr} ${boot_size} && " \  
  116.                    "sferase ${qbootenv_addr} ${qbootenv_size} && " \  
  117.                    "sfwrite 0x200000 0 ${filesize} && " \  
  118.                    "get_bitstream_name&& " \  
  119.                    "echo- Write ${bitstream_image}... && " \  
  120.                    "fatloadmmc 0 0x200000 ${bitstream_image} && " \  
  121.                    "sferase 0x0A0000 0x460000 && " \  
  122.                    "mw.l0x100000 ${filesize} && " \  
  123.                    "sfwrite 0x100000 0x0A0000 4 && " \  
  124.                    "sfwrite 0x200000 0x0A0004 ${filesize} && " \  
  125.                    "echo- Write uImage... && " \  
  126.                    "fatloadmmc 0 0x200000 uImage && " \  
  127.                    "sferase ${qkernel_addr} ${kernel_size} && " \  
  128.                    "sfwrite 0x200000 ${qkernel_addr} ${filesize} && " \  
  129.                    "echo- Write device tree... && " \  
  130.                    "fatloadmmc 0 0x200000 devicetree.dtb && " \  
  131.                    "sferase ${qdevtree_addr} ${devicetree_size} && " \  
  132.                    "sfwrite 0x200000 ${qdevtree_addr} ${filesize} && " \  
  133.                    "echo- Write Ramdisk... && " \  
  134.                    "fatloadmmc 0 0x200000 uramdisk.image.gz && " \  
  135.                    "sferase ${qramdisk_addr} ${ramdisk_size} && " \  
  136.                    "sfwrite 0x200000 ${qramdisk_addr} ${filesize} && " \  
  137.                    "echo- Done.\0"  


u-boot的环境变量用来存储一些经常使用的参数变量,uboot希望将环境变量存储在静态存储器中(如nand nor eeprom mmc)。

其中有一些也是大家经常使用,有一些是使用人员自己定义的,更改这些名字会出现错误,下面的表中我们列出了一些常用的环境变量:

     bootdelay    执行自动启动的等候秒数
     baudrate     串口控制台的波特率
     netmask     以太网接口的掩码
     ethaddr       以太网卡的网卡物理地址
     bootfile        缺省的下载文件
     bootargs     传递给内核的启动参数
     bootcmd     自动启动时执行的命令
     serverip       服务器端的ip地址
     ipaddr         本地ip 地址
     stdin           标准输入设备
     stdout        标准输出设备
     stderr         标准出错设备

上面这些是uboot默认存在的环境变量,uboot本身会使用这些环境变量来进行配置。我们可以自己定义一些环境变量来供我们自己uboot驱动来使用。

Uboot环境变量的设计逻辑是在启动过程中将env从静态存储器中读出放到RAM中,之后在uboot下对env的操作(如printenv editenv setenv)都是对RAM中env的操作,只有在执行saveenv时才会将RAM中的env重新写入静态存储器中。

这种设计逻辑可以加快对env的读写速度。

基于这种设计逻辑,2014.4版本uboot实现了saveenv这个保存env到静态存储器的命令,而没有实现读取env到RAM的命令。

那我们就来看一下uboot中env的数据结构 初始化 操作如何实现的。

一 env数据结构

在include/environment.h中定义了env_t,如下:

[cpp]  view plain  copy
  1. #ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT    
  2. # define ENV_HEADER_SIZE    (sizeof(uint32_t) + 1)    
  3. # define ACTIVE_FLAG   1    
  4. # define OBSOLETE_FLAG 0    
  5. #else    
  6. # define ENV_HEADER_SIZE    (sizeof(uint32_t))    
  7. #endif    
  8. #define ENV_SIZE (CONFIG_ENV_SIZE - ENV_HEADER_SIZE)    
  9. typedef struct environment_s {    
  10.     uint32_t    crc;        /* CRC32 over data bytes    */    
  11. #ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT    
  12.     unsigned char   flags;      /* active/obsolete flags    */    
  13. #endif    
  14.     unsigned char   data[ENV_SIZE]; /* Environment data     */    
  15. } env_t;    

CONFIG_ENV_SIZE是我们需要在配置文件中配置的环境变量的总长度。

这里我们使用的nand作为静态存储器,nand的一个block是128K,因此选用一个block来存储env,CONFIG_ENV_SIZE为128K。

Env_t结构体头4个bytes是对data的crc校验码,没有定义CONFIG_SYS_REDUNDAND_ENVIRONMENT,所以后面紧跟data数组,数组大小是ENV_SIZE.

ENV_SIZE是CONFIG_ENV_SIZE减掉ENV_HEADER_SIZE,也就是4bytes,

所以env_t这个结构体就包含了整个我们规定的长度为CONFIG_ENV_SIZE的存储区域。

头4bytes是crc校验码,后面剩余的空间全部用来存储环境变量。

需要说明的一点,crc校验码是uboot中在saveenv时计算出来,然后写入nand,所以在第一次启动uboot时crc校验会出错,

因为uboot从nand上读入的一个block数据是随机的,没有意义的,执行saveenv后重启uboot,crc校验就正确了。

data 字段保存实际的环境变量。u-boot  的 env  按 name=value”\0”的方式存储,在所有env 的最后以”\0\0”表示整个 env  的结束。

新的name=value 对总是被添加到 env  数据块的末尾,当删除一个 name=value 对时,后面的环境变量将前移,对一个已经存在的环境变量的修改实际上先删除再插入。 
u-boot 把env_t  的数据指针还保存在了另外一个地方,这就 
是 gd_t  结构(不同平台有不同的 gd_t  结构 ),这里以ARM 为例仅列出和 env  相关的部分 

[cpp]  view plain  copy
  1. typedef struct global_data     
  2. {     
  3.      …     
  4.      unsigned long env_off;        /* Relocation Offset */     
  5.      unsigned long env_addr;       /* Address of Environment struct ??? */     
  6.      unsigned long env_valid       /* Checksum of Environment valid */     
  7.      …     
  8. } gd_t;    

二 env的初始化

uboot中env的整个架构可以分为3层:

(1) 命令层,如saveenv,setenv editenv这些命令的实现,还有如启动时调用的env_relocate函数。

(2) 中间封装层,利用不同静态存储器特性封装出命令层需要使用的一些通用函数,如env_init,env_relocate_spec,saveenv这些函数。实现文件在common/env_xxx.c

(3) 驱动层,实现不同静态存储器的读写擦等操作,这些是uboot下不同子系统都必须的。

按照执行流顺序,首先分析一下uboot启动的env初始化过程。

首先在board_init_f中调用init_sequence的env_init,这个函数是不同存储器实现的函数,nand中的实现如下:

[cpp]  view plain  copy
  1. /*  
  2.  * This is called before nand_init() so we can't read NAND to  
  3.  * validate env data.  
  4.  *  
  5.  * Mark it OK for now. env_relocate() in env_common.c will call our  
  6.  * relocate function which does the real validation.  
  7.  *  
  8.  * When using a NAND boot image (like sequoia_nand), the environment  
  9.  * can be embedded or attached to the U-Boot image in NAND flash.  
  10.  * This way the SPL loads not only the U-Boot image from NAND but  
  11.  * also the environment.  
  12.  */    
  13. int env_init(void)    
  14. {    
  15.     gd->env_addr    = (ulong)&default_environment[0];    
  16.     gd->env_valid   = 1;    
  17.     return 0;    
  18. }  

从注释就基本可以看出这个函数的作用,因为env_init要早于静态存储器的初始化,所以无法进行env的读写,这里将gd中的env相关变量进行配置,

默认设置env为valid。方便后面env_relocate函数进行真正的env从nand到ram的relocate。

继续执行,在board_init_r中,如下:

[cpp]  view plain  copy
  1. /* initialize environment */    
  2.     if (should_load_env())    
  3.         env_relocate();    
  4.     else    
  5.         set_default_env(NULL);    

这是在所有存储器初始化完成后执行的。

首先调用should_load_env,如下:

[cpp]  view plain  copy
  1. /*  
  2.  * Tell if it's OK to load the environment early in boot.  
  3.  *  
  4.  * If CONFIG_OF_CONFIG is defined, we'll check with the FDT to see  
  5.  * if this is OK (defaulting to saying it's not OK).  
  6.  *  
  7.  * NOTE: Loading the environment early can be a bad idea if security is  
  8.  *       important, since no verification is done on the environment.  
  9.  *  
  10.  * @return 0 if environment should not be loaded, !=0 if it is ok to load  
  11.  */    
  12. static int should_load_env(void)    
  13. {    
  14. #ifdef CONFIG_OF_CONTROL    
  15.     return fdtdec_get_config_int(gd->fdt_blob, "load-environment", 1);    
  16. #elif defined CONFIG_DELAY_ENVIRONMENT    
  17.     return 0;    
  18. #else    
  19.     return 1;    
  20. #endif    
  21. }    

从注释可以看出,CONFIG_OF_CONTROL没有定义,鉴于考虑安全性问题,如果我们想要推迟env的load,可以定义CONFIG_DELAY_ENVIRONMENT,这里返回0,就调用set_default_env使用默认的env,默认env是在配置文件中CONFIG_EXTRA_ENV_SETTINGS设置的。

我们可以在之后的某个地方在调用env_relocate来load env。这里我们选择在这里直接load env。所以没有定义CONFIG_DELAY_ENVIRONMENT,返回1。调用env_relocate。

在common/env_common.c中:

[cpp]  view plain  copy
  1. void env_relocate(void)    
  2. {    
  3. #if defined(CONFIG_NEEDS_MANUAL_RELOC)    
  4.     env_reloc();    
  5.     env_htab.change_ok += gd->reloc_off;    
  6. #endif    
  7.     if (gd->env_valid == 0) {    
  8. #if defined(CONFIG_ENV_IS_NOWHERE) || defined(CONFIG_SPL_BUILD)    
  9.         /* Environment not changable */    
  10.         set_default_env(NULL);    
  11. #else    
  12.         bootstage_error(BOOTSTAGE_ID_NET_CHECKSUM);    
  13.         set_default_env("!bad CRC");    
  14. #endif    
  15.     } else {    
  16.         env_relocate_spec();    
  17.     }    
  18. }     

Gd->env_valid在之前的env_init中设置为1,所以这里调用env_relocate_spec,

这个函数也是不同存储器的中间封装层提供的函数,对于nand在common/env_nand.c中,如下:

[cpp]  view plain  copy
  1. void env_relocate_spec(void)    
  2. {    
  3.    int ret;    
  4.     ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);    
  5.     ret = readenv(CONFIG_ENV_OFFSET, (u_char *)buf);    
  6.     if (ret) {    
  7.         set_default_env("!readenv() failed");    
  8.         return;    
  9.     }    
  10.     env_import(buf, 1);    
  11. }    
 

首先定义一个长度为CONFIG_ENV_SIZE的buf,然后调用readenv,

CONFIG_ENV_OFFSET是在配置文件中定义的env在nand中偏移位置。我们这里定义的是在4M的位置。

Readenv也在env_nand.c中,如下:

[cpp]  view plain  copy
  1. int readenv(size_t offset, u_char *buf)    
  2. {    
  3.     size_t end = offset + CONFIG_ENV_RANGE;    
  4.     size_t amount_loaded = 0;    
  5.     size_t blocksize, len;    
  6.     u_char *char_ptr;    
  7.     blocksize = nand_info[0].erasesize;    
  8.     if (!blocksize)    
  9.         return 1;    
  10.     len = min(blocksize, CONFIG_ENV_SIZE);    
  11.     while (amount_loaded < CONFIG_ENV_SIZE && offset < end) {    
  12.         if (nand_block_isbad(&nand_info[0], offset)) {    
  13.             offset += blocksize;    
  14.         } else {    
  15.             char_ptr = &buf[amount_loaded];    
  16.             if (nand_read_skip_bad(&nand_info[0], offset,    
  17.                            &len, NULL,    
  18.                            nand_info[0].size, char_ptr))    
  19.                 return 1;    
  20.             offset += blocksize;    
  21.             amount_loaded += len;    
  22.         }    
  23.     }    
  24.     
  25.     if (amount_loaded != CONFIG_ENV_SIZE)    
  26.         return 1;    
  27.     
  28.     return 0;    
  29. }    


Readenv函数利用nand_info[0]对nand进行读操作,读出指定位置,指定长度的数据到buf中。Nand_info[0]是一个全局变量,来表征第一个nand device,这里在nand_init时会初始化这个变量。Nand_init必须在env_relocate之前。

回到env_relocate_spec中,buf读回后调用env_import,如下:

[cpp]  view plain  copy
  1. /*  
  2.  * Check if CRC is valid and (if yes) import the environment.  
  3.  * Note that "buf" may or may not be aligned.  
  4.  */    
  5. int env_import(const char *buf, int check)    
  6. {    
  7.     env_t *ep = (env_t *)buf;    
  8.     
  9.     if (check) {    
  10.         uint32_t crc;    
  11.     
  12.         memcpy(&crc, &ep->crc, sizeof(crc));    
  13.     
  14.         if (crc32(0, ep->data, ENV_SIZE) != crc) {    
  15.             set_default_env("!bad CRC");    
  16.             return 0;    
  17.         }    
  18.     }    
  19.     
  20.     if (himport_r(&env_htab, (char *)ep->data, ENV_SIZE, '\0', 0,    
  21.             0, NULL)) {    
  22.         gd->flags |= GD_FLG_ENV_READY;    
  23.         return 1;    
  24.     }    
  25.     
  26.     error("Cannot import environment: errno = %d\n", errno);    
  27.     
  28.     set_default_env("!import failed");    
  29.     
  30.     return 0;    
  31. }    

首先将buf强制转换为env_t类型,然后对data进行crc校验,跟buf中原有的crc对比,不一致则使用默认env。

最后调用himport_r,该函数将给出的data按照‘\0’分割填入env_htab的哈希表中。

之后对于env的操作,如printenv setenv editenv,都是对该哈希表的操作。

Env_relocate执行完成,env的初始化就完成了。

 

三 env的操作实现

Uboot对env的操作命令实现在common/cmd_nvedit.c中。

对于setenv printenv editenv这3个命令,看其实现代码,都是对relocate到RAM中的env_htab的操作,这里就不再详细分析了,重点来看一下savenv实现。

[cpp]  view plain  copy
  1. static int do_env_save(cmd_tbl_t *cmdtp, int flag, int argc,    
  2.                char * const argv[])    
  3. {    
  4.     printf("Saving Environment to %s...\n", env_name_spec);    
  5.     
  6.     return saveenv() ? 1 : 0;    
  7. }    
  8.     
  9. U_BOOT_CMD(    
  10.     saveenv, 1, 0,  do_env_save,    
  11.     "save environment variables to persistent storage",    
  12.     ""    
  13. );    

在do_env_save调用saveenv,这个函数是不同存储器实现的封装层函数。对于nand,在common/env_nand.c中,如下:

[cpp]  view plain  copy
  1. int saveenv(void)    
  2. {    
  3.     int ret = 0;    
  4.     ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);    
  5.     ssize_t len;    
  6.     char    *res;    
  7.     int env_idx = 0;    
  8.     static const struct env_location location[] = {    
  9.         {    
  10.             .name = "NAND",    
  11.             .erase_opts = {    
  12.                 .length = CONFIG_ENV_RANGE,    
  13.                 .offset = CONFIG_ENV_OFFSET,    
  14.             },    
  15.         },    
  16. #ifdef CONFIG_ENV_OFFSET_REDUND    
  17.         {    
  18.             .name = "redundant NAND",    
  19.             .erase_opts = {    
  20.                 .length = CONFIG_ENV_RANGE,    
  21.                 .offset = CONFIG_ENV_OFFSET_REDUND,    
  22.             },    
  23.         },    
  24. #endif    
  25.     };    
  26.     
  27.     if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)    
  28.         return 1;    
  29.     
  30.     res = (char *)&env_new->data;    
  31.     len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);    
  32.     if (len < 0) {    
  33.         error("Cannot export environment: errno = %d\n", errno);    
  34.         return 1;    
  35.     }    
  36.     env_new->crc   = crc32(0, env_new->data, ENV_SIZE);    
  37. #ifdef CONFIG_ENV_OFFSET_REDUND    
  38.     env_new->flags = ++env_flags; /* increase the serial */    
  39.     env_idx = (gd->env_valid == 1);    
  40. #endif    
  41.     
  42.     ret = erase_and_write_env(&location[env_idx], (u_char *)env_new);    
  43. #ifdef CONFIG_ENV_OFFSET_REDUND    
  44.     if (!ret) {    
  45.         /* preset other copy for next write */    
  46.         gd->env_valid = gd->env_valid == 2 ? 1 : 2;    
  47.         return ret;    
  48.     }    
  49.     
  50.     env_idx = (env_idx + 1) & 1;    
  51.     ret = erase_and_write_env(&location[env_idx], (u_char *)env_new);    
  52.     if (!ret)    
  53.         printf("Warning: primary env write failed,"    
  54.                 " redundancy is lost!\n");    
  55. #endif    
  56.     
  57.     return ret;    
  58. }    

 

定义env_t类型的变量env_new,准备来存储env。

利用函数hexport_r对env_htab操作,读取env内容到env_new->data,

校验data,获取校验码env_new->crc。

最后调用erase_and_write_env将env_new先擦后写入由location定义的偏移量和长度的nand区域中。

这样就完成了env写入nand的操作。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值