Merge branch '2020-07-31-more-env-updates'

- Fix EFI selftest to not force setting serial# environment (and also
  get the U-Boot prompt dynamically).
- Support for append only environment and other related features.
- Improved ext4 environment support
- Fix the case of fw_setenv being used on flash devices that were not
  already locked.
diff --git a/board/sandbox/sandbox.c b/board/sandbox/sandbox.c
index c1317a8..937ce28 100644
--- a/board/sandbox/sandbox.c
+++ b/board/sandbox/sandbox.c
@@ -7,6 +7,7 @@
 #include <cpu_func.h>
 #include <cros_ec.h>
 #include <dm.h>
+#include <env_internal.h>
 #include <init.h>
 #include <led.h>
 #include <os.h>
@@ -44,6 +45,20 @@
 }
 #endif
 
+/* specific order for sandbox: nowhere is the first value, used by default */
+static enum env_location env_locations[] = {
+	ENVL_NOWHERE,
+	ENVL_EXT4,
+};
+
+enum env_location env_get_location(enum env_operation op, int prio)
+{
+	if (prio >= ARRAY_SIZE(env_locations))
+		return ENVL_UNKNOWN;
+
+	return env_locations[prio];
+}
+
 int dram_init(void)
 {
 	gd->ram_size = CONFIG_SYS_SDRAM_SIZE;
diff --git a/cmd/Kconfig b/cmd/Kconfig
index bea2ddf..d7136b0 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -604,6 +604,17 @@
 	  [-q] : quiet output
 	  The result of multiple evaluations will be combined with AND.
 
+config CMD_NVEDIT_LOAD
+	bool "env load"
+	help
+	  Load all environment variables from the compiled-in persistent
+	  storage.
+
+config CMD_NVEDIT_SELECT
+	bool "env select"
+	help
+	  Select the compiled-in persistent storage of environment variables.
+
 endmenu
 
 menu "Memory commands"
diff --git a/cmd/nvedit.c b/cmd/nvedit.c
index acd9f82..d188c6a 100644
--- a/cmd/nvedit.c
+++ b/cmd/nvedit.c
@@ -794,6 +794,23 @@
 );
 #endif
 #endif
+
+#if defined(CONFIG_CMD_NVEDIT_LOAD)
+static int do_env_load(struct cmd_tbl *cmdtp, int flag, int argc,
+		       char *const argv[])
+{
+	return env_reload() ? 1 : 0;
+}
+#endif
+
+#if defined(CONFIG_CMD_NVEDIT_SELECT)
+static int do_env_select(struct cmd_tbl *cmdtp, int flag, int argc,
+			 char *const argv[])
+{
+	return env_select(argv[1]) ? 1 : 0;
+}
+#endif
+
 #endif /* CONFIG_SPL_BUILD */
 
 int env_match(uchar *s1, int i2)
@@ -1347,6 +1364,9 @@
 #if defined(CONFIG_CMD_NVEDIT_INFO)
 	U_BOOT_CMD_MKENT(info, 3, 0, do_env_info, "", ""),
 #endif
+#if defined(CONFIG_CMD_NVEDIT_LOAD)
+	U_BOOT_CMD_MKENT(load, 1, 0, do_env_load, "", ""),
+#endif
 	U_BOOT_CMD_MKENT(print, CONFIG_SYS_MAXARGS, 1, do_env_print, "", ""),
 #if defined(CONFIG_CMD_RUN)
 	U_BOOT_CMD_MKENT(run, CONFIG_SYS_MAXARGS, 1, do_run, "", ""),
@@ -1357,6 +1377,9 @@
 	U_BOOT_CMD_MKENT(erase, 1, 0, do_env_erase, "", ""),
 #endif
 #endif
+#if defined(CONFIG_CMD_NVEDIT_SELECT)
+	U_BOOT_CMD_MKENT(select, 2, 0, do_env_select, "", ""),
+#endif
 	U_BOOT_CMD_MKENT(set, CONFIG_SYS_MAXARGS, 0, do_env_set, "", ""),
 #if defined(CONFIG_CMD_ENV_EXISTS)
 	U_BOOT_CMD_MKENT(exists, 2, 0, do_env_exists, "", ""),
@@ -1441,6 +1464,12 @@
 #if defined(CONFIG_CMD_ERASEENV)
 	"env erase - erase environment\n"
 #endif
+#endif
+#if defined(CONFIG_CMD_NVEDIT_LOAD)
+	"env load - load environment\n"
+#endif
+#if defined(CONFIG_CMD_NVEDIT_SELECT)
+	"env select [target] - select environment target\n"
 #endif
 #if defined(CONFIG_CMD_NVEDIT_EFI)
 	"env set -e [-nv][-bs][-rt][-at][-a][-i addr,size][-v] name [arg ...]\n"
diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig
index e8a1d57..1228c49 100644
--- a/configs/sandbox64_defconfig
+++ b/configs/sandbox64_defconfig
@@ -27,10 +27,13 @@
 # CONFIG_CMD_ELF is not set
 CONFIG_CMD_ASKENV=y
 CONFIG_CMD_GREPENV=y
+CONFIG_CMD_ERASEENV=y
 CONFIG_CMD_ENV_CALLBACK=y
 CONFIG_CMD_ENV_FLAGS=y
 CONFIG_CMD_NVEDIT_EFI=y
 CONFIG_CMD_NVEDIT_INFO=y
+CONFIG_CMD_NVEDIT_LOAD=y
+CONFIG_CMD_NVEDIT_SELECT=y
 CONFIG_LOOPW=y
 CONFIG_CMD_MD5SUM=y
 CONFIG_CMD_MEMINFO=y
@@ -83,6 +86,10 @@
 CONFIG_OF_LIVE=y
 CONFIG_OF_HOSTFILE=y
 CONFIG_BOOTP_SEND_HOSTNAME=y
