zoukankan      html  css  js  c++  java
  • u-boot中环境变量的实现

    转载: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 "" 
    #endif 
    #if defined(CONFIG_BOOTCOMMAND) 
        "bootcmd=" CONFIG_BOOTCOMMAND "" 
    #endif 
    #if defined(CONFIG_RAMBOOTCOMMAND) 
        "ramboot=" CONFIG_RAMBOOTCOMMAND "" 
    #endif 
    #if defined(CONFIG_NFSBOOTCOMMAND) 
        "nfsboot=" CONFIG_NFSBOOTCOMMAND "" 
    #endif 
    #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0
        "bootdelay=" MK_STR (CONFIG_BOOTDELAY) "" 
    #endif 
    #if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0
        "baudrate=" MK_STR (CONFIG_BAUDRATE) "" 
    #endif 
    #ifdef    CONFIG_LOADS_ECHO 
        "loads_echo=" MK_STR (CONFIG_LOADS_ECHO) "" 
    #endif 
    #ifdef    CONFIG_ETHADDR 
        "ethaddr=" MK_STR (CONFIG_ETHADDR) "" 
    #endif 
    #ifdef    CONFIG_ETH1ADDR 
        "eth1addr=" MK_STR (CONFIG_ETH1ADDR) "" 
    #endif 
    #ifdef    CONFIG_ETH2ADDR 
        "eth2addr=" MK_STR (CONFIG_ETH2ADDR) "" 
    #endif 
    #ifdef    CONFIG_ETH3ADDR 
        "eth3addr=" MK_STR (CONFIG_ETH3ADDR) "" 
    #endif 
    #ifdef    CONFIG_ETHPRIME 
        "ethprime=" CONFIG_ETHPRIME "" 
    #endif 
    #ifdef    CONFIG_IPADDR 
        "ipaddr=" MK_STR (CONFIG_IPADDR) "" 
    #endif 
    #ifdef    CONFIG_SERVERIP 
        "serverip=" MK_STR (CONFIG_SERVERIP) "" 
    #endif 
    #ifdef    CFG_AUTOLOAD 
        "autoload=" CFG_AUTOLOAD "" 
    #endif 
    #ifdef    CONFIG_ROOTPATH 
        "rootpath=" MK_STR (CONFIG_ROOTPATH) "" 
    #endif 
    #ifdef    CONFIG_GATEWAYIP 
        "gatewayip=" MK_STR (CONFIG_GATEWAYIP) "" 
    #endif 
    #ifdef    CONFIG_NETMASK 
        "netmask=" MK_STR (CONFIG_NETMASK) "" 
    #endif 
    #ifdef    CONFIG_HOSTNAME 
        "hostname=" MK_STR (CONFIG_HOSTNAME) "" 
    #endif 
    #ifdef    CONFIG_BOOTFILE 
        "bootfile=" MK_STR (CONFIG_BOOTFILE) "" 
    #endif 
    #ifdef    CONFIG_LOADADDR 
        "loadaddr=" MK_STR (CONFIG_LOADADDR) "" 
    #endif 
    #ifdef    CONFIG_PREBOOT 
        "preboot=" CONFIG_PREBOOT "" 
    #endif 
    #ifdef    CONFIG_CLOCKS_IN_MHZ 
        "clocks_in_mhz=" "1" "" 
    #endif 
    #if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0
        "pcidelay=" MK_STR (CONFIG_PCI_BOOTDELAY) "" 
    #endif 
    #ifdef  CONFIG_EXTRA_ENV_SETTINGS 
        CONFIG_EXTRA_ENV_SETTINGS 
    #endif 
        ""            /* Termimate env_t data with 2 NULs */ 
    }; 

    实际上就是xxxx=xxxx”’’”xxxxx=xxxxxx”’,每个环境变量之间用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 ", __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 ", __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 "); 
    #else 
            puts ("*** Warning - bad CRC, using default environment "); 
            SHOW_BOOT_PROGRESS (-1); 
    #endif 
     
            if (sizeof(default_environment) > ENV_SIZE) 
            { 
                puts ("*** Error - default environment is too large "); 
                return
            } 
     
            memset (env_ptr, 0sizeof(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, 0sizeof(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) != ''; i = nxt + 1
        { 
            int val; 
     
            for (nxt = i; env_get_char(nxt) != ''; ++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 "); 
        return ret; 

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

    #define CFG_ENV_OFFSET      0x40000

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

  • 相关阅读:
    js 获取当前时间
    html5拨打电话及发短信
    ::before和::after伪元素的使用
    vue单页面应用刷新网页后vuex的state数据丢失问题以及beforeunload的兼容性
    CSS3径向渐变实现优惠券波浪造型
    iOS 幻灯片的自动循环滚动
    iOS 编译正常,但无法运行到真机和模拟器上,Choose a destination with a supported architecture in order to run on this device.
    iOS webView抓取改变js的alertView
    iOS 创建单例的方法
    webView图片点击可以实现预览效果
  • 原文地址:https://www.cnblogs.com/pengdonglin137/p/3293871.html
Copyright © 2011-2022 走看看