u-boot环境变量

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] );
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值