+CONFIG_ENV_IS_NOWHERE=y
+CONFIG_ENV_IS_IN_EXT4=y
+CONFIG_ENV_EXT4_INTERFACE="host"
+CONFIG_ENV_EXT4_DEVICE_AND_PART="0:0"
 CONFIG_NETCONSOLE=y
 CONFIG_IP_DEFRAG=y
 CONFIG_REGMAP=y
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index 5bb44c7..f4f97f3 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -32,10 +32,13 @@
 # CONFIG_CMD_ELF is not set
 CONFIG_CMD_ASKENV=y
 CONFIG_CMD_GREPENV=y
+CONFIG_CMD_ERASEENV=y
 CONFIG_CMD_ENV_CALLBACK=y
 CONFIG_CMD_ENV_FLAGS=y
 CONFIG_CMD_NVEDIT_EFI=y
 CONFIG_CMD_NVEDIT_INFO=y
+CONFIG_CMD_NVEDIT_LOAD=y
+CONFIG_CMD_NVEDIT_SELECT=y
 CONFIG_LOOPW=y
 CONFIG_CMD_MD5SUM=y
 CONFIG_CMD_MEMINFO=y
@@ -94,6 +97,10 @@
 CONFIG_OF_LIVE=y
 CONFIG_OF_HOSTFILE=y
 CONFIG_BOOTP_SEND_HOSTNAME=y
+CONFIG_ENV_IS_NOWHERE=y
+CONFIG_ENV_IS_IN_EXT4=y
+CONFIG_ENV_EXT4_INTERFACE="host"
+CONFIG_ENV_EXT4_DEVICE_AND_PART="0:0"
 CONFIG_NETCONSOLE=y
 CONFIG_IP_DEFRAG=y
 CONFIG_REGMAP=y
diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig
index 4b28b8e..78d732d 100644
--- a/configs/sandbox_flattree_defconfig
+++ b/configs/sandbox_flattree_defconfig
@@ -24,7 +24,10 @@
 # CONFIG_CMD_ELF is not set
 CONFIG_CMD_ASKENV=y
 CONFIG_CMD_GREPENV=y
+CONFIG_CMD_ERASEENV=y
 CONFIG_CMD_NVEDIT_INFO=y
+CONFIG_CMD_NVEDIT_LOAD=y
+CONFIG_CMD_NVEDIT_SELECT=y
 CONFIG_LOOPW=y
 CONFIG_CMD_MD5SUM=y
 CONFIG_CMD_MEMINFO=y
@@ -66,6 +69,10 @@
 CONFIG_OF_CONTROL=y
 CONFIG_OF_HOSTFILE=y
 CONFIG_BOOTP_SEND_HOSTNAME=y
+CONFIG_ENV_IS_NOWHERE=y
+CONFIG_ENV_IS_IN_EXT4=y
+CONFIG_ENV_EXT4_INTERFACE="host"
+CONFIG_ENV_EXT4_DEVICE_AND_PART="0:0"
 CONFIG_NETCONSOLE=y
 CONFIG_IP_DEFRAG=y
 CONFIG_REGMAP=y
diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig
index 4549a81..b846487 100644
--- a/configs/sandbox_spl_defconfig
+++ b/configs/sandbox_spl_defconfig
@@ -34,9 +34,12 @@
 # CONFIG_CMD_ELF is not set
 CONFIG_CMD_ASKENV=y
 CONFIG_CMD_GREPENV=y
+CONFIG_CMD_ERASEENV=y
 CONFIG_CMD_ENV_CALLBACK=y
 CONFIG_CMD_ENV_FLAGS=y
 CONFIG_CMD_NVEDIT_INFO=y
+CONFIG_CMD_NVEDIT_LOAD=y
+CONFIG_CMD_NVEDIT_SELECT=y
 CONFIG_LOOPW=y
 CONFIG_CMD_MD5SUM=y
 CONFIG_CMD_MEMINFO=y
@@ -83,6 +86,10 @@
 CONFIG_OF_HOSTFILE=y
 CONFIG_SPL_OF_PLATDATA=y
 CONFIG_BOOTP_SEND_HOSTNAME=y
+CONFIG_ENV_IS_NOWHERE=y
+CONFIG_ENV_IS_IN_EXT4=y
+CONFIG_ENV_EXT4_INTERFACE="host"
+CONFIG_ENV_EXT4_DEVICE_AND_PART="0:0"
 CONFIG_NETCONSOLE=y
 CONFIG_IP_DEFRAG=y
 CONFIG_SPL_DM=y
diff --git a/env/Kconfig b/env/Kconfig
index 4113628..5d0a8ec 100644
--- a/env/Kconfig
+++ b/env/Kconfig
@@ -479,7 +479,7 @@
 config ENV_EXT4_FILE
 	string "Name of the EXT4 file to use for the environment"
 	depends on ENV_IS_IN_EXT4
-	default "uboot.env"
+	default "/uboot.env"
 	help
 	  It's a string of the EXT4 file name. This file use to store the
 	  environment (explicit path to the file)
@@ -614,6 +614,23 @@
 	  later by U-Boot code. With CONFIG_OF_CONTROL this is instead
 	  controlled by the value of /config/load-environment.
 
+config ENV_APPEND
+	bool "Always append the environment with new data"
+	default n
+	help
+	  If defined, the environment hash table is only ever appended with new
+	  data, but the existing hash table can never be dropped and reloaded
+	  with newly imported data. This may be used in combination with static
+	  flags to e.g. to protect variables which must not be modified.
+
+config ENV_WRITEABLE_LIST
+	bool "Permit write access only to listed variables"
+	default n
+	help
+	  If defined, only environment variables which explicitly set the 'w'
+	  writeable flag can be written and modified at runtime. No variables
+	  can be otherwise created, written or imported into the environment.
+
 config ENV_ACCESS_IGNORE_FORCE
 	bool "Block forced environment operations"
 	default n
diff --git a/env/common.c b/env/common.c
index 088b2ae..ed18378 100644
--- a/env/common.c
+++ b/env/common.c
@@ -81,6 +81,7 @@
 		debug("Using default environment\n");
 	}
 
