[uboot]支持env保存到Nor Flash

1 概述

        uboot在命令行界面可以查看、更改flash中相关的环境变量,这些环境变量可以用于uboot本身进行相关设置,也可以被kernel识别,用于完成特定的配置。

        本小节主要讲述如何通过配置将环境变量保存到Nor Flash中。

1.1 环境变量的概念

     可以理解为用户对软件的全局配置信息,这部分信息应该可以从永久性存储器上读取,能被查询,能被修改。

  启动过程中,应该首先把环境变量读取到合适的内存区域,然后利用环境变量初始化硬件、启动操作系统等等。

1.2 启动过程中环境变量初始化过程涉及的问题

       这里涉及到两个问题:

   环境变量在哪个地方存着(从哪个地方取)

   将环境变量存储到哪里(放到哪)

        (1)环境变量位于存储器(norflash、nandflash )

                 “CFG_ENV_IS_IN_XXX“定义了则这种情况有效,以在flash上为例。

CONFIG_ENV_IS_NOWHERE   系统中没有存储器存放环境变量
CONFIG_ENV_IS_IN_EEPROM 环境变量存放在EEPROM
CONFIG_ENV_IS_IN_FAT    环境变量存放在FAT文件系统 
CONFIG_ENV_IS_IN_EXT4   环境变量存放在EXT4文件系统
CONFIG_ENV_IS_IN_FLASH  
CONFIG_ENV_IS_IN_MMC    环境变量存放在EMMC
CONFIG_ENV_IS_IN_NAND   环境变量存放在NAND FLASH
CONFIG_ENV_IS_IN_NVRAM  环境变量存放在NVRAM
CONFIG_ENV_IS_IN_ONENAND 环境变量存放在NAND
CONFIG_ENV_IS_IN_REMOTE  环境变量存放在远程环境
CONFIG_ENV_IS_IN_SPI_FLASH 环境变量存放在SPI FLASH


CONFIG_ENV_IS_IN_FLASH  和 CONFIG_ENV_IS_IN_SPI_FLASH 有什么区别????

                定义ENV_IS_EMBEDDED

                        这种情况的环境变量在flash上存着(但是占了flash一个扇区),并且随着代码段(因为环境变量区嵌在代码段内)在start.s重定位时一同载入内存。在环境变量初始化时候,如果这部分能通过校验,就不需要先在堆区开辟空间然后搬移的工作,而是直接使用这部分环境变量(省了搬移工作)。倘若不能通过校验,则使用默认环境变量放到重定位时环境变量所占的空间中。

                未定义ENV_IS_EMBEDDED

                       这种情况会在堆区为环境变量区开辟空间,如果flash上存储的是有效的(能通过校验)环境变量,则需要把flash上的数据搬运到堆区指定的位置;如果flash上的存储是错误的环境变量,那么使用默认的环境变量(default_environment)放到堆区。

        (2)没有存储器上存储有环境变量 

                “CFG_ENV_IS_NOWHERE”定义了则选择这种模式,使用common/env_nowhere.c文件而不是用env_flash.c、env_nvram.c等等文件。

          这种情况下,使用默认的环境变量(default_environment)。先在堆区为环境变量开辟空间,然后启动搬运工作。

2 功能开启

2.1 相关设置

功能配置宏

#
# Environment
#
CONFIG_ENV_IS_IN_SPI_FLASH=y #支持将环境变量保存到nor flash上
CONFIG_ENV_OVERWRITE=y #允许修改环境变量
CONFIG_ENV_SIZE=0x1000 #实际环境变量使用的内存大小,这个变量决定了实际从nor上拷贝到内存的数据大小。
CONFIG_ENV_OFFSET=0x140000 #env在nor flash上的便宜,相对于0x0(nor flash默认以0x0开始)
#CONFIG_ENV_ADDR=CONFIG_ENV_OFFSET #环境变量起始地址的另外一种表达方式
CONFIG_ENV_SECT_SIZE=0x10000 #环境变量占用nor flash区域的大小

2.2 基本调用关系

在最新的uboot实现中,与环境变量相关的源码实现已经放到了uboot/env目录下

目前我所使用的Nor Flash通过sf命令进行probe,在CONFIG_ENV_IS_IN_SPI_FLASH使能之后会使用到env/sf.c

在初始化函数中,uboot会自动判断环境变量是否在flash中,如果不存在会使用默认的环境变量

static int initr_env(void)
    >>> env_relocate();
        >>> 
    >>> env_import_fdt();

2.3 环境变量初始化流程

以环境变量位于NorFlash上,并且没有使能“ENV_IS_EMBEDDED”功能为例,进行以下内容的分析。其他情况本文不讨论。

2.3.1 校验

 直接在NorFlash上校验环境变量,实际上这一步是确定环境变量的源。如果NorFlash上存储的是有效的环境变量的话,那么就从NorFlash上读取数据。倘若NorFlash上存储的是无效的环境变量,那么使用默认的环境变量作为源。

2.3.2 重定位

  将从存储环境变量的存储区加载到系统指定的位置。

2.3.3 涉及到的函数

(1)env_init

  完成校验,并决定环境变量的源

  此函数在start_armboot函数的开始阶段,会依次执行“init_sequence”中的每一个函数,其中一个就是env_init。

_start // start.S
    >>>bl _main // arch/arm/lib/crt0_64.S
        >>> bl	board_init_f // common/board_f.c
            >>>initcall_run_list(init_sequence_f)
        >>> b	board_init_r // common/board_r.c
            >>> initcall_run_list(init_sequence_r)

static const init_fnc_t init_sequence_f[] = {
    。。。
	env_init,		/* initialize environment */
    。。。
}

