转载:http://blog.chinaunix.net/uid-28236237-id-3867041.html
U-boot中通过环境参数保存一些配置,这些配置可以通过修改环境参数、保存环境参数、读取环境参数等操作进行灵活的配置,便于调试开发。这篇文章主要来分析一下u-boot中环境参数的实现。文章主要分为四个部分,第一是环境参数的存储格式,第二部分是环境参数的初始化,第三部分是环境参数的读取,第四个部分是环境参数保存过程。
首先,我们来看一下环境参数的存储格式。一般嵌入式系统的第一个分区是boot分区,而环境参数一般会采用一种格式保存到boot代码区之后,当然,这个位置不能超出第一个分区的边界。
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; |
环境参数就是以这样的格式存储到flash上的,其中crc表示对整个环境参数数据的校验码。Data中保存环境参数,参数的组织格式是这样的。
static uchar default_environment[] = { #if defined(CONFIG_BOOTARGS) "bootargs=" CONFIG_BOOTARGS "\0" #endif #if defined(CONFIG_BOOTCOMMAND) "bootcmd=" CONFIG_BOOTCOMMAND "\0" #endif #if defined(CONFIG_RAMBOOTCOMMAND) "ramboot=" CONFIG_RAMBOOTCOMMAND "\0" #endif #if defined(CONFIG_NFSBOOTCOMMAND) "nfsboot=" CONFIG_NFSBOOTCOMMAND "\0" #endif #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) "bootdelay=" MK_STR (CONFIG_BOOTDELAY) "\0" #endif #if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0) "baudrate=" MK_STR (CONFIG_BAUDRATE) "\0" #endif #ifdef CONFIG_LOADS_ECHO "loads_echo=" MK_STR (CONFIG_LOADS_ECHO) "\0" #endif #ifdef CONFIG_ETHADDR "ethaddr=" MK_STR (CONFIG_ETHADDR) "\0" #endif #ifdef CONFIG_ETH1ADDR "eth1addr=" MK_STR (CONFIG_ETH1ADDR) "\0" #endif #ifdef CONFIG_ETH2ADDR "eth2addr=" MK_STR (CONFIG_ETH2ADDR) "\0" #endif #ifdef CONFIG_ETH3ADDR "eth3addr=" MK_STR (CONFIG_ETH3ADDR) "\0" #endif #ifdef CONFIG_ETHPRIME "ethprime=" CONFIG_ETHPRIME "\0" #endif #ifdef CONFIG_IPADDR "ipaddr=" MK_STR (CONFIG_IPADDR) "\0" #endif #ifdef CONFIG_SERVERIP "serverip=" MK_STR (CONFIG_SERVERIP) "\0" #endif #ifdef CFG_AUTOLOAD "autoload=" CFG_AUTOLOAD "\0" #endif #ifdef CONFIG_ROOTPATH "rootpath=" MK_STR (CONFIG_ROOTPATH) "\0" #endif #ifdef CONFIG_GATEWAYIP "gatewayip=" MK_STR (CONFIG_GATEWAYIP) "\0" #endif #ifdef CONFIG_NETMASK "netmask=" MK_STR (CONFIG_NETMASK) "\0" #endif #ifdef CONFIG_HOSTNAME "hostname=" MK_STR (CONFIG_HOSTNAME) "\0" #endif #ifdef CONFIG_BOOTFILE "bootfile=" MK_STR (CONFIG_BOOTFILE) "\0" #endif #ifdef CONFIG_LOADADDR "loadaddr=" MK_STR (CONFIG_LOADADDR) "\0" #endif #ifdef CONFIG_PREBOOT "preboot=" CONFIG_PREBOOT "\0" #endif #ifdef CONFIG_CLOCKS_IN_MHZ "clocks_in_mhz=" "1" "\0" #endif #if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0) "pcidelay=" MK_STR (CONFIG_PCI_BOOTDELAY) "\0" #endif #ifdef CONFIG_EXTRA_ENV_SETTINGS CONFIG_EXTRA_ENV_SETTINGS #endif "\0" /* Termimate env_t data with 2 NULs */ }; |
实际上就是”xxxx=xxxx”’\0’”xxxxx=xxxxxx”’\0’,每个环境变量之间用NULL隔开
U-boot的环境变量最开始是保存在flash上的,在u-boot第二阶段中会将环境变量从flash上读到内存里,并进行相应的初始化。
/*最开始,调用env_init函数对环境变量进行初始化, 这里暂时不考虑ENV_IS_EMBEDDED的情况,所以,初始化 工作就是设置env_addr地址,并设置env_valid为有限 */ int env_init(void) { #if defined(ENV_IS_EMBEDDED) ulong total; int crc1_ok = 0, crc2_ok = 0; env_t *tmp_env1, *tmp_env2; total = CFG_ENV_SIZE; tmp_env1 = env_ptr; tmp_env2 = (env_t *)((ulong)env_ptr + CFG_ENV_SIZE); crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc); crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc); if (!crc1_ok && !crc2_ok) gd->env_valid = 0; else if(crc1_ok && !crc2_ok) gd->env_valid = 1; else if(!crc1_ok && crc2_ok) gd->env_valid = 2; else { /* both ok - check serial */ if(tmp_env1->flags == 255 && tmp_env2->flags == 0) gd->env_valid = 2; else if(tmp_env2->flags == 255 && tmp_env1->flags == 0) gd->env_valid = 1; else if(tmp_env1->flags > tmp_env2->flags) gd->env_valid = 1; else if(tmp_env2->flags > tmp_env1->flags) gd->env_valid = 2; else /* flags are equal - almost impossible */ gd->env_valid = 1; } if (gd->env_valid == 1) env_ptr = tmp_env1; else if (gd->env_valid == 2) env_ptr = tmp_env2; #else /* ENV_IS_EMBEDDED */ gd->env_addr = (ulong)&default_environment[0]; gd->env_valid = 1; #endif /* ENV_IS_EMBEDDED */ return (0); } |
初始化环境变量地址为default值之后,调用下面env_relocate函数具体分配内存空间,将环境变量从flash中读到内存中来,完成初始化过程
void env_relocate (void) { DEBUGF ("%s[%d] offset = 0x%lx\n", __FUNCTION__, __LINE__, gd->reloc_off); /*后面需要从flash中读出环境变量来,首先分配一块buffer来装这些数据 这里调用malloc分配空间,env_ptr指向这个空间*/ env_ptr = (env_t *)malloc (CFG_ENV_SIZE); DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__, __LINE__, env_ptr); /* * After relocation to RAM, we can always use the "memory" functions */ env_get_char = env_get_char_memory; if (gd->env_valid == 0) { #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 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)); #ifdef CFG_REDUNDAND_ENVIRONMENT env_ptr->flags = 0xFF; #endif env_crc_update (); gd->env_valid = 1; } else { /*调用下面的函数完成具体的环境参数读取动作*/ env_relocate_spec (); } /*将环境变量具体内存中buffer位置赋值给env_addr中*/ gd->env_addr = (ulong) & (env_ptr->data); } |
具体的读操作通过env_relocate_spec来说完成,u-boot根据flash种类不同,这个函数的实现方式也不一样。对于nandflash的实现,这个函数定义在env_nand.c这个文件中。函数具体实现如下
void env_relocate_spec (void) { #if !defined(ENV_IS_EMBEDDED) ulong total = CFG_ENV_SIZE; int ret; #ifdef CONFIG_SURPORT_WINCE nand_read_options_t opts; memset(&opts, 0, sizeof(opts)); opts.buffer = (u_char *)env_ptr; opts.length = total; opts.offset = CFG_ENV_OFFSET; opts.readoob = 0; opts.quiet = 1; opts.noecc = 1; opts.nocheckbadblk = 1; ret = nand_read_opts(&nand_info[0], &opts); #else ret = nand_read(&nand_info[0], CFG_ENV_OFFSET, &total, (u_char *)env_ptr); #endif 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 */ } |
前面介绍了环境变量初始化的过程,在完成了初始化之后。U-boot其它部分的代码在要调用环境变的时候可以调用相应的接口读取。这个接口就是getenv
char *getenv (char *name) { int i, nxt; WATCHDOG_RESET(); for (i = 0; env_get_char(i) != '\0'; i = nxt + 1) { int val; for (nxt = i; env_get_char(nxt) != '\0'; ++nxt) { if (nxt >= CFG_ENV_SIZE) { return (NULL); } } if ((val = envmatch((uchar *)name, i)) < 0) continue; return ((char *)env_get_addr(val)); } return (NULL); } |
Getenv函数就是在gd->env_addr这个buffer中不断的寻找name相对应的字符串,找到这个字符串”name=xxxxxx”之后将第一个x的地址返回。
本文还需要分析一下的就是对环境参数的保存,如果通过u-boot命令setenv修改了环境参数,我们必须还要通过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; } |
这个函数比较简单,首先就是擦除相应部分的flash,然后将环境变量结构体写到对应的flash部分,我分析的mini2440中环境变量的偏移地址是256K,总共大小为64K
#define CFG_ENV_OFFSET 0x40000
#define CFG_ENV_SIZE0x10000/* Total Size of Environment Sector */