+	flags |= H_DEFAULT;
 	if (himport_r(&env_htab, (char *)default_environment,
 			sizeof(default_environment), '\0', flags, 0,
 			0, NULL) == 0)
@@ -99,7 +100,7 @@
 	 * Special use-case: import from default environment
 	 * (and use \0 as a separator)
 	 */
-	flags |= H_NOCLEAR;
+	flags |= H_NOCLEAR | H_DEFAULT;
 	return himport_r(&env_htab, (const char *)default_environment,
 				sizeof(default_environment), '\0',
 				flags, 0, nvars, vars);
@@ -109,7 +110,7 @@
  * Check if CRC is valid and (if yes) import the environment.
  * Note that "buf" may or may not be aligned.
  */
-int env_import(const char *buf, int check)
+int env_import(const char *buf, int check, int flags)
 {
 	env_t *ep = (env_t *)buf;
 
@@ -124,7 +125,7 @@
 		}
 	}
 
-	if (himport_r(&env_htab, (char *)ep->data, ENV_SIZE, '\0', 0, 0,
+	if (himport_r(&env_htab, (char *)ep->data, ENV_SIZE, '\0', flags, 0,
 			0, NULL)) {
 		gd->flags |= GD_FLG_ENV_READY;
 		return 0;
@@ -141,7 +142,8 @@
 static unsigned char env_flags;
 
 int env_import_redund(const char *buf1, int buf1_read_fail,
-		      const char *buf2, int buf2_read_fail)
+		      const char *buf2, int buf2_read_fail,
+		      int flags)
 {
 	int crc1_ok, crc2_ok;
 	env_t *ep, *tmp_env1, *tmp_env2;
@@ -161,10 +163,10 @@
 		return -EIO;
 	} else if (!buf1_read_fail && buf2_read_fail) {
 		gd->env_valid = ENV_VALID;
-		return env_import((char *)tmp_env1, 1);
+		return env_import((char *)tmp_env1, 1, flags);
 	} else if (buf1_read_fail && !buf2_read_fail) {
 		gd->env_valid = ENV_REDUND;
-		return env_import((char *)tmp_env2, 1);
+		return env_import((char *)tmp_env2, 1, flags);
 	}
 
 	crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) ==
@@ -199,7 +201,7 @@
 		ep = tmp_env2;
 
 	env_flags = ep->flags;
-	return env_import((char *)ep, 0);
+	return env_import((char *)ep, 0, flags);
 }
 #endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */
 
diff --git a/env/eeprom.c b/env/eeprom.c
index e8126cf..e300470 100644
--- a/env/eeprom.c
+++ b/env/eeprom.c
@@ -188,7 +188,7 @@
 	eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
 		off, (uchar *)buf_env, CONFIG_ENV_SIZE);
 
-	return env_import(buf_env, 1);
+	return env_import(buf_env, 1, H_EXTERNAL);
 }
 
 static int env_eeprom_save(void)
diff --git a/env/env.c b/env/env.c
index 2e64346..42c7d81 100644
--- a/env/env.c
+++ b/env/env.c
@@ -131,8 +131,6 @@
 	if (prio >= ARRAY_SIZE(env_locations))
 		return ENVL_UNKNOWN;
 
-	gd->env_load_prio = prio;
-
 	return env_locations[prio];
 }
 
@@ -189,9 +187,6 @@
 	for (prio = 0; (drv = env_driver_lookup(ENVOP_LOAD, prio)); prio++) {
 		int ret;
 
-		if (!drv->load)
-			continue;
-
 		if (!env_has_inited(drv->location))
 			continue;
 
@@ -204,7 +199,11 @@
 		ret = drv->load();
 		if (!ret) {
 			printf("OK\n");
+			gd->env_load_prio = prio;
+
+#if !CONFIG_IS_ENABLED(ENV_APPEND)
 			return 0;
+#endif
 		} else if (ret == -ENOMSG) {
 			/* Handle "bad CRC" case */
 			if (best_prio == -1)
@@ -227,11 +226,40 @@
 		debug("Selecting environment with bad CRC\n");
 	else
 		best_prio = 0;
-	env_get_location(ENVOP_LOAD, best_prio);
+
+	gd->env_load_prio = best_prio;
 
 	return -ENODEV;
 }
 
+int env_reload(void)
+{
+	struct env_driver *drv;
+
+	drv = env_driver_lookup(ENVOP_LOAD, gd->env_load_prio);
+	if (drv) {
+		int ret;
+
+		printf("Loading Environment from %s... ", drv->name);
+
+		if (!env_has_inited(drv->location)) {
+			printf("not initialized\n");
+			return -ENODEV;
+		}
+
+		ret = drv->load();
+		if (ret)
+			printf("Failed (%d)\n", ret);
+		else
+			printf("OK\n");
+
+		if (!ret)
+			return 0;
+	}
+
+	return -ENODEV;
+}
+
 int env_save(void)
 {
 	struct env_driver *drv;
@@ -318,3 +346,45 @@
 
 	return ret;
 }
+
+int env_select(const char *name)
+{
+	struct env_driver *drv;
+	const int n_ents = ll_entry_count(struct env_driver, env_driver);
+	struct env_driver *entry;
+	int prio;
+	bool found = false;
+
+	printf("Select Environment on %s: ", name);
+
+	/* search ENV driver by name */
+	drv = ll_entry_start(struct env_driver, env_driver);
+	for (entry = drv; entry != drv + n_ents; entry++) {
+		if (!strcmp(entry->name, name)) {
+			found = true;
+			break;
+		}
+	}
+
+	if (!found) {
+		printf("driver not found\n");
+		return -ENODEV;
+	}
+
+	/* search priority by driver */
+	for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) {
+		if (entry->location == env_get_location(ENVOP_LOAD, prio)) {
+			/* when priority change, reset the ENV flags */
+			if (gd->env_load_prio != prio) {
+				gd->env_load_prio = prio;
+				gd->env_valid = ENV_INVALID;
+				gd->flags &= ~GD_FLG_ENV_DEFAULT;
+			}
+			printf("OK\n");
+			return 0;
+		}
+	}
+	printf("priority not found\n");
+
+	return -ENODEV;
+}
diff --git a/env/ext4.c b/env/ext4.c
index 8e90bb7..f823b69 100644
--- a/env/ext4.c
+++ b/env/ext4.c
@@ -32,6 +32,8 @@
 #include <ext4fs.h>
 #include <mmc.h>
 
