u-boot环境变量

来源:互联网 发布:java语言爱心代码表白 编辑:程序博客网 时间:2024/06/01 10:21

u-boot通过环境变量为用户提供一定程度的可配置性,如波特率,启动参数等。环境变量固化在非易失性存储介质中,通过saveenv来保存。可配置性意味着环境变量是可以添加、删除和修改的,也就是说环境变量的内容可能会频繁变化,为了不让这种变化对u-boot的代码和数据造成破坏,通常的选择是在FLASH中预留一个专门用来存储环境变量的块。
U-boot环境变量大致分为四个部分:环境变量的结构,初始化,环境变量的保存,环境变量的读取。根据这个思路,依次学习各个部分。
本例中环境变量存储在NAND FLASH上,且环境变量没有嵌入到u-boot中,即ENV_IS_EMBEDDED宏状态是undefined。NAND FLASH擦除以块为单位,一块的大小时128K,下图是本例u-boot和环境变量在NAND FLASH上的存储, 单独为环境变量分配了一个块的存储空间。
这里写图片描述

一、环境变量的结构

环境变量是一个结构体

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保存的是整个环境变量值做CRC运算的结果,用于校验环境变量是否合法。
元素data[ENV_SIZE] 保存了环境变量的值,下面是默认的环境变量的值,可以看出环境变量其实是一个字符串,由”\0”将每个变量分开,环境变量的末尾是”\0\0”

uchar default_environment[] = {#ifdef  CONFIG_BOOTARGS    "bootargs=" CONFIG_BOOTARGS         "\0"#endif#ifdef  CONFIG_BOOTCOMMAND    "bootcmd="  CONFIG_BOOTCOMMAND      "\0"#endif#ifdef  CONFIG_RAMBOOTCOMMAND    "ramboot="  CONFIG_RAMBOOTCOMMAND       "\0"#endif#ifdef  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_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_PREBOOT    "preboot="  CONFIG_PREBOOT          "\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_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"};

二、环境变量的初始化

u-boot首先调用common/env_nand.c下的int env_init(void)函数。由于ENV_IS_EMBEDDED是未定义的,故初始化函数执行了两条语句,这里暂时认为CRC是正确的,在环境变量拷贝到SDRAM上之后再做校验。

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);}

环境变量原本是存放在NAND FLASH上的,由于环境变量经常被用到,且NAND FLASH的擦写速度太慢,需要将环境变量从FLASH上拷贝到SDRAM上,u-boot通过调用env_relocate()完成这部分的操作。

