Possibly misconfigured fw_env.conf

I’m working on implementation of the redundancy and range of environment variables in U-Boot. So far the changes in U-Boot config were pretty straight forward.

CONFIG_ENV_RANGE=CONFIG_ENV_SIZE * 2
CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
CONFIG_ENV_OFFSET_REDUND=CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE

But modifications to the configuration file fw_env.config used by fw_printenv and fw_setenv utility proved to be more challenging, than I expected. The meaning of parameters like Flash sector size or Number of sectors is not very straightforward, but I finally got it working by analyzing the source code of this utility.

Now I cannot stop to think that your original fw_env.config is misconfigured. Here is the content of the original file taken from meta-toradex-nxp git repository at recipes-bsp/u-boot/files/colibri-imx7/fw_env.config:

# MTD device name   Device offset  Env. size   Flash sector size   Number of sectors
# Colibri iMX7
/dev/mtd3           0x00000000     0x00020000  0x20000             4

First four parameters are correct, they are just taken directly from U-Boot configuration, but not the last one Number of sectors. As far as I’m concerned this value should be equal to configuration makro CONFIG_ENV_RANGE in U-Boot, which in colibri_imx7_defconfig is not set. The only difference between them is that in U-Boot you have to manually align the length of range to multiple erase-blocks and here it’s just number of erase-blocks (less prone to error).

To further prove my point when you look at source code of fw_env.c, the last value from config file is parsed as &ENVSECTORS parameter.

https://source.denx.de/u-boot/u-boot/-/blob/master/tools/env/fw_env.c

#if defined(CONFIG_FILE)
static int get_config(char *fname)
{
	...
	while (i < 2 && getline(&line, &linesize, fp) != -1) {
		/* Skip comment strings */
		if (line[0] == '#')
			continue;

		rc = sscanf(line, "%ms %lli %lx %lx %lx",
			    &devname,
			    &DEVOFFSET(i),
			    &ENVSIZE(i), &DEVESIZE(i), &ENVSECTORS(i));
	...

The &ENVSECTORS parameter is then referenced in different functions like for example following one, where in the comment is written, that we have to stay within those sectors. This sounds like description for CONFIG_ENV_RANGE.

/*
 * Write count bytes from begin of environment, but stay within
 * ENVSECTORS(dev) sectors of
 * DEVOFFSET (dev). Similar to the read case above, on NOR and dataflash we
 * erase and write the whole data at once.
 */
static int flash_write_buf(int dev, int fd, void *buf, size_t count)
{
	 ...

	 /*
	 * For mtd devices only offset and size of the environment do matter
	 */
	if (DEVTYPE(dev) == MTD_ABSENT) {
		blocklen = count;
		erase_len = blocklen;
		blockstart = DEVOFFSET(dev);
		block_seek = 0;
		write_total = blocklen;
	} else {
		blocklen = DEVESIZE(dev);

		erase_offset = DEVOFFSET(dev);

		/* Maximum area we may use */
		erase_len = environment_end(dev) - erase_offset;

		blockstart = erase_offset;

		/* Offset inside a block */
		block_seek = DEVOFFSET(dev) - erase_offset;

Also in that snippet you can see that calculation of the memory area is using function environment_end(dev) which looks like this:

off_t environment_end(int dev)
{
	/* environment is block aligned */
	return DEVOFFSET(dev) + ENVSECTORS(dev) * DEVESIZE(dev);
}

This function calculates the end of the reserved memory space for environmental variables by taking the Number of sectors, multiplying it by Flash sector size and finally adding it to Device offset.

Final conlusion
My suspicion is that your current configuration in fw_env.config corresponds to setting the range of env. vars memory array to 4 erase-blocks. Something like this CONFIG_ENV_RANGE=CONFIG_ENV_SIZE * 4 will do, but the default board configuration in colibri_imx7_defconfig don’t includes this. (see https://source.denx.de/u-boot/u-boot/-/blob/master/configs/colibri_imx7_defconfig)

In my mind the only way to detect this bug is to rewrite the env. vars so many times, that the first erase-block in NAND became unreadable. At this point you could theoretically be able to write vars from Linux because it just skips the bad block, but not from uboot (it knows only the first one block). This scenario is pretty impossible to encounter.

What do you think?

P.S.
Colibri iMX7 512MB
Linux BSP 5.2.0

Hi @cicicok

Thanks for writing to the Toradex Community and bringing up the issue. I will open an internal Ticket to analyze this.

May I ask if your configuration is working for you?

Best regards,
Jaski

I’m using range and redundant environment variables as well. Here are my changes:

U-Boot config

CONFIG_ENV_RANGE=0x40000
CONFIG_ENV_OFFSET_REDUND=0x3c0000
CONFIG_SYS_REDUNDAND_ENVIRONMENT=y

fw_env.config

# MTD device name       Device offset   Env. size       Flash sector size       Number of sectors
# Colibri iMX7
/dev/mtd3               0x00000000      0x00020000      0x20000                 2
/dev/mtd3               0x00040000      0x00020000      0x20000                 2

I can tell you, that redundant environment variables is working. But testing the range is not that easy, because you’ll have to destroy the first erase block where env vars are stored. I’m not sure how to simulate bad erase block.

Hi
Thanks for this Information. We will let you know once we have more news on this issue.

Best regards,
Jaski