+DECLARE_GLOBAL_DATA_PTR;
+
 __weak const char *env_ext4_get_intf(void)
 {
 	return (const char *)CONFIG_ENV_EXT4_INTERFACE;
@@ -42,9 +44,8 @@
 	return (const char *)CONFIG_ENV_EXT4_DEVICE_AND_PART;
 }
 
-static int env_ext4_save(void)
+static int env_ext4_save_buffer(env_t *env_new)
 {
-	env_t	env_new;
 	struct blk_desc *dev_desc = NULL;
 	struct disk_partition info;
 	int dev, part;
@@ -52,10 +53,6 @@
 	const char *ifname = env_ext4_get_intf();
 	const char *dev_and_part = env_ext4_get_dev_part();
 
-	err = env_export(&env_new);
-	if (err)
-		return err;
-
 	part = blk_get_device_part_str(ifname, dev_and_part,
 				       &dev_desc, &info, 1);
 	if (part < 0)
@@ -70,7 +67,7 @@
 		return 1;
 	}
 
-	err = ext4fs_write(CONFIG_ENV_EXT4_FILE, (void *)&env_new,
+	err = ext4fs_write(CONFIG_ENV_EXT4_FILE, (void *)env_new,
 			   sizeof(env_t), FILETYPE_REG);
 	ext4fs_close();
 
@@ -80,10 +77,45 @@
 		return 1;
 	}
 
+	return 0;
+}
+
+static int env_ext4_save(void)
+{
+	env_t env_new;
+	int err;
+
+	err = env_export(&env_new);
+	if (err)
+		return err;
+
+	err = env_ext4_save_buffer(&env_new);
+	if (err)
+		return err;
+
+	gd->env_valid = ENV_VALID;
 	puts("done\n");
+
 	return 0;
 }
 
+static int env_ext4_erase(void)
+{
+	env_t env_new;
+	int err;
+
+	memset(&env_new, 0, sizeof(env_t));
+
+	err = env_ext4_save_buffer(&env_new);
+	if (err)
+		return err;
+
+	gd->env_valid = ENV_INVALID;
+	puts("done\n");
+
+	return 0;
+}
+
 static int env_ext4_load(void)
 {
 	ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
@@ -124,7 +156,11 @@
 		goto err_env_relocate;
 	}
 
-	return env_import(buf, 1);
+	err = env_import(buf, 1, H_EXTERNAL);
+	if (!err)
+		gd->env_valid = ENV_VALID;
+
+	return err;
 
 err_env_relocate:
 	env_set_default(NULL, 0);
@@ -137,4 +173,6 @@
 	ENV_NAME("EXT4")
 	.load		= env_ext4_load,
 	.save		= ENV_SAVE_PTR(env_ext4_save),
+	.erase		= CONFIG_IS_ENABLED(CMD_ERASEENV) ? env_ext4_erase :
+							    NULL,
 };
diff --git a/env/fat.c b/env/fat.c
index 63aced9..71bf8bf 100644
--- a/env/fat.c
+++ b/env/fat.c
@@ -144,7 +144,7 @@
 		goto err_env_relocate;
 	}
 
-	return env_import(buf, 1);
+	return env_import(buf, 1, H_EXTERNAL);
 
 err_env_relocate:
 	env_set_default(NULL, 0);
diff --git a/env/flags.c b/env/flags.c
index b88fe7b..df4aed2 100644
--- a/env/flags.c
+++ b/env/flags.c
@@ -28,8 +28,15 @@
 #define ENV_FLAGS_NET_VARTYPE_REPS ""
 #endif
 
+#ifdef CONFIG_ENV_WRITEABLE_LIST
+#define ENV_FLAGS_WRITEABLE_VARACCESS_REPS "w"
+#else
+#define ENV_FLAGS_WRITEABLE_VARACCESS_REPS ""
+#endif
+
 static const char env_flags_vartype_rep[] = "sdxb" ENV_FLAGS_NET_VARTYPE_REPS;
-static const char env_flags_varaccess_rep[] = "aroc";
+static const char env_flags_varaccess_rep[] =
+	"aroc" ENV_FLAGS_WRITEABLE_VARACCESS_REPS;
 static const int env_flags_varaccess_mask[] = {
 	0,
 	ENV_FLAGS_VARACCESS_PREVENT_DELETE |
@@ -38,7 +45,11 @@
 	ENV_FLAGS_VARACCESS_PREVENT_DELETE |
 		ENV_FLAGS_VARACCESS_PREVENT_OVERWR,
 	ENV_FLAGS_VARACCESS_PREVENT_DELETE |
-		ENV_FLAGS_VARACCESS_PREVENT_NONDEF_OVERWR};
+		ENV_FLAGS_VARACCESS_PREVENT_NONDEF_OVERWR,
+#ifdef CONFIG_ENV_WRITEABLE_LIST
+	ENV_FLAGS_VARACCESS_WRITEABLE,
+#endif
+	};
 
 #ifdef CONFIG_CMD_ENV_FLAGS
 static const char * const env_flags_vartype_names[] = {
@@ -56,6 +67,9 @@
 	"read-only",
 	"write-once",
 	"change-default",
+#ifdef CONFIG_ENV_WRITEABLE_LIST
+	"writeable",
+#endif
 };
 
 /*
@@ -130,21 +144,25 @@
  */
 enum env_flags_varaccess env_flags_parse_varaccess(const char *flags)
 {
+	enum env_flags_varaccess va_default = env_flags_varaccess_any;
+	enum env_flags_varaccess va;
 	char *access;
 
 	if (strlen(flags) <= ENV_FLAGS_VARACCESS_LOC)
-		return env_flags_varaccess_any;
+		return va_default;
 
 	access = strchr(env_flags_varaccess_rep,
 		flags[ENV_FLAGS_VARACCESS_LOC]);
 
-	if (access != NULL)
-		return (enum env_flags_varaccess)
+	if (access != NULL) {
+		va = (enum env_flags_varaccess)
 			(access - &env_flags_varaccess_rep[0]);
+		return va;
+	}
 
 	printf("## Warning: Unknown environment variable access method '%c'\n",
 		flags[ENV_FLAGS_VARACCESS_LOC]);
-	return env_flags_varaccess_any;
+	return va_default;
 }
 
 /*
@@ -152,17 +170,21 @@
  */
 enum env_flags_varaccess env_flags_parse_varaccess_from_binflags(int binflags)
 {
+	enum env_flags_varaccess va_default = env_flags_varaccess_any;
+	enum env_flags_varaccess va;
 	int i;
 
 	for (i = 0; i < ARRAY_SIZE(env_flags_varaccess_mask); i++)
 		if (env_flags_varaccess_mask[i] ==
-		    (binflags & ENV_FLAGS_VARACCESS_BIN_MASK))
-			return (enum env_flags_varaccess)i;
+		    (binflags & ENV_FLAGS_VARACCESS_BIN_MASK)) {
+			va = (enum env_flags_varaccess)i;
+			return va;
+	}
 
 	printf("Warning: Non-standard access flags. (0x%x)\n",
 		binflags & ENV_FLAGS_VARACCESS_BIN_MASK);
 
