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

来源:互联网 发布:淘宝企业店铺还不如c店 编辑:程序博客网 时间:2024/06/06 10:58

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

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

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

#define CONFIG_EXTRA_ENV_SETTINGS        \         "qboot_addr=0x000000\0"\         "qbootenv_addr=0x080000\0"\         "qbootenv_size=0x020000\0"\         "qkernel_addr=0x500000\0"\         "qdevtree_addr=0x980000\0"\         "qramdisk_addr=0x990000\0"\         "kernel_size=0x480000\0"      \         "devicetree_size=0x010000\0"       \         "ramdisk_size=0x600000\0"   \         "boot_size=0x080000\0"         \         "ethaddr=00:0a:35:00:01:22\0"     \         "kernel_image=uImage\0"      \         "kernel_load_address=0x2080000\0"\         "ramdisk_image=uramdisk.image.gz\0"         \         "ramdisk_load_address=0x4000000\0" \         "devicetree_image=devicetree.dtb\0"   \         "devicetree_load_address=0x2000000\0"     \         "bitstream_image=system.bit.bin\0"     \         "boot_image=BOOT.bin\0"     \         "loadbit_addr=0x100000\0"   \         "loadbootenv_addr=0x2000000\0"\         "fdt_high=0x20000000\0"       \         "initrd_high=0x20000000\0"  \         "bootenv=uEnv.txt\0"\         "loadbootenv=fatloadmmc 0 ${loadbootenv_addr} ${bootenv}\0" \         "importbootenv=echoImporting environment from SD ...; " \                   "envimport -t ${loadbootenv_addr} $filesize\0" \         "mmc_loadbit_fat=echoLoading bitstream from SD/MMC/eMMC to RAM.. && " \                   "get_bitstream_name&& mmcinfo && " \                   "fatloadmmc 0 ${loadbit_addr} ${bitstream_image} && " \                   "fpgaloadb 0 ${loadbit_addr} ${filesize}\0" \         "norboot=echoCopying Linux from NOR flash to RAM... && " \                   "cp.b0xE2100000 ${kernel_load_address} ${kernel_size} && " \                   "cp.b0xE2600000 ${devicetree_load_address} ${devicetree_size} && " \                   "echoCopying ramdisk... && " \                   "cp.b0xE2620000 ${ramdisk_load_address} ${ramdisk_size} && " \                   "bootm${kernel_load_address} ${ramdisk_load_address}${devicetree_load_address}\0" \         "uenvboot="\                   "ifrun loadbootenv; then " \                            "echoLoaded environment from ${bootenv}; " \                            "runimportbootenv; " \                   "fi;" \                   "iftest -n $uenvcmd; then " \                            "echoRunning uenvcmd ...; " \                            "runuenvcmd; " \                   "fi\0"\         "sdboot=ifmmcinfo; then " \                            "runuenvboot; " \                            "get_bitstream_name&& " \                            "echo- load ${bitname} to PL... && " \                            "fatloadmmc 0 0x200000 ${bitname} && " \                            "fpgaloadb 0 0x200000 ${filesize} && " \                            "echoCopying Linux from SD to RAM... && " \                            "fatloadmmc 0 ${kernel_load_address} ${kernel_image} && " \                            "fatloadmmc 0 ${devicetree_load_address} ${devicetree_image} && " \                            "fatloadmmc 0 ${ramdisk_load_address} ${ramdisk_image} && " \                            "bootm${kernel_load_address} ${ramdisk_load_address} ${devicetree_load_address};" \                   "fi\0"\         "usbboot=ifusb start; then " \                            "runuenvboot; " \                            "echoCopying Linux from USB to RAM... && " \                            "fatloadusb 0 ${kernel_load_address} ${kernel_image} && " \                            "fatloadusb 0 ${devicetree_load_address} ${devicetree_image} && " \                            "fatloadusb 0 ${ramdisk_load_address} ${ramdisk_image} && " \                            "bootm${kernel_load_address} ${ramdisk_load_address} ${devicetree_load_address};" \                   "fi\0"\         "nandboot=echoCopying Linux from NAND flash to RAM... && " \                   "nandread ${kernel_load_address} 0x100000 ${kernel_size} && " \                   "nandread ${devicetree_load_address} 0x600000 ${devicetree_size} && " \                   "echoCopying ramdisk... && " \                   "nandread ${ramdisk_load_address} 0x620000 ${ramdisk_size} && " \                   "bootm${kernel_load_address} ${ramdisk_load_address}${devicetree_load_address}\0" \         "jtagboot=echoTFTPing Linux to RAM... && " \                   "tftpboot${kernel_load_address} ${kernel_image} && " \                   "tftpboot${devicetree_load_address} ${devicetree_image} && " \                   "tftpboot${ramdisk_load_address} ${ramdisk_image} && " \                   "bootm${kernel_load_address} ${ramdisk_load_address}${devicetree_load_address}\0" \         "rsa_norboot=echoCopying Image from NOR flash to RAM... && " \                   "cp.b0xE2100000 0x100000 ${boot_size} && " \                   "zynqrsa0x100000 && " \                   "bootm${kernel_load_address} ${ramdisk_load_address}${devicetree_load_address}\0" \         "rsa_nandboot=echoCopying Image from NAND flash to RAM... && " \                   "nandread 0x100000 0x0 ${boot_size} && " \                   "zynqrsa0x100000 && " \                   "bootm${kernel_load_address} ${ramdisk_load_address}${devicetree_load_address}\0" \         "rsa_qspiboot=echoCopying Image from QSPI flash to RAM... && " \                   "sfprobe 0 0 0 && " \                   "sfread 0x100000 0x0 ${boot_size} && " \                   "zynqrsa0x100000 && " \                   "bootm${kernel_load_address} ${ramdisk_load_address}${devicetree_load_address}\0" \         "rsa_sdboot=echoCopying Image from SD to RAM... && " \                   "fatloadmmc 0 0x100000 ${boot_image} && " \                   "zynqrsa0x100000 && " \                   "bootm${kernel_load_address} ${ramdisk_load_address}${devicetree_load_address}\0" \         "rsa_jtagboot=echoTFTPing Image to RAM... && " \                   "tftpboot0x100000 ${boot_image} && " \                   "zynqrsa0x100000 && " \                   "bootm${kernel_load_address} ${ramdisk_load_address}${devicetree_load_address}\0" \         "qspiboot=echoCopying Linux from QSPI flash to RAM... && " \                   "sfprobe 0 0 0 && " \                   "qspi_get_bitsize0x0A0000 && " \                   "sfread ${loadbit_addr} 0x0A0004 ${bitsize} && " \                   "fpgaloadb 0 ${loadbit_addr} ${bitsize} && " \                   "sfread ${kernel_load_address} ${qkernel_addr} ${kernel_size} && " \                   "sfread ${devicetree_load_address} ${qdevtree_addr} ${devicetree_size} &&" \                   "echoCopying ramdisk... && " \                   "sfread ${ramdisk_load_address} ${qramdisk_addr} ${ramdisk_size} && "\                   "bootm${kernel_load_address} ${ramdisk_load_address}${devicetree_load_address}\0" \         "qspiupdate=echoUpdate qspi images from sd card... && " \                   "echo- Init mmc... && mmc rescan && " \                   "echo- Init qspi flash... && sf probe 0 0 0 && " \                   "echo- Write boot.bin... && " \                   "fatloadmmc 0 0x200000 boot.bin && " \                   "sferase ${qboot_addr} ${boot_size} && " \                   "sferase ${qbootenv_addr} ${qbootenv_size} && " \                   "sfwrite 0x200000 0 ${filesize} && " \                   "get_bitstream_name&& " \                   "echo- Write ${bitstream_image}... && " \                   "fatloadmmc 0 0x200000 ${bitstream_image} && " \                   "sferase 0x0A0000 0x460000 && " \                   "mw.l0x100000 ${filesize} && " \                   "sfwrite 0x100000 0x0A0000 4 && " \                   "sfwrite 0x200000 0x0A0004 ${filesize} && " \                   "echo- Write uImage... && " \                   "fatloadmmc 0 0x200000 uImage && " \                   "sferase ${qkernel_addr} ${kernel_size} && " \                   "sfwrite 0x200000 ${qkernel_addr} ${filesize} && " \                   "echo- Write device tree... && " \                   "fatloadmmc 0 0x200000 devicetree.dtb && " \                   "sferase ${qdevtree_addr} ${devicetree_size} && " \                   "sfwrite 0x200000 ${qdevtree_addr} ${filesize} && " \                   "echo- Write Ramdisk... && " \                   "fatloadmmc 0 0x200000 uramdisk.image.gz && " \                   "sferase ${qramdisk_addr} ${ramdisk_size} && " \                   "sfwrite 0x200000 ${qramdisk_addr} ${filesize} && " \                   "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,如下:

#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT  # define ENV_HEADER_SIZE    (sizeof(uint32_t) + 1)  # define ACTIVE_FLAG   1  # define OBSOLETE_FLAG 0  #else  # define ENV_HEADER_SIZE    (sizeof(uint32_t))  #endif  #define ENV_SIZE (CONFIG_ENV_SIZE - ENV_HEADER_SIZE)  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;  

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  相关的部分 

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;  

二 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中的实现如下:

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

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

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

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

/* initialize environment */      if (should_load_env())          env_relocate();      else          set_default_env(NULL);  

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

首先调用should_load_env,如下:

/*  * Tell if it's OK to load the environment early in boot.  *  * If CONFIG_OF_CONFIG is defined, we'll check with the FDT to see  * if this is OK (defaulting to saying it's not OK).  *  * NOTE: Loading the environment early can be a bad idea if security is  *       important, since no verification is done on the environment.  *  * @return 0 if environment should not be loaded, !=0 if it is ok to load  */  static int should_load_env(void)  {  #ifdef CONFIG_OF_CONTROL      return fdtdec_get_config_int(gd->fdt_blob, "load-environment", 1);  #elif defined CONFIG_DELAY_ENVIRONMENT      return 0;  #else      return 1;  #endif  }  

从注释可以看出,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中:

void env_relocate(void)  {  #if defined(CONFIG_NEEDS_MANUAL_RELOC)      env_reloc();      env_htab.change_ok += gd->reloc_off;  #endif      if (gd->env_valid == 0) {  #if defined(CONFIG_ENV_IS_NOWHERE) || defined(CONFIG_SPL_BUILD)          /* Environment not changable */          set_default_env(NULL);  #else          bootstage_error(BOOTSTAGE_ID_NET_CHECKSUM);          set_default_env("!bad CRC");  #endif      } else {          env_relocate_spec();      }  }   

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

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

void env_relocate_spec(void)  {     int ret;      ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);      ret = readenv(CONFIG_ENV_OFFSET, (u_char *)buf);      if (ret) {          set_default_env("!readenv() failed");          return;      }      env_import(buf, 1);  }  
 

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

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

Readenv也在env_nand.c中,如下:

int readenv(size_t offset, u_char *buf)  {      size_t end = offset + CONFIG_ENV_RANGE;      size_t amount_loaded = 0;      size_t blocksize, len;      u_char *char_ptr;      blocksize = nand_info[0].erasesize;      if (!blocksize)          return 1;      len = min(blocksize, CONFIG_ENV_SIZE);      while (amount_loaded < CONFIG_ENV_SIZE && offset < end) {          if (nand_block_isbad(&nand_info[0], offset)) {              offset += blocksize;          } else {              char_ptr = &buf[amount_loaded];              if (nand_read_skip_bad(&nand_info[0], offset,                             &len, NULL,                             nand_info[0].size, char_ptr))                  return 1;              offset += blocksize;              amount_loaded += len;          }      }        if (amount_loaded != CONFIG_ENV_SIZE)          return 1;        return 0;  }  


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

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

/*  * Check if CRC is valid and (if yes) import the environment.  * Note that "buf" may or may not be aligned.  */  int env_import(const char *buf, int check)  {      env_t *ep = (env_t *)buf;        if (check) {          uint32_t crc;            memcpy(&crc, &ep->crc, sizeof(crc));            if (crc32(0, ep->data, ENV_SIZE) != crc) {              set_default_env("!bad CRC");              return 0;          }      }        if (himport_r(&env_htab, (char *)ep->data, ENV_SIZE, '\0', 0,              0, NULL)) {          gd->flags |= GD_FLG_ENV_READY;          return 1;      }        error("Cannot import environment: errno = %d\n", errno);        set_default_env("!import failed");        return 0;  }  

首先将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实现。

static int do_env_save(cmd_tbl_t *cmdtp, int flag, int argc,                 char * const argv[])  {      printf("Saving Environment to %s...\n", env_name_spec);        return saveenv() ? 1 : 0;  }    U_BOOT_CMD(      saveenv, 1, 0,  do_env_save,      "save environment variables to persistent storage",      ""  );  

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

int saveenv(void)  {      int ret = 0;      ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);      ssize_t len;      char    *res;      int env_idx = 0;      static const struct env_location location[] = {          {              .name = "NAND",              .erase_opts = {                  .length = CONFIG_ENV_RANGE,                  .offset = CONFIG_ENV_OFFSET,              },          },  #ifdef CONFIG_ENV_OFFSET_REDUND          {              .name = "redundant NAND",              .erase_opts = {                  .length = CONFIG_ENV_RANGE,                  .offset = CONFIG_ENV_OFFSET_REDUND,              },          },  #endif      };        if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)          return 1;        res = (char *)&env_new->data;      len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);      if (len < 0) {          error("Cannot export environment: errno = %d\n", errno);          return 1;      }      env_new->crc   = crc32(0, env_new->data, ENV_SIZE);  #ifdef CONFIG_ENV_OFFSET_REDUND      env_new->flags = ++env_flags; /* increase the serial */      env_idx = (gd->env_valid == 1);  #endif        ret = erase_and_write_env(&location[env_idx], (u_char *)env_new);  #ifdef CONFIG_ENV_OFFSET_REDUND      if (!ret) {          /* preset other copy for next write */          gd->env_valid = gd->env_valid == 2 ? 1 : 2;          return ret;      }        env_idx = (env_idx + 1) & 1;      ret = erase_and_write_env(&location[env_idx], (u_char *)env_new);      if (!ret)          printf("Warning: primary env write failed,"                  " redundancy is lost!\n");  #endif        return ret;  }  

 

定义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的操作。

 

 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 硫酸铬钾中毒怎么办 门锁能扭动却打不开怎么办 防盗门保险坏了怎么办 厦门工会卡过期怎么办 阴雨天被子受潮怎么办 衣服晾臭了怎么办 喷砂机油水分离泵有油怎么办 水太烫玻璃瓶盖打不开怎么办 玻璃罐的玻璃盖打不开怎么办? 开红酒没有开瓶器怎么办 手机卡突然坏了怎么办 滚筒洗衣机打不开门怎么办 全自动洗衣机门开不开怎么办 好期待评分不足怎么办 单片机数码管亮度低怎么办 猫的同轴灯不亮怎么办 楼下邻居太吵怎么办 冰箱冷却液内露怎么办 冷却水没有了会怎么办 金立m7信号不好怎么办 csgo凉了饰品怎么办 模型拟合度低怎么办 石膏线用发泡胶怎么办 电表上显示err10怎么办 电表显示欠压怎么办 tcl电视遥控器失灵怎么办 玩具汽车遥控器失灵怎么办 玩具车遥控丢了怎么办 按压扣坏了怎么办 电脑用不了鼠标怎么办 多肉上的肉虫子怎么办 警察被取消警衔怎么办 隐形眼镜带歪了怎么办 gta5正在加载慢怎么办 gta5线上卡住了怎么办 皮子掉的颜色怎么办 gta5死了之后车怎么办 潜水口里有水怎么办 800度近视潜水怎么办 不会游泳深潜怎么办 高度近视浮潜怎么办