Merge patch series "test: env: Add test for environment storage in SPI NOR"

This patch series from Marek Vasut <marek.vasut+renesas@mailbox.org>
adds support for having a platform be able to convert from a
non-redundant envrionment to a redundant one at run-time.

Link: https://lore.kernel.org/r/20251223143130.16266-1-marek.vasut+renesas@mailbox.org
This commit is contained in:
Tom Rini
2026-01-07 10:20:32 -06:00
6 changed files with 229 additions and 1 deletions

View File

@@ -89,6 +89,7 @@ static enum env_location env_locations[] = {
ENVL_NOWHERE,
ENVL_EXT4,
ENVL_FAT,
ENVL_SPI_FLASH,
};
enum env_location env_get_location(enum env_operation op, int prio)

View File

@@ -2,10 +2,13 @@ CONFIG_TEXT_BASE=0
CONFIG_SYS_MALLOC_LEN=0x6000000
CONFIG_NR_DRAM_BANKS=1
CONFIG_ENV_SIZE=0x2000
CONFIG_ENV_OFFSET=0x0
CONFIG_ENV_SECT_SIZE=0x1000
CONFIG_DEFAULT_DEVICE_TREE="sandbox64"
CONFIG_DM_RESET=y
CONFIG_SYS_LOAD_ADDR=0x0
CONFIG_PRE_CON_BUF_ADDR=0x100000
CONFIG_ENV_OFFSET_REDUND=0x10000
CONFIG_PCI=y
CONFIG_SANDBOX64=y
CONFIG_DEBUG_UART=y
@@ -105,6 +108,10 @@ CONFIG_OF_LIVE=y
CONFIG_ENV_IS_NOWHERE=y
CONFIG_ENV_IS_IN_FAT=y
CONFIG_ENV_IS_IN_EXT4=y
CONFIG_ENV_IS_IN_SPI_FLASH=y
CONFIG_ENV_SECT_SIZE_AUTO=y
CONFIG_ENV_REDUNDANT=y
CONFIG_ENV_REDUNDANT_UPGRADE=y
CONFIG_ENV_EXT4_INTERFACE="host"
CONFIG_ENV_EXT4_DEVICE_AND_PART="0:0"
CONFIG_ENV_IMPORT_FDT=y

View File

@@ -2,9 +2,12 @@ CONFIG_TEXT_BASE=0
CONFIG_SYS_MALLOC_LEN=0x6000000
CONFIG_NR_DRAM_BANKS=1
CONFIG_ENV_SIZE=0x2000
CONFIG_ENV_OFFSET=0x0
CONFIG_ENV_SECT_SIZE=0x1000
CONFIG_DM_RESET=y
CONFIG_SYS_LOAD_ADDR=0x0
CONFIG_PRE_CON_BUF_ADDR=0xf0000
CONFIG_ENV_OFFSET_REDUND=0x10000
CONFIG_PCI=y
CONFIG_DEBUG_UART=y
CONFIG_SYS_MEMTEST_START=0x00100000
@@ -152,6 +155,10 @@ CONFIG_OF_LIVE=y
CONFIG_ENV_IS_NOWHERE=y
CONFIG_ENV_IS_IN_FAT=y
CONFIG_ENV_IS_IN_EXT4=y
CONFIG_ENV_IS_IN_SPI_FLASH=y
CONFIG_ENV_SECT_SIZE_AUTO=y
CONFIG_ENV_REDUNDANT=y
CONFIG_ENV_REDUNDANT_UPGRADE=y
CONFIG_ENV_EXT4_INTERFACE="host"
CONFIG_ENV_EXT4_DEVICE_AND_PART="0:0"
CONFIG_ENV_IMPORT_FDT=y

11
env/Kconfig vendored
View File

@@ -489,6 +489,17 @@ config ENV_REDUNDANT
which is used by env import/export commands which are independent of
storing variables to redundant location on a non volatile device.
config ENV_REDUNDANT_UPGRADE
bool "Enable single-copy to redundant environment upgrade support"
depends on ENV_REDUNDANT
help
Normally, redundant environment is expected to always operate on
two copies of the environment. However, hardware that may have
originally shipped with single-copy environment can be upgraded
to redundant environment without loss of existing environment
content by correctly configuring the location of the redundant
environment copy and by enabling this option.
config ENV_FAT_INTERFACE
string "Name of the block device for the environment"
depends on ENV_IS_IN_FAT