-	return env_flags_varaccess_any;
+	return va_default;
 }
 
 static inline int is_hex_prefix(const char *value)
@@ -326,13 +348,14 @@
 enum env_flags_varaccess env_flags_get_varaccess(const char *name)
 {
 	const char *flags_list = env_get(ENV_FLAGS_VAR);
+	enum env_flags_varaccess va_default = env_flags_varaccess_any;
 	char flags[ENV_FLAGS_ATTR_MAX_LEN + 1];
 
 	if (env_flags_lookup(flags_list, name, flags))
-		return env_flags_varaccess_any;
+		return va_default;
 
 	if (strlen(flags) <= ENV_FLAGS_VARACCESS_LOC)
-		return env_flags_varaccess_any;
+		return va_default;
 
 	return env_flags_parse_varaccess(flags);
 }
@@ -426,7 +449,11 @@
 	int ret = 1;
 
 	if (first_call) {
+#ifdef CONFIG_ENV_WRITEABLE_LIST
+		flags_list = ENV_FLAGS_LIST_STATIC;
+#else
 		flags_list = env_get(ENV_FLAGS_VAR);
+#endif
 		first_call = 0;
 	}
 	/* look in the ".flags" and static for a reference to this variable */
@@ -523,9 +550,24 @@
 	}
 
 	/* check for access permission */
+#ifdef CONFIG_ENV_WRITEABLE_LIST
+	if (flag & H_DEFAULT)
+		return 0;	/* Default env is always OK */
+
+	/*
+	 * External writeable variables can be overwritten by external env,
+	 * anything else can not be overwritten by external env.
+	 */
+	if ((flag & H_EXTERNAL) &&
+	    !(item->flags & ENV_FLAGS_VARACCESS_WRITEABLE))
+		return 1;
+#endif
+
 #ifndef CONFIG_ENV_ACCESS_IGNORE_FORCE
-	if (flag & H_FORCE)
+	if (flag & H_FORCE) {
+		printf("## Error: Can't force access to \"%s\"\n", name);
 		return 0;
+	}
 #endif
 	switch (op) {
 	case env_op_delete:
diff --git a/env/flash.c b/env/flash.c
index 3198147..722d5ad 100644
--- a/env/flash.c
+++ b/env/flash.c
@@ -351,7 +351,7 @@
 		     "reading environment; recovered successfully\n\n");
 #endif /* CONFIG_ENV_ADDR_REDUND */
 
-	return env_import((char *)flash_addr, 1);
+	return env_import((char *)flash_addr, 1, H_EXTERNAL);
 }
 #endif /* LOADENV */
 
diff --git a/env/mmc.c b/env/mmc.c
index aca61b7..af7e5fb 100644
--- a/env/mmc.c
+++ b/env/mmc.c
@@ -338,7 +338,7 @@
 	read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2);
 
 	ret = env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2,
-				read2_fail);
+				read2_fail, H_EXTERNAL);
 
 fini:
 	fini_mmc_for_env(mmc);
@@ -380,7 +380,7 @@
 		goto fini;
 	}
 
-	ret = env_import(buf, 1);
+	ret = env_import(buf, 1, H_EXTERNAL);
 	if (!ret) {
 		ep = (env_t *)buf;
 		gd->env_addr = (ulong)&ep->data;
diff --git a/env/nand.c b/env/nand.c
index 8b0027d..0d7ee19 100644
--- a/env/nand.c
+++ b/env/nand.c
@@ -331,7 +331,7 @@
 	read2_fail = readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2);
 
 	ret = env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2,
-				read2_fail);
+				read2_fail, H_EXTERNAL);
 
 done:
 	free(tmp_env1);
@@ -372,7 +372,7 @@
 		return -EIO;
 	}
 
-	return env_import(buf, 1);
+	return env_import(buf, 1, H_EXTERNAL);
 #endif /* ! ENV_IS_EMBEDDED */
 
 	return 0;
diff --git a/env/nowhere.c b/env/nowhere.c
index f5b0a17..d33fdf2 100644
--- a/env/nowhere.c
+++ b/env/nowhere.c
@@ -27,8 +27,25 @@
 	return 0;
 }
 