void env_relocate (void){    DEBUGF ("%s[%d] offset = 0x%lx\n", __FUNCTION__,__LINE__,        gd->reloc_off);#ifdef ENV_IS_EMBEDDED    /*     * The environment buffer is embedded with the text segment,     * just relocate the environment pointer     */    env_ptr = (env_t *)((ulong)env_ptr + gd->reloc_off);    DEBUGF ("%s[%d] embedded ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);#else    /*     * We must allocate a buffer for the environment     */     //在malloc区分配大小为CFG_ENV_SIZE的空间,并将env_ptr指向该空间的起始地址。    env_ptr = (env_t *)malloc (CFG_ENV_SIZE);    DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);#endif    /*     * After relocation to RAM, we can always use the "memory" functions     */    env_get_char = env_get_char_memory;    // gd->env_valid的值为1    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 {        // 从FLASH中读取环境变量的内容到SDRAM上。        env_relocate_spec ();    }    // gd->env_addr指向环境变量的第一个参数。    gd->env_addr = (ulong)&(env_ptr->data);}

通过env_relocate_spec ()读取环境变量到SDRAM上。请注意,刚烧写的板子上仅仅给环境变量预留了一定大小的空间,并没有环境变量的内容。根据重定位函数,我们知道板子第一次开机时,读出来的环境变量进行CRC校验时肯定是失败的,会使用预存的默认环境变量。

void env_relocate_spec (void){#if !defined(ENV_IS_EMBEDDED)    ulong total = CFG_ENV_SIZE;    int ret;    // 读取FLASH上环境变量到env_ptr所指向的地址,若读取失败,则使用默认的环境变量。    ret = nand_read(&nand_info[0], CFG_ENV_OFFSET, &total, (u_char*)env_ptr);    if (ret || total != CFG_ENV_SIZE)        return use_default();    // 校验CRC,若失败,则使用默认的环境变量。    if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc)        return use_default();#endif /* ! ENV_IS_EMBEDDED */}
static void use_default(){    puts ("*** Warning - bad CRC or NAND, using default environment\n\n");    if (default_environment_size > CFG_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,            default_environment_size);    // 计算CRC,并赋给SDRAM上的env_ptr->crc    env_ptr->crc = crc32(0, env_ptr->data, ENV_SIZE);    gd->env_valid = 1;}

至此,环境变量的初始化就结束了,环境变量的值被拷贝到SDRAM,全局变量
gd->env_addr = (ulong)&(env_ptr->data); 指向了环境变量的第一个参数。
gd->env_valid = 1;

三、环境变量的保存

int saveenv(void)用来保存环境变量的。由于NAND FLASH的每个位只能从1->0,而不能相反,所以我们需要先对其擦除,然后再进行写操作。

int saveenv(void){    ulong total;    int ret = 0;    puts ("Erasing Nand...");    // 擦除FLASH,&nand_info[0]是NAND的起始地址,擦除操作从CFG_ENV_OFFSET开始,大小为CFG_ENV_SIZE。    if (nand_erase(&nand_info[0], CFG_ENV_OFFSET, CFG_ENV_SIZE))        return 1;    puts ("Writing to Nand... ");    total = CFG_ENV_SIZE;    // 写FLASH,&nand_info[0]是NAND的起始地址,将env_ptr所指向的内容写到CFG_ENV_OFFSET开始的位置,大小为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的块擦除的大小为128K (0x20000),下面的宏定义保证了环境变量保存在单独的一个NAND 块上。

#define CFG_ENV_OFFSET      0x40000#define CFG_ENV_SIZE        0x20000 /* Total Size of Environment Sector */

四、环境变量的读取

在u-boot运行时,我们可以使用U-boot命令配置环境变量而不是修改代码来控制U-boot的行为,如我们可以修改环境变量来配置开发板的IP地址。u-boot配置环境变量就是配置存储器上的某个地址的内容,读取这个地址的内容再进行接下来的操作。u-boot中读取环境变量的函数是common/cmd_nvedit.c下的
int getenv_r (char *name, char *buf, unsigned len)。
@para1: 需要读取的环境变量名
@para2: 若找到了该环境变量,将值存放到buf。
@para3: buf的长度
@return value: 若有该环境变量,返回值为环境变量参数的长度。否则返回-1

int getenv_r (char *name, char *buf, unsigned len){    int i, nxt;    for (i=0; env_get_char(i) != '\0'; i=nxt+1) {        int val, n;        for (nxt=i; env_get_char(nxt) != '\0'; ++nxt) {            if (nxt >= CFG_ENV_SIZE) {                return (-1);            }        }        if ((val=envmatch((uchar *)name, i)) < 0)            continue;        /* found; copy out */        n = 0;        while ((len > n++) && (*buf++ = env_get_char(val++)) != '\0')            ;        if (len == n)            *buf = '\0';        return (n);    }    return (-1);}

其中 env_get_char指向函数env_get_char_memory。
功能是获取环境变量中,索引为index处的字符。

uchar env_get_char_memory (int index){    if (gd->env_valid) {        // gd->env_addr是环境变量的首地址。        return ( *((uchar *)(gd->env_addr + index)) );    } else {        return ( default_environment[index] );    }}
原创粉丝点击