31
env/common.c vendored
View File

@@ -473,14 +473,24 @@ int env_import(const char *buf, int check, int flags)
#ifdef CONFIG_ENV_REDUNDANT
static unsigned char env_flags;
#define ENV_SINGLE_HEADER_SIZE (sizeof(uint32_t))
#define ENV_SINGLE_SIZE (CONFIG_ENV_SIZE - ENV_SINGLE_HEADER_SIZE)
typedef struct {
uint32_t crc; /* CRC32 over data bytes */
unsigned char data[ENV_SINGLE_SIZE]; /* Environment data */
} env_single_t;
int env_check_redund(const char *buf1, int buf1_read_fail,
const char *buf2, int buf2_read_fail)
{
int crc1_ok = 0, crc2_ok = 0;
int crc1_ok = 0, crc2_ok = 0, i;
env_t *tmp_env1, *tmp_env2;
env_single_t *tmp_envs;
tmp_env1 = (env_t *)buf1;
tmp_env2 = (env_t *)buf2;
tmp_envs = (env_single_t *)buf1;
if (buf1_read_fail && buf2_read_fail) {
puts("*** Error - No Valid Environment Area found\n");
@@ -498,6 +508,25 @@ int env_check_redund(const char *buf1, int buf1_read_fail,
tmp_env2->crc;
if (!crc1_ok && !crc2_ok) {
/*
* Upgrade single-copy environment to redundant environment.
* In case CRC checks on both environment copies fail, try
* one more CRC check on the primary environment copy and
* treat it as single-copy environment. If that check does
* pass, rewrite the single-copy environment into redundant
* environment format and indicate the environment is valid.
* The follow up calls will import the environment as if it
* was a redundant environment. Follow up 'env save' will
* then store two environment copies.
*/
if (CONFIG_IS_ENABLED(ENV_REDUNDANT_UPGRADE) && !buf1_read_fail &&
crc32(0, tmp_envs->data, ENV_SINGLE_SIZE) == tmp_envs->crc) {
for (i = ENV_SIZE - 1; i >= 0; i--)
tmp_env1->data[i] = tmp_envs->data[i];
tmp_env1->flags = 0;
gd->env_valid = ENV_VALID;
return 0;
}
gd->env_valid = ENV_INVALID;
return -ENOMSG; /* needed for env_load() */
} else if (crc1_ok && !crc2_ok) {

View File

@@ -457,6 +457,42 @@ def mk_env_ext4(state_test_env):
utils.run_and_log(c, ['cp', '-f', persistent, fs_img])
return fs_img
def mk_env_spi_flash(state_test_env):
"""Create an empty SPI NOR image."""
c = state_test_env.ubman
filename = 'spi.bin'
persistent = c.config.persistent_data_dir + '/' + filename
spi_flash_img = c.config.source_dir + '/' + filename
if os.path.exists(persistent):
c.log.action('SPI NOR image file ' + persistent + ' already exists')
else:
try:
utils.run_and_log(c, 'dd if=/dev/zero of=%s bs=1M count=2' % persistent)
except CalledProcessError:
call('rm -f %s' % persistent, shell=True)
raise
utils.run_and_log(c, ['cp', '-f', persistent, spi_flash_img])
return spi_flash_img
def mk_env_spi_flash_single(state_test_env):
"""Create an single-copy SPI NOR image with foo=bar entry."""
c = state_test_env.ubman
filename = 'spi.bin'
spi_flash_img = c.config.source_dir + '/' + filename
try:
mkenvimage = os.path.join(c.config.build_dir, 'tools/mkenvimage')
call('( echo foo=bar | %s -s 8192 -p 0x00 - ; dd if=/dev/zero bs=2088960 count=1 2>/dev/null ) > %s' % ( mkenvimage , spi_flash_img ), shell=True)
except CalledProcessError:
call('rm -f %s' % spi_flash_img, shell=True)
raise
return spi_flash_img
@pytest.mark.boardspec('sandbox')
@pytest.mark.buildconfigspec('cmd_echo')
@pytest.mark.buildconfigspec('cmd_nvedit_info')
@@ -544,6 +580,143 @@ def test_env_ext4(state_test_env):
if fs_img:
call('rm -f %s' % fs_img, shell=True)
@pytest.mark.boardspec('sandbox')
@pytest.mark.buildconfigspec('cmd_echo')
@pytest.mark.buildconfigspec('cmd_nvedit_info')
@pytest.mark.buildconfigspec('cmd_nvedit_load')
@pytest.mark.buildconfigspec('cmd_nvedit_select')
@pytest.mark.buildconfigspec('env_is_in_spi_flash')
def test_env_spi_flash(state_test_env):
"""Test ENV in SPI NOR on sandbox."""
c = state_test_env.ubman
spi_flash_img = ''
try:
spi_flash_img = mk_env_spi_flash_single(state_test_env)
response = c.run_command('sf probe')
assert 'SF: Detected m25p16 with page size 256 Bytes, erase size 64 KiB, total 2 MiB' in response
# force env location: SF
response = c.run_command('env select SPIFlash')
assert 'Select Environment on SPIFlash: OK' in response
response = c.run_command('env load')
assert 'Loading Environment from SPIFlash... OK' in response
response = c.run_command('env print foo')
assert 'foo=bar' in response
response = c.run_command('env save')
assert 'Saving Environment to SPIFlash' in response
response = c.run_command('env load')
assert 'Loading Environment from SPIFlash... OK' in response
response = c.run_command('env print foo')
assert 'foo=bar' in response
response = c.run_command('env save')
assert 'Saving Environment to SPIFlash' in response
response = c.run_command('env save')
assert 'Saving Environment to SPIFlash' in response
response = c.run_command('env load')
assert 'Loading Environment from SPIFlash... OK' in response
response = c.run_command('env print foo')
assert 'foo=bar' in response
# restore env location: NOWHERE (prio 0 in sandbox)
response = c.run_command('env select nowhere')
assert 'Select Environment on nowhere: OK' in response
response = c.run_command('env load')
assert 'Loading Environment from nowhere... OK' in response
response = c.run_command('env info')
assert 'env_valid = invalid' in response
assert 'env_ready = true' in response
assert 'env_use_default = true' in response
response = c.run_command('env info -p -d')
assert 'Default environment is used' in response
assert 'Environment cannot be persisted' in response
finally:
if spi_flash_img:
call('rm -f %s' % spi_flash_img, shell=True)
spi_flash_img = ''
try:
spi_flash_img = mk_env_spi_flash(state_test_env)
# force env location: SF
response = c.run_command('env select SPIFlash')
assert 'Select Environment on SPIFlash: OK' in response
response = c.run_command('env save')
assert 'Saving Environment to SPIFlash' in response
response = c.run_command('env load')
assert 'Loading Environment from SPIFlash... OK' in response
response = c.run_command('env info')
assert 'env_valid = valid' in response
assert 'env_ready = true' in response
assert 'env_use_default = false' in response
response = c.run_command('env info -p -d')
assert 'Environment was loaded from persistent storage' in response
assert 'Environment can be persisted' in response
response = c.run_command('env info -d -q')
assert response == ""
response = c.run_command('echo $?')
assert response == "1"
response = c.run_command('env info -p -q')
assert response == ""
response = c.run_command('echo $?')
assert response == "0"
response = c.run_command('env erase')
assert 'OK' in response
response = c.run_command('env load')
assert 'Loading Environment from SPIFlash... ' in response
assert 'bad CRC, using default environment' in response
response = c.run_command('env info')
assert 'env_valid = invalid' in response
assert 'env_ready = true' in response
assert 'env_use_default = true' in response
response = c.run_command('env info -p -d')
assert 'Default environment is used' in response
assert 'Environment can be persisted' in response
# restore env location: NOWHERE (prio 0 in sandbox)
response = c.run_command('env select nowhere')
assert 'Select Environment on nowhere: OK' in response
response = c.run_command('env load')
assert 'Loading Environment from nowhere... OK' in response
response = c.run_command('env info')
assert 'env_valid = invalid' in response
assert 'env_ready = true' in response
assert 'env_use_default = true' in response
response = c.run_command('env info -p -d')
assert 'Default environment is used' in response
assert 'Environment cannot be persisted' in response
finally:
if spi_flash_img:
call('rm -f %s' % spi_flash_img, shell=True)
def test_env_text(ubman):
"""Test the script that converts the environment to a text file"""