• ”ENV_IS_EMBEDDED“解惑以及相关的移植实验


    一、概述( ENV_IS_EMBEDDED的目的)

    经典资料

    认识     

        ENV_IS_EMBEDDED只有在CFG_ENV_IS_IN_FLASH或者CFG_ENV_IS_IN_NAND定义了才有效。也就是说,这种功能只有在使用了norflash或者nandflash时才有效。本文是以使用norflash为例来说明的。

        上边贴的几张图片讲的内容主要是这个意思:由于uboot.bin是100多kb,而环境变量要占一个扇区。norflash前100Kb中才有空间比较小的扇区,例如第二个扇区才8Kb,而后边的扇区比较大,都是64Kb。程序运行后,通常需要将环境变量拷贝到RAM中,这样访问速度才快,因此RAM中也需要为环境变量区占用和flash中一样大的空间。

        倘若,不使用 ENV_IS_EMBEDDED这种功能,将环境变量区放在所有代码段的最后边,环境变量区只能占用64Kb的扇区,而环境变量区实际上有可能不需要那么大的空间,就造成了flash和RAM空间的浪费。

        倘若使用ENV_IS_EMBEDDED功能,将环境变量区嵌入到代码段中,例如占用第二个扇区的8Kb(环境变量区的前后都有代码,故曰"嵌入环境变量"),就可以节省lash和RAM空间。

        此外,这种功能的好处不止此。重定位时,将代码拷贝到RAM中了,顺便也将环境变量区拷贝了进来,这样环境变量初始化过程中,就不需要在堆区开辟空间然后搬移,省了不少事。

    二、程序分析(怎样实现这个目的的)

    1、定义(include/environment.h)

    #if defined(CFG_ENV_IS_IN_FLASH)
    some other define
    # if (CFG_ENV_ADDR >= CFG_MONITOR_BASE) &&
    (CFG_ENV_ADDR+CFG_ENV_SIZE) <= (CFG_MONITOR_BASE + CFG_MONITOR_LEN)
    # define ENV_IS_EMBEDDED 1
    # endif
    # if defined(CFG_ENV_ADDR_REDUND) || defined(CFG_ENV_OFFSET_REDUND)
    # define CFG_REDUNDAND_ENVIRONMENT 1
    # endif
    #endif /* CFG_ENV_IS_IN_FLASH */

        只有定义了CFG_ENV_IS_IN_FLASH,并且满足下边的条件才有可能定义这个宏,这个宏是在smdk2410.h中定义的。

    # if (CFG_ENV_ADDR >= CFG_MONITOR_BASE) && 
    (CFG_ENV_ADDR+CFG_ENV_SIZE) <= (CFG_MONITOR_BASE + CFG_MONITOR_LEN)

    2、与ENV_IS_EMBEDDED相关的全局变量定义(common/environment.c)

    #if defined(ENV_IS_EMBEDDED)
     

    /* XXX - This only works with GNU C */
    # define __PPCENV__ __attribute__ ((section(".text"))) //强制将environment转换为代码区
    # define __PPCTEXT__ __attribute__ ((section(".text")))

    /*
    * Macros to generate global absolutes.
    */
    #define GEN_SYMNAME(str) SYM_CHAR #str
    #define GEN_VALUE(str) #str
    #define GEN_ABS(name, value)
    asm (".globl " GEN_SYMNAME(name));
    asm (GEN_SYMNAME(name) " = " GEN_VALUE(value))

    /*
    * Macros to transform values
    * into environment strings.
    */
    #define XMK_STR(x) #x
    #define MK_STR(x) XMK_STR(x)

    /*
    * Check to see if we are building with a
    * computed CRC. Otherwise define it as ~0.
    */
    #if !defined(ENV_CRC)
    # define ENV_CRC ~0
    #endif

    env_t environment __PPCENV__ = {
    ENV_CRC, /* CRC Sum */
    #ifdef CFG_REDUNDAND_ENVIRONMENT
    1, /* Flags: valid */
    #endif
    {
    #if defined(CONFIG_BOOTARGS)
    "bootargs=" CONFIG_BOOTARGS ""
    #endif

    other defines

    #ifdef CONFIG_EXTRA_ENV_SETTINGS
    CONFIG_EXTRA_ENV_SETTINGS
    #endif
    "" /* Term. env_t.data with 2 NULs */
    }
    };
    #ifdef CFG_ENV_ADDR_REDUND
    env_t redundand_environment __PPCENV__ = {
    0, /* CRC Sum: invalid */
    0, /* Flags: invalid */
    {
    ""
    }
    };
    #endif /* CFG_ENV_ADDR_REDUND */

    /*
    * These will end up in the .text section
    * if the environment strings are embedded
    * in the image. When this is used for
    * tools/envcrc, they are placed in the
    * .data/.sdata section.
    *
    */
    unsigned long env_size __PPCTEXT__ = sizeof(env_t);

    /*
    * Add in absolutes.
    */
    GEN_ABS(env_offset, CFG_ENV_OFFSET);

    #endif /* ENV_IS_EMBEDDED */

        这部分程序就是建立嵌入环境变量区,并强制性转换为代码区。这个环境变量区是完整的,包含头部crc。嵌入环境变量区一开始也在程序中初始化了(用常数初始化),我们知道crc是环境变量(data)的校验码,也不知道编译器是怎么实现在编译的时候直接根据环境变量data区实现crc的赋值(有初始化了data区生成的crc)。

    3、 ENV_IS_EMBEDDED所涉及的在(common/env_flash.c)中的定义

    #ifdef ENV_IS_EMBEDDED
    
    extern uchar environment[];
    env_t *env_ptr = (env_t *)(&environment[0]); //倘若定义了ENV_IS_EMBEDDED,那么指向嵌入环境区
    
    #ifdef CMD_SAVEENV //如果使用了保存环境变量命令
    /* static env_t *flash_addr = (env_t *)(&environment[0]);-broken on ARM-wd-*/
    static env_t *flash_addr = (env_t *)CFG_ENV_ADDR; //保存环境变量区时的下载地址
    #endif
    
    #else /* ! ENV_IS_EMBEDDED */
    
    env_t *env_ptr = (env_t *)CFG_ENV_ADDR; // 定义flash中环境变量的地址
    #ifdef CMD_SAVEENV
    static env_t *flash_addr = (env_t *)CFG_ENV_ADDR;
    #endif
    
    #endif /* ENV_IS_EMBEDDED */

    4、env_init()函数(common/env_flash.c)

    int  env_init(void)  
    {
    #ifdef CONFIG_OMAP2420H4 //没有定义
        int flash_probe(void);
    
        if(flash_probe() == 0)
            goto bad_flash;
    #endif
        if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) { //如果校验成功
            gd->env_addr  = (ulong)&(env_ptr->data); //定位在flash中
            gd->env_valid = 1; //代表flash中存在环境变量
            return(0);
        }
    #ifdef CONFIG_OMAP2420H4
    bad_flash:
    #endif
        gd->env_addr  = (ulong)&default_environment[0]; 
    //不成功,则使用系统默认的环境变量
    //default_environment仅仅是环境变量的data区,不包含头部crc 、flags
        gd->env_valid = 0; //0代表使用默认的环境变量
        return (0);
    }

    注意:由于编译器在编译的时候,已经给利用data生成了crc,所以肯定能够校验通过。

    5、env_relocate()函数(common/env_common.c)

    void env_relocate (void)
    {
        DEBUGF ("%s[%d] offset = 0x%lx
    ", __FUNCTION__,__LINE__,
            gd->reloc_off);  //gd->reloc_off为0
    
    #ifdef CONFIG_AMIGAONEG3SE
        enable_nvram();
    #endif
    
    #ifdef ENV_IS_EMBEDDED //有定义,所以下边的有效 
    //倘若定义了 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); //重定位
    //我还不知道reloc_off的作用哦 DEBUGF (
    "%s[%d] embedded ENV at %p ", __FUNCTION__,__LINE__,env_ptr); #else //下边的无效 /* * We must allocate a buffer for the environment */ env_ptr = (env_t *)malloc (CFG_ENV_SIZE); //为环境变量区分配空间
    //在ENV_IS_EMBEDDED定义了的情况,不会为环境变量在堆区分配空间
    DEBUGF ("%s[%d] malloced ENV at %p ", __FUNCTION__,__LINE__,env_ptr); #endif /* * After relocation to RAM, we can always use the "memory" functions */ env_get_char = env_get_char_memory; //定义env_get_char函数 if (gd->env_valid == 0) { //env_init中决定了env_valid的值,倘若flash中存在的环境变量校验错误 #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 //#define ENV_SIZE (CFG_ENV_SIZE - ENV_HEADER_SIZE) ENV_SIZE在include/environment.h中定义 if (sizeof(default_environment) > ENV_SIZE) { puts ("*** Error - default environment is too large "); return; } memset (env_ptr, 0, sizeof(env_t)); //为环境变量存储区清零空间 memcpy (env_ptr->data, default_environment, sizeof(default_environment)); //将环境默认变量拷贝到RAM中的指定区域 #ifdef CFG_REDUNDAND_ENVIRONMENT //没有定义 env_ptr->flags = 0xFF; #endif env_crc_update (); //更新crc gd->env_valid = 1; //env_valid确认有效了 } else { //倘若flash中存在的环境变量校验成功 env_relocate_spec (); /*该函数实现真正的重定位功能,先从NAND flash中读取环境变量,如果读取成*/ } gd->env_addr = (ulong)&(env_ptr->data); //将环境变量的首地址(不含crc头部)赋给全局变量gd->env_addr #ifdef CONFIG_AMIGAONEG3SE disable_nvram(); #endif }

        由于env_init()函数中,env_valid已经置为1,所以执行else分支,即 env_relocate_spec ()函数。

    6、env_relocate_spec()函数(common/env_flash.c)

    void env_relocate_spec (void) //此函数被env_relocate()函数调用
    {
    #if !defined(ENV_IS_EMBEDDED) || defined(CFG_ENV_ADDR_REDUND) //这两个变量都没有定义
    #ifdef CFG_ENV_ADDR_REDUND
    some process;
    #endif /* CFG_ENV_ADDR_REDUND */
        memcpy (env_ptr, (void*)flash_addr, CFG_ENV_SIZE); //在env_relocate已经将env_ptr指向环境变量存储区
    #endif /* ! ENV_IS_EMBEDDED || CFG_ENV_ADDR_REDUND */ //倘若ENV_IS_EMBED定义了,并且CFG_ENV_ADDR_REDUND没有定义,那么这将是一个空函数
    }

        这个函数不包含任何代码,即不进行环境变量的搬移工作。 

    三、实验与分析

    1、修改include/configs/TQ2440.h

    /*-----------------------------------------------------------------------
     * FLASH and environment organization
     */
    
    #define CONFIG_AMD_LV800    1    /* uncomment this if you have a LV800 flash */
    #if 0
    #define CONFIG_AMD_LV400    1    /* uncomment this if you have a LV400 flash */
    #endif
    
    #define CFG_MAX_FLASH_BANKS    1    /* max number of memory banks */
    #ifdef CONFIG_AMD_LV800
    #define PHYS_FLASH_SIZE        0x00200000 /* flash为2MB */
    #define CFG_MAX_FLASH_SECT    (35)    /* 35个扇区*/
    #define CFG_ENV_ADDR        (CFG_FLASH_BASE + 0x04000) /* addr of environment */
    #endif
    #ifdef CONFIG_AMD_LV400
    #define PHYS_FLASH_SIZE        0x00080000 /* 512KB */
    #define CFG_MAX_FLASH_SECT    (11)    /* max number of sectors on one chip */
    #define CFG_ENV_ADDR        (CFG_FLASH_BASE + 0x04000) //环境变量下载地址
    #endif
    
    /* timeout values are in ticks */
    #define CFG_FLASH_ERASE_TOUT    (5*CFG_HZ) /* Timeout for Flash Erase */
    #define CFG_FLASH_WRITE_TOUT    (5*CFG_HZ) /* Timeout for Flash Write */
    
    #define    CFG_ENV_IS_IN_FLASH    1
    #define CFG_ENV_SIZE        0x2000    //环境变量大小
    /*定义下边两个参数使ENV_IS_EMBEDDED有效*/
    #define CFG_MONITOR_BASE    CFG_ENV_ADDR 
    #define CFG_MONITOR_LEN     0x200000

    实验现象

    1> 编译后下载并启动开发板(第一次运行)

        valid为1,环境变量在RAM中的0x33f926f0,在flash中的位置是0x33f926f0-0x33f80000=0x126f0,位于第五个扇区(SA4)。结合下边图片可以知道,上边第三个红框对应的行,正是下图中红色的框。也就是说ENV_IS_EMBEDDED确实生效了。

    2> 保存环境变量

        但是,环境变量却保存在第二个扇区(SA0)。

    3> 重启(第二次运行)

        遇到故障,重启不成功

    4> 原因分析

    0x33f926f0地址内容

    0x33f84000内容

    原因:环境变量区编译的地址不正确,所以保存环境变量后会把有效代码区(程序)破坏,所以重启不能正常执行。

    2、修改board/TQ2440/u-boot.lds

    . = ALIGN(4);
        .text      :
        {
          cpu/arm920t/start.o    (.text)
          . = env_offset;
          common/environment.o(.text)
          *(.text)
        }

    实验现象

    1> 编译、下载、上电(第一次运行)

    2> 保存环境变量

    3> 重启(第二次运行)

        保存后的环境变量不能使用,说是bad CRC。

    4> 原因分析

    原因:第一次上电,crc是编译器产生的(正确的),所以校验通过了。保存环境变量是放在flash的0x4000位置处,当加载到内存中,就是0x33f84000。但是,0x33f80000放的是env_size,被破坏了。而且,程序运行后,把环境变量定位在0x33f84004,产生了错误。

    3.修改common/environment.c

    /*
     * These will end up in the .text section
     * if the environment strings are embedded
     * in the image.  When this is used for
     * tools/envcrc, they are placed in the
     * .data/.sdata section.
     *
     */
    //unsigned long env_size __PPCTEXT__ = sizeof(env_t);
    
    /*
     * Add in absolutes.
     */
    GEN_ABS(env_offset, CFG_ENV_OFFSET);
    env_size这个变量没什么用,所以注释掉。

    实验现象
    1> 编译、下载、上电(第一次运行)

    正常
    2> 保存环境变量


    3> 重启(第二次运行)



    可以看到保存的环境变量已经能够正常加载。

     四、疑问

    1、  怎样把本应放在数据段的environment放在了代码段?   

    __attribute__ ((section(".text")))的测试  

    2、  编译器是怎样根据环境变量产生crc校验码的?

    3、  连接的时候是怎样将environment连接在0x33f84000位置上的?

    __attribute__ ((section(".text")))的测试  

  • 相关阅读:
    bootstrap table
    C# 解压
    上传图片并预览
    前端提交后台一般处理文件
    [剑指offer] 60. 把二叉树打印成多行
    [剑指offer] 59. 按之字形顺序打印二叉树
    [剑指offer] 58. 对称的二叉树
    [剑指offer] 57. 二叉树的下一个结点
    [剑指offer] 56. 删除链表中重复的结点
    [剑指offer] 55. 链表中环的入口结点
  • 原文地址:https://www.cnblogs.com/amanlikethis/p/3449347.html
Copyright © 2020-2023  润新知