转载: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 

    "bootdelay=" MK_STR (CONFIG_BOOTDELAY) "\0" 
#endif 

    "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 

    "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; 
    , crc2_ok = ; 
    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(, tmp_env1->data, ENV_SIZE) == tmp_env1->crc); 
    crc2_ok = (crc32(, tmp_env2->data, ENV_SIZE) == tmp_env2->crc); 
 
    if (!crc1_ok && !crc2_ok) 
        gd->env_valid = ; 
    else if(crc1_ok && !crc2_ok) 
        gd->env_valid = ; 
    else if(!crc1_ok && crc2_ok) 
        gd->env_valid = ; 
    else 
    { 
        /* both ok - check serial */ 
         && tmp_env2->flags == ) 
            gd->env_valid = ; 
         && tmp_env1->flags == ) 
            gd->env_valid = ; 
        else if(tmp_env1->flags > tmp_env2->flags) 
            gd->env_valid = ; 
        else if(tmp_env2->flags > tmp_env1->flags) 
            gd->env_valid = ; 
        else /* flags are equal - almost impossible */ 
            gd->env_valid = ; 
    } 
 
    ) 
        env_ptr = tmp_env1; 
    ) 
        env_ptr = tmp_env2; 
#else /* ENV_IS_EMBEDDED */ 
    gd->env_addr  = (ulong)&default_environment[]; 
    gd->env_valid = ; 
#endif /* ENV_IS_EMBEDDED */ 
 
    ); 
}

初始化环境变量地址为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 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 (-); 
#endif 
 
        if (sizeof(default_environment) > ENV_SIZE) 
        { 
            puts ("*** Error - default environment is too large\n\n"); 
            return; 
        } 
 
        memset (env_ptr, , 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 = ; 
    } 
    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, , sizeof(opts)); 
 
    opts.buffer = (u_char *)env_ptr; 
    opts.length = total; 
    opts.offset = CFG_ENV_OFFSET; 
    opts.readoob = ; 
    opts.quiet   = ; 
    opts.noecc   = ; 
    opts.nocheckbadblk = ; 
    ret = nand_read_opts(&nand_info[], &opts); 
 
#else 
    ret = nand_read(&nand_info[], CFG_ENV_OFFSET, &total, (u_char *)env_ptr); 
#endif 
    if (ret || total != CFG_ENV_SIZE) 
        return use_default(); 
 
    , 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(); 
 
    ; env_get_char(i) != ) 
    { 
        int val; 
 
        for (nxt = i; env_get_char(nxt) != '\0'; ++nxt) 
        { 
            if (nxt >= CFG_ENV_SIZE) 
            { 
                return (NULL); 
            } 
        } 
        ) 
            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; 
    ; 
 
    puts ("Erasing Nand..."); 
    ], CFG_ENV_OFFSET, CFG_ENV_SIZE)) 
        ; 
 
    puts ("Writing to Nand... "); 
    total = CFG_ENV_SIZE; 
    ret = nand_write(&nand_info[], CFG_ENV_OFFSET, &total, (u_char *)env_ptr); 
    if (ret || total != CFG_ENV_SIZE) 
        ; 
 
    puts ("done\n"); 
    return ret; 
}

这个函数比较简单,首先就是擦除相应部分的flash,然后将环境变量结构体写到对应的flash部分,我分析的mini2440中环境变量的偏移地址是256K,总共大小为64K

#define CFG_ENV_OFFSET      0x40000

#define CFG_ENV_SIZE0x10000/* Total Size of Environment Sector */