(2)env_relocate

  首先,在堆区为环境变量开辟存储缓冲区。另外,当NorFlash上的环境变量区是无效的时候,选择默认的环境变量区进行定位。当NorFlash上的环境变量区是有效的时候,调用env_relocate_spec进行重定位。

  此函数是在start_armboot函数的中途阶段,在NorFlash和NandFlash都初始化后,会调用这个函数。

(3)env_relocate_spec

  此函数是被env_relocate所调用。

3 保存环境变量(saveenv命令)的函数调用过程

文件:cmd/nvedit.c

#if     defined(CONFIG_ENV_IS_IN_EEPROM)        || \
        defined(CONFIG_ENV_IS_IN_FLASH)         || \
        defined(CONFIG_ENV_IS_IN_MMC)           || \
        defined(CONFIG_ENV_IS_IN_FAT)           || \
        defined(CONFIG_ENV_IS_IN_EXT4)          || \
        defined(CONFIG_ENV_IS_IN_NAND)          || \
        defined(CONFIG_ENV_IS_IN_NVRAM)         || \
        defined(CONFIG_ENV_IS_IN_ONENAND)       || \
        defined(CONFIG_ENV_IS_IN_SATA)          || \
        defined(CONFIG_ENV_IS_IN_SPI_FLASH)     || \
        defined(CONFIG_ENV_IS_IN_REMOTE)        || \
        defined(CONFIG_ENV_IS_IN_UBI)
 
#define ENV_IS_IN_DEVICE  //定义了此变量

#endif

当执行saveenv时会执行do_env_save
#if defined(CONFIG_CMD_SAVEENV) && defined(ENV_IS_IN_DEVICE)
static int do_env_save(struct cmd_tbl *cmdtp, int flag, int argc,
                       char *const argv[])
{
        return env_save() ? 1 : 0;
}

U_BOOT_CMD(
        saveenv, 1, 0,  do_env_save,
        "save environment variables to persistent storage",
        ""
);

再来看下env_save函数

文件:env/env.c
int env_save(void)
{
	struct env_driver *drv;

	drv = env_driver_lookup(ENVOP_SAVE, gd->env_load_prio);
	if (drv) {
		int ret;

		printf("Saving Environment to %s... ", drv->name);
		if (!drv->save) {
			printf("not possible\n");
			return -ENODEV;
		}

		if (!env_has_inited(drv->location)) {
			printf("not initialized\n");
			return -ENODEV;
		}

		ret = drv->save(); //调用回掉
		if (ret)
			printf("Failed (%d)\n", ret);
		else
			printf("OK\n");

		if (!ret)
			return 0;
	}

	return -ENODEV;
}

env_save函数会调用具体的save回掉函数来实现的。那么这个回掉函数在哪儿实现的呢?

通过env_driver_lookup函数的实现可知,struct env_driver类似于U_BOOT_DRIVER,通过编译时静态初始化结构体来实现。

然后通过搜索ENVL_SPI_FLASH最终找到了env_driver的实现方式

#define U_BOOT_ENV_LOCATION(__name)					\
	ll_entry_declare(struct env_driver, __name, env_driver)

文件:env/sf.c

U_BOOT_ENV_LOCATION(sf) = {
	.location	= ENVL_SPI_FLASH,
	ENV_NAME("SPIFlash")
	.load		= env_sf_load,
	.save		= ENV_SAVE_PTR(env_sf_save), //从这儿我们找到了save的具体实现函数
	.erase		= ENV_ERASE_PTR(env_sf_erase),
	.init		= env_sf_init,
};

我们再回过头来看下env/sf.c函数有没有编译

env/Makefile:30:obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_SPI_FLASH) += sf.o

从上面的Makefile中可以确认sf.c文件依赖与CONFIG_ENV_IS_IN_SPI_FLASH这个变量已经被打开。

下面我们看下env_sf_save函数的实现

static int env_sf_save(void)
    >>> ret = setup_flash_device(&env_flash); //找到用sf probe的spi nor flash
        >>> ret = spi_flash_probe_bus_cs(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS,
				     CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE,
				     &new);
        >>> *env_flash = dev_get_uclass_priv(new);
    >>> (1)先把nor flash中的参数区域没有使用的部分(CONFIG_ENV_SIZE != CONFIG_ENV_SECT_SIZE)读到动态分布的内存中
        if (sect_size > CONFIG_ENV_SIZE)
            saved_size = sect_size - CONFIG_ENV_SIZE;
            saved_offset = CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE;
            saved_buffer = malloc(saved_size);
            ret = spi_flash_read(env_flash, saved_offset,
			                    saved_size, saved_buffer);
    >>> (2)从hash表中获取环境变量,这个环境变量是在初始化过程中导入到hash表中的,后面详解
        ret = env_export(&env_new);
    >>> (3)擦除要写的nor flash内存空间
        	sector = DIV_ROUND_UP(CONFIG_ENV_SIZE, sect_size);
	        ret = spi_flash_erase(env_flash, CONFIG_ENV_OFFSET,
		                            sector * sect_size);
    >>> (4)将环境变量写入到nor flash 内存空间区域
        ret = spi_flash_write(env_flash, CONFIG_ENV_OFFSET,
		                        CONFIG_ENV_SIZE, &env_new);
    >>> (5)把之前读出来的剩余的nor flash参数区域中的数据再写回去
        ret = spi_flash_write(env_flash, saved_offset,
			                    saved_size, saved_buffer);

还有一个问题,环境变量何时导入到hash表中

文件:common/board_r.c
static int initr_env(void)
    >>> env_relocate();
    >>> env_set_default(NULL, 0);
        >>> himport_r(&env_htab, (char *)default_environment,
			sizeof(default_environment), '\0', flags, 0,
			0, NULL) == 0 //将default_environment导入到env_htab

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值