+static int env_nowhere_load(void)
+{
+	/*
+	 * for SPL, set env_valid = ENV_INVALID is enougth as env_get_char()
+	 * return the default env if env_get is used
+	 * and SPL don't used env_import to reduce its size
+	 * For U-Boot proper, import the default environment to allow reload.
+	 */
+	if (!IS_ENABLED(CONFIG_SPL_BUILD))
+		env_set_default(NULL, 0);
+
+	gd->env_valid	= ENV_INVALID;
+
+	return 0;
+}
+
 U_BOOT_ENV_LOCATION(nowhere) = {
 	.location	= ENVL_NOWHERE,
 	.init		= env_nowhere_init,
+	.load		= env_nowhere_load,
 	ENV_NAME("nowhere")
 };
diff --git a/env/nvram.c b/env/nvram.c
index 1a9fcf1..7c8ea26 100644
--- a/env/nvram.c
+++ b/env/nvram.c
@@ -64,7 +64,7 @@
 #else
 	memcpy(buf, (void *)CONFIG_ENV_ADDR, CONFIG_ENV_SIZE);
 #endif
-	return env_import(buf, 1);
+	return env_import(buf, 1, H_EXTERNAL);
 }
 
 static int env_nvram_save(void)
diff --git a/env/onenand.c b/env/onenand.c
index dfd4e93..a2477ce 100644
--- a/env/onenand.c
+++ b/env/onenand.c
@@ -55,7 +55,7 @@
 		mtd->writesize = MAX_ONENAND_PAGESIZE;
 #endif /* !ENV_IS_EMBEDDED */
 
-	rc = env_import(buf, 1);
+	rc = env_import(buf, 1, H_EXTERNAL);
 	if (!rc)
 		gd->env_valid = ENV_VALID;
 
diff --git a/env/remote.c b/env/remote.c
index e3f0608..d93a137 100644
--- a/env/remote.c
+++ b/env/remote.c
@@ -45,7 +45,7 @@
 static int env_remote_load(void)
 {
 #ifndef ENV_IS_EMBEDDED
-	return env_import((char *)env_ptr, 1);
+	return env_import((char *)env_ptr, 1, H_EXTERNAL);
 #endif
 
 	return 0;
diff --git a/env/sata.c b/env/sata.c
index 8bfcc94..9442cfc 100644
--- a/env/sata.c
+++ b/env/sata.c
@@ -111,7 +111,7 @@
 		return -EIO;
 	}
 
-	return env_import(buf, 1);
+	return env_import(buf, 1, H_EXTERNAL);
 }
 
 U_BOOT_ENV_LOCATION(sata) = {
diff --git a/env/sf.c b/env/sf.c
index 3e524f2..937778a 100644
--- a/env/sf.c
+++ b/env/sf.c
@@ -172,7 +172,7 @@
 				    CONFIG_ENV_SIZE, tmp_env2);
 
 	ret = env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2,
-				read2_fail);
+				read2_fail, H_EXTERNAL);
 
 	spi_flash_free(env_flash);
 	env_flash = NULL;
@@ -265,7 +265,7 @@
 		goto err_read;
 	}
 
-	ret = env_import(buf, 1);
+	ret = env_import(buf, 1, H_EXTERNAL);
 	if (!ret)
 		gd->env_valid = ENV_VALID;
 
@@ -305,7 +305,7 @@
 
 U_BOOT_ENV_LOCATION(sf) = {
 	.location	= ENVL_SPI_FLASH,
-	ENV_NAME("SPI Flash")
+	ENV_NAME("SPIFlash")
 	.load		= env_sf_load,
 	.save		= CONFIG_IS_ENABLED(SAVEENV) ? ENV_SAVE_PTR(env_sf_save) : NULL,
 #if defined(INITENV) && (CONFIG_ENV_ADDR != 0x0)
diff --git a/env/ubi.c b/env/ubi.c
index 08aac47..5502efe 100644
--- a/env/ubi.c
+++ b/env/ubi.c
@@ -141,7 +141,7 @@
 		       CONFIG_ENV_UBI_PART, CONFIG_ENV_UBI_VOLUME_REDUND);
 
 	return env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2,
-							 read2_fail);
+				 read2_fail, H_EXTERNAL);
 }
 #else /* ! CONFIG_SYS_REDUNDAND_ENVIRONMENT */
 static int env_ubi_load(void)
@@ -172,7 +172,7 @@
 		return -EIO;
 	}
 
-	return env_import(buf, 1);
+	return env_import(buf, 1, H_EXTERNAL);
 }
 #endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */
 
diff --git a/include/env.h b/include/env.h
index d6c2d75..af40595 100644
--- a/include/env.h
+++ b/include/env.h
@@ -266,6 +266,13 @@
 int env_load(void);
 
 /**
+ * env_reload() - Re-Load the environment from current storage
+ *
+ * @return 0 if OK, -ve on error
+ */
+int env_reload(void);
+
+/**
  * env_save() - Save the environment to storage
  *
  * @return 0 if OK, -ve on error
@@ -280,6 +287,13 @@
 int env_erase(void);
 
 /**
+ * env_select() - Select the environment storage
+ *
+ * @return 0 if OK, -ve on error
+ */
+int env_select(const char *name);
+
+/**
  * env_import() - Import from a binary representation into hash table
  *
  * This imports the environment from a buffer. The format for each variable is
@@ -288,10 +302,11 @@
  * @buf: Buffer containing the environment (struct environemnt_s *)
  * @check: non-zero to check the CRC at the start of the environment, 0 to
  *	ignore it
+ * @flags: Flags controlling matching (H_... - see search.h)
  * @return 0 if imported successfully, -ENOMSG if the CRC was bad, -EIO if
  *	something else went wrong
  */
-int env_import(const char *buf, int check);
+int env_import(const char *buf, int check, int flags);
 
 /**
  * env_export() - Export the environment to a buffer
@@ -310,10 +325,12 @@
  * @buf1_read_fail: 0 if buf1 is valid, non-zero if invalid
  * @buf2: Second environment (struct environemnt_s *)
  * @buf2_read_fail: 0 if buf2 is valid, non-zero if invalid
+ * @flags: Flags controlling matching (H_... - see search.h)
  * @return 0 if OK, -EIO if no environment is valid, -ENOMSG if the CRC was bad
  */
 int env_import_redund(const char *buf1, int buf1_read_fail,
-		      const char *buf2, int buf2_read_fail);
+		      const char *buf2, int buf2_read_fail,
+		      int flags);
 
 /**
  * env_get_default() - Look up a variable from the default environment
@@ -342,5 +359,4 @@
  * This is used for those unfortunate archs with crappy toolchains
  */
 void env_reloc(void);
