summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <[email protected]>2026-01-07 10:20:32 -0600
committerTom Rini <[email protected]>2026-01-07 12:31:56 -0600
commit141be72e2a9d43aa4e3d1a6a6e0ab8f9eb8784c6 (patch)
tree701f7132128d81c0de564eb1817eb4f1d0561e72
parent94a4e845dbddb299467573784e1e6d3bf4465a78 (diff)
parent8dd76166e3dc47f898155a047f1594fdc7a63d65 (diff)
Merge patch series "test: env: Add test for environment storage in SPI NOR"
This patch series from Marek Vasut <[email protected]> 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/[email protected]
-rw-r--r--board/sandbox/sandbox.c1
-rw-r--r--configs/sandbox64_defconfig7
-rw-r--r--configs/sandbox_defconfig7
-rw-r--r--env/Kconfig11
-rw-r--r--env/common.c31
-rw-r--r--test/py/tests/test_env.py173
6 files changed, 229 insertions, 1 deletions
diff --git a/board/sandbox/sandbox.c b/board/sandbox/sandbox.c
index d0bb3e3bb48..13006a0ffc2 100644
--- a/board/sandbox/sandbox.c
+++ b/board/sandbox/sandbox.c
@@ -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)
diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig
index 05134558d00..440e5efa340 100644
--- a/configs/sandbox64_defconfig
+++ b/configs/sandbox64_defconfig
@@ -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
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index 03369508cae..92fb5f844b1 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -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
diff --git a/env/Kconfig b/env/Kconfig
index 4430669964c..b312f9b5324 100644
--- a/env/Kconfig
+++ b/env/Kconfig
@@ -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
diff --git a/env/common.c b/env/common.c
index 05e78d63874..b2adbe93dbe 100644
--- a/env/common.c
+++ b/env/common.c
@@ -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) {
diff --git a/test/py/tests/test_env.py b/test/py/tests/test_env.py
index 383e26c03b0..f8713a59ba9 100644
--- a/test/py/tests/test_env.py
+++ b/test/py/tests/test_env.py
@@ -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)
[email protected]('cmd_echo')
[email protected]('cmd_nvedit_info')
[email protected]('cmd_nvedit_load')
[email protected]('cmd_nvedit_select')
[email protected]('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"""