[U-Boot] [PATCH v5 01/19] env: extend interfaces allowing for env contexts

AKASHI Takahiro takahiro.akashi at linaro.org
Thu Sep 5 08:21:15 UTC 2019


The notion of "env context" (or simply "context") will provide users
of env interfaces to manage their own domain of variables, which is
separated from other contexts and can be loaded/stored with dedicated
backing storage device.

With this patch, almost of all env interfaces are extended to accept
additional argument of a pointer to env context to identify targeted
domain after Wolfgang's requirement.

Optionally, a context may have no backing storage, in this case,
all the entries associated with this context will be lost after reboot
and env_save() returns -ENODEV.

In addition, "env" command is also modified to allow for specifying
a context with "-C <context name>" option.
Please note "-e" in "env set" and "env print" is still maintained
not only for backward compatibility but also for usefullness
at non-U-Boot-environment-based UEFI variables implementation,
in particular, secure storage solution in the future.

Signed-off-by: AKASHI Takahiro <takahiro.akashi at linaro.org>
---
 cmd/nvedit.c           | 394 +++++++++++++++++-------
 env/Kconfig            | 682 ++---------------------------------------
 env/Makefile           |  30 +-
 env/callback.c         |  40 ++-
 env/common.c           | 255 ++++++++-------
 env/env.c              | 158 ++++++----
 env/flags.c            |  35 ++-
 include/_exports.h     |   6 +-
 include/common.h       |   6 +-
 include/env.h          |  95 ++++--
 include/env_internal.h |  89 +++++-
 include/exports.h      |   5 +-
 12 files changed, 802 insertions(+), 993 deletions(-)

diff --git a/cmd/nvedit.c b/cmd/nvedit.c
index 1cb0bc1460b9..04af78d71b47 100644
--- a/cmd/nvedit.c
+++ b/cmd/nvedit.c
@@ -40,34 +40,24 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
-#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
-
-#if	!defined(ENV_IS_IN_DEVICE)		&& \
-	!defined(CONFIG_ENV_IS_NOWHERE)
-# error Define one of CONFIG_ENV_IS_IN_{EEPROM|FLASH|MMC|FAT|EXT4|\
-NAND|NVRAM|ONENAND|SATA|SPI_FLASH|REMOTE|UBI} or CONFIG_ENV_IS_NOWHERE
-#endif
-
 /*
  * Maximum expected input data size for import command
  */
 #define	MAX_ENV_SIZE	(1 << 20)	/* 1 MiB */
 
+static struct env_context *get_env_context(const char *arg)
+{
+	struct env_context *ctx;
+	int i;
+
+	for (i = 0, ctx = U_BOOT_ENV_CTX_START; i < U_BOOT_ENV_CTX_COUNT;
+	     i++, ctx++)
+		if (!strcmp(arg, ctx->name))
+			return ctx;
+
+	return NULL;
+}
+
 /*
  * This variable is incremented on each do_env_set(), so it can
  * be used via env_get_id() as an indication, if the environment
@@ -75,11 +65,9 @@ NAND|NVRAM|ONENAND|SATA|SPI_FLASH|REMOTE|UBI} or CONFIG_ENV_IS_NOWHERE
  * variable only if the environment was changed ... done so for
  * example in NetInitLoop()
  */
-static int env_id = 1;
-
-int env_get_id(void)
+int env_get_id(struct env_context *ctx)
 {
-	return env_id;
+	return ctx->env_id;
 }
 
 #ifndef CONFIG_SPL_BUILD
@@ -88,7 +76,7 @@ int env_get_id(void)
  *
  * Returns 0 in case of error, or length of printed string
  */