-
 #endif
diff --git a/include/env_flags.h b/include/env_flags.h
index 725841a..313cb8c 100644
--- a/include/env_flags.h
+++ b/include/env_flags.h
@@ -24,6 +24,9 @@
 	env_flags_varaccess_readonly,
 	env_flags_varaccess_writeonce,
 	env_flags_varaccess_changedefault,
+#ifdef CONFIG_ENV_WRITEABLE_LIST
+	env_flags_varaccess_writeable,
+#endif
 	env_flags_varaccess_end
 };
 
@@ -173,6 +176,7 @@
 #define ENV_FLAGS_VARACCESS_PREVENT_CREATE		0x00000010
 #define ENV_FLAGS_VARACCESS_PREVENT_OVERWR		0x00000020
 #define ENV_FLAGS_VARACCESS_PREVENT_NONDEF_OVERWR	0x00000040
-#define ENV_FLAGS_VARACCESS_BIN_MASK			0x00000078
+#define ENV_FLAGS_VARACCESS_WRITEABLE			0x00000080
+#define ENV_FLAGS_VARACCESS_BIN_MASK			0x000000f8
 
 #endif /* __ENV_FLAGS_H__ */
diff --git a/include/env_internal.h b/include/env_internal.h
index 6655043..b26dc62 100644
--- a/include/env_internal.h
+++ b/include/env_internal.h
@@ -154,8 +154,7 @@
 	/**
 	 * load() - Load the environment from storage
 	 *
-	 * This method is optional. If not provided, no environment will be
-	 * loaded.
+	 * This method is required for loading environment
 	 *
 	 * @return 0 if OK, -ve on error
 	 */
@@ -212,6 +211,26 @@
 extern struct hsearch_data env_htab;
 
 /**
+ * env_ext4_get_intf() - Provide the interface for env in EXT4
+ *
+ * It is a weak function allowing board to overidde the default interface for
+ * U-Boot env in EXT4: CONFIG_ENV_EXT4_INTERFACE
+ *
+ * @return string of interface, empty if not supported
+ */
+const char *env_ext4_get_intf(void);
+
+/**
+ * env_ext4_get_dev_part() - Provide the device and partition for env in EXT4
+ *
+ * It is a weak function allowing board to overidde the default device and
+ * partition used for U-Boot env in EXT4: CONFIG_ENV_EXT4_DEVICE_AND_PART
+ *
+ * @return string of device and partition
+ */
+const char *env_ext4_get_dev_part(void);
+
+/**
  * env_get_location()- Provide the best location for the U-Boot environment
  *
  * It is a weak function allowing board to overidde the environment location
diff --git a/include/search.h b/include/search.h
index bca36d3..e56843c 100644
--- a/include/search.h
+++ b/include/search.h
@@ -112,5 +112,7 @@
 #define H_MATCH_METHOD	(H_MATCH_IDENT | H_MATCH_SUBSTR | H_MATCH_REGEX)
 #define H_PROGRAMMATIC	(1 << 9) /* indicate that an import is from env_set() */
 #define H_ORIGIN_FLAGS	(H_INTERACTIVE | H_PROGRAMMATIC)
+#define H_DEFAULT	(1 << 10) /* indicate that an import is default env */
+#define H_EXTERNAL	(1 << 11) /* indicate that an import is external env */
 
 #endif /* _SEARCH_H_ */
diff --git a/lib/hashtable.c b/lib/hashtable.c
index 7b6781b..4a8c50b 100644
--- a/lib/hashtable.c
+++ b/lib/hashtable.c
@@ -826,6 +826,10 @@
 	if (nvars)
 		memcpy(localvars, vars, sizeof(vars[0]) * nvars);
 