u-boot中环境变量的实现的更多相关文章

  1. Linux中环境变量文件及配置

    Linux中环境变量文件及配置   一.环境变量文件介绍 转自:http://blog.csdn.net/cscmaker/article/details/7261921 Linux中环境变量包括系统 ...

  2. Linux中环境变量文件及配置(转载)

    一.环境变量文件介绍 转自:http://blog.csdn.net/cscmaker/article/details/7261921 Linux中环境变量包括系统级和用户级,系统级的环境变量是每个登 ...

  3. Linux中环境变量中文件执行顺序

        Linux 的变量可分为两类:环境变量和本地变量   环境变量:或者称为全局变量,存在于所有的shell 中,在你登陆系统的时候就已经有了相应的系统定义的环境变量了.Linux 的环境变量具有 ...

  4. Node.js中环境变量process.env详解

    Node.js中环境变量process.env详解process | Node.js API 文档http://nodejs.cn/api/process.html官方解释:process 对象是一个 ...

  5. Linux中环境变量文件

    一.环境变量文件介绍 转自:http://blog.csdn.net/cscmaker/article/details/7261921 Linux中环境变量包括系统级和用户级,系统级的环境变量是每个登 ...

  6. shell中环境变量

    Linux中环境变量包括系统级和用户级,系统级的环境变量是每个登录到系统的用户都要读取的系统变量,而用户级的环境变量则是该用户使用系统时加载的环境变量. 所以管理环境变量的文件也分为系统级和用户级的, ...

  7. setlocal启动批处理文件中环境变量的本地化

    setlocal启动批处理文件中环境变量的本地化 在执行 SETLOCAL 之后所做的环境改动只限于批处理文件.要还原原先的设置,必须执行 ENDLOCAL. 学习了:https://baike.ba ...

  8. Windows系统中环境变量不展开的问题

    Windows系统中环境变量不展开的问题 问题现象:Windows.System32等系统目录里命令,无法通过Path搜索路径来执行.查看Path环境变量结果如下: D:\>echo %Path ...

  9. ubuntu下关于profile和bashrc中环境变量的理解(转)

    ubuntu下关于profile和bashrc中环境变量的理解(转)   (0) 写在前面 有些名词可能需要解释一下.(也可以先不看这一节,在后面看到有疑惑再上来看相关解释) $PS1和交互式运行(r ...

随机推荐

  1. 杂乱的code

    /*o(n)的堆化方法*/ void myjust(vector<int>& A,int i){ int l=i*2+1; int r=i*2+2; int minn=i; if( ...

  2. JDK动态代理小例子

    一个小汽车,有一个跑run()的方法,我们想使用jdk动态代理使小汽车执行run之前 加点油,run之后洗车. 有四个类,接口Car(小汽车)Kayan(具体实现类(卡宴)) CarProxy(汽车的 ...

  3. 在go中连接mysql

    5.访问数据库 5.1 database/sql接口 5.2 使用MySQL数据库 5.3 使用SQLite数据库 5.4 使用PostgreSQL数据库 5.5 使用Beego orm库进行ORM开 ...

  4. lr_start_timer,lr_get_transaction_duration,lr_get_transaction_wasted_time函数使用总结

    lr_start_timer: 函数的功能: 为了计算时间更加精确,可以用这个函数去掉LR自身的检查点所浪费的时间.如text check and image time Action() { doub ...

  5. Codeforces Round #278 (Div. 1) B - Strip dp+st表+单调队列

    B - Strip 思路:简单dp,用st表+单调队列维护一下. #include<bits/stdc++.h> #define LL long long #define fi first ...

  6. CentOS下Redis安装与配置

    本文详细介绍redis单机单实例安装与配置,服务及开机自启动.如有不对的地方,欢迎大家拍砖o(∩_∩)o (以下配置基于CentOS release 6.5 Final, redis版本3.0.2 [ ...

  7. 图解安装CLion编写程序并进行单元测试

    我对JetBrains公司的IDE情有独钟,于是这里使用的IDE是CLion,CLion是以IntelliJ为基础,专为开发C及C++所设计的跨平台IDE,可以在Windows.Linux及MacOS ...

  8. Java面向对象封装和继承

    面向对象 什么是面向过程.面向对象? 面向过程与面向对象都是我们编程中,编写程序的一种思维方式. 面向过程的程序设计方式,是遇到一件事时,思考“我该怎么做”,然后一步步实现的过程.例如:公司打扫卫生( ...

  9. Hibernate 过滤查询(hibernate过滤器的使用)

    我们在开发过程中过滤查询使用的还是挺多的,今天来学习一下hibernate的过滤器的使用,首先学习在配置文件中如何使用,然后再介绍如何使用注解配置. 1.使用配置文件配置过滤器  1)首先我们使用my ...

  10. 我的OI生涯 第七章 终篇

    11.10日. 我们TSOI再次来到了熟悉的燕山大学,只不过这次是真刀真枪的干了. 望着那座熟悉的小桥,身边的好友不知此行过后还有多少. 下午才到,出人意外的是这次没有住燕大宾馆而是选择了熟悉的格林豪 ...