-static int env_print(char *name, int flag)
+static int env_print(struct env_context *ctx, char *name, int flag)
 {
 	char *res = NULL;
 	ssize_t len;
@@ -96,9 +84,10 @@ static int env_print(char *name, int flag)
 	if (name) {		/* print a single name */
 		struct env_entry e, *ep;
 
+		e.ctx = ctx;
 		e.key = name;
 		e.data = NULL;
-		hsearch_r(e, ENV_FIND, &ep, &env_htab, flag);
+		hsearch_r(e, ENV_FIND, &ep, ctx->htab, flag);
 		if (ep == NULL)
 			return 0;
 		len = printf("%s=%s\n", ep->key, ep->data);
@@ -106,7 +95,7 @@ static int env_print(char *name, int flag)
 	}
 
 	/* print whole list */
-	len = hexport_r(&env_htab, '\n', flag, &res, 0, 0, NULL);
+	len = hexport_r(ctx->htab, '\n', flag, &res, 0, 0, NULL);
 
 	if (len > 0) {
 		puts(res);
@@ -122,24 +111,48 @@ static int env_print(char *name, int flag)
 static int do_env_print(cmd_tbl_t *cmdtp, int flag, int argc,
 			char * const argv[])
 {
+	struct env_context *ctx = ctx_uboot;
 	int i;
 	int rcode = 0;
 	int env_flag = H_HIDE_DOT;
 
+	while (argc > 1 && argv[1][0] == '-') {
 #if defined(CONFIG_CMD_NVEDIT_EFI)
-	if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'e')
-		return do_env_print_efi(cmdtp, flag, --argc, ++argv);
+		if (!strcmp(argv[1], "-e")) {
+			argc--;
+			argv++;
+
+			return do_env_print_efi(cmdtp, flag, argc, argv);
+		}
 #endif
 
-	if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'a') {
-		argc--;
-		argv++;
-		env_flag &= ~H_HIDE_DOT;
+		if (!strcmp(argv[1], "-a")) {
+			argc--;
+			argv++;
+			env_flag &= ~H_HIDE_DOT;
+			continue;
+		}
+
+		if (!strcmp(argv[1], "-C")) {
+			if (argc == 2) {
+				printf("\nNo context specified\n");
+				return 0;
+			}
+			ctx = get_env_context(argv[2]);
+			if (!ctx) {
+				printf("\nInvalid context: %s\n", argv[2]);
+				return 0;
+			}
+			argc -= 2;
+			argv += 2;
+
+			continue;
+		}
 	}
 
 	if (argc == 1) {
 		/* print all env vars */
-		rcode = env_print(NULL, env_flag);
+		rcode = env_print(ctx, NULL, env_flag);
 		if (!rcode)
 			return 1;
 		printf("\nEnvironment size: %d/%ld bytes\n",
@@ -150,7 +163,7 @@ static int do_env_print(cmd_tbl_t *cmdtp, int flag, int argc,
 	/* print selected env vars */
 	env_flag &= ~H_HIDE_DOT;
 	for (i = 1; i < argc; ++i) {
-		int rc = env_print(argv[i], env_flag);
+		int rc = env_print(ctx, argv[i], env_flag);
 		if (!rc) {
 			printf("## Error: \"%s\" not defined\n", argv[i]);
 			++rcode;
@@ -164,12 +177,23 @@ static int do_env_print(cmd_tbl_t *cmdtp, int flag, int argc,
 static int do_env_grep(cmd_tbl_t *cmdtp, int flag,
 		       int argc, char * const argv[])
 {
+	struct env_context *ctx = ctx_uboot;
 	char *res = NULL;
 	int len, grep_how, grep_what;
 
 	if (argc < 2)
 		return CMD_RET_USAGE;
 
+	if (argc >= 5 && !strcmp(argc[0], "-C")) {
+		ctx = get_env_context(argv[1]);
+		if (!ctx) {
+			printf("\nInvalid context: %s\n", argv[2]);
+			return CMD_RET_FAILURE;
+		}
+		argc -= 2;
+		argv += 2;
+	}
+
 	grep_how  = H_MATCH_SUBSTR;	/* default: substring search	*/
 	grep_what = H_MATCH_BOTH;	/* default: grep names and values */
 
@@ -200,7 +224,7 @@ static int do_env_grep(cmd_tbl_t *cmdtp, int flag,
 	}
 
 DONE:
-	len = hexport_r(&env_htab, '\n',
+	len = hexport_r(ctx->htab, '\n',
 			flag | grep_what | grep_how,
 			&res, 0, argc, argv);
 
@@ -223,6 +247,7 @@ DONE:
  */
 static int _do_env_set(int flag, int argc, char * const argv[], int env_flag)
 {
+	struct env_context *ctx = ctx_uboot;
 	int   i, len;
 	char  *name, *value, *s;
 	struct env_entry e, *ep;
@@ -230,9 +255,23 @@ static int _do_env_set(int flag, int argc, char * const argv[], int env_flag)
 	debug("Initial value for argc=%d\n", argc);
 
 #if CONFIG_IS_ENABLED(CMD_NVEDIT_EFI)
-	if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'e')
-		return do_env_set_efi(NULL, flag, --argc, ++argv);
+	if (argc > 1 && !strcmp(argv[1], "-e")) {
+		argc--;
+		argv++;
+
+		/* FIXME: env_flag */
+		return do_env_set_efi(NULL, flag, argc, argv);
+	}
 #endif
+	if (argc >= 3 && !strcmp(argv[1], "-C")) {
+		ctx = get_env_context(argv[2]);
+		if (!ctx) {
+			printf("\nInvalid context: %s\n", argv[2]);
+			return CMD_RET_FAILURE;
+		}
+		argc -= 2;
+		argv += 2;
+	}
 
 	while (argc > 1 && **(argv + 1) == '-') {
 		char *arg = *++argv;
@@ -257,11 +296,11 @@ static int _do_env_set(int flag, int argc, char * const argv[], int env_flag)
 		return 1;
 	}
 
-	env_id++;
+	ctx->env_id++;
 
 	/* Delete only ? */
 	if (argc < 3 || argv[2] == NULL) {
-		int rc = hdelete_r(name, &env_htab, env_flag);
+		int rc = hdelete_r(name, ctx->htab, env_flag);
 		return !rc;
 	}
 
@@ -286,9 +325,10 @@ static int _do_env_set(int flag, int argc, char * const argv[], int env_flag)
 	if (s != value)
 		*--s = '\0';
 
+	e.ctx	= ctx;
 	e.key	= name;
 	e.data	= value;
-	hsearch_r(e, ENV_ENTER, &ep, &env_htab, env_flag);
+	hsearch_r(e, ENV_ENTER, &ep, ctx->htab, env_flag);
 	free(value);
 	if (!ep) {
 		printf("## Error inserting \"%s\" variable, errno=%d\n",
@@ -299,18 +339,19 @@ static int _do_env_set(int flag, int argc, char * const argv[], int env_flag)
 	return 0;
 }
 
-int env_set(const char *varname, const char *varvalue)
+int env_set(struct env_context *ctx, const char *varname, const char *varvalue)
 {
-	const char * const argv[4] = { "setenv", varname, varvalue, NULL };
+	const char * const argv[6] = { "setenv", "-C", ctx->name,
+					varname, varvalue, NULL };
 
 	/* before import into hashtable */
-	if (!(gd->flags & GD_FLG_ENV_READY))
+	if (!env_is_ready(ctx))
 		return 1;
 
 	if (varvalue == NULL || varvalue[0] == '\0')
-		return _do_env_set(0, 2, (char * const *)argv, H_PROGRAMMATIC);
+		return _do_env_set(0, 4, (char * const *)argv, H_PROGRAMMATIC);
 	else
-		return _do_env_set(0, 3, (char * const *)argv, H_PROGRAMMATIC);
+		return _do_env_set(0, 5, (char * const *)argv, H_PROGRAMMATIC);
 }
 
 /**
@@ -320,12 +361,12 @@ int env_set(const char *varname, const char *varvalue)
  * @param value		Value to set it to
  * @return 0 if ok, 1 on error
  */
-int env_set_ulong(const char *varname, ulong value)
+int env_set_ulong(struct env_context *ctx, const char *varname, ulong value)
 {
 	/* TODO: this should be unsigned */
 	char *str = simple_itoa(value);
 
-	return env_set(varname, str);
+	return env_set(ctx, varname, str);
 }
 
 /**
@@ -335,21 +376,22 @@ int env_set_ulong(const char *varname, ulong value)
  * @param value		Value to set it to
  * @return 0 if ok, 1 on error
  */
-int env_set_hex(const char *varname, ulong value)
+int env_set_hex(struct env_context *ctx, const char *varname, ulong value)
 {
 	char str[17];
 
 	sprintf(str, "%lx", value);
-	return env_set(varname, str);
+	return env_set(ctx, varname, str);
 }
 
-ulong env_get_hex(const char *varname, ulong default_val)
+ulong env_get_hex(struct env_context *ctx, const char *varname,
+		  ulong default_val)
 {
 	const char *s;
 	ulong value;
 	char *endp;
 
-	s = env_get(varname);
+	s = env_get(ctx, varname);
 	if (s)
 		value = simple_strtoul(s, &endp, 16);
 	if (!s || endp == s)
@@ -360,7 +402,7 @@ ulong env_get_hex(const char *varname, ulong default_val)
 
 int eth_env_get_enetaddr(const char *name, uint8_t *enetaddr)
 {
-	eth_parse_enetaddr(env_get(name), enetaddr);
+	eth_parse_enetaddr(env_get(ctx_uboot, name), enetaddr);
 	return is_valid_ethaddr(enetaddr);
 }
 
@@ -373,7 +415,7 @@ int eth_env_set_enetaddr(const char *name, const uint8_t *enetaddr)
 
 	sprintf(buf, "%pM", enetaddr);
 
-	return env_set(name, buf);
+	return env_set(ctx_uboot, name, buf);
 }
 
 #ifndef CONFIG_SPL_BUILD
@@ -509,10 +551,21 @@ static int print_active_callback(struct env_entry *entry)
  */
 int do_env_callback(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
+	struct env_context *ctx = ctx_uboot;
 	struct env_clbk_tbl *clbkp;
 	int i;
 	int num_callbacks;
 
+	if (argc > 3 && !strcmp(argv[1], "-C")) {
+		ctx = get_env_context(argv[2]);
+		if (!ctx) {
+			printf("\nInvalid context: %s\n", argv[2]);
+			return CMD_RET_FAILURE;
+		}
+		argc -= 2;
+		argv += 2;
+	}
+
 	/* Print the available callbacks */
 	puts("Available callbacks:\n");
 	puts("\tCallback Name\n");
@@ -535,7 +588,7 @@ int do_env_callback(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 	puts("Active callback bindings:\n");
 	printf("\t%-20s %-20s\n", "Variable Name", "Callback Name");
 	printf("\t%-20s %-20s\n", "-------------", "-------------");
-	hwalk_r(&env_htab, print_active_callback);
+	hwalk_r(ctx->htab, print_active_callback);
 	return 0;
 }
 #endif
@@ -577,6 +630,18 @@ static int print_active_flags(struct env_entry *entry)
  */
 int do_env_flags(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
+	struct env_context *ctx = ctx_uboot;
+
+	if (argc > 3 && !strcmp(argv[1], "-C")) {
+		ctx = get_env_context(argv[2]);
+		if (!ctx) {
+			printf("\nInvalid context: %s\n", argv[2]);
+			return CMD_RET_FAILURE;
+		}
+		argc -= 2;
+		argv += 2;
+	}
+
 	/* Print the available variable types */
 	printf("Available variable type flags (position %d):\n",
 		ENV_FLAGS_VARTYPE_LOC);
@@ -608,7 +673,7 @@ int do_env_flags(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 		"Variable Access");
 	printf("\t%-20s %-20s %-20s\n", "-------------", "-------------",
 		"---------------");
-	hwalk_r(&env_htab, print_active_flags);
+	hwalk_r(ctx->htab, print_active_flags);
 	return 0;
 }
 #endif
@@ -620,18 +685,31 @@ int do_env_flags(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 static int do_env_edit(cmd_tbl_t *cmdtp, int flag, int argc,
 		       char * const argv[])
 {
+	struct env_context *ctx = ctx_uboot;
 	char buffer[CONFIG_SYS_CBSIZE];
 	char *init_val;
 
 	if (argc < 2)
 		return CMD_RET_USAGE;
 
+	if (argc > 3 && !strcmp(argv[1], "-C")) {
+		ctx = get_env_context(argv[2]);
+		if (!ctx) {
+			printf("\nInvalid context: %s\n", argv[2]);
+			return CMD_RET_FAILURE;
+		}
+		argc -= 2;
+		argv += 2;
+		if (argc < 2)
+			return CMD_RET_USAGE;
+	}
+
 	/* before import into hashtable */
-	if (!(gd->flags & GD_FLG_ENV_READY))
+	if (!ctx->is_ready(ctx))
 		return 1;
 
 	/* Set read buffer to initial value or empty sting */
-	init_val = env_get(argv[1]);
+	init_val = env_get(ctx, argv[1]);
 	if (init_val)
 		snprintf(buffer, CONFIG_SYS_CBSIZE, "%s", init_val);
 	else
@@ -641,14 +719,15 @@ static int do_env_edit(cmd_tbl_t *cmdtp, int flag, int argc,
 		return 1;
 
 	if (buffer[0] == '\0') {
-		const char * const _argv[3] = { "setenv", argv[1], NULL };
+		const char * const _argv[5] = { "setenv", "-C", ctx->name,
+						argv[1], NULL };
 
-		return _do_env_set(0, 2, (char * const *)_argv, H_INTERACTIVE);
+		return _do_env_set(0, 4, (char * const *)_argv, H_INTERACTIVE);
 	} else {
-		const char * const _argv[4] = { "setenv", argv[1], buffer,
-			NULL };
+		const char * const _argv[6] = { "setenv", "-C", ctx->name,
+						argv[1], buffer, NULL };
 
-		return _do_env_set(0, 3, (char * const *)_argv, H_INTERACTIVE);
+		return _do_env_set(0, 5, (char * const *)_argv, H_INTERACTIVE);
 	}
 }
 #endif /* CONFIG_CMD_EDITENV */
@@ -659,22 +738,28 @@ static int do_env_edit(cmd_tbl_t *cmdtp, int flag, int argc,
  * return address of storage for that variable,
  * or NULL if not found
  */
-char *env_get(const char *name)
+char *env_get(struct env_context *ctx, const char *name)
 {
-	if (gd->flags & GD_FLG_ENV_READY) { /* after import into hashtable */
+	if (env_is_ready(ctx)) { /* after import into hashtable */
 		struct env_entry e, *ep;
 
 		WATCHDOG_RESET();
 
+		e.ctx	= ctx;
 		e.key	= name;
 		e.data	= NULL;
-		hsearch_r(e, ENV_FIND, &ep, &env_htab, 0);
+		hsearch_r(e, ENV_FIND, &ep, ctx->htab, 0);
 
 		return ep ? ep->data : NULL;
 	}
 
+	/* FIXME: env_buf should be place in env_context? */
+	if (ctx != ctx_uboot)
+		return NULL;
+
 	/* restricted capabilities before import */
-	if (env_get_f(name, (char *)(gd->env_buf), sizeof(gd->env_buf)) > 0)
+	if (env_get_f(ctx, name, (char *)gd->env_buf, sizeof(gd->env_buf))
+	    > 0)
 		return (char *)(gd->env_buf);
 
 	return NULL;
@@ -683,27 +768,28 @@ char *env_get(const char *name)
 /*
  * Look up variable from environment for restricted C runtime env.
  */
-int env_get_f(const char *name, char *buf, unsigned len)
+int env_get_f(struct env_context *ctx, const char *name, char *buf,
+	      unsigned int len)
 {
 	int i, nxt, c;
 
-	for (i = 0; env_get_char(i) != '\0'; i = nxt + 1) {
+	for (i = 0; env_get_char(ctx, i) != '\0'; i = nxt + 1) {
 		int val, n;
 
-		for (nxt = i; (c = env_get_char(nxt)) != '\0'; ++nxt) {
+		for (nxt = i; (c = env_get_char(ctx, nxt)) != '\0'; ++nxt) {
 			if (c < 0)
 				return c;
-			if (nxt >= CONFIG_ENV_SIZE)
+			if (nxt >= ctx->env_size)
 				return -1;
 		}
 
-		val = env_match((uchar *)name, i);
+		val = env_match(ctx, (uchar *)name, i);
 		if (val < 0)
 			continue;
 
 		/* found; copy out */
 		for (n = 0; n < len; ++n, ++buf) {
-			c = env_get_char(val++);
+			c = env_get_char(ctx, val++);
 			if (c < 0)
 				return c;
 			*buf = c;
@@ -732,13 +818,14 @@ int env_get_f(const char *name, char *buf, unsigned len)
  *			found
  * @return the decoded value, or default_val if not found
  */
-ulong env_get_ulong(const char *name, int base, ulong default_val)
+ulong env_get_ulong(struct env_context *ctx, const char *name, int base,
+		    ulong default_val)
 {
 	/*
 	 * We can use env_get() here, even before relocation, since the
 	 * environment variable value is an integer and thus short.
 	 */
-	const char *str = env_get(name);
+	const char *str = env_get(ctx, name);
 
 	return str ? simple_strtoul(str, NULL, base) : default_val;
 }
@@ -748,11 +835,23 @@ ulong env_get_ulong(const char *name, int base, ulong default_val)
 static int do_env_save(cmd_tbl_t *cmdtp, int flag, int argc,
 		       char * const argv[])
 {
-	return env_save() ? 1 : 0;
+	struct env_context *ctx = ctx_uboot;
+
+	if (argc >= 3 && !strcmp(argv[1], "-C")) {
+		ctx = get_env_context(argv[2]);
+		if (!ctx) {
+			printf("\nInvalid context: %s\n", argv[2]);
+			return CMD_RET_FAILURE;
+		}
+		argc -= 2;
+		argv += 2;
+	}
+
+	return env_save(ctx) ? 1 : 0;
 }
 
 U_BOOT_CMD(
-	saveenv, 1, 0,	do_env_save,
+	saveenv, 3, 0,	do_env_save,
 	"save environment variables to persistent storage",
 	""
 );
@@ -761,11 +860,21 @@ U_BOOT_CMD(
 static int do_env_erase(cmd_tbl_t *cmdtp, int flag, int argc,
 			char * const argv[])
 {
-	return env_erase() ? 1 : 0;
+	if (argc >= 3 && !strcmp(argv[1], "-C")) {
+		ctx = get_env_context(argv[2]);
+		if (!ctx) {
+			printf("\nInvalid context: %s\n", argv[2]);
+			return CMD_RET_FAILURE;
+		}
+		argc -= 2;
+		argv += 2;
+	}
+
+	return env_erase(ctx) ? 1 : 0;
 }
 
 U_BOOT_CMD(
-	eraseenv, 1, 0,	do_env_erase,
+	eraseenv, 3, 0,	do_env_erase,
 	"erase environment variables from persistent storage",
 	""
 );
@@ -773,16 +882,16 @@ U_BOOT_CMD(
 #endif
 #endif /* CONFIG_SPL_BUILD */
 
-int env_match(uchar *s1, int i2)
+int env_match(struct env_context *ctx, uchar *s1, int i2)
 {
 	if (s1 == NULL)
 		return -1;
 
-	while (*s1 == env_get_char(i2++))
+	while (*s1 == env_get_char(ctx, i2++))
 		if (*s1++ == '=')
 			return i2;
 
-	if (*s1 == '\0' && env_get_char(i2-1) == '=')
+	if (*s1 == '\0' && env_get_char(ctx, i2 - 1) == '=')
 		return i2;
 
 	return -1;
@@ -792,9 +901,20 @@ int env_match(uchar *s1, int i2)
 static int do_env_default(cmd_tbl_t *cmdtp, int flag,
 			  int argc, char * const argv[])
 {
+	struct env_context *ctx = ctx_uboot;
 	int all = 0, env_flag = H_INTERACTIVE;
 
 	debug("Initial value for argc=%d\n", argc);
+	if (argc >= 3 && !strcmp(argv[1], "-C")) {
+		ctx = get_env_context(argv[2]);
+		if (!ctx) {
+			printf("\nInvalid context: %s\n", argv[2]);
+			return CMD_RET_FAILURE;
+		}
+		argc -= 2;
+		argv += 2;
+	}
+
 	while (--argc > 0 && **++argv == '-') {
 		char *arg = *argv;
 
@@ -814,13 +934,13 @@ static int do_env_default(cmd_tbl_t *cmdtp, int flag,
 	debug("Final value for argc=%d\n", argc);
 	if (all && (argc == 0)) {
 		/* Reset the whole environment */
-		env_set_default("## Resetting to default environment\n",
+		env_set_default(ctx, "## Resetting to default environment\n",
 				env_flag);
 		return 0;
 	}
 	if (!all && (argc > 0)) {
 		/* Reset individual variables */
-		env_set_default_vars(argc, argv, env_flag);
+		env_set_default_vars(ctx, argc, argv, env_flag);
 		return 0;
 	}
 
@@ -830,10 +950,21 @@ static int do_env_default(cmd_tbl_t *cmdtp, int flag,
 static int do_env_delete(cmd_tbl_t *cmdtp, int flag,
 			 int argc, char * const argv[])
 {
+	struct env_context *ctx = ctx_uboot;
 	int env_flag = H_INTERACTIVE;
 	int ret = 0;
 
 	debug("Initial value for argc=%d\n", argc);
+	if (argc >= 3 && !strcmp(argv[1], "-C")) {
+		ctx = get_env_context(argv[2]);
+		if (!ctx) {
+			printf("\nInvalid context: %s\n", argv[2]);
+			return CMD_RET_FAILURE;
+		}
+		argc -= 2;
+		argv += 2;
+	}
+
 	while (argc > 1 && **(argv + 1) == '-') {
 		char *arg = *++argv;
 
@@ -850,12 +981,12 @@ static int do_env_delete(cmd_tbl_t *cmdtp, int flag,
 	}
 	debug("Final value for argc=%d\n", argc);
 
-	env_id++;
+	ctx->env_id++;
 
 	while (--argc > 0) {
 		char *name = *++argv;
 
-		if (!hdelete_r(name, &env_htab, env_flag))
+		if (!hdelete_r(name, ctx->htab, env_flag))
 			ret = 1;
 	}
 
@@ -911,12 +1042,13 @@ static int do_env_delete(cmd_tbl_t *cmdtp, int flag,
 static int do_env_export(cmd_tbl_t *cmdtp, int flag,
 			 int argc, char * const argv[])
 {
+	struct  env_context *ctx = ctx_uboot;
 	char	buf[32];
 	ulong	addr;
 	char	*ptr, *cmd, *res;
 	size_t	size = 0;
 	ssize_t	len;
-	env_t	*envp;
+	env_hdr_t *envp;
 	char	sep = '\n';
 	int	chk = 0;
 	int	fmt = 0;
@@ -927,6 +1059,18 @@ static int do_env_export(cmd_tbl_t *cmdtp, int flag,
 		char *arg = *argv;
 		while (*++arg) {
 			switch (*arg) {
+			case 'C':
+				if (--argc <= 0)
+					return cmd_usage(cmdtp);
+
+				++argv;
+				ctx = get_env_context(argv[0]);
+				if (!ctx) {
+					printf("\nInvalid context: %s\n",
+					       argv[0]);
+					return CMD_RET_FAILURE;
+				}
+				break;
 			case 'b':		/* raw binary format */
 				if (fmt++)
 					goto sep_err;
@@ -968,7 +1112,7 @@ NXTARG:		;
 	argv++;
 
 	if (sep) {		/* export as text file */
-		len = hexport_r(&env_htab, sep,
+		len = hexport_r(ctx->htab, sep,
 				H_MATCH_KEY | H_MATCH_IDENT,
 				&ptr, size, argc, argv);
 		if (len < 0) {
@@ -977,21 +1121,21 @@ NXTARG:		;
 			return 1;
 		}
 		sprintf(buf, "%zX", (size_t)len);
-		env_set("filesize", buf);
+		env_set(ctx_uboot, "filesize", buf);
 
 		return 0;
 	}
 
-	envp = (env_t *)ptr;
+	envp = (env_hdr_t *)ptr;
 
 	if (chk)		/* export as checksum protected block */
 		res = (char *)envp->data;
 	else			/* export as raw binary data */
 		res = ptr;
 
-	len = hexport_r(&env_htab, '\0',
+	len = hexport_r(ctx->htab, '\0',
 			H_MATCH_KEY | H_MATCH_IDENT,
-			&res, ENV_SIZE, argc, argv);
+			&res, ctx->env_size, argc, argv);
 	if (len < 0) {
 		pr_err("## Error: Cannot export environment: errno = %d\n",
 		       errno);
@@ -1000,12 +1144,13 @@ NXTARG:		;
 
 	if (chk) {
 		envp->crc = crc32(0, envp->data,
-				size ? size - offsetof(env_t, data) : ENV_SIZE);
+				size ? size - offsetof(env_t, data)
+					: ctx->env_size);
 #ifdef CONFIG_ENV_ADDR_REDUND
 		envp->flags = ENV_REDUND_ACTIVE;
 #endif
 	}
-	env_set_hex("filesize", len + offsetof(env_t, data));
+	env_set_hex(ctx_uboot, "filesize", len + offsetof(env_t, data));
 
 	return 0;
 
@@ -1044,6 +1189,7 @@ sep_err:
 static int do_env_import(cmd_tbl_t *cmdtp, int flag,
 			 int argc, char * const argv[])
 {
+	struct env_context *ctx = ctx_uboot;
 	ulong	addr;
 	char	*cmd, *ptr;
 	char	sep = '\n';
@@ -1060,6 +1206,18 @@ static int do_env_import(cmd_tbl_t *cmdtp, int flag,
 		char *arg = *argv;
 		while (*++arg) {
 			switch (*arg) {
+			case 'C':
+				if (--argc <= 0)
+					return cmd_usage(cmdtp);
+
+				++argv;
+				ctx = get_env_context(argv[0]);
+				if (!ctx) {
+					printf("\nInvalid context: %s\n",
+					       argv[0]);
+					return CMD_RET_FAILURE;
+				}
+				break;
 			case 'b':		/* raw binary format */
 				if (fmt++)
 					goto sep_err;
@@ -1129,9 +1287,9 @@ static int do_env_import(cmd_tbl_t *cmdtp, int flag,
 
 	if (chk) {
 		uint32_t crc;
-		env_t *ep = (env_t *)ptr;
+		env_hdr_t *ep = (env_hdr_t *)ptr;
 
-		size -= offsetof(env_t, data);
+		size -= offsetof(env_hdr_t, data);
 		memcpy(&crc, &ep->crc, sizeof(crc));
 
 		if (crc32(0, ep->data, size) != crc) {
@@ -1141,13 +1299,13 @@ static int do_env_import(cmd_tbl_t *cmdtp, int flag,
 		ptr = (char *)ep->data;
 	}
 
-	if (!himport_r(&env_htab, ptr, size, sep, del ? 0 : H_NOCLEAR,
+	if (!himport_r(ctx->htab, ptr, size, sep, del ? 0 : H_NOCLEAR,
 		       crlf_is_lf, wl ? argc - 2 : 0, wl ? &argv[2] : NULL)) {
 		pr_err("## Error: Environment import failed: errno = %d\n",
 		       errno);
 		return 1;
 	}
-	gd->flags |= GD_FLG_ENV_READY;
+	env_set_ready(ctx);
 
 	return 0;
 
@@ -1262,14 +1420,28 @@ static int do_env_info(cmd_tbl_t *cmdtp, int flag,
 static int do_env_exists(cmd_tbl_t *cmdtp, int flag, int argc,
 		       char * const argv[])
 {
+	struct env_context *ctx =  ctx_uboot;
 	struct env_entry e, *ep;
 
 	if (argc < 2)
 		return CMD_RET_USAGE;
 
+	if (argc >= 3 && !strcmp(argv[1], "-c")) {
+		ctx = get_env_context(argv[2]);
+		if (!ctx) {
+			printf("\nInvalid context: %s\n", argv[2]);
+			return CMD_RET_FAILURE;
+		}
+		argc -= 2;
+		argv += 2;
+		if (argc < 2)
+			return CMD_RET_USAGE;
+	}
+
+	e.ctx = ctx;
 	e.key = argv[1];
 	e.data = NULL;
-	hsearch_r(e, ENV_FIND, &ep, &env_htab, 0);
+	hsearch_r(e, ENV_FIND, &ep, ctx->htab, 0);
 
 	return (ep == NULL) ? 1 : 0;
 }
@@ -1285,22 +1457,22 @@ static cmd_tbl_t cmd_env_sub[] = {
 	U_BOOT_CMD_MKENT(default, 1, 0, do_env_default, "", ""),
 	U_BOOT_CMD_MKENT(delete, CONFIG_SYS_MAXARGS, 0, do_env_delete, "", ""),
 #if defined(CONFIG_CMD_EDITENV)
-	U_BOOT_CMD_MKENT(edit, 2, 0, do_env_edit, "", ""),
+	U_BOOT_CMD_MKENT(edit, 4, 0, do_env_edit, "", ""),
 #endif
 #if defined(CONFIG_CMD_ENV_CALLBACK)
-	U_BOOT_CMD_MKENT(callbacks, 1, 0, do_env_callback, "", ""),
+	U_BOOT_CMD_MKENT(callbacks, 3, 0, do_env_callback, "", ""),
 #endif
 #if defined(CONFIG_CMD_ENV_FLAGS)
-	U_BOOT_CMD_MKENT(flags, 1, 0, do_env_flags, "", ""),
+	U_BOOT_CMD_MKENT(flags, 3, 0, do_env_flags, "", ""),
 #endif
 #if defined(CONFIG_CMD_EXPORTENV)
-	U_BOOT_CMD_MKENT(export, 4, 0, do_env_export, "", ""),
+	U_BOOT_CMD_MKENT(export, 6, 0, do_env_export, "", ""),
 #endif
 #if defined(CONFIG_CMD_GREPENV)
 	U_BOOT_CMD_MKENT(grep, CONFIG_SYS_MAXARGS, 1, do_env_grep, "", ""),
 #endif
 #if defined(CONFIG_CMD_IMPORTENV)
-	U_BOOT_CMD_MKENT(import, 5, 0, do_env_import, "", ""),
+	U_BOOT_CMD_MKENT(import, 7, 0, do_env_import, "", ""),
 #endif
 #if defined(CONFIG_CMD_NVEDIT_INFO)
 	U_BOOT_CMD_MKENT(info, 2, 0, do_env_info, "", ""),
@@ -1310,14 +1482,14 @@ static cmd_tbl_t cmd_env_sub[] = {
 	U_BOOT_CMD_MKENT(run, CONFIG_SYS_MAXARGS, 1, do_run, "", ""),
 #endif
 #if defined(CONFIG_CMD_SAVEENV) && defined(ENV_IS_IN_DEVICE)
-	U_BOOT_CMD_MKENT(save, 1, 0, do_env_save, "", ""),
+	U_BOOT_CMD_MKENT(save, 3, 0, do_env_save, "", ""),
 #if defined(CONFIG_CMD_ERASEENV)
-	U_BOOT_CMD_MKENT(erase, 1, 0, do_env_erase, "", ""),
+	U_BOOT_CMD_MKENT(erase, 3, 0, do_env_erase, "", ""),
 #endif
 #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, "", ""),
+	U_BOOT_CMD_MKENT(exists, 4, 0, do_env_exists, "", ""),
 #endif
 };
 
@@ -1415,7 +1587,7 @@ U_BOOT_CMD(
 
 #if defined(CONFIG_CMD_EDITENV)
 U_BOOT_CMD_COMPLETE(
-	editenv, 2, 0,	do_env_edit,
+	editenv, 4, 0,	do_env_edit,
 	"edit environment variable",
 	"name\n"
 	"    - edit environment variable 'name'",
diff --git a/env/Kconfig b/env/Kconfig
index 74db2f38cc19..ae96cf75bbaa 100644
--- a/env/Kconfig
+++ b/env/Kconfig
@@ -1,667 +1,51 @@
 menu "Environment"
 
-config ENV_IS_NOWHERE
-	bool "Environment is not stored"
-	default y if !ENV_IS_IN_EEPROM && !ENV_IS_IN_EXT4 && \
-		     !ENV_IS_IN_FAT && !ENV_IS_IN_FLASH && \
-		     !ENV_IS_IN_MMC && !ENV_IS_IN_NAND && \
-		     !ENV_IS_IN_NVRAM && !ENV_IS_IN_ONENAND && \
-		     !ENV_IS_IN_REMOTE && !ENV_IS_IN_SPI_FLASH && \
-		     !ENV_IS_IN_UBI
-	help
-	  Define this if you don't want to or can't have an environment stored
-	  on a storage medium. In this case the environment will still exist
-	  while U-Boot is running, but once U-Boot exits it will not be
-	  stored. U-Boot will therefore always start up with a default
-	  environment.
-
-config ENV_IS_IN_EEPROM
-	bool "Environment in EEPROM"
-	depends on !CHAIN_OF_TRUST
-	help
-	  Use this if you have an EEPROM or similar serial access
-	  device and a driver for it.
-
-	  - CONFIG_ENV_OFFSET:
-	  - CONFIG_ENV_SIZE:
-
-	  These two #defines specify the offset and size of the
-	  environment area within the total memory of your EEPROM.
-
-	  Note that we consider the length of the address field to
-	  still be one byte because the extra address bits are hidden
-	  in the chip address.
-
-	  - CONFIG_ENV_EEPROM_IS_ON_I2C
-	  define this, if you have I2C and SPI activated, and your
-	  EEPROM, which holds the environment, is on the I2C bus.
-
-	  - CONFIG_I2C_ENV_EEPROM_BUS
-	  if you have an Environment on an EEPROM reached over
-	  I2C muxes, you can define here, how to reach this
-	  EEPROM. For example:
-
-	  #define CONFIG_I2C_ENV_EEPROM_BUS	  1
-
-	  EEPROM which holds the environment, is reached over
-	  a pca9547 i2c mux with address 0x70, channel 3.
-
-config ENV_IS_IN_FAT
-	bool "Environment is in a FAT filesystem"
-	depends on !CHAIN_OF_TRUST
-	default y if ARCH_BCM283X
-	default y if ARCH_SUNXI && MMC
-	default y if MMC_OMAP_HS && TI_COMMON_CMD_OPTIONS
+config ENV_DRV_NONE
+	bool
+	default y if !ENV_DRV_EEPROM && !ENV_DRV_EXT4 && \
+		     !ENV_DRV_FAT && !ENV_DRV_FLASH && \
+		     !ENV_DRV_MMC && !ENV_DRV_NAND && \
+		     !ENV_DRV_NVRAM && !ENV_DRV_ONENAND && \
+		     !ENV_DRV_REMOTE && !ENV_DRV_SPI_FLASH && \
+		     !ENV_DRV_UBI
+
+config ENV_DRV_EEPROM
+	bool
+
+config ENV_DRV_FAT
+	bool
 	select FS_FAT
 	select FAT_WRITE
-	help
-	  Define this if you want to use the FAT file system for the environment.
 
-config ENV_IS_IN_EXT4
-	bool "Environment is in a EXT4 filesystem"
-	depends on !CHAIN_OF_TRUST
+config ENV_DRV_EXT4
+	bool
 	select EXT4_WRITE
-	help
-	  Define this if you want to use the EXT4 file system for the environment.
-
-config ENV_IS_IN_FLASH
-	bool "Environment in flash memory"
-	depends on !CHAIN_OF_TRUST
-	default y if ARCH_CINTEGRATOR
-	default y if ARCH_INTEGRATOR_CP
-	default y if M548x || M547x || M5282 || MCF547x_8x
-	default y if MCF532x || MCF52x2
-	default y if MPC86xx || MPC83xx
-	default y if ARCH_MPC8572 || ARCH_MPC8548 || ARCH_MPC8641
-	default y if SH && !CPU_SH4
-	help
-	  Define this if you have a flash device which you want to use for the
-	  environment.
-
-	  a) The environment occupies one whole flash sector, which is
-	   "embedded" in the text segment with the U-Boot code. This
-	   happens usually with "bottom boot sector" or "top boot
-	   sector" type flash chips, which have several smaller
-	   sectors at the start or the end. For instance, such a
-	   layout can have sector sizes of 8, 2x4, 16, Nx32 kB. In
-	   such a case you would place the environment in one of the
-	   4 kB sectors - with U-Boot code before and after it. With
-	   "top boot sector" type flash chips, you would put the
-	   environment in one of the last sectors, leaving a gap
-	   between U-Boot and the environment.
-
-	  CONFIG_ENV_OFFSET:
-
-	   Offset of environment data (variable area) to the
-	   beginning of flash memory; for instance, with bottom boot
-	   type flash chips the second sector can be used: the offset
-	   for this sector is given here.
-
-	   CONFIG_ENV_OFFSET is used relative to CONFIG_SYS_FLASH_BASE.
 
-	  CONFIG_ENV_ADDR:
+config ENV_DRV_FLASH
+	bool
 
-	   This is just another way to specify the start address of
-	   the flash sector containing the environment (instead of
-	   CONFIG_ENV_OFFSET).
-
-	  CONFIG_ENV_SECT_SIZE:
-
-	   Size of the sector containing the environment.
-
-
-	  b) Sometimes flash chips have few, equal sized, BIG sectors.
-	   In such a case you don't want to spend a whole sector for
-	   the environment.
-
-	  CONFIG_ENV_SIZE:
-
-	   If you use this in combination with CONFIG_ENV_IS_IN_FLASH
-	   and CONFIG_ENV_SECT_SIZE, you can specify to use only a part
-	   of this flash sector for the environment. This saves
-	   memory for the RAM copy of the environment.
-
-	   It may also save flash memory if you decide to use this
-	   when your environment is "embedded" within U-Boot code,
-	   since then the remainder of the flash sector could be used
-	   for U-Boot code. It should be pointed out that this is
-	   STRONGLY DISCOURAGED from a robustness point of view:
-	   updating the environment in flash makes it always
-	   necessary to erase the WHOLE sector. If something goes
-	   wrong before the contents has been restored from a copy in
-	   RAM, your target system will be dead.
-
-	  CONFIG_ENV_ADDR_REDUND
-	  CONFIG_ENV_SIZE_REDUND
-
-	   These settings describe a second storage area used to hold
-	   a redundant copy of the environment data, so that there is
-	   a valid backup copy in case there is a power failure during
-	   a "saveenv" operation.
-
-	  BE CAREFUL! Any changes to the flash layout, and some changes to the
-	  source code will make it necessary to adapt <board>/u-boot.lds*
-	  accordingly!
-
-config ENV_IS_IN_MMC
-	bool "Environment in an MMC device"
-	depends on !CHAIN_OF_TRUST
+config ENV_DRV_MMC
+	bool
 	depends on MMC
-	default y if ARCH_EXYNOS4
-	default y if MX6SX || MX7D
-	default y if TEGRA30 || TEGRA124
-	default y if TEGRA_ARMV8_COMMON
-	help
-	  Define this if you have an MMC device which you want to use for the
-	  environment.
-
-	  CONFIG_SYS_MMC_ENV_DEV:
-
-	  Specifies which MMC device the environment is stored in.
-
-	  CONFIG_SYS_MMC_ENV_PART (optional):
-
-	  Specifies which MMC partition the environment is stored in. If not
-	  set, defaults to partition 0, the user area. Common values might be
-	  1 (first MMC boot partition), 2 (second MMC boot partition).
-
-	  CONFIG_ENV_OFFSET:
-	  CONFIG_ENV_SIZE:
-
-	  These two #defines specify the offset and size of the environment
-	  area within the specified MMC device.
-
-	  If offset is positive (the usual case), it is treated as relative to
-	  the start of the MMC partition. If offset is negative, it is treated
-	  as relative to the end of the MMC partition. This can be useful if
-	  your board may be fitted with different MMC devices, which have
-	  different sizes for the MMC partitions, and you always want the
-	  environment placed at the very end of the partition, to leave the
-	  maximum possible space before it, to store other data.
-
-	  These two values are in units of bytes, but must be aligned to an
-	  MMC sector boundary.
-
-	  CONFIG_ENV_OFFSET_REDUND (optional):
-
-	  Specifies a second storage area, of CONFIG_ENV_SIZE size, used to
-	  hold a redundant copy of the environment data. This provides a
-	  valid backup copy in case the other copy is corrupted, e.g. due
-	  to a power failure during a "saveenv" operation.
-
-	  This value may also be positive or negative; this is handled in the
-	  same way as CONFIG_ENV_OFFSET.
-
-	  This value is also in units of bytes, but must also be aligned to
-	  an MMC sector boundary.
-
-	  CONFIG_ENV_SIZE_REDUND (optional):
-
-	  This value need not be set, even when CONFIG_ENV_OFFSET_REDUND is
-	  set. If this value is set, it must be set to the same value as
-	  CONFIG_ENV_SIZE.
-
-config ENV_IS_IN_NAND
-	bool "Environment in a NAND device"
-	depends on !CHAIN_OF_TRUST
-	help
-	  Define this if you have a NAND device which you want to use for the
-	  environment.
-
-	  - CONFIG_ENV_OFFSET:
-	  - CONFIG_ENV_SIZE:
-
-	  These two #defines specify the offset and size of the environment
-	  area within the first NAND device.  CONFIG_ENV_OFFSET must be
-	  aligned to an erase block boundary.
-
-	  - CONFIG_ENV_OFFSET_REDUND (optional):
-
-	  This setting describes a second storage area of CONFIG_ENV_SIZE
-	  size used to hold a redundant copy of the environment data, so
-	  that there is a valid backup copy in case there is a power failure
-	  during a "saveenv" operation.	 CONFIG_ENV_OFFSET_REDUND must be
-	  aligned to an erase block boundary.
-
-	  - CONFIG_ENV_RANGE (optional):
-
-	  Specifies the length of the region in which the environment
-	  can be written.  This should be a multiple of the NAND device's
-	  block size.  Specifying a range with more erase blocks than
-	  are needed to hold CONFIG_ENV_SIZE allows bad blocks within
-	  the range to be avoided.
-
-	  - CONFIG_ENV_OFFSET_OOB (optional):
-
-	  Enables support for dynamically retrieving the offset of the
-	  environment from block zero's out-of-band data.  The
-	  "nand env.oob" command can be used to record this offset.
-	  Currently, CONFIG_ENV_OFFSET_REDUND is not supported when
-	  using CONFIG_ENV_OFFSET_OOB.
-
-config ENV_IS_IN_NVRAM
-	bool "Environment in a non-volatile RAM"
-	depends on !CHAIN_OF_TRUST
-	help
-	  Define this if you have some non-volatile memory device
-	  (NVRAM, battery buffered SRAM) which you want to use for the
-	  environment.
-
-	  - CONFIG_ENV_ADDR:
-	  - CONFIG_ENV_SIZE:
-
-	  These two #defines are used to determine the memory area you
-	  want to use for environment. It is assumed that this memory
-	  can just be read and written to, without any special
-	  provision.
-
-config ENV_IS_IN_ONENAND
-	bool "Environment is in OneNAND"
-	depends on !CHAIN_OF_TRUST
-	help
-	  Define this if you want to put your local device's environment in
-	  OneNAND.
-
-	  - CONFIG_ENV_ADDR:
-	  - CONFIG_ENV_SIZE:
-
-	  These two #defines are used to determine the device range you
-	  want to use for environment. It is assumed that this memory
-	  can just be read and written to, without any special
-	  provision.
-
-config ENV_IS_IN_REMOTE
-	bool "Environment is in remote memory space"
-	depends on !CHAIN_OF_TRUST
-	help
-	  Define this if you have a remote memory space which you
-	  want to use for the local device's environment.
-
-	  - CONFIG_ENV_ADDR:
-	  - CONFIG_ENV_SIZE:
-
-	  These two #defines specify the address and size of the
-	  environment area within the remote memory space. The
-	  local device can get the environment from remote memory
-	  space by SRIO or PCIE links.
-
-config ENV_IS_IN_SPI_FLASH
-	bool "Environment is in SPI flash"
-	depends on !CHAIN_OF_TRUST && SPI
-	default y if ARMADA_XP
-	default y if INTEL_BAYTRAIL
-	default y if INTEL_BRASWELL
-	default y if INTEL_BROADWELL
-	default y if NORTHBRIDGE_INTEL_IVYBRIDGE
-	default y if INTEL_QUARK
-	default y if INTEL_QUEENSBAY
-	help
-	  Define this if you have a SPI Flash memory device which you
-	  want to use for the environment.
-
-	  - CONFIG_ENV_OFFSET:
-	  - CONFIG_ENV_SIZE:
-
-	  These two #defines specify the offset and size of the
-	  environment area within the SPI Flash. CONFIG_ENV_OFFSET must be
-	  aligned to an erase sector boundary.
-
-	  - CONFIG_ENV_SECT_SIZE:
-
-	  Define the SPI flash's sector size.
-
-	  - CONFIG_ENV_OFFSET_REDUND (optional):
-
-	  This setting describes a second storage area of CONFIG_ENV_SIZE
-	  size used to hold a redundant copy of the environment data, so
-	  that there is a valid backup copy in case there is a power failure
-	  during a "saveenv" operation. CONFIG_ENV_OFFSET_REDUND must be
-	  aligned to an erase sector boundary.
-
-config USE_ENV_SPI_BUS
-	bool "SPI flash bus for environment"
-	depends on ENV_IS_IN_SPI_FLASH
-	help
-	  Force the SPI bus for environment.
-	  If not defined, use CONFIG_SF_DEFAULT_BUS.
-
-config ENV_SPI_BUS
-	int "Value of SPI flash bus for environment"
-	depends on USE_ENV_SPI_BUS
-	help
-	  Value the SPI bus and chip select for environment.
-
-config USE_ENV_SPI_CS
-	bool "SPI flash chip select for environment"
-	depends on ENV_IS_IN_SPI_FLASH
-	help
-	  Force the SPI chip select for environment.
-	  If not defined, use CONFIG_SF_DEFAULT_CS.
-
-config ENV_SPI_CS
-	int "Value of SPI flash chip select for environment"
-	depends on USE_ENV_SPI_CS
-	help
-	  Value of the SPI chip select for environment.
-
-config USE_ENV_SPI_MAX_HZ
-	bool "SPI flash max frequency for environment"
-	depends on ENV_IS_IN_SPI_FLASH
-	help
-	  Force the SPI max work clock for environment.
-	  If not defined, use CONFIG_SF_DEFAULT_SPEED.
-
-config ENV_SPI_MAX_HZ
-	int "Value of SPI flash max frequency for environment"
-	depends on USE_ENV_SPI_MAX_HZ
-	help
-	  Value of the SPI max work clock for environment.
-
-config USE_ENV_SPI_MODE
-	bool "SPI flash mode for environment"
-	depends on ENV_IS_IN_SPI_FLASH
-	help
-	  Force the SPI work mode for environment.
-
-config ENV_SPI_MODE
-	hex "Value of SPI flash work mode for environment"
-	depends on USE_ENV_SPI_MODE
-	help
-	  Value of the SPI work mode for environment.
-	  See include/spi.h for value.
-
-config ENV_IS_IN_UBI
-	bool "Environment in a UBI volume"
-	depends on !CHAIN_OF_TRUST
-	help
-	  Define this if you have an UBI volume that you want to use for the
-	  environment.  This has the benefit of wear-leveling the environment
-	  accesses, which is important on NAND.
-
-	  - CONFIG_ENV_UBI_PART:
-
-	  Define this to a string that is the mtd partition containing the UBI.
-
-	  - CONFIG_ENV_UBI_VOLUME:
-
-	  Define this to the name of the volume that you want to store the
-	  environment in.
-
-	  - CONFIG_ENV_UBI_VOLUME_REDUND:
-
-	  Define this to the name of another volume to store a second copy of
-	  the environment in.  This will enable redundant environments in UBI.
-	  It is assumed that both volumes are in the same MTD partition.
-
-config ENV_FAT_INTERFACE
-	string "Name of the block device for the environment"
-	depends on ENV_IS_IN_FAT
-	default "mmc" if ARCH_SUNXI
-	default "mmc" if TI_COMMON_CMD_OPTIONS || ARCH_ZYNQMP || ARCH_AT91
-	help
-	  Define this to a string that is the name of the block device.
-
-config ENV_FAT_DEVICE_AND_PART
-	string "Device and partition for where to store the environemt in FAT"
-	depends on ENV_IS_IN_FAT
-	default "0:1" if TI_COMMON_CMD_OPTIONS
-	default "0:auto" if ARCH_ZYNQMP
-	default "0:auto" if ARCH_SUNXI && MMC_SUNXI_SLOT_EXTRA = -1
-	default "1:auto" if ARCH_SUNXI && MMC_SUNXI_SLOT_EXTRA != -1
-	default "0" if ARCH_AT91
-	help
-	  Define this to a string to specify the partition of the device. It can
-	  be as following:
-
-	    "D:P", "D:0", "D", "D:" or "D:auto" (D, P are integers. And P >= 1)
-	       - "D:P": device D partition P. Error occurs if device D has no
-	                partition table.
-	       - "D:0": device D.
-	       - "D" or "D:": device D partition 1 if device D has partition
-	                      table, or the whole device D if has no partition
-	                      table.
-	       - "D:auto": first partition in device D with bootable flag set.
-	                   If none, first valid partition in device D. If no
-	                   partition table then means device D.
-
-config ENV_FAT_FILE
-	string "Name of the FAT file to use for the environment"
-	depends on ENV_IS_IN_FAT
-	default "uboot.env"
-	help
-	  It's a string of the FAT file name. This file use to store the
-	  environment.
-
-config ENV_EXT4_INTERFACE
-	string "Name of the block device for the environment"
-	depends on ENV_IS_IN_EXT4
-	help
-	  Define this to a string that is the name of the block device.
-
-config ENV_EXT4_DEVICE_AND_PART
-	string "Device and partition for where to store the environemt in EXT4"
-	depends on ENV_IS_IN_EXT4
-	help
-	  Define this to a string to specify the partition of the device. It can
-	  be as following:
-
-	    "D:P", "D:0", "D", "D:" or "D:auto" (D, P are integers. And P >= 1)
-	       - "D:P": device D partition P. Error occurs if device D has no
-	                partition table.
-	       - "D:0": device D.
-	       - "D" or "D:": device D partition 1 if device D has partition
-	                      table, or the whole device D if has no partition
-	                      table.
-	       - "D:auto": first partition in device D with bootable flag set.
-	                   If none, first valid partition in device D. If no
-	                   partition table then means device D.
-
-config ENV_EXT4_FILE
-	string "Name of the EXT4 file to use for the environment"
-	depends on ENV_IS_IN_EXT4
-	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)
-
-if ARCH_ROCKCHIP || ARCH_SUNXI || ARCH_ZYNQ || ARCH_ZYNQMP || ARCH_VERSAL || ARC || ARCH_STM32MP || ARCH_OMAP2PLUS || ARCH_AT91
-
-config ENV_OFFSET
-	hex "Environment Offset"
-	depends on (!ENV_IS_IN_UBI && !ENV_IS_NOWHERE) || ARCH_STM32MP
-	default 0x3f8000 if ARCH_ROCKCHIP
-	default 0x88000 if ARCH_SUNXI
-	default 0xE0000 if ARCH_ZYNQ
-	default 0x1E00000 if ARCH_ZYNQMP
-	default 0 if ARC
-	default 0x140000 if ARCH_AT91
-	default 0x260000 if ARCH_OMAP2PLUS
-	help
-	  Offset from the start of the device (or partition)
-
-config ENV_SIZE
-	hex "Environment Size"
-	default 0x40000 if ENV_IS_IN_SPI_FLASH && ARCH_ZYNQMP
-	default 0x20000 if ARCH_SUNXI || ARCH_ZYNQ || ARCH_OMAP2PLUS || ARCH_AT91
-	default 0x8000 if ARCH_ROCKCHIP || ARCH_ZYNQMP || ARCH_VERSAL
-	default 0x4000 if ARC
-	default 0x1f000
-	help
-	  Size of the environment storage area
-
-config ENV_SECT_SIZE
-	hex "Environment Sector-Size"
-	depends on (!ENV_IS_NOWHERE && (ARCH_ZYNQ || ARCH_ZYNQMP || ARCH_OMAP2PLUS || ARCH_AT91) )|| ARCH_STM32MP
-	default 0x40000 if ARCH_ZYNQMP
-	default 0x20000 if ARCH_ZYNQ || ARCH_OMAP2PLUS || ARCH_AT91
-	help
-	  Size of the sector containing the environment.
-
-config ENV_UBI_PART
-	string "UBI partition name"
-	depends on ENV_IS_IN_UBI
-	help
-	  MTD partition containing the UBI device
-
-config ENV_UBI_VOLUME
-	string "UBI volume name"
-	depends on ENV_IS_IN_UBI
-	help
-	  Name of the volume that you want to store the environment in.
-
-config ENV_UBI_VOLUME_REDUND
-	string "UBI redundant volume name"
-	depends on ENV_IS_IN_UBI
-	help
-	  Name of the redundant volume that you want to store the environment in.
-
-config ENV_UBI_VID_OFFSET
-	int "ubi environment VID offset"
-	depends on ENV_IS_IN_UBI
-	default 0
-	help
-	  UBI VID offset for environment. If 0, no custom VID offset is used.
-
-endif
-
-config USE_DEFAULT_ENV_FILE
-	bool "Create default environment from file"
-	help
-	  Normally, the default environment is automatically generated
-	  based on the settings of various CONFIG_* options, as well
-	  as the CONFIG_EXTRA_ENV_SETTINGS. By selecting this option,
-	  you can instead define the entire default environment in an
-	  external file.
-
-config DEFAULT_ENV_FILE
-	string "Path to default environment file"
-	depends on USE_DEFAULT_ENV_FILE
-	help
-	  The path containing the default environment. The format is
-	  the same as accepted by the mkenvimage tool: lines
-	  containing key=value pairs, blank lines and lines beginning
-	  with # are ignored.
-
-config ENV_VARS_UBOOT_RUNTIME_CONFIG
-	bool "Add run-time information to the environment"
-	help
-	  Enable this in order to add variables describing certain
-	  run-time determined information about the hardware to the
-	  environment.  These will be named board_name, board_rev.
-
-if SPL_ENV_SUPPORT
-config SPL_ENV_IS_NOWHERE
-	bool "SPL Environment is not stored"
-	default y if ENV_IS_NOWHERE
-	help
-	  Similar to ENV_IS_NOWHERE, used for SPL environment.
-
-config SPL_ENV_IS_IN_MMC
-	bool "SPL Environment in an MMC device"
-	depends on !SPL_ENV_IS_NOWHERE
-	depends on ENV_IS_IN_MMC
-	default y
-	help
-	  Similar to ENV_IS_IN_MMC, used for SPL environment.
-
-config SPL_ENV_IS_IN_FAT
-	bool "SPL Environment is in a FAT filesystem"
-	depends on !SPL_ENV_IS_NOWHERE
-	depends on ENV_IS_IN_FAT
-	default y
-	help
-	  Similar to ENV_IS_IN_FAT, used for SPL environment.
-
-config SPL_ENV_IS_IN_EXT4
-	bool "SPL Environment is in a EXT4 filesystem"
-	depends on !SPL_ENV_IS_NOWHERE
-	depends on ENV_IS_IN_EXT4
-	default y
-	help
-	  Similar to ENV_IS_IN_EXT4, used for SPL environment.
-
-config SPL_ENV_IS_IN_NAND
-	bool "SPL Environment in a NAND device"
-	depends on !SPL_ENV_IS_NOWHERE
-	depends on ENV_IS_IN_NAND
-	default y
-	help
-	  Similar to ENV_IS_IN_NAND, used for SPL environment.
-
-config SPL_ENV_IS_IN_SPI_FLASH
-	bool "SPL Environment is in SPI flash"
-	depends on !SPL_ENV_IS_NOWHERE
-	depends on ENV_IS_IN_SPI_FLASH
-	default y
-	help
-	  Similar to ENV_IS_IN_SPI_FLASH, used for SPL environment.
-
-config SPL_ENV_IS_IN_FLASH
-	bool "SPL Environment in flash memory"
-	depends on !SPL_ENV_IS_NOWHERE
-	depends on ENV_IS_IN_FLASH
-	default y
-	help
-	  Similar to ENV_IS_IN_FLASH, used for SPL environment.
-
-endif
-
-if TPL_ENV_SUPPORT
-
-config TPL_ENV_IS_NOWHERE
-	bool "TPL Environment is not stored"
-	default y if ENV_IS_NOWHERE
-	help
-	  Similar to ENV_IS_NOWHERE, used for TPL environment.
 
-config TPL_ENV_IS_IN_MMC
-	bool "TPL Environment in an MMC device"
-	depends on !TPL_ENV_IS_NOWHERE
-	depends on ENV_IS_IN_MMC
-	default y
-	help
-	  Similar to ENV_IS_IN_MMC, used for TPL environment.
+config ENV_DRV_NAND
+	bool
 
-config TPL_ENV_IS_IN_FAT
-	bool "TPL Environment is in a FAT filesystem"
-	depends on !TPL_ENV_IS_NOWHERE
-	depends on ENV_IS_IN_FAT
-	default y
-	help
-	  Similar to ENV_IS_IN_FAT, used for TPL environment.
+config ENV_DRV_NVRAM
+	bool
 
-config TPL_ENV_IS_IN_EXT4
-	bool "TPL Environment is in a EXT4 filesystem"
-	depends on !TPL_ENV_IS_NOWHERE
-	depends on ENV_IS_IN_EXT4
-	default y
-	help
-	  Similar to ENV_IS_IN_EXT4, used for TPL environment.
+config ENV_DRV_ONENAND
+	bool
 
-config TPL_ENV_IS_IN_NAND
-	bool "TPL Environment in a NAND device"
-	depends on !TPL_ENV_IS_NOWHERE
-	depends on ENV_IS_IN_NAND
-	default y
-	help
-	  Similar to ENV_IS_IN_NAND, used for TPL environment.
+config ENV_DRV_REMOTE
+	bool
 
-config TPL_ENV_IS_IN_SPI_FLASH
-	bool "TPL Environment is in SPI flash"
-	depends on !TPL_ENV_IS_NOWHERE
-	depends on ENV_IS_IN_SPI_FLASH
-	default y
-	help
-	  Similar to ENV_IS_IN_SPI_FLASH, used for TPL environment.
+config ENV_DRV_SPI_FLASH
+	bool
 
-config TPL_ENV_IS_IN_FLASH
-	bool "TPL Environment in flash memory"
-	depends on !TPL_ENV_IS_NOWHERE
-	depends on ENV_IS_IN_FLASH
-	default y
-	help
-	  Similar to ENV_IS_IN_FLASH, used for TPL environment.
+config ENV_DRV_UBI
+	bool
 
-endif
+source "env/Kconfig.uboot"
 
 endmenu
diff --git a/env/Makefile b/env/Makefile
index 90144d6caf34..ee37cc822024 100644
--- a/env/Makefile
+++ b/env/Makefile
@@ -9,16 +9,16 @@ ifndef CONFIG_SPL_BUILD
 obj-y += attr.o
 obj-y += callback.o
 obj-y += flags.o
-obj-$(CONFIG_ENV_IS_IN_EEPROM) += eeprom.o
+obj-$(CONFIG_ENV_DRV_EEPROM) += eeprom.o
 extra-$(CONFIG_ENV_IS_EMBEDDED) += embedded.o
-obj-$(CONFIG_ENV_IS_IN_EEPROM) += embedded.o
-extra-$(CONFIG_ENV_IS_IN_FLASH) += embedded.o
-obj-$(CONFIG_ENV_IS_IN_NVRAM) += embedded.o
-obj-$(CONFIG_ENV_IS_IN_NVRAM) += nvram.o
-obj-$(CONFIG_ENV_IS_IN_ONENAND) += onenand.o
-obj-$(CONFIG_ENV_IS_IN_SATA) += sata.o
-obj-$(CONFIG_ENV_IS_IN_REMOTE) += remote.o
-obj-$(CONFIG_ENV_IS_IN_UBI) += ubi.o
+obj-$(CONFIG_ENV_DRV_EEPROM) += embedded.o
+extra-$(CONFIG_ENV_DRV_FLASH) += embedded.o
+obj-$(CONFIG_ENV_DRV_NVRAM) += embedded.o
+obj-$(CONFIG_ENV_DRV_NVRAM) += nvram.o
+obj-$(CONFIG_ENV_DRV_ONENAND) += onenand.o
+obj-$(CONFIG_ENV_DRV_SATA) += sata.o
+obj-$(CONFIG_ENV_DRV_REMOTE) += remote.o
+obj-$(CONFIG_ENV_DRV_UBI) += ubi.o
 else
 obj-$(CONFIG_$(SPL_TPL_)ENV_SUPPORT) += attr.o
 obj-$(CONFIG_$(SPL_TPL_)ENV_SUPPORT) += flags.o
@@ -26,11 +26,11 @@ obj-$(CONFIG_$(SPL_TPL_)ENV_SUPPORT) += callback.o
 endif
 
 obj-$(CONFIG_$(SPL_TPL_)ENV_IS_NOWHERE) += nowhere.o
-obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_MMC) += mmc.o
-obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_FAT) += fat.o
-obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_EXT4) += ext4.o
-obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_NAND) += nand.o
-obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_SPI_FLASH) += sf.o
-obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_FLASH) += flash.o
+obj-$(CONFIG_$(SPL_TPL_)ENV_DRV_MMC) += mmc.o
+obj-$(CONFIG_$(SPL_TPL_)ENV_DRV_FAT) += fat.o
+obj-$(CONFIG_$(SPL_TPL_)ENV_DRV_EXT4) += ext4.o
+obj-$(CONFIG_$(SPL_TPL_)ENV_DRV_NAND) += nand.o
+obj-$(CONFIG_$(SPL_TPL_)ENV_DRV_SPI_FLASH) += sf.o
+obj-$(CONFIG_$(SPL_TPL_)ENV_DRV_FLASH) += flash.o
 
 CFLAGS_embedded.o := -Wa,--no-warn -DENV_CRC=$(shell tools/envcrc 2>/dev/null)
diff --git a/env/callback.c b/env/callback.c
index f0904cfdc535..4c7b0103f286 100644
--- a/env/callback.c
+++ b/env/callback.c
@@ -15,7 +15,8 @@ DECLARE_GLOBAL_DATA_PTR;
 /*
  * Look up a callback function pointer by name
  */
-static struct env_clbk_tbl *find_env_callback(const char *name)
+static struct env_clbk_tbl *find_env_callback(struct env_context *ctx,
+					      const char *name)
 {
 	struct env_clbk_tbl *clbkp;
 	int i;
@@ -28,13 +29,18 @@ static struct env_clbk_tbl *find_env_callback(const char *name)
 	for (i = 0, clbkp = ll_entry_start(struct env_clbk_tbl, env_clbk);
 	     i < num_callbacks;
 	     i++, clbkp++) {
-		if (strcmp(name, clbkp->name) == 0)
+		if (!strcmp(name, clbkp->name))
 			return clbkp;
 	}
 
 	return NULL;
 }
 
+/*
+ * TODO
+ * Should we provide per-context callback list,
+ * either for ".callbacks"(default) or "callbacks"(user defined)?
+ */
 static int first_call = 1;
 static const char *callback_list;
 
@@ -51,7 +57,7 @@ void env_callback_init(struct env_entry *var_entry)
 	int ret = 1;
 
 	if (first_call) {
-		callback_list = env_get(ENV_CALLBACK_VAR);
+		callback_list = env_get(ctx_uboot, ENV_CALLBACK_VAR);
 		first_call = 0;
 	}
 
@@ -66,7 +72,7 @@ void env_callback_init(struct env_entry *var_entry)
 
 	/* if an association was found, set the callback pointer */
 	if (!ret && strlen(callback_name)) {
-		clbkp = find_env_callback(callback_name);
+		clbkp = find_env_callback(var_entry->ctx, callback_name);
 		if (clbkp != NULL)
 #if defined(CONFIG_NEEDS_MANUAL_RELOC)
 			var_entry->callback = clbkp->callback + gd->reloc_off;
@@ -92,13 +98,15 @@ static int clear_callback(struct env_entry *entry)
  */
 static int set_callback(const char *name, const char *value, void *priv)
 {
+	struct env_context *ctx = priv;
 	struct env_entry e, *ep;
 	struct env_clbk_tbl *clbkp;
 
 	e.key	= name;
+	e.ctx	= ctx;
 	e.data	= NULL;
 	e.callback = NULL;
-	hsearch_r(e, ENV_FIND, &ep, &env_htab, 0);
+	hsearch_r(e, ENV_FIND, &ep, ctx->htab, 0);
 
 	/* does the env variable actually exist? */
 	if (ep != NULL) {
@@ -107,7 +115,7 @@ static int set_callback(const char *name, const char *value, void *priv)
 			ep->callback = NULL;
 		else {
 			/* assign the requested callback */
-			clbkp = find_env_callback(value);
+			clbkp = find_env_callback(ctx, value);
 			if (clbkp != NULL)
 #if defined(CONFIG_NEEDS_MANUAL_RELOC)
 				ep->callback = clbkp->callback + gd->reloc_off;
@@ -121,15 +129,21 @@ static int set_callback(const char *name, const char *value, void *priv)
 }
 
 static int on_callbacks(const char *name, const char *value, enum env_op op,
-	int flags)
+			int flags)
 {
-	/* remove all callbacks */
-	hwalk_r(&env_htab, clear_callback);
+	struct env_context *ctx;
+	int i;
 
-	/* configure any static callback bindings */
-	env_attr_walk(ENV_CALLBACK_LIST_STATIC, set_callback, NULL);
-	/* configure any dynamic callback bindings */
-	env_attr_walk(value, set_callback, NULL);
+	for (i = 0, ctx = U_BOOT_ENV_CTX_START; i < U_BOOT_ENV_CTX_COUNT;
+	     i++, ctx++) {
+		/* remove all callbacks */
+		hwalk_r(ctx->htab, clear_callback);
+
+		/* configure any static callback bindings */
+		env_attr_walk(ENV_CALLBACK_LIST_STATIC, set_callback, ctx);
+		/* configure any dynamic callback bindings */
+		env_attr_walk(value, set_callback, ctx);
+	}
 
 	return 0;
 }
diff --git a/env/common.c b/env/common.c
index 3fb60509dd85..6cf481c36d8f 100644
--- a/env/common.c
+++ b/env/common.c
@@ -18,25 +18,13 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
-/************************************************************************
- * Default settings to be used when no valid environment is found
- */
-#include <env_default.h>
-
-struct hsearch_data env_htab = {
-#if CONFIG_IS_ENABLED(ENV_SUPPORT)
-	/* defined in flags.c, only compile with ENV_SUPPORT */
-	.change_ok = env_flags_validate,
-#endif
-};
-
 /*
  * Read an environment variable as a boolean
  * Return -1 if variable does not exist (default to true)
  */
 int env_get_yesno(const char *var)
 {
-	char *s = env_get(var);
+	char *s = env_get(ctx_uboot, var);
 
 	if (s == NULL)
 		return -1;
@@ -44,109 +32,124 @@ int env_get_yesno(const char *var)
 		1 : 0;
 }
 
-/*
- * Look up the variable from the default environment
- */
-char *env_get_default(const char *name)
+char *env_get_default(struct env_context *ctx, const char *name)
 {
-	char *ret_val;
-	unsigned long really_valid = gd->env_valid;
-	unsigned long real_gd_flags = gd->flags;
-
-	/* Pretend that the image is bad. */
-	gd->flags &= ~GD_FLG_ENV_READY;
-	gd->env_valid = ENV_INVALID;
-	ret_val = env_get(name);
-	gd->env_valid = really_valid;
-	gd->flags = real_gd_flags;
-	return ret_val;
+	if (ctx->get_default)
+		return ctx->get_default(ctx, name);
+
+	/* no default action */
+	return NULL;
 }
 
-void env_set_default(const char *s, int flags)
+void env_set_default(struct env_context *ctx, const char *s, int flags)
 {
-	if (sizeof(default_environment) > ENV_SIZE) {
-		puts("*** Error - default environment is too large\n\n");
-		return;
-	}
+	if (ctx->set_default)
+		return ctx->set_default(ctx, s, flags);
 
-	if (s) {
-		if ((flags & H_INTERACTIVE) == 0) {
-			printf("*** Warning - %s, "
-				"using default environment\n\n", s);
-		} else {
-			puts(s);
-		}
-	} else {
-		debug("Using default environment\n");
-	}
+	/* no default action */
+}
 
-	if (himport_r(&env_htab, (char *)default_environment,
-			sizeof(default_environment), '\0', flags, 0,
-			0, NULL) == 0)
-		pr_err("## Error: Environment import failed: errno = %d\n",
-		       errno);
+/* [re]set individual variables to their value in the default environment */
+int env_set_default_vars(struct env_context *ctx, int nvars,
+			 char * const vars[], int flags)
+{
+	if (ctx->set_default_vars)
+		return ctx->set_default_vars(ctx, nvars, vars, flags);
 
-	gd->flags |= GD_FLG_ENV_READY;
-	gd->flags |= GD_FLG_ENV_DEFAULT;
+	/* no default action */
+	return 1;
 }
 
+void env_set_ready(struct env_context *ctx)
+{
+	if (ctx->set_ready)
+		ctx->set_ready(ctx);
+	else
+		/* TODO: define another macro? */
+		ctx->flags |= GD_FLG_ENV_READY;
+}
 
-/* [re]set individual variables to their value in the default environment */
-int env_set_default_vars(int nvars, char * const vars[], int flags)
+bool env_is_ready(struct env_context *ctx)
+{
+	if (ctx->is_ready)
+		return ctx->is_ready(ctx);
+
+	return ctx->flags & GD_FLG_ENV_READY;
+}
+
+void env_set_valid(struct env_context *ctx, enum env_valid valid)
+{
+	if (ctx->set_ready)
+		ctx->set_valid(ctx, valid);
+	else
+		ctx->valid = valid;
+}
+
+enum env_valid env_get_valid(struct env_context *ctx)
+{
+	if (ctx->get_valid)
+		return ctx->get_valid(ctx);
+
+	return ctx->valid;
+}
+
+void env_set_env_addr(struct env_context *ctx, ulong env_addr)
+{
+	if (ctx->set_addr)
+		ctx->set_addr(ctx, env_addr);
+}
+
+ulong env_get_env_addr(struct env_context *ctx)
 {
-	/*
-	 * Special use-case: import from default environment
-	 * (and use \0 as a separator)
-	 */
-	flags |= H_NOCLEAR;
-	return himport_r(&env_htab, (const char *)default_environment,
-				sizeof(default_environment), '\0',
-				flags, 0, nvars, vars);
+	if (ctx->get_addr)
+		return ctx->get_addr(ctx);
+
+	return 0; /* FIXME: invalid value */
 }
 
 /*
  * 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(struct env_context *ctx, const char *buf, int check)
 {
-	env_t *ep = (env_t *)buf;
+	struct environment_hdr *ep = (struct environment_hdr *)buf;
 
 	if (check) {
 		uint32_t crc;
 
 		memcpy(&crc, &ep->crc, sizeof(crc));
 
-		if (crc32(0, ep->data, ENV_SIZE) != crc) {
-			env_set_default("bad CRC", 0);
+		if (crc32(0, ep->data, ctx->env_size) != crc) {
+			env_set_default(ctx, "bad CRC", 0);
 			return -ENOMSG; /* needed for env_load() */
 		}
 	}
 
-	if (himport_r(&env_htab, (char *)ep->data, ENV_SIZE, '\0', 0, 0,
-			0, NULL)) {
-		gd->flags |= GD_FLG_ENV_READY;
+	ctx->htab->ctx = ctx; /* FIXME: why needed here? */
+	if (himport_r(ctx->htab, (char *)ep->data, ctx->env_size, '\0', 0, 0,
+		      0, NULL)) {
+		env_set_ready(ctx);
 		return 0;
 	}
 
 	pr_err("Cannot import environment: errno = %d\n", errno);
 
-	env_set_default("import failed", 0);
+	env_set_default(ctx, "import failed", 0);
 
 	return -EIO;
 }
 
 #ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
-static unsigned char env_flags;
-
-int env_import_redund(const char *buf1, int buf1_read_fail,
+int env_import_redund(struct env_context *ctx,
+		      const char *buf1, int buf1_read_fail,
 		      const char *buf2, int buf2_read_fail)
 {
 	int crc1_ok, crc2_ok;
-	env_t *ep, *tmp_env1, *tmp_env2;
+	struct environment_hdr *ep, *tmp_env1, *tmp_env2;
 
-	tmp_env1 = (env_t *)buf1;
-	tmp_env2 = (env_t *)buf2;
+	tmp_env1 = (struct environment_hdr *)buf1;
+	tmp_env2 = (struct environment_hdr *)buf2;
 
 	if (buf1_read_fail && buf2_read_fail) {
 		puts("*** Error - No Valid Environment Area found\n");
@@ -156,69 +159,70 @@ int env_import_redund(const char *buf1, int buf1_read_fail,
 	}
 
 	if (buf1_read_fail && buf2_read_fail) {
-		env_set_default("bad env area", 0);
+		env_set_default(ctx, "bad env area", 0);
 		return -EIO;
 	} else if (!buf1_read_fail && buf2_read_fail) {
-		gd->env_valid = ENV_VALID;
-		return env_import((char *)tmp_env1, 1);
+		env_set_valid(ctx, ENV_VALID);
+		return env_import(ctx, (char *)tmp_env1, 1);
 	} else if (buf1_read_fail && !buf2_read_fail) {
-		gd->env_valid = ENV_REDUND;
-		return env_import((char *)tmp_env2, 1);
+		env_set_valid(ctx, ENV_REDUND);
+		return env_import(ctx, (char *)tmp_env2, 1);
 	}
 
-	crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) ==
+	crc1_ok = crc32(0, tmp_env1->data, ctx->env_size) ==
 			tmp_env1->crc;
-	crc2_ok = crc32(0, tmp_env2->data, ENV_SIZE) ==
+	crc2_ok = crc32(0, tmp_env2->data, ctx->env_size) ==
 			tmp_env2->crc;
 
 	if (!crc1_ok && !crc2_ok) {
-		env_set_default("bad CRC", 0);
+		env_set_default(ctx, "bad CRC", 0);
 		return -ENOMSG; /* needed for env_load() */
 	} else if (crc1_ok && !crc2_ok) {
-		gd->env_valid = ENV_VALID;
+		env_set_valid(ctx, ENV_VALID);
 	} else if (!crc1_ok && crc2_ok) {
-		gd->env_valid = ENV_REDUND;
+		env_set_valid(ctx, ENV_REDUND);
 	} else {
 		/* both ok - check serial */
 		if (tmp_env1->flags == 255 && tmp_env2->flags == 0)
-			gd->env_valid = ENV_REDUND;
+			env_set_valid(ctx, ENV_REDUND);
 		else if (tmp_env2->flags == 255 && tmp_env1->flags == 0)
-			gd->env_valid = ENV_VALID;
+			env_set_valid(ctx, ENV_VALID);
 		else if (tmp_env1->flags > tmp_env2->flags)
-			gd->env_valid = ENV_VALID;
+			env_set_valid(ctx, ENV_VALID);
 		else if (tmp_env2->flags > tmp_env1->flags)
-			gd->env_valid = ENV_REDUND;
+			env_set_valid(ctx, ENV_REDUND);
 		else /* flags are equal - almost impossible */
-			gd->env_valid = ENV_VALID;
+			env_set_valid(ctx, ENV_VALID);
 	}
 
-	if (gd->env_valid == ENV_VALID)
+	if (env_get_valid(ctx) == ENV_VALID)
 		ep = tmp_env1;
 	else
 		ep = tmp_env2;
 
-	env_flags = ep->flags;
-	return env_import((char *)ep, 0);
+	/* FIXME: functionize? */
+	ctx->env_flags = ep->flags;
+	return env_import(ctx, (char *)ep, 0);
 }
 #endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */
 
 /* Export the environment and generate CRC for it. */
-int env_export(env_t *env_out)
+int env_export(struct env_context *ctx, struct environment_hdr *env_out)
 {
 	char *res;
 	ssize_t	len;
 
 	res = (char *)env_out->data;
-	len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
+	len = hexport_r(ctx->htab, '\0', 0, &res, ctx->env_size, 0, NULL);
 	if (len < 0) {
 		pr_err("Cannot export environment: errno = %d\n", errno);
 		return 1;
 	}
 
-	env_out->crc = crc32(0, env_out->data, ENV_SIZE);
+	env_out->crc = crc32(0, env_out->data, ctx->env_size);
 
 #ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
-	env_out->flags = ++env_flags; /* increase the serial */
+	env_out->flags = ++ctx->env_flags; /* increase the serial */
 #endif
 
 	return 0;
@@ -226,27 +230,60 @@ int env_export(env_t *env_out)
 
 void env_relocate(void)
 {
+	struct env_context *ctx;
+	int i;
+
 #if defined(CONFIG_NEEDS_MANUAL_RELOC)
 	env_reloc();
 	env_fix_drivers();
 
-	if (env_htab.change_ok)
-		env_htab.change_ok += gd->reloc_off;
-#endif
-	if (gd->env_valid == ENV_INVALID) {
-#if defined(CONFIG_ENV_IS_NOWHERE) || defined(CONFIG_SPL_BUILD)
-		/* Environment not changable */
-		env_set_default(NULL, 0);
-#else
-		bootstage_error(BOOTSTAGE_ID_NET_CHECKSUM);
-		env_set_default("bad CRC", 0);
-#endif
-	} else {
-		env_load();
+	for (i = 0; i < U_BOOT_ENV_CTX_COUNT; i++) {
+		if (ctx->htab.change_ok)
+			ctx->htab.change_ok += gd->reloc_off;
+
+		/*
+		 * TODO:
+		 * Some of functions may not be called after relocation
+		 */
+		if (ctx->has_inited)
+			ctx->has_inited += gd->reloc_off;
+		if (ctx->set_inited)
+			ctx->set_inited += gd->reloc_off;
+		if (ctx->get_location)
+			ctx->get_location += gd->reloc_off;
+		if (ctx->get_char)
+			ctx->get_char += gd->reloc_off;
+		if (ctx->get_char_default)
+			ctx->get_char_default += gd->reloc_off;
+		if (ctx->get_char_spec)
+			ctx->get_char_spec += gd->reloc_off;
+		if (ctx->init)
+			ctx->init += gd->reloc_off;
+		if (ctx->get_default)
+			ctx->get_default += gd->reloc_off;
+		if (ctx->set_default)
+			ctx->set_default += gd->reloc_off;
+		if (ctx->set_default_vars)
+			ctx->set_default_vars += gd->reloc_off;
+		if (ctx->set_ready)
+			ctx->set_ready += gd->reloc_off;
+		if (ctx->set_valid)
+			ctx->set_valid += gd->reloc_off;
+		if (ctx->get_valid)
+			ctx->get_valid += gd->reloc_off;
 	}
+#endif
+
+	for (i = 0, ctx = U_BOOT_ENV_CTX_START; i < U_BOOT_ENV_CTX_COUNT;
+	     i++, ctx++)
+		if (ctx->post_relocate)
+			ctx->post_relocate(ctx);
 }
 
 #ifdef CONFIG_AUTO_COMPLETE
+/*
+ * TODO: Currently U-Boot environment context only
+ */
 int env_complete(char *var, int maxv, char *cmdv[], int bufsz, char *buf,
 		 bool dollar_comp)
 {
diff --git a/env/env.c b/env/env.c
index 9237bb9c742a..b47e97972b50 100644
--- a/env/env.c
+++ b/env/env.c
@@ -49,51 +49,54 @@ static struct env_driver *_env_driver_lookup(enum env_location loc)
 	return NULL;
 }
 
-static enum env_location env_locations[] = {
-#ifdef CONFIG_ENV_IS_IN_EEPROM
+enum env_location env_locations[] = {
+#ifdef CONFIG_ENV_DRV_EEPROM
 	ENVL_EEPROM,
 #endif
-#ifdef CONFIG_ENV_IS_IN_EXT4
+#ifdef CONFIG_ENV_DRV_EXT4
 	ENVL_EXT4,
 #endif
-#ifdef CONFIG_ENV_IS_IN_FAT
+#ifdef CONFIG_ENV_DRV_FAT
 	ENVL_FAT,
 #endif
-#ifdef CONFIG_ENV_IS_IN_FLASH
+#ifdef CONFIG_ENV_DRV_FLASH
 	ENVL_FLASH,
 #endif
-#ifdef CONFIG_ENV_IS_IN_MMC
+#ifdef CONFIG_ENV_DRV_MMC
 	ENVL_MMC,
 #endif
-#ifdef CONFIG_ENV_IS_IN_NAND
+#ifdef CONFIG_ENV_DRV_NAND
 	ENVL_NAND,
 #endif
-#ifdef CONFIG_ENV_IS_IN_NVRAM
+#ifdef CONFIG_ENV_DRV_NVRAM
 	ENVL_NVRAM,
 #endif
-#ifdef CONFIG_ENV_IS_IN_REMOTE
+#ifdef CONFIG_ENV_DRV_REMOTE
 	ENVL_REMOTE,
 #endif
-#ifdef CONFIG_ENV_IS_IN_SATA
+#ifdef CONFIG_ENV_DRV_SATA
 	ENVL_ESATA,
 #endif
-#ifdef CONFIG_ENV_IS_IN_SPI_FLASH
+#ifdef CONFIG_ENV_DRV_SPI_FLASH
 	ENVL_SPI_FLASH,
 #endif
-#ifdef CONFIG_ENV_IS_IN_UBI
+#ifdef CONFIG_ENV_DRV_UBI
 	ENVL_UBI,
 #endif
-#ifdef CONFIG_ENV_IS_NOWHERE
+#ifdef CONFIG_ENV_DRV_NONE
 	ENVL_NOWHERE,
 #endif
 };
 
-static bool env_has_inited(enum env_location location)
+static bool env_has_inited(struct env_context *ctx, enum env_location location)
 {
-	return gd->env_has_init & BIT(location);
+	if (ctx->has_inited)
+		return ctx->has_inited(ctx, location);
+
+	return ctx->has_init & BIT(location);
 }
 
-static void env_set_inited(enum env_location location)
+static void env_set_inited(struct env_context *ctx, enum env_location location)
 {
 	/*
 	 * We're using a 32-bits bitmask stored in gd (env_has_init)
@@ -102,11 +105,25 @@ static void env_set_inited(enum env_location location)
 	 */
 	BUILD_BUG_ON(ARRAY_SIZE(env_locations) > BITS_PER_LONG);
 
-	gd->env_has_init |= BIT(location);
+	if (ctx->set_inited) {
+		ctx->set_inited(ctx, location);
+		return;
+	}
+
+	ctx->has_init |= BIT(location);
+}
+
+static int env_get_load_prio(struct env_context *ctx)
+{
+	if (ctx->get_load_prio)
+		return ctx->get_load_prio(ctx);
+
+	return ctx->load_prio;
 }
 
 /**
  * env_get_location() - Returns the best env location for a board
+ * @ctx: pointer to environment context
  * @op: operations performed on the environment
  * @prio: priority between the multiple environments, 0 being the
  *        highest priority
@@ -123,19 +140,23 @@ static void env_set_inited(enum env_location location)
  * Returns:
  * an enum env_location value on success, a negative error code otherwise
  */
-__weak enum env_location env_get_location(enum env_operation op, int prio)
+__weak enum env_location env_get_location(struct env_context *ctx,
+					  enum env_operation op, int prio)
 {
 	if (prio >= ARRAY_SIZE(env_locations))
 		return ENVL_UNKNOWN;
 
-	gd->env_load_prio = prio;
+	if (ctx->get_location)
+		return ctx->get_location(ctx, op, prio);
+
+	ctx->load_prio = prio;
 
 	return env_locations[prio];
 }
 
-
 /**
  * env_driver_lookup() - Finds the most suited environment location
+ * @ctx: pointer to environment context
  * @op: operations performed on the environment
  * @prio: priority between the multiple environments, 0 being the
  *        highest priority
@@ -146,9 +167,10 @@ __weak enum env_location env_get_location(enum env_operation op, int prio)
  * Returns:
  * NULL on error, a pointer to a struct env_driver otherwise
  */
-static struct env_driver *env_driver_lookup(enum env_operation op, int prio)
+struct env_driver *env_driver_lookup(struct env_context *ctx,
+				     enum env_operation op, int prio)
 {
-	enum env_location loc = env_get_location(op, prio);
+	enum env_location loc = env_get_location(ctx, op, prio);
 	struct env_driver *drv;
 
 	if (loc == ENVL_UNKNOWN)
@@ -164,32 +186,43 @@ static struct env_driver *env_driver_lookup(enum env_operation op, int prio)
 	return drv;
 }
 
-__weak int env_get_char_spec(int index)
+__weak int env_get_char_spec(struct env_context *ctx, int index)
 {
-	return *(uchar *)(gd->env_addr + index);
+	if (ctx->get_char_spec)
+		return ctx->get_char_spec(ctx, index);
+
+	return 0; /* FIXME: invalid value */
 }
 
-int env_get_char(int index)
+int env_get_char(struct env_context *ctx, int index)
 {
-	if (gd->env_valid == ENV_INVALID)
-		return default_environment[index];
-	else
-		return env_get_char_spec(index);
+	if (ctx->get_char)
+		return ctx->get_char(ctx, index);
+
+	if (ctx->get_valid(ctx) == ENV_INVALID) {
+		if (ctx->get_char_default)
+			return ctx->get_char_default(ctx, index);
+
+		return 0; /* FIXME: invalid value */
+	} else {
+		return env_get_char_spec(ctx, index);
+	}
 }
 
-int env_load(void)
+int env_load(struct env_context *ctx)
 {
 	struct env_driver *drv;
 	int best_prio = -1;
 	int prio;
 
-	for (prio = 0; (drv = env_driver_lookup(ENVOP_LOAD, prio)); prio++) {
+	for (prio = 0; (drv = env_driver_lookup(ctx, ENVOP_LOAD, prio));
+	     prio++) {
 		int ret;
 
 		if (!drv->load)
 			continue;
 
-		if (!env_has_inited(drv->location))
+		if (!env_has_inited(ctx, drv->location))
 			continue;
 
 		printf("Loading Environment from %s... ", drv->name);
@@ -198,7 +231,7 @@ int env_load(void)
 		 * drv->load() in some underlying API, and it must be exactly
 		 * one message.
 		 */
-		ret = drv->load();
+		ret = drv->load(ctx);
 		if (!ret) {
 			printf("OK\n");
 			return 0;
@@ -224,27 +257,27 @@ int env_load(void)
 		debug("Selecting environment with bad CRC\n");
 	else
 		best_prio = 0;
-	env_get_location(ENVOP_LOAD, best_prio);
+	env_get_location(ctx, ENVOP_LOAD, best_prio);
 
 	return -ENODEV;
 }
 
-int env_save(void)
+int env_save(struct env_context *ctx)
 {
 	struct env_driver *drv;
 
-	drv = env_driver_lookup(ENVOP_SAVE, gd->env_load_prio);
+	drv = env_driver_lookup(ctx, ENVOP_SAVE, env_get_load_prio(ctx));
 	if (drv) {
 		int ret;
 
 		if (!drv->save)
 			return -ENODEV;
 
-		if (!env_has_inited(drv->location))
+		if (!env_has_inited(ctx, drv->location))
 			return -ENODEV;
 
 		printf("Saving Environment to %s... ", drv->name);
-		ret = drv->save();
+		ret = drv->save(ctx);
 		if (ret)
 			printf("Failed (%d)\n", ret);
 		else
@@ -257,22 +290,22 @@ int env_save(void)
 	return -ENODEV;
 }
 
-int env_erase(void)
+int env_erase(struct env_context *ctx)
 {
 	struct env_driver *drv;
 
-	drv = env_driver_lookup(ENVOP_ERASE, gd->env_load_prio);
+	drv = env_driver_lookup(ctx, ENVOP_ERASE, env_get_load_prio(ctx));
 	if (drv) {
 		int ret;
 
 		if (!drv->erase)
 			return -ENODEV;
 
-		if (!env_has_inited(drv->location))
+		if (!env_has_inited(ctx, drv->location))
 			return -ENODEV;
 
 		printf("Erasing Environment on %s... ", drv->name);
-		ret = drv->erase();
+		ret = drv->erase(ctx);
 		if (ret)
 			printf("Failed (%d)\n", ret);
 		else
@@ -285,29 +318,46 @@ int env_erase(void)
 	return -ENODEV;
 }
 
-int env_init(void)
+int env_ctx_init(struct env_context *ctx)
 {
 	struct env_driver *drv;
 	int ret = -ENOENT;
-	int prio;
+	int prio, drv_count;
 
-	for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) {
-		if (!drv->init || !(ret = drv->init()))
-			env_set_inited(drv->location);
+	ctx->htab->ctx = ctx;
+	ctx->env_id = 1;
+	env_set_valid(ctx, ENV_INVALID);
+
+	if (ctx->init)
+		return ctx->init(ctx);
+
+	for (prio = 0, drv_count = 0;
+	     (drv = env_driver_lookup(ctx, ENVOP_INIT, prio)); prio++) {
+		if (!drv->init || !(ret = drv->init(ctx))) {
+			env_set_inited(ctx, drv->location);
+			drv_count++;
+		}
 
 		debug("%s: Environment %s init done (ret=%d)\n", __func__,
 		      drv->name, ret);
 	}
 
-	if (!prio)
+	if (!drv_count)
 		return -ENODEV;
 
-	if (ret == -ENOENT) {
-		gd->env_addr = (ulong)&default_environment[0];
-		gd->env_valid = ENV_VALID;
-
-		return 0;
+	/* Dummy table creation, or hcreate_r()? */
+	if (!himport_r(ctx->htab, NULL, 0, 0, 0, 0, 0, NULL)) {
+		debug("%s: Creating entry tables failed (ret=%d)\n", __func__,
+		      errno);
+		return errno;
 	}
 
-	return ret;
+	env_set_ready(ctx);
+
+	return 0;
+}
+
+int env_init(void)
+{
+	return env_ctx_init(ctx_uboot);
 }
diff --git a/env/flags.c b/env/flags.c
index 418d6cc7425a..334bb08c88db 100644
--- a/env/flags.c
+++ b/env/flags.c
@@ -308,7 +308,7 @@ static inline int env_flags_lookup(const char *flags_list, const char *name,
  */
 enum env_flags_vartype env_flags_get_type(const char *name)
 {
-	const char *flags_list = env_get(ENV_FLAGS_VAR);
+	const char *flags_list = env_get(ctx_uboot, ENV_FLAGS_VAR);
 	char flags[ENV_FLAGS_ATTR_MAX_LEN + 1];
 
 	if (env_flags_lookup(flags_list, name, flags))
@@ -325,7 +325,7 @@ enum env_flags_vartype env_flags_get_type(const char *name)
  */
 enum env_flags_varaccess env_flags_get_varaccess(const char *name)
 {
-	const char *flags_list = env_get(ENV_FLAGS_VAR);
+	const char *flags_list = env_get(ctx_uboot, ENV_FLAGS_VAR);
 	char flags[ENV_FLAGS_ATTR_MAX_LEN + 1];
 
 	if (env_flags_lookup(flags_list, name, flags))
@@ -411,6 +411,11 @@ static int env_parse_flags_to_bin(const char *flags)
 	return binflags;
 }
 
+/*
+ * TODO
+ * Should we provide per-context flags list,
+ * either for ".flags"(default) or "flags"(user defined)?
+ */
 static int first_call = 1;
 static const char *flags_list;
 
@@ -426,7 +431,7 @@ void env_flags_init(struct env_entry *var_entry)
 	int ret = 1;
 
 	if (first_call) {
-		flags_list = env_get(ENV_FLAGS_VAR);
+		flags_list = env_get(ctx_uboot, ENV_FLAGS_VAR);
 		first_call = 0;
 	}
 	/* look in the ".flags" and static for a reference to this variable */
@@ -453,12 +458,14 @@ static int clear_flags(struct env_entry *entry)
  */
 static int set_flags(const char *name, const char *value, void *priv)
 {
+	struct env_context *ctx = priv;
 	struct env_entry e, *ep;
 
 	e.key	= name;
+	e.ctx	= ctx;
 	e.data	= NULL;
 	e.callback = NULL;
-	hsearch_r(e, ENV_FIND, &ep, &env_htab, 0);
+	hsearch_r(e, ENV_FIND, &ep, ctx->htab, 0);
 
 	/* does the env variable actually exist? */
 	if (ep != NULL) {
@@ -476,13 +483,19 @@ static int set_flags(const char *name, const char *value, void *priv)
 static int on_flags(const char *name, const char *value, enum env_op op,
 	int flags)
 {
-	/* remove all flags */
-	hwalk_r(&env_htab, clear_flags);
+	struct env_context *ctx;
+	int i;
 
-	/* configure any static flags */
-	env_attr_walk(ENV_FLAGS_LIST_STATIC, set_flags, NULL);
-	/* configure any dynamic flags */
-	env_attr_walk(value, set_flags, NULL);
+	for (i = 0, ctx = U_BOOT_ENV_CTX_START; i < U_BOOT_ENV_CTX_COUNT;
+	     i++, ctx++) {
+		/* remove all flags */
+		hwalk_r(ctx->htab, clear_flags);
+
+		/* configure any static flags */
+		env_attr_walk(ENV_FLAGS_LIST_STATIC, set_flags, ctx);
+		/* configure any dynamic flags */
+		env_attr_walk(value, set_flags, ctx);
+	}
 
 	return 0;
 }
@@ -541,7 +554,7 @@ int env_flags_validate(const struct env_entry *item, const char *newval,
 			return 1;
 		} else if (item->flags &
 		    ENV_FLAGS_VARACCESS_PREVENT_NONDEF_OVERWR) {
-			const char *defval = env_get_default(name);
+			const char *defval = env_get_default(item->ctx, name);
 
 			if (defval == NULL)
 				defval = "";
diff --git a/include/_exports.h b/include/_exports.h
index 0dee05f077a6..8e072668d198 100644
--- a/include/_exports.h
+++ b/include/_exports.h
@@ -31,8 +31,10 @@
 	EXPORT_FUNC(vprintf, int, vprintf, const char *, va_list)
 	EXPORT_FUNC(do_reset, int, do_reset, cmd_tbl_t *,
 		    int , int , char * const [])
-	EXPORT_FUNC(env_get, char  *, env_get, const char*)
-	EXPORT_FUNC(env_set, int, env_set, const char *, const char *)
+	EXPORT_FUNC(env_get, char  *, env_get, struct env_context *,
+		    const char*)
+	EXPORT_FUNC(env_set, int, env_set, struct env_context *, const char *,
+		    const char *)
 	EXPORT_FUNC(simple_strtoul, unsigned long, simple_strtoul,
 		    const char *, char **, unsigned int)
 	EXPORT_FUNC(strict_strtoul, int, strict_strtoul,
diff --git a/include/common.h b/include/common.h
index d8f302ea92f0..cdc0bf7042c2 100644
--- a/include/common.h
+++ b/include/common.h
@@ -46,6 +46,8 @@ typedef volatile unsigned char	vu_char;
 
 #include <log.h>
 
+struct env_context;
+
 typedef void (interrupt_handler_t)(void *);
 
 #include <asm/u-boot.h> /* boot information for Linux kernel */
@@ -340,9 +342,9 @@ int	serial_printf (const char *fmt, ...)
 
 /* lib/net_utils.c */
 #include <net.h>
-static inline struct in_addr env_get_ip(char *var)
+static inline struct in_addr env_get_ip(struct env_context *ctx, char *var)
 {
-	return string_to_ip(env_get(var));
+	return string_to_ip(env_get(ctx, var));
 }
 
 #ifdef CONFIG_LED_STATUS
diff --git a/include/env.h b/include/env.h
index a74a261337e6..203605e5e778 100644
--- a/include/env.h
+++ b/include/env.h
@@ -12,7 +12,9 @@
 #include <stdbool.h>
 #include <linux/types.h>
 
+struct env_context;
 struct environment_s;
+enum env_operation; /* TODO: move it from env_internal.h? */
 
 /* Value for environment validity */
 enum env_valid {
@@ -61,15 +63,24 @@ enum env_redund_flags {
 	ENV_REDUND_ACTIVE = 1,
 };
 
+/* Accessor functions */
+void env_set_ready(struct env_context *ctx);
+bool env_is_ready(struct env_context *ctx);
+void env_set_valid(struct env_context *ctx, enum env_valid valid);
+enum env_valid env_get_valid(struct env_context *ctx);
+void env_set_env_addr(struct env_context *ctx, ulong env_addr);
+ulong env_get_env_addr(struct env_context *ctx);
+
 /**
  * env_get_id() - Gets a sequence number for the environment
  *
  * This value increments every time the environment changes, so can be used an
  * an indication of this
  *
+ * @ctx: Context
  * @return environment ID
  */
-int env_get_id(void);
+int env_get_id(struct env_context *ctx);
 
 /**
  * env_init() - Set up the pre-relocation environment
@@ -98,7 +109,7 @@ void env_relocate(void);
  * @index: The environment index for a 'name2=value2' pair.
  * @return index for the value if the names match, else -1.
  */
-int env_match(unsigned char *name, int index);
+int env_match(struct env_context *ctx, unsigned char *name, int index);
 
 /**
  * env_get() - Look up the value of an environment variable
@@ -107,10 +118,11 @@ int env_match(unsigned char *name, int index);
  * environment is loaded from storage, i.e. GD_FLG_ENV_READY is 0). In that
  * case this function calls env_get_f().
  *
+ * @ctx:	Context
  * @varname:	Variable to look up
  * @return value of variable, or NULL if not found
  */
-char *env_get(const char *varname);
+char *env_get(struct env_context *ctx, const char *varname);
 
 /**
  * env_get_f() - Look up the value of an environment variable (early)
@@ -119,10 +131,12 @@ char *env_get(const char *varname);
  * loaded yet (GD_FLG_ENV_READY flag is 0). Some environment locations will
  * support reading the value (slowly) and some will not.
  *
+ * @ctx:	Context
  * @varname:	Variable to look up
  * @return value of variable, or NULL if not found
  */
-int env_get_f(const char *name, char *buf, unsigned int len);
+int env_get_f(struct env_context *ctx, const char *name, char *buf,
+	      unsigned int len);
 
 /**
  * env_get_yesno() - Read an environment variable as a boolean
@@ -138,11 +152,12 @@ int env_get_yesno(const char *var);
  * This sets or deletes the value of an environment variable. For setting the
  * value the variable is created if it does not already exist.
  *
+ * @ctx: Context
  * @varname: Variable to adjust
  * @value: Value to set for the variable, or NULL or "" to delete the variable
  * @return 0 if OK, 1 on error
  */
-int env_set(const char *varname, const char *value);
+int env_set(struct env_context *ctx, const char *varname, const char *value);
 
 /**
  * env_get_ulong() - Return an environment variable as an integer value
@@ -150,21 +165,24 @@ int env_set(const char *varname, const char *value);
  * Most U-Boot environment variables store hex values. For those which store
  * (e.g.) base-10 integers, this function can be used to read the value.
  *
+ * @ctx:	Context
  * @name:	Variable to look up
  * @base:	Base to use (e.g. 10 for base 10, 2 for binary)
  * @default_val: Default value to return if no value is found
  * @return the value found, or @default_val if none
  */
-ulong env_get_ulong(const char *name, int base, ulong default_val);
+ulong env_get_ulong(struct env_context *ctx, const char *name, int base,
+		    ulong default_val);
 
 /**
  * env_set_ulong() - set an environment variable to an integer
  *
+ * @ctx: Context
  * @varname: Variable to adjust
  * @value: Value to set for the variable (will be converted to a string)
  * @return 0 if OK, 1 on error
  */
-int env_set_ulong(const char *varname, ulong value);
+int env_set_ulong(struct env_context *ctx, const char *varname, ulong value);
 
 /**
  * env_get_hex() - Return an environment variable as a hex value
@@ -173,30 +191,35 @@ int env_set_ulong(const char *varname, ulong value);
  * prefix). If the environment variable cannot be found, or does not start
  * with hex digits, the default value is returned.
  *
+ * @ctx:		Context
  * @varname:		Variable to decode
  * @default_val:	Value to return on error
  */
-ulong env_get_hex(const char *varname, ulong default_val);
+ulong env_get_hex(struct env_context *ctx, const char *varname,
+		  ulong default_val);
 
 /**
  * env_set_hex() - set an environment variable to a hex value
  *
+ * @ctx: Context
  * @varname: Variable to adjust
  * @value: Value to set for the variable (will be converted to a hex string)
  * @return 0 if OK, 1 on error
  */
-int env_set_hex(const char *varname, ulong value);
+int env_set_hex(struct env_context *ctx, const char *varname, ulong value);
 
 /**
  * env_set_addr - Set an environment variable to an address in hex
  *
+ * @ctx:	Context
  * @varname:	Environment variable to set
  * @addr:	Value to set it to
  * @return 0 if ok, 1 on error
  */
-static inline int env_set_addr(const char *varname, const void *addr)
+static inline int env_set_addr(struct env_context *ctx, const char *varname,
+			       const void *addr)
 {
-	return env_set_hex(varname, (ulong)addr);
+	return env_set_hex(ctx, varname, (ulong)addr);
 }
 
 /**
@@ -241,32 +264,56 @@ void env_fix_drivers(void);
  *
  * This resets individual variables to their value in the default environment
  *
+ * @ctx: Context
  * @nvars: Number of variables to set/reset
  * @vars: List of variables to set/reset
  * @flags: Flags controlling matching (H_... - see search.h)
  */
-int env_set_default_vars(int nvars, char *const vars[], int flags);
+int env_set_default_vars(struct env_context *ctx,
+			 int nvars, char *const vars[], int flags);
+
+/**
+ * env_driver_looup()
+ *
+ * @ctx: Context
+ * @op: Operation
+ * @prio: Priority
+ * @return pointer to env_driver if found, otherwise NULL
+ */
+struct env_driver *env_driver_lookup(struct env_context *ctx,
+				     enum env_operation op, int prio);
 
 /**
  * env_load() - Load the environment from storage
  *
+ * @ctx: Context
  * @return 0 if OK, -ve on error
  */
-int env_load(void);
+int env_load(struct env_context *ctx);
 
 /**
  * env_save() - Save the environment to storage
  *
+ * @ctx: Context
+ * @return 0 if OK, -ve on error
+ */
+int env_save(struct env_context *ctx);
+
+/**
+ * env_ctx_init() - initialize the context environment
+ *
+ * @ctx: Context
  * @return 0 if OK, -ve on error
  */
-int env_save(void);
+int env_ctx_init(struct env_context *ctx);
 
 /**
  * env_erase() - Erase the environment on storage
  *
+ * @ctx: Context
  * @return 0 if OK, -ve on error
  */
-int env_erase(void);
+int env_erase(struct env_context *ctx);
 
 /**
  * env_import() - Import from a binary representation into hash table
@@ -274,56 +321,62 @@ int env_erase(void);
  * This imports the environment from a buffer. The format for each variable is
  * var=value\0 with a double \0 at the end of the buffer.
  *
+ * @ctx: Context
  * @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
  * @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(struct env_context *ctx, const char *buf, int check);
 
 /**
  * env_export() - Export the environment to a buffer
  *
  * Export from hash table into binary representation
  *
+ * @ctx: Context
  * @env_out: Buffer to contain the environment (must be large enough!)
  * @return 0 if OK, 1 on error
  */
-int env_export(struct environment_s *env_out);
+int env_export(struct env_context *ctx, struct environment_hdr *env_out);
 
 /**
  * env_import_redund() - Select and import one of two redundant environments
  *
+ * @ctx: Context
  * @buf1: First environment (struct environemnt_s *)
  * @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
  * @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,
+int env_import_redund(struct env_context *ctx,
+		      const char *buf1, int buf1_read_fail,
 		      const char *buf2, int buf2_read_fail);
 
 /**
  * env_get_default() - Look up a variable from the default environment
  *
+ * @ctx: Context
  * @name: Variable to look up
  * @return value if found, NULL if not found in default environment
  */
-char *env_get_default(const char *name);
+char *env_get_default(struct env_context *ctx, const char *name);
 
 /* [re]set to the default environment */
-void env_set_default(const char *s, int flags);
+void env_set_default(struct env_context *ctx, const char *s, int flags);
 
 /**
  * env_get_char() - Get a character from the early environment
  *
  * This reads from the pre-relocation environment
  *
+ * @ctx: Context
  * @index: Index of character to read (0 = first)
  * @return character read, or -ve on error
  */
-int env_get_char(int index);
+int env_get_char(struct env_context *ctx, int index);
 
 /**
  * env_reloc() - Relocate the 'env' sub-commands
diff --git a/include/env_internal.h b/include/env_internal.h
index b1ddcb5adfd3..9514f6649c3e 100644
--- a/include/env_internal.h
+++ b/include/env_internal.h
@@ -15,6 +15,7 @@
 #ifndef _ENV_INTERNAL_H_
 #define _ENV_INTERNAL_H_
 
+#include <stdbool.h>
 #include <linux/kconfig.h>
 
 /**************************************************************************
@@ -163,6 +164,14 @@ extern unsigned long nand_env_oob_offset;
 #define	TOTAL_MALLOC_LEN	CONFIG_SYS_MALLOC_LEN
 #endif
 
+typedef struct environment_hdr {
+	uint32_t	crc;		/* CRC32 over data bytes	*/
+#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
+	unsigned char	flags;		/* active/obsolete flags	*/
+#endif
+	unsigned char	data[];		/* Environment data		*/
+} env_hdr_t;
+
 typedef struct environment_s {
 	uint32_t	crc;		/* CRC32 over data bytes	*/
 #ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
@@ -182,6 +191,7 @@ extern const unsigned char default_environment[];
 #include <env_attr.h>
 #include <env_callback.h>
 #include <env_flags.h>
+#include <linker_lists.h>
 #include <search.h>
 
 enum env_location {
@@ -211,6 +221,67 @@ enum env_operation {
 	ENVOP_ERASE,	/* we want to call the erase function */
 };
 
+#if	defined(CONFIG_ENV_DRV_EEPROM)		|| \
+	defined(CONFIG_ENV_DRV_FLASH)		|| \
+	defined(CONFIG_ENV_DRV_MMC)		|| \
+	defined(CONFIG_ENV_DRV_FAT)		|| \
+	defined(CONFIG_ENV_DRV_EXT4)		|| \
+	defined(CONFIG_ENV_DRV_NAND)		|| \
+	defined(CONFIG_ENV_DRV_NVRAM)		|| \
+	defined(CONFIG_ENV_DRV_ONENAND)		|| \
+	defined(CONFIG_ENV_DRV_SATA)		|| \
+	defined(CONFIG_ENV_DRV_SPI_FLASH)	|| \
+	defined(CONFIG_ENV_DRV_REMOTE)		|| \
+	defined(CONFIG_ENV_DRV_UBI)
+
+#define ENV_IS_IN_DEVICE
+
+#endif
+
+/* defined in search.h */
+struct hsearch_data;
+
+struct env_context {
+	const char *name;
+	int env_id;
+	/* TODO: Some flag bits can be assembled into single flag */
+	unsigned long valid;
+	unsigned long flags;
+#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
+	unsigned char env_flags;
+#endif
+	int has_init;
+	int load_prio;
+	void *drv_params[ENVL_COUNT];
+	struct hsearch_data *htab;	/* hash table on memory */
+	uint32_t env_size;		/* data bytes in env */
+
+	/* driver-related functions in env/env.c */
+	bool (*has_inited)(struct env_context *ctx, enum env_location location);
+	void (*set_inited)(struct env_context *ctx, enum env_location location);
+	int (*get_load_prio)(struct env_context *ctx);
+	enum env_location (*get_location)(struct env_context *ctx,
+					  enum env_operation op, int prio);
+	int (*get_char)(struct env_context *ctx, int index);
+	int (*get_char_default)(struct env_context *ctx, int index);
+	int (*get_char_spec)(struct env_context *ctx, int index);
+	int (*init)(struct env_context *ctx);
+	int (*drv_init)(struct env_context *ctx, enum env_location loc);
+
+	/* save/load-related functions in env/common.c */
+	char *(*get_default)(struct env_context *ctx, const char *name);
+	void (*set_default)(struct env_context *ctx, const char *s, int flags);
+	int (*set_default_vars)(struct env_context *ctx,
+				int nvars, char * const vars[], int flags);
+	void (*set_ready)(struct env_context *ctx);
+	bool (*is_ready)(struct env_context *ctx);
+	void (*set_valid)(struct env_context *ctx, enum env_valid valid);
+	enum env_valid (*get_valid)(struct env_context *ctx);
+	void (*set_addr)(struct env_context *ctx, ulong env_addr);
+	ulong (*get_addr)(struct env_context *ctx);
+	void (*post_relocate)(struct env_context *ctx);
+};
+
 struct env_driver {
 	const char *name;
 	enum env_location location;
@@ -221,18 +292,20 @@ struct env_driver {
 	 * This method is optional. If not provided, no environment will be
 	 * loaded.
 	 *
+	 * @ctx:   pointer to environment context
 	 * @return 0 if OK, -ve on error
 	 */
-	int (*load)(void);
+	int (*load)(struct env_context *ctx);
 
 	/**
 	 * save() - Save the environment to storage
 	 *
 	 * This method is required for 'saveenv' to work.
 	 *
+	 * @ctx:   pointer to environment context
 	 * @return 0 if OK, -ve on error
 	 */
-	int (*save)(void);
+	int (*save)(struct env_context *ctx);
 
 	/**
 	 * erase() - Erase the environment on storage
@@ -241,23 +314,30 @@ struct env_driver {
 	 *
 	 * @return 0 if OK, -ve on error
 	 */
-	int (*erase)(void);
+	int (*erase)(struct env_context *ctx);
 
 	/**
 	 * init() - Set up the initial pre-relocation environment
 	 *
 	 * This method is optional.
 	 *
+	 * @ctx:   pointer to environment context
 	 * @return 0 if OK, -ENOENT if no initial environment could be found,
 	 * other -ve on error
 	 */
-	int (*init)(void);
+	int (*init)(struct env_context *ctx);
 };
 
 /* Declare a new environment location driver */
 #define U_BOOT_ENV_LOCATION(__name)					\
 	ll_entry_declare(struct env_driver, __name, env_driver)
 
+/* Declare a new environment context */
+#define U_BOOT_ENV_CONTEXT(__name) \
+	ll_entry_declare(struct env_context, __name, env_contexts)
+#define U_BOOT_ENV_CTX_START ll_entry_start(struct env_context, env_contexts)
+#define U_BOOT_ENV_CTX_COUNT ll_entry_count(struct env_context, env_contexts)
+
 /* Declare the name of a location */
 #ifdef CONFIG_CMD_SAVEENV
 #define ENV_NAME(_name) .name = _name,
@@ -271,6 +351,7 @@ struct env_driver {
 #define env_save_ptr(x) NULL
 #endif
 
+extern enum env_location env_locations[];
 extern struct hsearch_data env_htab;
 
 #endif /* DO_DEPS_ONLY */
diff --git a/include/exports.h b/include/exports.h
index 147a00f860b0..60a482de1313 100644
--- a/include/exports.h
+++ b/include/exports.h
@@ -27,8 +27,9 @@ unsigned long get_timer(unsigned long);
 int vprintf(const char *, va_list);
 unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base);
 int strict_strtoul(const char *cp, unsigned int base, unsigned long *res);
-char *env_get(const char *name);
-int env_set(const char *varname, const char *value);
+struct env_context;
+char *env_get(struct env_context *ctx, const char *name);
+int env_set(struct env_context *ctx, const char *varname, const char *value);
 long simple_strtol(const char *cp, char **endp, unsigned int base);
 int strcmp(const char *cs, const char *ct);
 unsigned long ustrtoul(const char *cp, char **endp, unsigned int base);
-- 
2.21.0



More information about the U-Boot mailing list