+#if CONFIG_IS_ENABLED(ENV_APPEND)
+	flag |= H_NOCLEAR;
+#endif
+
 	if ((flag & H_NOCLEAR) == 0 && !nvars) {
 		/* Destroy old hash table if one exists */
 		debug("Destroy Hash Table: %p table = %p\n", htab,
@@ -946,9 +950,12 @@
 		e.data = value;
 
 		hsearch_r(e, ENV_ENTER, &rv, htab, flag);
-		if (rv == NULL)
+#if !CONFIG_IS_ENABLED(ENV_WRITEABLE_LIST)
+		if (rv == NULL) {
 			printf("himport_r: can't insert \"%s=%s\" into hash table\n",
 				name, value);
+		}
+#endif
 
 		debug("INSERT: table %p, filled %d/%d rv %p ==> name=\"%s\" value=\"%s\"\n",
 			htab, htab->filled, htab->size,
diff --git a/test/py/tests/test_efi_loader.py b/test/py/tests/test_efi_loader.py
index ca68626..fc8d6b8 100644
--- a/test/py/tests/test_efi_loader.py
+++ b/test/py/tests/test_efi_loader.py
@@ -199,6 +199,6 @@
     # Then exit cleanly
     u_boot_console.wait_for('grub>')
     u_boot_console.run_command('exit', wait_for_prompt=False, wait_for_echo=False)
-    u_boot_console.wait_for('=>')
+    u_boot_console.wait_for(u_boot_console.prompt)
     # And give us our U-Boot prompt back
     u_boot_console.run_command('')
diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py
index 971c9f6..9b520c2 100644
--- a/test/py/tests/test_efi_selftest.py
+++ b/test/py/tests/test_efi_selftest.py
@@ -36,7 +36,7 @@
     output = u_boot_console.run_command('bootefi selftest')
     assert '\'device tree\'' in output
     u_boot_console.run_command(cmd='setenv efi_selftest device tree')
-    u_boot_console.run_command(cmd='setenv -f serial# Testing DT')
+    u_boot_console.run_command(cmd='setenv serial# Testing DT')
     u_boot_console.run_command(cmd='bootefi selftest ${fdtcontroladdr}', wait_for_prompt=False)
     m = u_boot_console.p.expect(['serial-number: Testing DT', 'U-Boot'])
     if m != 0:
diff --git a/test/py/tests/test_env.py b/test/py/tests/test_env.py
index a64aaa9..86ec1b3 100644
--- a/test/py/tests/test_env.py
+++ b/test/py/tests/test_env.py
@@ -4,6 +4,10 @@
 
 # Test operation of shell commands relating to environment variables.
 
+import os
+import os.path
+from subprocess import call, check_call, CalledProcessError
+
 import pytest
 import u_boot_utils
 
@@ -374,7 +378,6 @@
 @pytest.mark.buildconfigspec('cmd_nvedit_info')
 @pytest.mark.buildconfigspec('cmd_echo')
 def test_env_info_sandbox(state_test_env):
-
     """Test 'env info' command result with several options on sandbox
        with a known ENV configuration: ready & default & persistent
     """
@@ -399,3 +402,111 @@
     response = c.run_command('env info -d -p -q')
     response = c.run_command('echo $?')
     assert response == "1"
+
+def mk_env_ext4(state_test_env):
+
+    """Create a empty ext4 file system volume."""
+    c = state_test_env.u_boot_console
+    filename = 'env.ext4.img'
+    persistent = c.config.persistent_data_dir + '/' + filename
+    fs_img = c.config.result_dir  + '/' + filename
+
+    if os.path.exists(persistent):
+        c.log.action('Disk image file ' + persistent + ' already exists')
+    else:
+        try:
+            u_boot_utils.run_and_log(c, 'dd if=/dev/zero of=%s bs=1M count=16' % persistent)
+            u_boot_utils.run_and_log(c, 'mkfs.ext4 -O ^metadata_csum %s' % persistent)
+        except CalledProcessError:
+            call('rm -f %s' % persistent, shell=True)
+            raise
+
+    u_boot_utils.run_and_log(c, ['cp',  '-f', persistent, fs_img])
+    return fs_img
+
+@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_ext4')
+def test_env_ext4(state_test_env):
+
+    """Test ENV in EXT4 on sandbox."""
+    c = state_test_env.u_boot_console
+    fs_img = ''
+    try:
+        fs_img = mk_env_ext4(state_test_env)
+
+        c.run_command('host bind 0  %s' % fs_img)
+
+        response = c.run_command('ext4ls host 0:0')
+        assert 'uboot.env' not in response
+
+        # force env location: EXT4 (prio 1 in sandbox)
+        response = c.run_command('env select EXT4')
+        assert 'Select Environment on EXT4: OK' in response
+
+        response = c.run_command('env save')
+        assert 'Saving Environment to EXT4' in response
+
+        response = c.run_command('env load')
+        assert 'Loading Environment from EXT4... OK' in response
+
+        response = c.run_command('ext4ls host 0:0')
+        assert '8192 uboot.env' 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 EXT4... ' 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 fs_img:
+            call('rm -f %s' % fs_img, shell=True)
diff --git a/tools/env/fw_env.c b/tools/env/fw_env.c
index c6378ec..3ab1ae6 100644
--- a/tools/env/fw_env.c
+++ b/tools/env/fw_env.c
@@ -995,6 +995,7 @@
 				   of the data */
 	loff_t blockstart;	/* running start of the current block -
 				   MEMGETBADBLOCK needs 64 bits */
+	int was_locked;		/* flash lock flag */
 	int rc;
 
 	/*
@@ -1080,6 +1081,12 @@
 	}
 
 	erase.length = erasesize;
+	if (DEVTYPE(dev) != MTD_ABSENT) {
+		was_locked = ioctl(fd, MEMISLOCKED, &erase);
+		/* treat any errors as unlocked flash */
+		if (was_locked < 0)
+			was_locked = 0;
+	}
 
 	/* This only runs once on NOR flash and SPI-dataflash */
 	while (processed < write_total) {
@@ -1099,7 +1106,8 @@
 
 		if (DEVTYPE(dev) != MTD_ABSENT) {
 			erase.start = blockstart;
-			ioctl(fd, MEMUNLOCK, &erase);
+			if (was_locked)
+				ioctl(fd, MEMUNLOCK, &erase);
 			/* These do not need an explicit erase cycle */
 			if (DEVTYPE(dev) != MTD_DATAFLASH)
 				if (ioctl(fd, MEMERASE, &erase) != 0) {
@@ -1127,8 +1135,10 @@
 			return -1;
 		}
 
-		if (DEVTYPE(dev) != MTD_ABSENT)
-			ioctl(fd, MEMLOCK, &erase);
+		if (DEVTYPE(dev) != MTD_ABSENT) {
+			if (was_locked)
+				ioctl(fd, MEMLOCK, &erase);
+		}
 
 		processed += erasesize;
 		block_seek = 0;
@@ -1149,7 +1159,9 @@
 	int rc;
 	struct erase_info_user erase;
 	char tmp = ENV_REDUND_OBSOLETE;
+	int was_locked;	/* flash lock flag */
 
+	was_locked = ioctl(fd, MEMISLOCKED, &erase);
 	erase.start = DEVOFFSET(dev);
 	erase.length = DEVESIZE(dev);
 	/* This relies on the fact, that ENV_REDUND_OBSOLETE == 0 */
@@ -1159,9 +1171,11 @@
 			DEVNAME(dev));
 		return rc;
 	}
-	ioctl(fd, MEMUNLOCK, &erase);
+	if (was_locked)
+		ioctl(fd, MEMUNLOCK, &erase);
 	rc = write(fd, &tmp, sizeof(tmp));
-	ioctl(fd, MEMLOCK, &erase);
+	if (was_locked)
+		ioctl(fd, MEMLOCK, &erase);
 	if (rc < 0)
 		perror("Could not set obsolete flag");