summaryrefslogtreecommitdiff
path: root/env
diff options
context:
space:
mode:
authorSimon Glass <sjg@chromium.org>2017-08-03 12:21:49 -0600
committerTom Rini <trini@konsulko.com>2017-08-15 08:18:45 -0400
commit0649cd0d4908d9b983a0361b8665938ef25701be (patch)
tree5ba3f7ad481aefa2ff5b71bc11dc8b1660245755 /env
parentec74f5f9c38ce8e21f4aa413427cfec6fe6fb8da (diff)
Move environment files from common/ to env/
About a quarter of the files in common/ relate to the environment. It seems better to put these into their own subdirectory and remove the prefix. Signed-off-by: Simon Glass <sjg@chromium.org> Reviewed-by: Tom Rini <trini@konsulko.com>
Diffstat (limited to 'env')
-rw-r--r--env/Kconfig441
-rw-r--r--env/Makefile57
-rw-r--r--env/attr.c310
-rw-r--r--env/callback.c136
-rw-r--r--env/common.c353
-rw-r--r--env/dataflash.c84
-rw-r--r--env/eeprom.c245
-rw-r--r--env/embedded.c97
-rw-r--r--env/ext4.c129
-rw-r--r--env/fat.c110
-rw-r--r--env/flags.c567
-rw-r--r--env/flash.c338
-rw-r--r--env/mmc.c326
-rw-r--r--env/nand.c395
-rw-r--r--env/nowhere.c35
-rw-r--r--env/nvram.c114
-rw-r--r--env/onenand.c116
-rw-r--r--env/remote.c58
-rw-r--r--env/sata.c127
-rw-r--r--env/sf.c344
-rw-r--r--env/ubi.c179
21 files changed, 4561 insertions, 0 deletions
diff --git a/env/Kconfig b/env/Kconfig
new file mode 100644
index 0000000000..9f97f9c90c
--- /dev/null
+++ b/env/Kconfig
@@ -0,0 +1,441 @@
+menu "Environment"
+
+config ENV_IS_IN_DATAFLASH
+ bool "Environment in dataflash"
+ depends on !CHAIN_OF_TRUST
+ help
+ Define this if you have a DataFlash memory device which you
+ want to use for the environment.
+
+ - CONFIG_ENV_OFFSET:
+ - CONFIG_ENV_ADDR:
+ - CONFIG_ENV_SIZE:
+
+ These three #defines specify the offset and size of the
+ environment area within the total memory of your DataFlash placed
+ at the specified address.
+
+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.
+
+ - CONFIG_SYS_I2C_EEPROM_ADDR:
+ If defined, specified the chip address of the EEPROM device.
+ The default address is zero.
+
+ - CONFIG_SYS_I2C_EEPROM_BUS:
+ If defined, specified the i2c bus of the EEPROM device.
+
+ - CONFIG_SYS_EEPROM_PAGE_WRITE_BITS:
+ If defined, the number of bits used to address bytes in a
+ single page in the EEPROM device. A 64 byte page, for example
+ would require six bits.
+
+ - CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS:
+ If defined, the number of milliseconds to delay between
+ page writes. The default is zero milliseconds.
+
+ - CONFIG_SYS_I2C_EEPROM_ADDR_LEN:
+ The length in bytes of the EEPROM memory array address. Note
+ that this is NOT the chip address length!
+
+ - CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW:
+ EEPROM chips that implement "address overflow" are ones
+ like Catalyst 24WC04/08/16 which has 9/10/11 bits of
+ address and the extra bits end up in the "chip address" bit
+ slots. This makes a 24WC08 (1Kbyte) chip look like four 256
+ byte chips.
+
+ 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_SYS_EEPROM_SIZE:
+ The size in bytes of the EEPROM device.
+
+ - 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
+ select FAT_WRITE
+ help
+ Define this if you want to use the FAT file system for the environment.
+
+
+ - CONFIG_FAT_WRITE:
+ This must be enabled. Otherwise it cannot save the environment file.
+
+config ENV_IS_IN_FLASH
+ bool "Environment in flash memory"
+ depends on !CHAIN_OF_TRUST
+ 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:
+
+ 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
+ default y if ARCH_SUNXI
+ 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 remove 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
+ 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_ENV_SPI_BUS (optional):
+ - CONFIG_ENV_SPI_CS (optional):
+
+ Define the SPI bus and chip select. If not defined they will be 0.
+
+ - CONFIG_ENV_SPI_MAX_HZ (optional):
+
+ Define the SPI max work clock. If not defined then use 1MHz.
+
+ - CONFIG_ENV_SPI_MODE (optional):
+
+ Define the SPI work mode. If not defined then use SPI_MODE_3.
+
+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_UBI_SILENCE_MSG
+ - CONFIG_UBIFS_SILENCE_MSG
+
+ You will probably want to define these to avoid a really noisy system
+ when storing the env in UBI.
+
+config ENV_IS_NOWHERE
+ bool "Environment is not stored"
+ help
+ Define this if you don't want to or can't have an environment stored
+ on a storage medium
+
+config ENV_FAT_INTERFACE
+ string "Name of the block device for the environment"
+ depends on ENV_IS_IN_FAT
+ 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" 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 environemnt"
+ 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.
+
+if ARCH_SUNXI
+
+config ENV_OFFSET
+ hex "Environment Offset"
+ depends on !ENV_IS_IN_UBI
+ depends on !ENV_IS_NOWHERE
+ default 0x88000 if ARCH_SUNXI
+ help
+ Offset from the start of the device (or partition)
+
+config ENV_SIZE
+ hex "Environment Size"
+ depends on !ENV_IS_NOWHERE
+ default 0x20000 if ARCH_SUNXI
+ help
+ Size of the environment storage area
+
+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.
+
+endif
+
+endmenu
diff --git a/env/Makefile b/env/Makefile
new file mode 100644
index 0000000000..4c1bdcfdf4
--- /dev/null
+++ b/env/Makefile
@@ -0,0 +1,57 @@
+#
+# (C) Copyright 2004-2006
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+obj-y += common.o
+
+ifndef CONFIG_SPL_BUILD
+obj-y += attr.o
+obj-y += callback.o
+obj-y += flags.o
+obj-$(CONFIG_ENV_IS_IN_DATAFLASH) += dataflash.o
+obj-$(CONFIG_ENV_IS_IN_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_FLASH) += flash.o
+obj-$(CONFIG_ENV_IS_IN_MMC) += mmc.o
+obj-$(CONFIG_ENV_IS_IN_FAT) += fat.o
+obj-$(CONFIG_ENV_IS_IN_EXT4) += ext4.o
+obj-$(CONFIG_ENV_IS_IN_NAND) += nand.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_SPI_FLASH) += sf.o
+obj-$(CONFIG_ENV_IS_IN_REMOTE) += remote.o
+obj-$(CONFIG_ENV_IS_IN_UBI) += ubi.o
+obj-$(CONFIG_ENV_IS_NOWHERE) += nowhere.o
+endif
+
+ifdef CONFIG_SPL_BUILD
+obj-$(CONFIG_ENV_IS_IN_FLASH) += flash.o
+# environment
+ifdef CONFIG_TPL_BUILD
+obj-$(CONFIG_TPL_ENV_SUPPORT) += attr.o
+obj-$(CONFIG_TPL_ENV_SUPPORT) += flags.o
+obj-$(CONFIG_TPL_ENV_SUPPORT) += callback.o
+else
+obj-$(CONFIG_SPL_ENV_SUPPORT) += attr.o
+obj-$(CONFIG_SPL_ENV_SUPPORT) += flags.o
+obj-$(CONFIG_SPL_ENV_SUPPORT) += callback.o
+endif
+ifneq ($(CONFIG_TPL_ENV_SUPPORT)$(CONFIG_SPL_ENV_SUPPORT),)
+obj-$(CONFIG_ENV_IS_NOWHERE) += nowhere.o
+obj-$(CONFIG_ENV_IS_IN_MMC) += mmc.o
+obj-$(CONFIG_ENV_IS_IN_FAT) += fat.o
+obj-$(CONFIG_ENV_IS_IN_EXT4) += ext4.o
+obj-$(CONFIG_ENV_IS_IN_NAND) += nand.o
+obj-$(CONFIG_ENV_IS_IN_SPI_FLASH) += sf.o
+obj-$(CONFIG_ENV_IS_IN_FLASH) += flash.o
+endif
+endif
+
+CFLAGS_embedded.o := -Wa,--no-warn -DENV_CRC=$(shell tools/envcrc 2>/dev/null)
diff --git a/env/attr.c b/env/attr.c
new file mode 100644
index 0000000000..f965b4bbb6
--- /dev/null
+++ b/env/attr.c
@@ -0,0 +1,310 @@
+/*
+ * (C) Copyright 2012
+ * Joe Hershberger, National Instruments, joe.hershberger@ni.com
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifdef USE_HOSTCC /* Eliminate "ANSI does not permit..." warnings */
+#include <stdint.h>
+#include <stdio.h>
+#include <linux/linux_string.h>
+#else
+#include <common.h>
+#include <slre.h>
+#endif
+
+#include <env_attr.h>
+#include <errno.h>
+#include <linux/string.h>
+#include <malloc.h>
+
+/*
+ * Iterate through the whole list calling the callback for each found element.
+ * "attr_list" takes the form:
+ * attributes = [^,:\s]*
+ * entry = name[:attributes]
+ * list = entry[,list]
+ */
+int env_attr_walk(const char *attr_list,
+ int (*callback)(const char *name, const char *attributes, void *priv),
+ void *priv)
+{
+ const char *entry, *entry_end;
+ char *name, *attributes;
+
+ if (!attr_list)
+ /* list not found */
+ return 1;
+
+ entry = attr_list;
+ do {
+ char *entry_cpy = NULL;
+
+ entry_end = strchr(entry, ENV_ATTR_LIST_DELIM);
+ /* check if this is the last entry in the list */
+ if (entry_end == NULL) {
+ int entry_len = strlen(entry);
+
+ if (entry_len) {
+ /*
+ * allocate memory to copy the entry into since
+ * we will need to inject '\0' chars and squash
+ * white-space before calling the callback
+ */
+ entry_cpy = malloc(entry_len + 1);
+ if (entry_cpy)
+ /* copy the rest of the list */
+ strcpy(entry_cpy, entry);
+ else
+ return -ENOMEM;
+ }
+ } else {
+ int entry_len = entry_end - entry;
+
+ if (entry_len) {
+ /*
+ * allocate memory to copy the entry into since
+ * we will need to inject '\0' chars and squash
+ * white-space before calling the callback
+ */
+ entry_cpy = malloc(entry_len + 1);
+ if (entry_cpy) {
+ /* copy just this entry and null term */
+ strncpy(entry_cpy, entry, entry_len);
+ entry_cpy[entry_len] = '\0';
+ } else
+ return -ENOMEM;
+ }
+ }
+
+ /* check if there is anything to process (e.g. not ",,,") */
+ if (entry_cpy != NULL) {
+ attributes = strchr(entry_cpy, ENV_ATTR_SEP);
+ /* check if there is a ':' */
+ if (attributes != NULL) {
+ /* replace the ':' with '\0' to term name */
+ *attributes++ = '\0';
+ /* remove white-space from attributes */
+ attributes = strim(attributes);
+ }
+ /* remove white-space from name */
+ name = strim(entry_cpy);
+
+ /* only call the callback if there is a name */
+ if (strlen(name) != 0) {
+ int retval = 0;
+
+ retval = callback(name, attributes, priv);
+ if (retval) {
+ free(entry_cpy);
+ return retval;
+ }
+ }
+ }
+
+ free(entry_cpy);
+ entry = entry_end + 1;
+ } while (entry_end != NULL);
+
+ return 0;
+}
+
+#if defined(CONFIG_REGEX)
+struct regex_callback_priv {
+ const char *searched_for;
+ char *regex;
+ char *attributes;
+};
+
+static int regex_callback(const char *name, const char *attributes, void *priv)
+{
+ int retval = 0;
+ struct regex_callback_priv *cbp = (struct regex_callback_priv *)priv;
+ struct slre slre;
+ char regex[strlen(name) + 3];
+
+ /* Require the whole string to be described by the regex */
+ sprintf(regex, "^%s$", name);
+ if (slre_compile(&slre, regex)) {
+ struct cap caps[slre.num_caps + 2];
+
+ if (slre_match(&slre, cbp->searched_for,
+ strlen(cbp->searched_for), caps)) {
+ free(cbp->regex);
+ if (!attributes) {
+ retval = -EINVAL;
+ goto done;
+ }
+ cbp->regex = malloc(strlen(regex) + 1);
+ if (cbp->regex) {
+ strcpy(cbp->regex, regex);
+ } else {
+ retval = -ENOMEM;
+ goto done;
+ }
+
+ free(cbp->attributes);
+ cbp->attributes = malloc(strlen(attributes) + 1);
+ if (cbp->attributes) {
+ strcpy(cbp->attributes, attributes);
+ } else {
+ retval = -ENOMEM;
+ free(cbp->regex);
+ cbp->regex = NULL;
+ goto done;
+ }
+ }
+ } else {
+ printf("Error compiling regex: %s\n", slre.err_str);
+ retval = -EINVAL;
+ }
+done:
+ return retval;
+}
+
+/*
+ * Retrieve the attributes string associated with a single name in the list
+ * There is no protection on attributes being too small for the value
+ */
+int env_attr_lookup(const char *attr_list, const char *name, char *attributes)
+{
+ if (!attributes)
+ /* bad parameter */
+ return -EINVAL;
+ if (!attr_list)
+ /* list not found */
+ return -EINVAL;
+
+ struct regex_callback_priv priv;
+ int retval;
+
+ priv.searched_for = name;
+ priv.regex = NULL;
+ priv.attributes = NULL;
+ retval = env_attr_walk(attr_list, regex_callback, &priv);
+ if (retval)
+ return retval; /* error */
+
+ if (priv.regex) {
+ strcpy(attributes, priv.attributes);
+ free(priv.attributes);
+ free(priv.regex);
+ /* success */
+ return 0;
+ }
+ return -ENOENT; /* not found in list */
+}
+#else
+
+/*
+ * Search for the last exactly matching name in an attribute list
+ */
+static int reverse_name_search(const char *searched, const char *search_for,
+ const char **result)
+{
+ int result_size = 0;
+ const char *cur_searched = searched;
+
+ if (result)
+ *result = NULL;
+
+ if (*search_for == '\0') {
+ if (result)
+ *result = searched;
+ return strlen(searched);
+ }
+
+ for (;;) {
+ const char *match = strstr(cur_searched, search_for);
+ const char *prevch;
+ const char *nextch;
+
+ /* Stop looking if no new match is found */
+ if (match == NULL)
+ break;
+
+ prevch = match - 1;
+ nextch = match + strlen(search_for);
+
+ /* Skip spaces */
+ while (*prevch == ' ' && prevch >= searched)
+ prevch--;
+ while (*nextch == ' ')
+ nextch++;
+
+ /* Start looking past the current match so last is found */
+ cur_searched = match + 1;
+ /* Check for an exact match */
+ if (match != searched &&
+ *prevch != ENV_ATTR_LIST_DELIM &&
+ prevch != searched - 1)
+ continue;
+ if (*nextch != ENV_ATTR_SEP &&
+ *nextch != ENV_ATTR_LIST_DELIM &&
+ *nextch != '\0')
+ continue;
+
+ if (result)
+ *result = match;
+ result_size = strlen(search_for);
+ }
+
+ return result_size;
+}
+
+/*
+ * Retrieve the attributes string associated with a single name in the list
+ * There is no protection on attributes being too small for the value
+ */
+int env_attr_lookup(const char *attr_list, const char *name, char *attributes)
+{
+ const char *entry = NULL;
+ int entry_len;
+
+ if (!attributes)
+ /* bad parameter */
+ return -EINVAL;
+ if (!attr_list)
+ /* list not found */
+ return -EINVAL;
+
+ entry_len = reverse_name_search(attr_list, name, &entry);
+ if (entry != NULL) {
+ int len;
+
+ /* skip the name */
+ entry += entry_len;
+ /* skip spaces */
+ while (*entry == ' ')
+ entry++;
+ if (*entry != ENV_ATTR_SEP)
+ len = 0;
+ else {
+ const char *delim;
+ static const char delims[] = {
+ ENV_ATTR_LIST_DELIM, ' ', '\0'};
+
+ /* skip the attr sep */
+ entry += 1;
+ /* skip spaces */
+ while (*entry == ' ')
+ entry++;
+
+ delim = strpbrk(entry, delims);
+ if (delim == NULL)
+ len = strlen(entry);
+ else
+ len = delim - entry;
+ memcpy(attributes, entry, len);
+ }
+ attributes[len] = '\0';
+
+ /* success */
+ return 0;
+ }
+
+ /* not found in list */
+ return -ENOENT;
+}
+#endif
diff --git a/env/callback.c b/env/callback.c
new file mode 100644
index 0000000000..1957cc1996
--- /dev/null
+++ b/env/callback.c
@@ -0,0 +1,136 @@
+/*
+ * (C) Copyright 2012
+ * Joe Hershberger, National Instruments, joe.hershberger@ni.com
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <environment.h>
+
+#if defined(CONFIG_NEEDS_MANUAL_RELOC)
+DECLARE_GLOBAL_DATA_PTR;
+#endif
+
+/*
+ * Look up a callback function pointer by name
+ */
+static struct env_clbk_tbl *find_env_callback(const char *name)
+{
+ struct env_clbk_tbl *clbkp;
+ int i;
+ int num_callbacks = ll_entry_count(struct env_clbk_tbl, env_clbk);
+
+ if (name == NULL)
+ return NULL;
+
+ /* look up the callback in the linker-list */
+ for (i = 0, clbkp = ll_entry_start(struct env_clbk_tbl, env_clbk);
+ i < num_callbacks;
+ i++, clbkp++) {
+ if (strcmp(name, clbkp->name) == 0)
+ return clbkp;
+ }
+
+ return NULL;
+}
+
+static int first_call = 1;
+static const char *callback_list;
+
+/*
+ * Look for a possible callback for a newly added variable
+ * This is called specifically when the variable did not exist in the hash
+ * previously, so the blanket update did not find this variable.
+ */
+void env_callback_init(ENTRY *var_entry)
+{
+ const char *var_name = var_entry->key;
+ char callback_name[256] = "";
+ struct env_clbk_tbl *clbkp;
+ int ret = 1;
+
+ if (first_call) {
+ callback_list = getenv(ENV_CALLBACK_VAR);
+ first_call = 0;
+ }
+
+ /* look in the ".callbacks" var for a reference to this variable */
+ if (callback_list != NULL)
+ ret = env_attr_lookup(callback_list, var_name, callback_name);
+
+ /* only if not found there, look in the static list */
+ if (ret)
+ ret = env_attr_lookup(ENV_CALLBACK_LIST_STATIC, var_name,
+ callback_name);
+
+ /* if an association was found, set the callback pointer */
+ if (!ret && strlen(callback_name)) {
+ clbkp = find_env_callback(callback_name);
+ if (clbkp != NULL)
+#if defined(CONFIG_NEEDS_MANUAL_RELOC)
+ var_entry->callback = clbkp->callback + gd->reloc_off;
+#else
+ var_entry->callback = clbkp->callback;
+#endif
+ }
+}
+
+/*
+ * Called on each existing env var prior to the blanket update since removing
+ * a callback association should remove its callback.
+ */
+static int clear_callback(ENTRY *entry)
+{
+ entry->callback = NULL;
+
+ return 0;
+}
+
+/*
+ * Call for each element in the list that associates variables to callbacks
+ */
+static int set_callback(const char *name, const char *value, void *priv)
+{
+ ENTRY e, *ep;
+ struct env_clbk_tbl *clbkp;
+
+ e.key = name;
+ e.data = NULL;
+ e.callback = NULL;
+ hsearch_r(e, FIND, &ep, &env_htab, 0);
+
+ /* does the env variable actually exist? */
+ if (ep != NULL) {
+ /* the assocaition delares no callback, so remove the pointer */
+ if (value == NULL || strlen(value) == 0)
+ ep->callback = NULL;
+ else {
+ /* assign the requested callback */
+ clbkp = find_env_callback(value);
+ if (clbkp != NULL)
+#if defined(CONFIG_NEEDS_MANUAL_RELOC)
+ ep->callback = clbkp->callback + gd->reloc_off;
+#else
+ ep->callback = clbkp->callback;
+#endif
+ }
+ }
+
+ return 0;
+}
+
+static int on_callbacks(const char *name, const char *value, enum env_op op,
+ int flags)
+{
+ /* remove all callbacks */
+ hwalk_r(&env_htab, clear_callback);
+
+ /* 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);
+
+ return 0;
+}
+U_BOOT_ENV_CALLBACK(callbacks, on_callbacks);
diff --git a/env/common.c b/env/common.c
new file mode 100644
index 0000000000..d9c0c4e3f3
--- /dev/null
+++ b/env/common.c
@@ -0,0 +1,353 @@
+/*
+ * (C) Copyright 2000-2010
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Andreas Heppel <aheppel@sysgo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <command.h>
+#include <environment.h>
+#include <linux/stddef.h>
+#include <search.h>
+#include <errno.h>
+#include <malloc.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/************************************************************************
+ * Default settings to be used when no valid environment is found
+ */
+#include <env_default.h>
+
+struct hsearch_data env_htab = {
+ .change_ok = env_flags_validate,
+};
+
+__weak uchar env_get_char_spec(int index)
+{
+ return *((uchar *)(gd->env_addr + index));
+}
+
+static uchar env_get_char_init(int index)
+{
+ /* if crc was bad, use the default environment */
+ if (gd->env_valid)
+ return env_get_char_spec(index);
+ else
+ return default_environment[index];
+}
+
+uchar env_get_char_memory(int index)
+{
+ return *env_get_addr(index);
+}
+
+uchar env_get_char(int index)
+{
+ /* if relocated to RAM */
+ if (gd->flags & GD_FLG_RELOC)
+ return env_get_char_memory(index);
+ else
+ return env_get_char_init(index);
+}
+
+const uchar *env_get_addr(int index)
+{
+ if (gd->env_valid)
+ return (uchar *)(gd->env_addr + index);
+ else
+ return &default_environment[index];
+}
+
+/*
+ * Read an environment variable as a boolean
+ * Return -1 if variable does not exist (default to true)
+ */
+int getenv_yesno(const char *var)
+{
+ char *s = getenv(var);
+
+ if (s == NULL)
+ return -1;
+ return (*s == '1' || *s == 'y' || *s == 'Y' || *s == 't' || *s == 'T') ?
+ 1 : 0;
+}
+
+/*
+ * Look up the variable from the default environment
+ */
+char *getenv_default(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 = 0;
+ ret_val = getenv(name);
+ gd->env_valid = really_valid;
+ gd->flags = real_gd_flags;
+ return ret_val;
+}
+
+void set_default_env(const char *s)
+{
+ int flags = 0;
+
+ if (sizeof(default_environment) > ENV_SIZE) {
+ puts("*** Error - default environment is too large\n\n");
+ return;
+ }
+
+ if (s) {
+ if (*s == '!') {
+ printf("*** Warning - %s, "
+ "using default environment\n\n",
+ s + 1);
+ } else {
+ flags = H_INTERACTIVE;
+ puts(s);
+ }
+ } else {
+ puts("Using default environment\n\n");
+ }
+
+ if (himport_r(&env_htab, (char *)default_environment,
+ sizeof(default_environment), '\0', flags, 0,
+ 0, NULL) == 0)
+ error("Environment import failed: errno = %d\n", errno);
+
+ gd->flags |= GD_FLG_ENV_READY;
+ gd->flags |= GD_FLG_ENV_DEFAULT;
+}
+
+
+/* [re]set individual variables to their value in the default environment */
+int set_default_vars(int nvars, char * const vars[])
+{
+ /*
+ * Special use-case: import from default environment
+ * (and use \0 as a separator)
+ */
+ return himport_r(&env_htab, (const char *)default_environment,
+ sizeof(default_environment), '\0',
+ H_NOCLEAR | H_INTERACTIVE, 0, nvars, vars);
+}
+
+#ifdef CONFIG_ENV_AES
+#include <uboot_aes.h>
+/**
+ * env_aes_cbc_get_key() - Get AES-128-CBC key for the environment
+ *
+ * This function shall return 16-byte array containing AES-128 key used
+ * to encrypt and decrypt the environment. This function must be overridden
+ * by the implementer as otherwise the environment encryption will not
+ * work.
+ */
+__weak uint8_t *env_aes_cbc_get_key(void)
+{
+ return NULL;
+}
+
+static int env_aes_cbc_crypt(env_t *env, const int enc)
+{
+ unsigned char *data = env->data;
+ uint8_t *key;
+ uint8_t key_exp[AES_EXPAND_KEY_LENGTH];
+ uint32_t aes_blocks;
+
+ key = env_aes_cbc_get_key();
+ if (!key)
+ return -EINVAL;
+
+ /* First we expand the key. */
+ aes_expand_key(key, key_exp);
+
+ /* Calculate the number of AES blocks to encrypt. */
+ aes_blocks = ENV_SIZE / AES_KEY_LENGTH;
+
+ if (enc)
+ aes_cbc_encrypt_blocks(key_exp, data, data, aes_blocks);
+ else
+ aes_cbc_decrypt_blocks(key_exp, data, data, aes_blocks);
+
+ return 0;
+}
+#else
+static inline int env_aes_cbc_crypt(env_t *env, const int enc)
+{
+ return 0;
+}
+#endif
+
+/*
+ * 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)
+{
+ env_t *ep = (env_t *)buf;
+ int ret;
+
+ if (check) {
+ uint32_t crc;
+
+ memcpy(&crc, &ep->crc, sizeof(crc));
+
+ if (crc32(0, ep->data, ENV_SIZE) != crc) {
+ set_default_env("!bad CRC");
+ return 0;
+ }
+ }
+
+ /* Decrypt the env if desired. */
+ ret = env_aes_cbc_crypt(ep, 0);
+ if (ret) {
+ error("Failed to decrypt env!\n");
+ set_default_env("!import failed");
+ return ret;
+ }
+
+ if (himport_r(&env_htab, (char *)ep->data, ENV_SIZE, '\0', 0, 0,
+ 0, NULL)) {
+ gd->flags |= GD_FLG_ENV_READY;
+ return 1;
+ }
+
+ error("Cannot import environment: errno = %d\n", errno);
+
+ set_default_env("!import failed");
+
+ return 0;
+}
+
+#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
+static unsigned char env_flags;
+
+int env_import_redund(const char *buf1, const char *buf2)
+{
+ int crc1_ok, crc2_ok;
+ env_t *ep, *tmp_env1, *tmp_env2;
+
+ tmp_env1 = (env_t *)buf1;
+ tmp_env2 = (env_t *)buf2;
+
+ crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) ==
+ tmp_env1->crc;
+ crc2_ok = crc32(0, tmp_env2->data, ENV_SIZE) ==
+ tmp_env2->crc;
+
+ if (!crc1_ok && !crc2_ok) {
+ set_default_env("!bad CRC");
+ return 0;
+ } else if (crc1_ok && !crc2_ok) {
+ gd->env_valid = 1;
+ } else if (!crc1_ok && crc2_ok) {
+ gd->env_valid = 2;
+ } else {
+ /* both ok - check serial */
+ if (tmp_env1->flags == 255 && tmp_env2->flags == 0)
+ gd->env_valid = 2;
+ else if (tmp_env2->flags == 255 && tmp_env1->flags == 0)
+ gd->env_valid = 1;
+ else if (tmp_env1->flags > tmp_env2->flags)
+ gd->env_valid = 1;
+ else if (tmp_env2->flags > tmp_env1->flags)
+ gd->env_valid = 2;
+ else /* flags are equal - almost impossible */
+ gd->env_valid = 1;
+ }
+
+ if (gd->env_valid == 1)
+ ep = tmp_env1;
+ else
+ ep = tmp_env2;
+
+ env_flags = ep->flags;
+ return env_import((char *)ep, 0);
+}
+#endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */
+
+/* Export the environment and generate CRC for it. */
+int env_export(env_t *env_out)
+{
+ char *res;
+ ssize_t len;
+ int ret;
+
+ res = (char *)env_out->data;
+ len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
+ if (len < 0) {
+ error("Cannot export environment: errno = %d\n", errno);
+ return 1;
+ }
+
+ /* Encrypt the env if desired. */
+ ret = env_aes_cbc_crypt(env_out, 1);
+ if (ret)
+ return ret;
+
+ env_out->crc = crc32(0, env_out->data, ENV_SIZE);
+
+#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
+ env_out->flags = ++env_flags; /* increase the serial */
+#endif
+
+ return 0;
+}
+
+void env_relocate(void)
+{
+#if defined(CONFIG_NEEDS_MANUAL_RELOC)
+ env_reloc();
+ env_htab.change_ok += gd->reloc_off;
+#endif
+ if (gd->env_valid == 0) {
+#if defined(CONFIG_ENV_IS_NOWHERE) || defined(CONFIG_SPL_BUILD)
+ /* Environment not changable */
+ set_default_env(NULL);
+#else
+ bootstage_error(BOOTSTAGE_ID_NET_CHECKSUM);
+ set_default_env("!bad CRC");
+#endif
+ } else {
+ env_relocate_spec();
+ }
+}
+
+#if defined(CONFIG_AUTO_COMPLETE) && !defined(CONFIG_SPL_BUILD)
+int env_complete(char *var, int maxv, char *cmdv[], int bufsz, char *buf)
+{
+ ENTRY *match;
+ int found, idx;
+
+ idx = 0;
+ found = 0;
+ cmdv[0] = NULL;
+
+ while ((idx = hmatch_r(var, idx, &match, &env_htab))) {
+ int vallen = strlen(match->key) + 1;
+
+ if (found >= maxv - 2 || bufsz < vallen)
+ break;
+
+ cmdv[found++] = buf;
+ memcpy(buf, match->key, vallen);
+ buf += vallen;
+ bufsz -= vallen;
+ }
+
+ qsort(cmdv, found, sizeof(cmdv[0]), strcmp_compar);
+
+ if (idx)
+ cmdv[found++] = "...";
+
+ cmdv[found] = NULL;
+ return found;
+}
+#endif
diff --git a/env/dataflash.c b/env/dataflash.c
new file mode 100644
index 0000000000..034e323169
--- /dev/null
+++ b/env/dataflash.c
@@ -0,0 +1,84 @@
+/*
+ * LowLevel function for DataFlash environment support
+ * Author : Gilles Gastaldi (Atmel)
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+#include <common.h>
+#include <command.h>
+#include <environment.h>
+#include <linux/stddef.h>
+#include <dataflash.h>
+#include <search.h>
+#include <errno.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+env_t *env_ptr;
+
+char *env_name_spec = "dataflash";
+
+uchar env_get_char_spec(int index)
+{
+ uchar c;
+
+ read_dataflash(CONFIG_ENV_ADDR + index + offsetof(env_t, data),
+ 1, (char *)&c);
+ return c;
+}
+
+void env_relocate_spec(void)
+{
+ ulong crc, new = 0;
+ unsigned off;
+ char buf[CONFIG_ENV_SIZE];
+
+ /* Read old CRC */
+ read_dataflash(CONFIG_ENV_ADDR + offsetof(env_t, crc),
+ sizeof(ulong), (char *)&crc);
+
+ /* Read whole environment */
+ read_dataflash(CONFIG_ENV_ADDR, CONFIG_ENV_SIZE, buf);
+
+ /* Calculate the CRC */
+ off = offsetof(env_t, data);
+ new = crc32(new, (unsigned char *)(buf + off), ENV_SIZE);
+
+ if (crc == new)
+ env_import(buf, 1);
+ else
+ set_default_env("!bad CRC");
+}
+
+#ifdef CONFIG_ENV_OFFSET_REDUND
+#error No support for redundant environment on dataflash yet!
+#endif
+
+int saveenv(void)
+{
+ env_t env_new;
+ int ret;
+
+ ret = env_export(&env_new);
+ if (ret)
+ return ret;
+
+ return write_dataflash(CONFIG_ENV_ADDR,
+ (unsigned long)&env_new,
+ CONFIG_ENV_SIZE);
+}
+
+/*
+ * Initialize environment use
+ *
+ * We are still running from ROM, so data use is limited.
+ * Use a (moderately small) buffer on the stack
+ */
+int env_init(void)
+{
+ /* use default */
+ gd->env_addr = (ulong)&default_environment[0];
+ gd->env_valid = 1;
+
+ return 0;
+}
diff --git a/env/eeprom.c b/env/eeprom.c
new file mode 100644
index 0000000000..5f63a6cd4a
--- /dev/null
+++ b/env/eeprom.c
@@ -0,0 +1,245 @@
+/*
+ * (C) Copyright 2000-2010
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Andreas Heppel <aheppel@sysgo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <command.h>
+#include <environment.h>
+#include <linux/stddef.h>
+#if defined(CONFIG_I2C_ENV_EEPROM_BUS)
+#include <i2c.h>
+#endif
+#include <search.h>
+#include <errno.h>
+#include <linux/compiler.h> /* for BUG_ON */
+
+DECLARE_GLOBAL_DATA_PTR;
+
+env_t *env_ptr;
+
+char *env_name_spec = "EEPROM";
+
+static int eeprom_bus_read(unsigned dev_addr, unsigned offset,
+ uchar *buffer, unsigned cnt)
+{
+ int rcode;
+#if defined(CONFIG_I2C_ENV_EEPROM_BUS)
+ int old_bus = i2c_get_bus_num();
+
+ if (old_bus != CONFIG_I2C_ENV_EEPROM_BUS)
+ i2c_set_bus_num(CONFIG_I2C_ENV_EEPROM_BUS);
+#endif
+
+ rcode = eeprom_read(dev_addr, offset, buffer, cnt);
+
+#if defined(CONFIG_I2C_ENV_EEPROM_BUS)
+ i2c_set_bus_num(old_bus);
+#endif
+
+ return rcode;
+}
+
+static int eeprom_bus_write(unsigned dev_addr, unsigned offset,
+ uchar *buffer, unsigned cnt)
+{
+ int rcode;
+#if defined(CONFIG_I2C_ENV_EEPROM_BUS)
+ int old_bus = i2c_get_bus_num();
+
+ if (old_bus != CONFIG_I2C_ENV_EEPROM_BUS)
+ i2c_set_bus_num(CONFIG_I2C_ENV_EEPROM_BUS);
+#endif
+
+ rcode = eeprom_write(dev_addr, offset, buffer, cnt);
+
+#if defined(CONFIG_I2C_ENV_EEPROM_BUS)
+ i2c_set_bus_num(old_bus);
+#endif
+
+ return rcode;
+}
+
+uchar env_get_char_spec(int index)
+{
+ uchar c;
+ unsigned int off = CONFIG_ENV_OFFSET;
+
+#ifdef CONFIG_ENV_OFFSET_REDUND
+ if (gd->env_valid == 2)
+ off = CONFIG_ENV_OFFSET_REDUND;
+#endif
+ eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
+ off + index + offsetof(env_t, data), &c, 1);
+
+ return c;
+}
+
+void env_relocate_spec(void)
+{
+ char buf_env[CONFIG_ENV_SIZE];
+ unsigned int off = CONFIG_ENV_OFFSET;
+
+#ifdef CONFIG_ENV_OFFSET_REDUND
+ ulong len, crc[2], crc_tmp;
+ unsigned int off_env[2];
+ uchar rdbuf[64], flags[2];
+ int i, crc_ok[2] = {0, 0};
+
+ eeprom_init(-1); /* prepare for EEPROM read/write */
+
+ off_env[0] = CONFIG_ENV_OFFSET;
+ off_env[1] = CONFIG_ENV_OFFSET_REDUND;
+
+ for (i = 0; i < 2; i++) {
+ /* read CRC */
+ eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
+ off_env[i] + offsetof(env_t, crc),
+ (uchar *)&crc[i], sizeof(ulong));
+ /* read FLAGS */
+ eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
+ off_env[i] + offsetof(env_t, flags),
+ (uchar *)&flags[i], sizeof(uchar));
+
+ crc_tmp = 0;
+ len = ENV_SIZE;
+ off = off_env[i] + offsetof(env_t, data);
+ while (len > 0) {
+ int n = (len > sizeof(rdbuf)) ? sizeof(rdbuf) : len;
+
+ eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, off,
+ rdbuf, n);
+
+ crc_tmp = crc32(crc_tmp, rdbuf, n);
+ len -= n;
+ off += n;
+ }
+
+ if (crc_tmp == crc[i])
+ crc_ok[i] = 1;
+ }
+
+ if (!crc_ok[0] && !crc_ok[1]) {
+ gd->env_addr = 0;
+ gd->env_valid = 0;
+ } else if (crc_ok[0] && !crc_ok[1]) {
+ gd->env_valid = 1;
+ } else if (!crc_ok[0] && crc_ok[1]) {
+ gd->env_valid = 2;
+ } else {
+ /* both ok - check serial */
+ if (flags[0] == ACTIVE_FLAG && flags[1] == OBSOLETE_FLAG)
+ gd->env_valid = 1;
+ else if (flags[0] == OBSOLETE_FLAG && flags[1] == ACTIVE_FLAG)
+ gd->env_valid = 2;
+ else if (flags[0] == 0xFF && flags[1] == 0)
+ gd->env_valid = 2;
+ else if (flags[1] == 0xFF && flags[0] == 0)
+ gd->env_valid = 1;
+ else /* flags are equal - almost impossible */
+ gd->env_valid = 1;
+ }
+
+#else /* CONFIG_ENV_OFFSET_REDUND */
+ ulong crc, len, new;
+ uchar rdbuf[64];
+
+ eeprom_init(-1); /* prepare for EEPROM read/write */
+
+ /* read old CRC */
+ eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
+ CONFIG_ENV_OFFSET + offsetof(env_t, crc),
+ (uchar *)&crc, sizeof(ulong));
+
+ new = 0;
+ len = ENV_SIZE;
+ off = offsetof(env_t, data);
+ while (len > 0) {
+ int n = (len > sizeof(rdbuf)) ? sizeof(rdbuf) : len;
+
+ eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
+ CONFIG_ENV_OFFSET + off, rdbuf, n);
+ new = crc32(new, rdbuf, n);
+ len -= n;
+ off += n;
+ }
+
+ if (crc == new) {
+ gd->env_valid = 1;
+ } else {
+ gd->env_valid = 0;
+ }
+#endif /* CONFIG_ENV_OFFSET_REDUND */
+
+ off = CONFIG_ENV_OFFSET;
+#ifdef CONFIG_ENV_OFFSET_REDUND
+ if (gd->env_valid == 2)
+ off = CONFIG_ENV_OFFSET_REDUND;
+#endif
+
+ eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
+ off, (uchar *)buf_env, CONFIG_ENV_SIZE);
+
+ env_import(buf_env, 1);
+}
+
+int saveenv(void)
+{
+ env_t env_new;
+ int rc;
+ unsigned int off = CONFIG_ENV_OFFSET;
+#ifdef CONFIG_ENV_OFFSET_REDUND
+ unsigned int off_red = CONFIG_ENV_OFFSET_REDUND;
+ char flag_obsolete = OBSOLETE_FLAG;
+#endif
+
+ BUG_ON(env_ptr != NULL);
+
+ rc = env_export(&env_new);
+ if (rc)
+ return rc;
+
+#ifdef CONFIG_ENV_OFFSET_REDUND
+ if (gd->env_valid == 1) {
+ off = CONFIG_ENV_OFFSET_REDUND;
+ off_red = CONFIG_ENV_OFFSET;
+ }
+
+ env_new.flags = ACTIVE_FLAG;
+#endif
+
+ rc = eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
+ off, (uchar *)&env_new, CONFIG_ENV_SIZE);
+
+#ifdef CONFIG_ENV_OFFSET_REDUND
+ if (rc == 0) {
+ eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
+ off_red + offsetof(env_t, flags),
+ (uchar *)&flag_obsolete, 1);
+
+ if (gd->env_valid == 1)
+ gd->env_valid = 2;
+ else
+ gd->env_valid = 1;
+ }
+#endif
+ return rc;
+}
+
+/*
+ * Initialize Environment use
+ *
+ * We are still running from ROM, so data use is limited.
+ * Use a (moderately small) buffer on the stack
+ */
+int env_init(void)
+{
+ gd->env_addr = (ulong)&default_environment[0];
+ gd->env_valid = 1;
+ return 0;
+}
diff --git a/env/embedded.c b/env/embedded.c
new file mode 100644
index 0000000000..43694db70f
--- /dev/null
+++ b/env/embedded.c
@@ -0,0 +1,97 @@
+/*
+ * (C) Copyright 2001
+ * Erik Theisen, Wave 7 Optics, etheisen@mindspring.com.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <linux/kconfig.h>
+
+#ifndef __ASSEMBLY__
+#define __ASSEMBLY__ /* Dirty trick to get only #defines */
+#endif
+#define __ASM_STUB_PROCESSOR_H__ /* don't include asm/processor. */
+#include <config.h>
+#undef __ASSEMBLY__
+#include <environment.h>
+#include <linux/stringify.h>
+
+/* Handle HOSTS that have prepended crap on symbol names, not TARGETS. */
+#if defined(__APPLE__)
+/* Leading underscore on symbols */
+# define SYM_CHAR "_"
+#else /* No leading character on symbols */
+# define SYM_CHAR
+#endif
+
+/*
+ * Generate embedded environment table
+ * inside U-Boot image, if needed.
+ */
+#if defined(ENV_IS_EMBEDDED) || defined(CONFIG_BUILD_ENVCRC)
+/*
+ * Put the environment in the .text section when we are building
+ * U-Boot proper. The host based program "tools/envcrc" does not need
+ * a seperate section.
+ */
+#if defined(USE_HOSTCC) /* Native for 'tools/envcrc' */
+# define __UBOOT_ENV_SECTION__ /*XXX DO_NOT_DEL_THIS_COMMENT*/
+
+#else /* Environment is embedded in U-Boot's .text section */
+/* XXX - This only works with GNU C */
+# define __UBOOT_ENV_SECTION__ __attribute__ ((section(".text")))
+#endif
+
+/*
+ * Macros to generate global absolutes.
+ */
+#if defined(__bfin__)
+# define GEN_SET_VALUE(name, value) \
+ asm(".set " GEN_SYMNAME(name) ", " GEN_VALUE(value))
+#else
+# define GEN_SET_VALUE(name, value) \
+ asm(GEN_SYMNAME(name) " = " GEN_VALUE(value))
+#endif
+#define GEN_SYMNAME(str) SYM_CHAR #str
+#define GEN_VALUE(str) #str
+#define GEN_ABS(name, value) \
+ asm(".globl " GEN_SYMNAME(name)); \
+ GEN_SET_VALUE(name, value)
+
+/*
+ * Check to see if we are building with a
+ * computed CRC. Otherwise define it as ~0.
+ */
+#if !defined(ENV_CRC)
+# define ENV_CRC (~0)
+#endif
+
+#define DEFAULT_ENV_INSTANCE_EMBEDDED
+#include <env_default.h>
+
+#ifdef CONFIG_ENV_ADDR_REDUND
+env_t redundand_environment __UBOOT_ENV_SECTION__ = {
+ 0, /* CRC Sum: invalid */
+ 0, /* Flags: invalid */
+ {
+ "\0"
+ }
+};
+#endif /* CONFIG_ENV_ADDR_REDUND */
+
+/*
+ * These will end up in the .text section
+ * if the environment strings are embedded
+ * in the image. When this is used for
+ * tools/envcrc, they are placed in the
+ * .data/.sdata section.
+ *
+ */
+unsigned long env_size __UBOOT_ENV_SECTION__ = sizeof(env_t);
+
+/*
+ * Add in absolutes.
+ */
+GEN_ABS(env_offset, CONFIG_ENV_OFFSET);
+
+#endif /* ENV_IS_EMBEDDED */
diff --git a/env/ext4.c b/env/ext4.c
new file mode 100644
index 0000000000..adefa7dc99
--- /dev/null
+++ b/env/ext4.c
@@ -0,0 +1,129 @@
+/*
+ * (c) Copyright 2016 by VRT Technology
+ *
+ * Author:
+ * Stuart Longland <stuartl@vrt.com.au>
+ *
+ * Based on FAT environment driver
+ * (c) Copyright 2011 by Tigris Elektronik GmbH
+ *
+ * Author:
+ * Maximilian Schwerin <mvs@tigris.de>
+ *
+ * and EXT4 filesystem implementation
+ * (C) Copyright 2011 - 2012 Samsung Electronics
+ * EXT4 filesystem implementation in Uboot by
+ * Uma Shankar <uma.shankar@samsung.com>
+ * Manjunatha C Achar <a.manjunatha@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+
+#include <command.h>
+#include <environment.h>
+#include <linux/stddef.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <search.h>
+#include <errno.h>
+#include <ext4fs.h>
+#include <mmc.h>
+
+char *env_name_spec = "EXT4";
+
+env_t *env_ptr;
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int env_init(void)
+{
+ /* use default */
+ gd->env_addr = (ulong)&default_environment[0];
+ gd->env_valid = 1;
+
+ return 0;
+}
+
+#ifdef CONFIG_CMD_SAVEENV
+int saveenv(void)
+{
+ env_t env_new;
+ struct blk_desc *dev_desc = NULL;
+ disk_partition_t info;
+ int dev, part;
+ int err;
+
+ err = env_export(&env_new);
+ if (err)
+ return err;
+
+ part = blk_get_device_part_str(EXT4_ENV_INTERFACE,
+ EXT4_ENV_DEVICE_AND_PART,
+ &dev_desc, &info, 1);
+ if (part < 0)
+ return 1;
+
+ dev = dev_desc->devnum;
+ ext4fs_set_blk_dev(dev_desc, &info);
+
+ if (!ext4fs_mount(info.size)) {
+ printf("\n** Unable to use %s %s for saveenv **\n",
+ EXT4_ENV_INTERFACE, EXT4_ENV_DEVICE_AND_PART);
+ return 1;
+ }
+
+ err = ext4fs_write(EXT4_ENV_FILE, (void *)&env_new, sizeof(env_t));
+ ext4fs_close();
+
+ if (err == -1) {
+ printf("\n** Unable to write \"%s\" from %s%d:%d **\n",
+ EXT4_ENV_FILE, EXT4_ENV_INTERFACE, dev, part);
+ return 1;
+ }
+
+ puts("done\n");
+ return 0;
+}
+#endif /* CONFIG_CMD_SAVEENV */
+
+void env_relocate_spec(void)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
+ struct blk_desc *dev_desc = NULL;
+ disk_partition_t info;
+ int dev, part;
+ int err;
+ loff_t off;
+
+ part = blk_get_device_part_str(EXT4_ENV_INTERFACE,
+ EXT4_ENV_DEVICE_AND_PART,
+ &dev_desc, &info, 1);
+ if (part < 0)
+ goto err_env_relocate;
+
+ dev = dev_desc->devnum;
+ ext4fs_set_blk_dev(dev_desc, &info);
+
+ if (!ext4fs_mount(info.size)) {
+ printf("\n** Unable to use %s %s for loading the env **\n",
+ EXT4_ENV_INTERFACE, EXT4_ENV_DEVICE_AND_PART);
+ goto err_env_relocate;
+ }
+
+ err = ext4_read_file(EXT4_ENV_FILE, buf, 0, CONFIG_ENV_SIZE, &off);
+ ext4fs_close();
+
+ if (err == -1) {
+ printf("\n** Unable to read \"%s\" from %s%d:%d **\n",
+ EXT4_ENV_FILE, EXT4_ENV_INTERFACE, dev, part);
+ goto err_env_relocate;
+ }
+
+ env_import(buf, 1);
+ return;
+
+err_env_relocate:
+ set_default_env(NULL);
+}
diff --git a/env/fat.c b/env/fat.c
new file mode 100644
index 0000000000..b51c920cd4
--- /dev/null
+++ b/env/fat.c
@@ -0,0 +1,110 @@
+/*
+ * (c) Copyright 2011 by Tigris Elektronik GmbH
+ *
+ * Author:
+ * Maximilian Schwerin <mvs@tigris.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+
+#include <command.h>
+#include <environment.h>
+#include <linux/stddef.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <search.h>
+#include <errno.h>
+#include <fat.h>
+#include <mmc.h>
+
+char *env_name_spec = "FAT";
+
+env_t *env_ptr;
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int env_init(void)
+{
+ /* use default */
+ gd->env_addr = (ulong)&default_environment[0];
+ gd->env_valid = 1;
+
+ return 0;
+}
+
+#ifdef CONFIG_CMD_SAVEENV
+int saveenv(void)
+{
+ env_t env_new;
+ struct blk_desc *dev_desc = NULL;
+ disk_partition_t info;
+ int dev, part;
+ int err;
+ loff_t size;
+
+ err = env_export(&env_new);
+ if (err)
+ return err;
+
+ part = blk_get_device_part_str(CONFIG_ENV_FAT_INTERFACE,
+ CONFIG_ENV_FAT_DEVICE_AND_PART,
+ &dev_desc, &info, 1);
+ if (part < 0)
+ return 1;
+
+ dev = dev_desc->devnum;
+ if (fat_set_blk_dev(dev_desc, &info) != 0) {
+ printf("\n** Unable to use %s %d:%d for saveenv **\n",
+ CONFIG_ENV_FAT_INTERFACE, dev, part);
+ return 1;
+ }
+
+ err = file_fat_write(CONFIG_ENV_FAT_FILE, (void *)&env_new, 0, sizeof(env_t),
+ &size);
+ if (err == -1) {
+ printf("\n** Unable to write \"%s\" from %s%d:%d **\n",
+ CONFIG_ENV_FAT_FILE, CONFIG_ENV_FAT_INTERFACE, dev, part);
+ return 1;
+ }
+
+ puts("done\n");
+ return 0;
+}
+#endif /* CONFIG_CMD_SAVEENV */
+
+void env_relocate_spec(void)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
+ struct blk_desc *dev_desc = NULL;
+ disk_partition_t info;
+ int dev, part;
+ int err;
+
+ part = blk_get_device_part_str(CONFIG_ENV_FAT_INTERFACE,
+ CONFIG_ENV_FAT_DEVICE_AND_PART,
+ &dev_desc, &info, 1);
+ if (part < 0)
+ goto err_env_relocate;
+
+ dev = dev_desc->devnum;
+ if (fat_set_blk_dev(dev_desc, &info) != 0) {
+ printf("\n** Unable to use %s %d:%d for loading the env **\n",
+ CONFIG_ENV_FAT_INTERFACE, dev, part);
+ goto err_env_relocate;
+ }
+
+ err = file_fat_read(CONFIG_ENV_FAT_FILE, buf, CONFIG_ENV_SIZE);
+ if (err == -1) {
+ printf("\n** Unable to read \"%s\" from %s%d:%d **\n",
+ CONFIG_ENV_FAT_FILE, CONFIG_ENV_FAT_INTERFACE, dev, part);
+ goto err_env_relocate;
+ }
+
+ env_import(buf, 1);
+ return;
+
+err_env_relocate:
+ set_default_env(NULL);
+}
diff --git a/env/flags.c b/env/flags.c
new file mode 100644
index 0000000000..3c50620cb3
--- /dev/null
+++ b/env/flags.c
@@ -0,0 +1,567 @@
+/*
+ * (C) Copyright 2012
+ * Joe Hershberger, National Instruments, joe.hershberger@ni.com
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <linux/string.h>
+#include <linux/ctype.h>
+
+#ifdef USE_HOSTCC /* Eliminate "ANSI does not permit..." warnings */
+#include <stdint.h>
+#include <stdio.h>
+#include "fw_env_private.h"
+#include "fw_env.h"
+#include <env_attr.h>
+#include <env_flags.h>
+#define getenv fw_getenv
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#else
+#include <common.h>
+#include <environment.h>
+#endif
+
+#ifdef CONFIG_CMD_NET
+#define ENV_FLAGS_NET_VARTYPE_REPS "im"
+#else
+#define ENV_FLAGS_NET_VARTYPE_REPS ""
+#endif
+
+static const char env_flags_vartype_rep[] = "sdxb" ENV_FLAGS_NET_VARTYPE_REPS;
+static const char env_flags_varaccess_rep[] = "aroc";
+static const int env_flags_varaccess_mask[] = {
+ 0,
+ ENV_FLAGS_VARACCESS_PREVENT_DELETE |
+ ENV_FLAGS_VARACCESS_PREVENT_CREATE |
+ ENV_FLAGS_VARACCESS_PREVENT_OVERWR,
+ ENV_FLAGS_VARACCESS_PREVENT_DELETE |
+ ENV_FLAGS_VARACCESS_PREVENT_OVERWR,
+ ENV_FLAGS_VARACCESS_PREVENT_DELETE |
+ ENV_FLAGS_VARACCESS_PREVENT_NONDEF_OVERWR};
+
+#ifdef CONFIG_CMD_ENV_FLAGS
+static const char * const env_flags_vartype_names[] = {
+ "string",
+ "decimal",
+ "hexadecimal",
+ "boolean",
+#ifdef CONFIG_CMD_NET
+ "IP address",
+ "MAC address",
+#endif
+};
+static const char * const env_flags_varaccess_names[] = {
+ "any",
+ "read-only",
+ "write-once",
+ "change-default",
+};
+
+/*
+ * Print the whole list of available type flags.
+ */
+void env_flags_print_vartypes(void)
+{
+ enum env_flags_vartype curtype = (enum env_flags_vartype)0;
+
+ while (curtype != env_flags_vartype_end) {
+ printf("\t%c -\t%s\n", env_flags_vartype_rep[curtype],
+ env_flags_vartype_names[curtype]);
+ curtype++;
+ }
+}
+
+/*
+ * Print the whole list of available access flags.
+ */
+void env_flags_print_varaccess(void)
+{
+ enum env_flags_varaccess curaccess = (enum env_flags_varaccess)0;
+
+ while (curaccess != env_flags_varaccess_end) {
+ printf("\t%c -\t%s\n", env_flags_varaccess_rep[curaccess],
+ env_flags_varaccess_names[curaccess]);
+ curaccess++;
+ }
+}
+
+/*
+ * Return the name of the type.
+ */
+const char *env_flags_get_vartype_name(enum env_flags_vartype type)
+{
+ return env_flags_vartype_names[type];
+}
+
+/*
+ * Return the name of the access.
+ */
+const char *env_flags_get_varaccess_name(enum env_flags_varaccess access)
+{
+ return env_flags_varaccess_names[access];
+}
+#endif /* CONFIG_CMD_ENV_FLAGS */
+
+/*
+ * Parse the flags string from a .flags attribute list into the vartype enum.
+ */
+enum env_flags_vartype env_flags_parse_vartype(const char *flags)
+{
+ char *type;
+
+ if (strlen(flags) <= ENV_FLAGS_VARTYPE_LOC)
+ return env_flags_vartype_string;
+
+ type = strchr(env_flags_vartype_rep,
+ flags[ENV_FLAGS_VARTYPE_LOC]);
+
+ if (type != NULL)
+ return (enum env_flags_vartype)
+ (type - &env_flags_vartype_rep[0]);
+
+ printf("## Warning: Unknown environment variable type '%c'\n",
+ flags[ENV_FLAGS_VARTYPE_LOC]);
+ return env_flags_vartype_string;
+}
+
+/*
+ * Parse the flags string from a .flags attribute list into the varaccess enum.
+ */
+enum env_flags_varaccess env_flags_parse_varaccess(const char *flags)
+{
+ char *access;
+
+ if (strlen(flags) <= ENV_FLAGS_VARACCESS_LOC)
+ return env_flags_varaccess_any;
+
+ access = strchr(env_flags_varaccess_rep,
+ flags[ENV_FLAGS_VARACCESS_LOC]);
+
+ if (access != NULL)
+ return (enum env_flags_varaccess)
+ (access - &env_flags_varaccess_rep[0]);
+
+ printf("## Warning: Unknown environment variable access method '%c'\n",
+ flags[ENV_FLAGS_VARACCESS_LOC]);
+ return env_flags_varaccess_any;
+}
+
+/*
+ * Parse the binary flags from a hash table entry into the varaccess enum.
+ */
+enum env_flags_varaccess env_flags_parse_varaccess_from_binflags(int binflags)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(env_flags_varaccess_mask); i++)
+ if (env_flags_varaccess_mask[i] ==
+ (binflags & ENV_FLAGS_VARACCESS_BIN_MASK))
+ return (enum env_flags_varaccess)i;
+
+ printf("Warning: Non-standard access flags. (0x%x)\n",
+ binflags & ENV_FLAGS_VARACCESS_BIN_MASK);
+
+ return env_flags_varaccess_any;
+}
+
+static inline int is_hex_prefix(const char *value)
+{
+ return value[0] == '0' && (value[1] == 'x' || value[1] == 'X');
+}
+
+static void skip_num(int hex, const char *value, const char **end,
+ int max_digits)
+{
+ int i;
+
+ if (hex && is_hex_prefix(value))
+ value += 2;
+
+ for (i = max_digits; i != 0; i--) {
+ if (hex && !isxdigit(*value))
+ break;
+ if (!hex && !isdigit(*value))
+ break;
+ value++;
+ }
+ if (end != NULL)
+ *end = value;
+}
+
+#ifdef CONFIG_CMD_NET
+int eth_validate_ethaddr_str(const char *addr)
+{
+ const char *end;
+ const char *cur;
+ int i;
+
+ cur = addr;
+ for (i = 0; i < 6; i++) {
+ skip_num(1, cur, &end, 2);
+ if (cur == end)
+ return -1;
+ if (cur + 2 == end && is_hex_prefix(cur))
+ return -1;
+ if (i != 5 && *end != ':')
+ return -1;
+ if (i == 5 && *end != '\0')
+ return -1;
+ cur = end + 1;
+ }
+
+ return 0;
+}
+#endif
+
+/*
+ * Based on the declared type enum, validate that the value string complies
+ * with that format
+ */
+static int _env_flags_validate_type(const char *value,
+ enum env_flags_vartype type)
+{
+ const char *end;
+#ifdef CONFIG_CMD_NET
+ const char *cur;
+ int i;
+#endif
+
+ switch (type) {
+ case env_flags_vartype_string:
+ break;
+ case env_flags_vartype_decimal:
+ skip_num(0, value, &end, -1);
+ if (*end != '\0')
+ return -1;
+ break;
+ case env_flags_vartype_hex:
+ skip_num(1, value, &end, -1);
+ if (*end != '\0')
+ return -1;
+ if (value + 2 == end && is_hex_prefix(value))
+ return -1;
+ break;
+ case env_flags_vartype_bool:
+ if (value[0] != '1' && value[0] != 'y' && value[0] != 't' &&
+ value[0] != 'Y' && value[0] != 'T' &&
+ value[0] != '0' && value[0] != 'n' && value[0] != 'f' &&
+ value[0] != 'N' && value[0] != 'F')
+ return -1;
+ if (value[1] != '\0')
+ return -1;
+ break;
+#ifdef CONFIG_CMD_NET
+ case env_flags_vartype_ipaddr:
+ cur = value;
+ for (i = 0; i < 4; i++) {
+ skip_num(0, cur, &end, 3);
+ if (cur == end)
+ return -1;
+ if (i != 3 && *end != '.')
+ return -1;
+ if (i == 3 && *end != '\0')
+ return -1;
+ cur = end + 1;
+ }
+ break;
+ case env_flags_vartype_macaddr:
+ if (eth_validate_ethaddr_str(value))
+ return -1;
+ break;
+#endif
+ case env_flags_vartype_end:
+ return -1;
+ }
+
+ /* OK */
+ return 0;
+}
+
+/*
+ * Look for flags in a provided list and failing that the static list
+ */
+static inline int env_flags_lookup(const char *flags_list, const char *name,
+ char *flags)
+{
+ int ret = 1;
+
+ if (!flags)
+ /* bad parameter */
+ return -1;
+
+ /* try the env first */
+ if (flags_list)
+ ret = env_attr_lookup(flags_list, name, flags);
+
+ if (ret != 0)
+ /* if not found in the env, look in the static list */
+ ret = env_attr_lookup(ENV_FLAGS_LIST_STATIC, name, flags);
+
+ return ret;
+}
+
+#ifdef USE_HOSTCC /* Functions only used from tools/env */
+/*
+ * Look up any flags directly from the .flags variable and the static list
+ * and convert them to the vartype enum.
+ */
+enum env_flags_vartype env_flags_get_type(const char *name)
+{
+ const char *flags_list = getenv(ENV_FLAGS_VAR);
+ char flags[ENV_FLAGS_ATTR_MAX_LEN + 1];
+
+ if (env_flags_lookup(flags_list, name, flags))
+ return env_flags_vartype_string;
+
+ if (strlen(flags) <= ENV_FLAGS_VARTYPE_LOC)
+ return env_flags_vartype_string;
+
+ return env_flags_parse_vartype(flags);
+}
+
+/*
+ * Look up the access of a variable directly from the .flags var.
+ */
+enum env_flags_varaccess env_flags_get_varaccess(const char *name)
+{
+ const char *flags_list = getenv(ENV_FLAGS_VAR);
+ char flags[ENV_FLAGS_ATTR_MAX_LEN + 1];
+
+ if (env_flags_lookup(flags_list, name, flags))
+ return env_flags_varaccess_any;
+
+ if (strlen(flags) <= ENV_FLAGS_VARACCESS_LOC)
+ return env_flags_varaccess_any;
+
+ return env_flags_parse_varaccess(flags);
+}
+
+/*
+ * Validate that the proposed new value for "name" is valid according to the
+ * defined flags for that variable, if any.
+ */
+int env_flags_validate_type(const char *name, const char *value)
+{
+ enum env_flags_vartype type;
+
+ if (value == NULL)
+ return 0;
+ type = env_flags_get_type(name);
+ if (_env_flags_validate_type(value, type) < 0) {
+ printf("## Error: flags type check failure for "
+ "\"%s\" <= \"%s\" (type: %c)\n",
+ name, value, env_flags_vartype_rep[type]);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Validate that the proposed access to variable "name" is valid according to
+ * the defined flags for that variable, if any.
+ */
+int env_flags_validate_varaccess(const char *name, int check_mask)
+{
+ enum env_flags_varaccess access;
+ int access_mask;
+
+ access = env_flags_get_varaccess(name);
+ access_mask = env_flags_varaccess_mask[access];
+
+ return (check_mask & access_mask) != 0;
+}
+
+/*
+ * Validate the parameters to "env set" directly
+ */
+int env_flags_validate_env_set_params(char *name, char * const val[], int count)
+{
+ if ((count >= 1) && val[0] != NULL) {
+ enum env_flags_vartype type = env_flags_get_type(name);
+
+ /*
+ * we don't currently check types that need more than
+ * one argument
+ */
+ if (type != env_flags_vartype_string && count > 1) {
+ printf("## Error: too many parameters for setting \"%s\"\n",
+ name);
+ return -1;
+ }
+ return env_flags_validate_type(name, val[0]);
+ }
+ /* ok */
+ return 0;
+}
+
+#else /* !USE_HOSTCC - Functions only used from lib/hashtable.c */
+
+/*
+ * Parse the flag charachters from the .flags attribute list into the binary
+ * form to be stored in the environment entry->flags field.
+ */
+static int env_parse_flags_to_bin(const char *flags)
+{
+ int binflags;
+
+ binflags = env_flags_parse_vartype(flags) & ENV_FLAGS_VARTYPE_BIN_MASK;
+ binflags |= env_flags_varaccess_mask[env_flags_parse_varaccess(flags)];
+
+ return binflags;
+}
+
+static int first_call = 1;
+static const char *flags_list;
+
+/*
+ * Look for possible flags for a newly added variable
+ * This is called specifically when the variable did not exist in the hash
+ * previously, so the blanket update did not find this variable.
+ */
+void env_flags_init(ENTRY *var_entry)
+{
+ const char *var_name = var_entry->key;
+ char flags[ENV_FLAGS_ATTR_MAX_LEN + 1] = "";
+ int ret = 1;
+
+ if (first_call) {
+ flags_list = getenv(ENV_FLAGS_VAR);
+ first_call = 0;
+ }
+ /* look in the ".flags" and static for a reference to this variable */
+ ret = env_flags_lookup(flags_list, var_name, flags);
+
+ /* if any flags were found, set the binary form to the entry */
+ if (!ret && strlen(flags))
+ var_entry->flags = env_parse_flags_to_bin(flags);
+}
+
+/*
+ * Called on each existing env var prior to the blanket update since removing
+ * a flag in the flag list should remove its flags.
+ */
+static int clear_flags(ENTRY *entry)
+{
+ entry->flags = 0;
+
+ return 0;
+}
+
+/*
+ * Call for each element in the list that defines flags for a variable
+ */
+static int set_flags(const char *name, const char *value, void *priv)
+{
+ ENTRY e, *ep;
+
+ e.key = name;
+ e.data = NULL;
+ e.callback = NULL;
+ hsearch_r(e, FIND, &ep, &env_htab, 0);
+
+ /* does the env variable actually exist? */
+ if (ep != NULL) {
+ /* the flag list is empty, so clear the flags */
+ if (value == NULL || strlen(value) == 0)
+ ep->flags = 0;
+ else
+ /* assign the requested flags */
+ ep->flags = env_parse_flags_to_bin(value);
+ }
+
+ return 0;
+}
+
+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);
+
+ /* 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);
+
+ return 0;
+}
+U_BOOT_ENV_CALLBACK(flags, on_flags);
+
+/*
+ * Perform consistency checking before creating, overwriting, or deleting an
+ * environment variable. Called as a callback function by hsearch_r() and
+ * hdelete_r(). Returns 0 in case of success, 1 in case of failure.
+ * When (flag & H_FORCE) is set, do not print out any error message and force
+ * overwriting of write-once variables.
+ */
+
+int env_flags_validate(const ENTRY *item, const char *newval, enum env_op op,
+ int flag)
+{
+ const char *name;
+ const char *oldval = NULL;
+
+ if (op != env_op_create)
+ oldval = item->data;
+
+ name = item->key;
+
+ /* Default value for NULL to protect string-manipulating functions */
+ newval = newval ? : "";
+
+ /* validate the value to match the variable type */
+ if (op != env_op_delete) {
+ enum env_flags_vartype type = (enum env_flags_vartype)
+ (ENV_FLAGS_VARTYPE_BIN_MASK & item->flags);
+
+ if (_env_flags_validate_type(newval, type) < 0) {
+ printf("## Error: flags type check failure for "
+ "\"%s\" <= \"%s\" (type: %c)\n",
+ name, newval, env_flags_vartype_rep[type]);
+ return -1;
+ }
+ }
+
+ /* check for access permission */
+#ifndef CONFIG_ENV_ACCESS_IGNORE_FORCE
+ if (flag & H_FORCE)
+ return 0;
+#endif
+ switch (op) {
+ case env_op_delete:
+ if (item->flags & ENV_FLAGS_VARACCESS_PREVENT_DELETE) {
+ printf("## Error: Can't delete \"%s\"\n", name);
+ return 1;
+ }
+ break;
+ case env_op_overwrite:
+ if (item->flags & ENV_FLAGS_VARACCESS_PREVENT_OVERWR) {
+ printf("## Error: Can't overwrite \"%s\"\n", name);
+ return 1;
+ } else if (item->flags &
+ ENV_FLAGS_VARACCESS_PREVENT_NONDEF_OVERWR) {
+ const char *defval = getenv_default(name);
+
+ if (defval == NULL)
+ defval = "";
+ printf("oldval: %s defval: %s\n", oldval, defval);
+ if (strcmp(oldval, defval) != 0) {
+ printf("## Error: Can't overwrite \"%s\"\n",
+ name);
+ return 1;
+ }
+ }
+ break;
+ case env_op_create:
+ if (item->flags & ENV_FLAGS_VARACCESS_PREVENT_CREATE) {
+ printf("## Error: Can't create \"%s\"\n", name);
+ return 1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/env/flash.c b/env/flash.c
new file mode 100644
index 0000000000..004e8849a7
--- /dev/null
+++ b/env/flash.c
@@ -0,0 +1,338 @@
+/*
+ * (C) Copyright 2000-2010
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Andreas Heppel <aheppel@sysgo.de>
+
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/* #define DEBUG */
+
+#include <common.h>
+#include <command.h>
+#include <environment.h>
+#include <linux/stddef.h>
+#include <malloc.h>
+#include <search.h>
+#include <errno.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#if defined(CONFIG_CMD_SAVEENV) && defined(CONFIG_CMD_FLASH)
+#define CMD_SAVEENV
+#elif defined(CONFIG_ENV_ADDR_REDUND)
+#error CONFIG_ENV_ADDR_REDUND must have CONFIG_CMD_SAVEENV & CONFIG_CMD_FLASH
+#endif
+
+#if defined(CONFIG_ENV_SIZE_REDUND) && \
+ (CONFIG_ENV_SIZE_REDUND < CONFIG_ENV_SIZE)
+#error CONFIG_ENV_SIZE_REDUND should not be less then CONFIG_ENV_SIZE
+#endif
+
+char *env_name_spec = "Flash";
+
+#ifdef ENV_IS_EMBEDDED
+env_t *env_ptr = &environment;
+
+static env_t *flash_addr = (env_t *)CONFIG_ENV_ADDR;
+
+#else /* ! ENV_IS_EMBEDDED */
+
+env_t *env_ptr = (env_t *)CONFIG_ENV_ADDR;
+static env_t *flash_addr = (env_t *)CONFIG_ENV_ADDR;
+#endif /* ENV_IS_EMBEDDED */
+
+#if defined(CMD_SAVEENV) || defined(CONFIG_ENV_ADDR_REDUND)
+/* CONFIG_ENV_ADDR is supposed to be on sector boundary */
+static ulong end_addr = CONFIG_ENV_ADDR + CONFIG_ENV_SECT_SIZE - 1;
+#endif
+
+#ifdef CONFIG_ENV_ADDR_REDUND
+static env_t *flash_addr_new = (env_t *)CONFIG_ENV_ADDR_REDUND;
+
+/* CONFIG_ENV_ADDR_REDUND is supposed to be on sector boundary */
+static ulong end_addr_new = CONFIG_ENV_ADDR_REDUND + CONFIG_ENV_SECT_SIZE - 1;
+#endif /* CONFIG_ENV_ADDR_REDUND */
+
+
+#ifdef CONFIG_ENV_ADDR_REDUND
+int env_init(void)
+{
+ int crc1_ok = 0, crc2_ok = 0;
+
+ uchar flag1 = flash_addr->flags;
+ uchar flag2 = flash_addr_new->flags;
+
+ ulong addr_default = (ulong)&default_environment[0];
+ ulong addr1 = (ulong)&(flash_addr->data);
+ ulong addr2 = (ulong)&(flash_addr_new->data);
+
+ crc1_ok = crc32(0, flash_addr->data, ENV_SIZE) == flash_addr->crc;
+ crc2_ok =
+ crc32(0, flash_addr_new->data, ENV_SIZE) == flash_addr_new->crc;
+
+ if (crc1_ok && !crc2_ok) {
+ gd->env_addr = addr1;
+ gd->env_valid = 1;
+ } else if (!crc1_ok && crc2_ok) {
+ gd->env_addr = addr2;
+ gd->env_valid = 1;
+ } else if (!crc1_ok && !crc2_ok) {
+ gd->env_addr = addr_default;
+ gd->env_valid = 0;
+ } else if (flag1 == ACTIVE_FLAG && flag2 == OBSOLETE_FLAG) {
+ gd->env_addr = addr1;
+ gd->env_valid = 1;
+ } else if (flag1 == OBSOLETE_FLAG && flag2 == ACTIVE_FLAG) {
+ gd->env_addr = addr2;
+ gd->env_valid = 1;
+ } else if (flag1 == flag2) {
+ gd->env_addr = addr1;
+ gd->env_valid = 2;
+ } else if (flag1 == 0xFF) {
+ gd->env_addr = addr1;
+ gd->env_valid = 2;
+ } else if (flag2 == 0xFF) {
+ gd->env_addr = addr2;
+ gd->env_valid = 2;
+ }
+
+ return 0;
+}
+
+#ifdef CMD_SAVEENV
+int saveenv(void)
+{
+ env_t env_new;
+ char *saved_data = NULL;
+ char flag = OBSOLETE_FLAG, new_flag = ACTIVE_FLAG;
+ int rc = 1;
+#if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE
+ ulong up_data = 0;
+#endif
+
+ debug("Protect off %08lX ... %08lX\n", (ulong)flash_addr, end_addr);
+
+ if (flash_sect_protect(0, (ulong)flash_addr, end_addr))
+ goto done;
+
+ debug("Protect off %08lX ... %08lX\n",
+ (ulong)flash_addr_new, end_addr_new);
+
+ if (flash_sect_protect(0, (ulong)flash_addr_new, end_addr_new))
+ goto done;
+
+ rc = env_export(&env_new);
+ if (rc)
+ return rc;
+ env_new.flags = new_flag;
+
+#if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE
+ up_data = end_addr_new + 1 - ((long)flash_addr_new + CONFIG_ENV_SIZE);
+ debug("Data to save 0x%lX\n", up_data);
+ if (up_data) {
+ saved_data = malloc(up_data);
+ if (saved_data == NULL) {
+ printf("Unable to save the rest of sector (%ld)\n",
+ up_data);
+ goto done;
+ }
+ memcpy(saved_data,
+ (void *)((long)flash_addr_new + CONFIG_ENV_SIZE),
+ up_data);
+ debug("Data (start 0x%lX, len 0x%lX) saved at 0x%p\n",
+ (long)flash_addr_new + CONFIG_ENV_SIZE,
+ up_data, saved_data);
+ }
+#endif
+ puts("Erasing Flash...");
+ debug(" %08lX ... %08lX ...", (ulong)flash_addr_new, end_addr_new);
+
+ if (flash_sect_erase((ulong)flash_addr_new, end_addr_new))
+ goto done;
+
+ puts("Writing to Flash... ");
+ debug(" %08lX ... %08lX ...",
+ (ulong)&(flash_addr_new->data),
+ sizeof(env_ptr->data) + (ulong)&(flash_addr_new->data));
+ rc = flash_write((char *)&env_new, (ulong)flash_addr_new,
+ sizeof(env_new));
+ if (rc)
+ goto perror;
+
+ rc = flash_write(&flag, (ulong)&(flash_addr->flags),
+ sizeof(flash_addr->flags));
+ if (rc)
+ goto perror;
+
+#if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE
+ if (up_data) { /* restore the rest of sector */
+ debug("Restoring the rest of data to 0x%lX len 0x%lX\n",
+ (long)flash_addr_new + CONFIG_ENV_SIZE, up_data);
+ if (flash_write(saved_data,
+ (long)flash_addr_new + CONFIG_ENV_SIZE,
+ up_data))
+ goto perror;
+ }
+#endif
+ puts("done\n");
+
+ {
+ env_t *etmp = flash_addr;
+ ulong ltmp = end_addr;
+
+ flash_addr = flash_addr_new;
+ flash_addr_new = etmp;
+
+ end_addr = end_addr_new;
+ end_addr_new = ltmp;
+ }
+
+ rc = 0;
+ goto done;
+perror:
+ flash_perror(rc);
+done:
+ if (saved_data)
+ free(saved_data);
+ /* try to re-protect */
+ flash_sect_protect(1, (ulong)flash_addr, end_addr);
+ flash_sect_protect(1, (ulong)flash_addr_new, end_addr_new);
+
+ return rc;
+}
+#endif /* CMD_SAVEENV */
+
+#else /* ! CONFIG_ENV_ADDR_REDUND */
+
+int env_init(void)
+{
+ if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) {
+ gd->env_addr = (ulong)&(env_ptr->data);
+ gd->env_valid = 1;
+ return 0;
+ }
+
+ gd->env_addr = (ulong)&default_environment[0];
+ gd->env_valid = 0;
+ return 0;
+}
+
+#ifdef CMD_SAVEENV
+int saveenv(void)
+{
+ env_t env_new;
+ int rc = 1;
+ char *saved_data = NULL;
+#if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE
+ ulong up_data = 0;
+
+ up_data = end_addr + 1 - ((long)flash_addr + CONFIG_ENV_SIZE);
+ debug("Data to save 0x%lx\n", up_data);
+ if (up_data) {
+ saved_data = malloc(up_data);
+ if (saved_data == NULL) {
+ printf("Unable to save the rest of sector (%ld)\n",
+ up_data);
+ goto done;
+ }
+ memcpy(saved_data,
+ (void *)((long)flash_addr + CONFIG_ENV_SIZE), up_data);
+ debug("Data (start 0x%lx, len 0x%lx) saved at 0x%lx\n",
+ (ulong)flash_addr + CONFIG_ENV_SIZE,
+ up_data,
+ (ulong)saved_data);
+ }
+#endif /* CONFIG_ENV_SECT_SIZE */
+
+ debug("Protect off %08lX ... %08lX\n", (ulong)flash_addr, end_addr);
+
+ if (flash_sect_protect(0, (long)flash_addr, end_addr))
+ goto done;
+
+ rc = env_export(&env_new);
+ if (rc)
+ goto done;
+
+ puts("Erasing Flash...");
+ if (flash_sect_erase((long)flash_addr, end_addr))
+ goto done;
+
+ puts("Writing to Flash... ");
+ rc = flash_write((char *)&env_new, (long)flash_addr, CONFIG_ENV_SIZE);
+ if (rc != 0)
+ goto perror;
+
+#if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE
+ if (up_data) { /* restore the rest of sector */
+ debug("Restoring the rest of data to 0x%lx len 0x%lx\n",
+ (ulong)flash_addr + CONFIG_ENV_SIZE, up_data);
+ if (flash_write(saved_data,
+ (long)flash_addr + CONFIG_ENV_SIZE,
+ up_data))
+ goto perror;
+ }
+#endif
+ puts("done\n");
+ rc = 0;
+ goto done;
+perror:
+ flash_perror(rc);
+done:
+ if (saved_data)
+ free(saved_data);
+ /* try to re-protect */
+ flash_sect_protect(1, (long)flash_addr, end_addr);
+ return rc;
+}
+#endif /* CMD_SAVEENV */
+
+#endif /* CONFIG_ENV_ADDR_REDUND */
+
+void env_relocate_spec(void)
+{
+#ifdef CONFIG_ENV_ADDR_REDUND
+ if (gd->env_addr != (ulong)&(flash_addr->data)) {
+ env_t *etmp = flash_addr;
+ ulong ltmp = end_addr;
+
+ flash_addr = flash_addr_new;
+ flash_addr_new = etmp;
+
+ end_addr = end_addr_new;
+ end_addr_new = ltmp;
+ }
+
+ if (flash_addr_new->flags != OBSOLETE_FLAG &&
+ crc32(0, flash_addr_new->data, ENV_SIZE) == flash_addr_new->crc) {
+ char flag = OBSOLETE_FLAG;
+
+ gd->env_valid = 2;
+ flash_sect_protect(0, (ulong)flash_addr_new, end_addr_new);
+ flash_write(&flag,
+ (ulong)&(flash_addr_new->flags),
+ sizeof(flash_addr_new->flags));
+ flash_sect_protect(1, (ulong)flash_addr_new, end_addr_new);
+ }
+
+ if (flash_addr->flags != ACTIVE_FLAG &&
+ (flash_addr->flags & ACTIVE_FLAG) == ACTIVE_FLAG) {
+ char flag = ACTIVE_FLAG;
+
+ gd->env_valid = 2;
+ flash_sect_protect(0, (ulong)flash_addr, end_addr);
+ flash_write(&flag,
+ (ulong)&(flash_addr->flags),
+ sizeof(flash_addr->flags));
+ flash_sect_protect(1, (ulong)flash_addr, end_addr);
+ }
+
+ if (gd->env_valid == 2)
+ puts("*** Warning - some problems detected "
+ "reading environment; recovered successfully\n\n");
+#endif /* CONFIG_ENV_ADDR_REDUND */
+
+ env_import((char *)flash_addr, 1);
+}
diff --git a/env/mmc.c b/env/mmc.c
new file mode 100644
index 0000000000..bb760a00ed
--- /dev/null
+++ b/env/mmc.c
@@ -0,0 +1,326 @@
+/*
+ * (C) Copyright 2008-2011 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/* #define DEBUG */
+
+#include <common.h>
+
+#include <command.h>
+#include <environment.h>
+#include <fdtdec.h>
+#include <linux/stddef.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <mmc.h>
+#include <search.h>
+#include <errno.h>
+
+#if defined(CONFIG_ENV_SIZE_REDUND) && \
+ (CONFIG_ENV_SIZE_REDUND != CONFIG_ENV_SIZE)
+#error CONFIG_ENV_SIZE_REDUND should be the same as CONFIG_ENV_SIZE
+#endif
+
+char *env_name_spec = "MMC";
+
+#ifdef ENV_IS_EMBEDDED
+env_t *env_ptr = &environment;
+#else /* ! ENV_IS_EMBEDDED */
+env_t *env_ptr;
+#endif /* ENV_IS_EMBEDDED */
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#if !defined(CONFIG_ENV_OFFSET)
+#define CONFIG_ENV_OFFSET 0
+#endif
+
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+static inline s64 mmc_offset(int copy)
+{
+ const char *propname = "u-boot,mmc-env-offset";
+ s64 defvalue = CONFIG_ENV_OFFSET;
+
+#if defined(CONFIG_ENV_OFFSET_REDUND)
+ if (copy) {
+ propname = "u-boot,mmc-env-offset-redundant";
+ defvalue = CONFIG_ENV_OFFSET_REDUND;
+ }
+#endif
+
+ return fdtdec_get_config_int(gd->fdt_blob, propname, defvalue);
+}
+#else
+static inline s64 mmc_offset(int copy)
+{
+ s64 offset = CONFIG_ENV_OFFSET;
+
+#if defined(CONFIG_ENV_OFFSET_REDUND)
+ if (copy)
+ offset = CONFIG_ENV_OFFSET_REDUND;
+#endif
+ return offset;
+}
+#endif
+
+__weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr)
+{
+ s64 offset = mmc_offset(copy);
+
+ if (offset < 0)
+ offset += mmc->capacity;
+
+ *env_addr = offset;
+
+ return 0;
+}
+
+__weak int mmc_get_env_dev(void)
+{
+ return CONFIG_SYS_MMC_ENV_DEV;
+}
+
+int env_init(void)
+{
+ /* use default */
+ gd->env_addr = (ulong)&default_environment[0];
+ gd->env_valid = 1;
+
+ return 0;
+}
+
+#ifdef CONFIG_SYS_MMC_ENV_PART
+__weak uint mmc_get_env_part(struct mmc *mmc)
+{
+ return CONFIG_SYS_MMC_ENV_PART;
+}
+
+static unsigned char env_mmc_orig_hwpart;
+
+static int mmc_set_env_part(struct mmc *mmc)
+{
+ uint part = mmc_get_env_part(mmc);
+ int dev = mmc_get_env_dev();
+ int ret = 0;
+
+ env_mmc_orig_hwpart = mmc_get_blk_desc(mmc)->hwpart;
+ ret = blk_select_hwpart_devnum(IF_TYPE_MMC, dev, part);
+ if (ret)
+ puts("MMC partition switch failed\n");
+
+ return ret;
+}
+#else
+static inline int mmc_set_env_part(struct mmc *mmc) {return 0; };
+#endif
+
+static const char *init_mmc_for_env(struct mmc *mmc)
+{
+ if (!mmc)
+ return "!No MMC card found";
+
+#ifdef CONFIG_BLK
+ struct udevice *dev;
+
+ if (blk_get_from_parent(mmc->dev, &dev))
+ return "!No block device";
+#else
+ if (mmc_init(mmc))
+ return "!MMC init failed";
+#endif
+ if (mmc_set_env_part(mmc))
+ return "!MMC partition switch failed";
+
+ return NULL;
+}
+
+static void fini_mmc_for_env(struct mmc *mmc)
+{
+#ifdef CONFIG_SYS_MMC_ENV_PART
+ int dev = mmc_get_env_dev();
+
+ blk_select_hwpart_devnum(IF_TYPE_MMC, dev, env_mmc_orig_hwpart);
+#endif
+}
+
+#ifdef CONFIG_CMD_SAVEENV
+static inline int write_env(struct mmc *mmc, unsigned long size,
+ unsigned long offset, const void *buffer)
+{
+ uint blk_start, blk_cnt, n;
+ struct blk_desc *desc = mmc_get_blk_desc(mmc);
+
+ blk_start = ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len;
+ blk_cnt = ALIGN(size, mmc->write_bl_len) / mmc->write_bl_len;
+
+ n = blk_dwrite(desc, blk_start, blk_cnt, (u_char *)buffer);
+
+ return (n == blk_cnt) ? 0 : -1;
+}
+
+int saveenv(void)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
+ int dev = mmc_get_env_dev();
+ struct mmc *mmc = find_mmc_device(dev);
+ u32 offset;
+ int ret, copy = 0;
+ const char *errmsg;
+
+ errmsg = init_mmc_for_env(mmc);
+ if (errmsg) {
+ printf("%s\n", errmsg);
+ return 1;
+ }
+
+ ret = env_export(env_new);
+ if (ret)
+ goto fini;
+
+#ifdef CONFIG_ENV_OFFSET_REDUND
+ if (gd->env_valid == 1)
+ copy = 1;
+#endif
+
+ if (mmc_get_env_addr(mmc, copy, &offset)) {
+ ret = 1;
+ goto fini;
+ }
+
+ printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "", dev);
+ if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
+ puts("failed\n");
+ ret = 1;
+ goto fini;
+ }
+
+ puts("done\n");
+ ret = 0;
+
+#ifdef CONFIG_ENV_OFFSET_REDUND
+ gd->env_valid = gd->env_valid == 2 ? 1 : 2;
+#endif
+
+fini:
+ fini_mmc_for_env(mmc);
+ return ret;
+}
+#endif /* CONFIG_CMD_SAVEENV */
+
+static inline int read_env(struct mmc *mmc, unsigned long size,
+ unsigned long offset, const void *buffer)
+{
+ uint blk_start, blk_cnt, n;
+ struct blk_desc *desc = mmc_get_blk_desc(mmc);
+
+ blk_start = ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len;
+ blk_cnt = ALIGN(size, mmc->read_bl_len) / mmc->read_bl_len;
+
+ n = blk_dread(desc, blk_start, blk_cnt, (uchar *)buffer);
+
+ return (n == blk_cnt) ? 0 : -1;
+}
+
+#ifdef CONFIG_ENV_OFFSET_REDUND
+void env_relocate_spec(void)
+{
+#if !defined(ENV_IS_EMBEDDED)
+ struct mmc *mmc;
+ u32 offset1, offset2;
+ int read1_fail = 0, read2_fail = 0;
+ int ret;
+ int dev = mmc_get_env_dev();
+ const char *errmsg = NULL;
+
+ ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1);
+ ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1);
+
+ mmc = find_mmc_device(dev);
+
+ errmsg = init_mmc_for_env(mmc);
+ if (errmsg) {
+ ret = 1;
+ goto err;
+ }
+
+ if (mmc_get_env_addr(mmc, 0, &offset1) ||
+ mmc_get_env_addr(mmc, 1, &offset2)) {
+ ret = 1;
+ goto fini;
+ }
+
+ read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1);
+ read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2);
+
+ if (read1_fail && read2_fail)
+ puts("*** Error - No Valid Environment Area found\n");
+ else if (read1_fail || read2_fail)
+ puts("*** Warning - some problems detected "
+ "reading environment; recovered successfully\n");
+
+ if (read1_fail && read2_fail) {
+ errmsg = "!bad CRC";
+ ret = 1;
+ goto fini;
+ } else if (!read1_fail && read2_fail) {
+ gd->env_valid = 1;
+ env_import((char *)tmp_env1, 1);
+ } else if (read1_fail && !read2_fail) {
+ gd->env_valid = 2;
+ env_import((char *)tmp_env2, 1);
+ } else {
+ env_import_redund((char *)tmp_env1, (char *)tmp_env2);
+ }
+
+ ret = 0;
+
+fini:
+ fini_mmc_for_env(mmc);
+err:
+ if (ret)
+ set_default_env(errmsg);
+#endif
+}
+#else /* ! CONFIG_ENV_OFFSET_REDUND */
+void env_relocate_spec(void)
+{
+#if !defined(ENV_IS_EMBEDDED)
+ ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
+ struct mmc *mmc;
+ u32 offset;
+ int ret;
+ int dev = mmc_get_env_dev();
+ const char *errmsg;
+
+ mmc = find_mmc_device(dev);
+
+ errmsg = init_mmc_for_env(mmc);
+ if (errmsg) {
+ ret = 1;
+ goto err;
+ }
+
+ if (mmc_get_env_addr(mmc, 0, &offset)) {
+ ret = 1;
+ goto fini;
+ }
+
+ if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) {
+ errmsg = "!read failed";
+ ret = 1;
+ goto fini;
+ }
+
+ env_import(buf, 1);
+ ret = 0;
+
+fini:
+ fini_mmc_for_env(mmc);
+err:
+ if (ret)
+ set_default_env(errmsg);
+#endif
+}
+#endif /* CONFIG_ENV_OFFSET_REDUND */
diff --git a/env/nand.c b/env/nand.c
new file mode 100644
index 0000000000..760f6859e3
--- /dev/null
+++ b/env/nand.c
@@ -0,0 +1,395 @@
+/*
+ * (C) Copyright 2000-2010
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * (C) Copyright 2008
+ * Stuart Wood, Lab X Technologies <stuart.wood@labxtechnologies.com>
+ *
+ * (C) Copyright 2004
+ * Jian Zhang, Texas Instruments, jzhang@ti.com.
+ *
+ * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Andreas Heppel <aheppel@sysgo.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <command.h>
+#include <environment.h>
+#include <linux/stddef.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <nand.h>
+#include <search.h>
+#include <errno.h>
+
+#if defined(CONFIG_CMD_SAVEENV) && defined(CONFIG_CMD_NAND)
+#define CMD_SAVEENV
+#elif defined(CONFIG_ENV_OFFSET_REDUND)
+#error CONFIG_ENV_OFFSET_REDUND must have CONFIG_CMD_SAVEENV & CONFIG_CMD_NAND
+#endif
+
+#if defined(CONFIG_ENV_SIZE_REDUND) && \
+ (CONFIG_ENV_SIZE_REDUND != CONFIG_ENV_SIZE)
+#error CONFIG_ENV_SIZE_REDUND should be the same as CONFIG_ENV_SIZE
+#endif
+
+#ifndef CONFIG_ENV_RANGE
+#define CONFIG_ENV_RANGE CONFIG_ENV_SIZE
+#endif
+
+char *env_name_spec = "NAND";
+
+#if defined(ENV_IS_EMBEDDED)
+env_t *env_ptr = &environment;
+#elif defined(CONFIG_NAND_ENV_DST)
+env_t *env_ptr = (env_t *)CONFIG_NAND_ENV_DST;
+#else /* ! ENV_IS_EMBEDDED */
+env_t *env_ptr;
+#endif /* ENV_IS_EMBEDDED */
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/*
+ * This is called before nand_init() so we can't read NAND to
+ * validate env data.
+ *
+ * Mark it OK for now. env_relocate() in env_common.c will call our
+ * relocate function which does the real validation.
+ *
+ * When using a NAND boot image (like sequoia_nand), the environment
+ * can be embedded or attached to the U-Boot image in NAND flash.
+ * This way the SPL loads not only the U-Boot image from NAND but
+ * also the environment.
+ */
+int env_init(void)
+{
+#if defined(ENV_IS_EMBEDDED) || defined(CONFIG_NAND_ENV_DST)
+ int crc1_ok = 0, crc2_ok = 0;
+ env_t *tmp_env1;
+
+#ifdef CONFIG_ENV_OFFSET_REDUND
+ env_t *tmp_env2;
+
+ tmp_env2 = (env_t *)((ulong)env_ptr + CONFIG_ENV_SIZE);
+ crc2_ok = crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc;
+#endif
+ tmp_env1 = env_ptr;
+ crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc;
+
+ if (!crc1_ok && !crc2_ok) {
+ gd->env_addr = 0;
+ gd->env_valid = 0;
+
+ return 0;
+ } else if (crc1_ok && !crc2_ok) {
+ gd->env_valid = 1;
+ }
+#ifdef CONFIG_ENV_OFFSET_REDUND
+ else if (!crc1_ok && crc2_ok) {
+ gd->env_valid = 2;
+ } else {
+ /* both ok - check serial */
+ if (tmp_env1->flags == 255 && tmp_env2->flags == 0)
+ gd->env_valid = 2;
+ else if (tmp_env2->flags == 255 && tmp_env1->flags == 0)
+ gd->env_valid = 1;
+ else if (tmp_env1->flags > tmp_env2->flags)
+ gd->env_valid = 1;
+ else if (tmp_env2->flags > tmp_env1->flags)
+ gd->env_valid = 2;
+ else /* flags are equal - almost impossible */
+ gd->env_valid = 1;
+ }
+
+ if (gd->env_valid == 2)
+ env_ptr = tmp_env2;
+ else
+#endif
+ if (gd->env_valid == 1)
+ env_ptr = tmp_env1;
+
+ gd->env_addr = (ulong)env_ptr->data;
+
+#else /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */
+ gd->env_addr = (ulong)&default_environment[0];
+ gd->env_valid = 1;
+#endif /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */
+
+ return 0;
+}
+
+#ifdef CMD_SAVEENV
+/*
+ * The legacy NAND code saved the environment in the first NAND device i.e.,
+ * nand_dev_desc + 0. This is also the behaviour using the new NAND code.
+ */
+static int writeenv(size_t offset, u_char *buf)
+{
+ size_t end = offset + CONFIG_ENV_RANGE;
+ size_t amount_saved = 0;
+ size_t blocksize, len;
+ struct mtd_info *mtd;
+ u_char *char_ptr;
+
+ mtd = get_nand_dev_by_index(0);
+ if (!mtd)
+ return 1;
+
+ blocksize = mtd->erasesize;
+ len = min(blocksize, (size_t)CONFIG_ENV_SIZE);
+
+ while (amount_saved < CONFIG_ENV_SIZE && offset < end) {
+ if (nand_block_isbad(mtd, offset)) {
+ offset += blocksize;
+ } else {
+ char_ptr = &buf[amount_saved];
+ if (nand_write(mtd, offset, &len, char_ptr))
+ return 1;
+
+ offset += blocksize;
+ amount_saved += len;
+ }
+ }
+ if (amount_saved != CONFIG_ENV_SIZE)
+ return 1;
+
+ return 0;
+}
+
+struct env_location {
+ const char *name;
+ const nand_erase_options_t erase_opts;
+};
+
+static int erase_and_write_env(const struct env_location *location,
+ u_char *env_new)
+{
+ struct mtd_info *mtd;
+ int ret = 0;
+
+ mtd = get_nand_dev_by_index(0);
+ if (!mtd)
+ return 1;
+
+ printf("Erasing %s...\n", location->name);
+ if (nand_erase_opts(mtd, &location->erase_opts))
+ return 1;
+
+ printf("Writing to %s... ", location->name);
+ ret = writeenv(location->erase_opts.offset, env_new);
+ puts(ret ? "FAILED!\n" : "OK\n");
+
+ return ret;
+}
+
+int saveenv(void)
+{
+ int ret = 0;
+ ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
+ int env_idx = 0;
+ static const struct env_location location[] = {
+ {
+ .name = "NAND",
+ .erase_opts = {
+ .length = CONFIG_ENV_RANGE,
+ .offset = CONFIG_ENV_OFFSET,
+ },
+ },
+#ifdef CONFIG_ENV_OFFSET_REDUND
+ {
+ .name = "redundant NAND",
+ .erase_opts = {
+ .length = CONFIG_ENV_RANGE,
+ .offset = CONFIG_ENV_OFFSET_REDUND,
+ },
+ },
+#endif
+ };
+
+
+ if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)
+ return 1;
+
+ ret = env_export(env_new);
+ if (ret)
+ return ret;
+
+#ifdef CONFIG_ENV_OFFSET_REDUND
+ env_idx = (gd->env_valid == 1);
+#endif
+
+ ret = erase_and_write_env(&location[env_idx], (u_char *)env_new);
+#ifdef CONFIG_ENV_OFFSET_REDUND
+ if (!ret) {
+ /* preset other copy for next write */
+ gd->env_valid = gd->env_valid == 2 ? 1 : 2;
+ return ret;
+ }
+
+ env_idx = (env_idx + 1) & 1;
+ ret = erase_and_write_env(&location[env_idx], (u_char *)env_new);
+ if (!ret)
+ printf("Warning: primary env write failed,"
+ " redundancy is lost!\n");
+#endif
+
+ return ret;
+}
+#endif /* CMD_SAVEENV */
+
+#if defined(CONFIG_SPL_BUILD)
+static int readenv(size_t offset, u_char *buf)
+{
+ return nand_spl_load_image(offset, CONFIG_ENV_SIZE, buf);
+}
+#else
+static int readenv(size_t offset, u_char *buf)
+{
+ size_t end = offset + CONFIG_ENV_RANGE;
+ size_t amount_loaded = 0;
+ size_t blocksize, len;
+ struct mtd_info *mtd;
+ u_char *char_ptr;
+
+ mtd = get_nand_dev_by_index(0);
+ if (!mtd)
+ return 1;
+
+ blocksize = mtd->erasesize;
+ len = min(blocksize, (size_t)CONFIG_ENV_SIZE);
+
+ while (amount_loaded < CONFIG_ENV_SIZE && offset < end) {
+ if (nand_block_isbad(mtd, offset)) {
+ offset += blocksize;
+ } else {
+ char_ptr = &buf[amount_loaded];
+ if (nand_read_skip_bad(mtd, offset,
+ &len, NULL,
+ mtd->size, char_ptr))
+ return 1;
+
+ offset += blocksize;
+ amount_loaded += len;
+ }
+ }
+
+ if (amount_loaded != CONFIG_ENV_SIZE)
+ return 1;
+
+ return 0;
+}
+#endif /* #if defined(CONFIG_SPL_BUILD) */
+
+#ifdef CONFIG_ENV_OFFSET_OOB
+int get_nand_env_oob(struct mtd_info *mtd, unsigned long *result)
+{
+ struct mtd_oob_ops ops;
+ uint32_t oob_buf[ENV_OFFSET_SIZE / sizeof(uint32_t)];
+ int ret;
+
+ ops.datbuf = NULL;
+ ops.mode = MTD_OOB_AUTO;
+ ops.ooboffs = 0;
+ ops.ooblen = ENV_OFFSET_SIZE;
+ ops.oobbuf = (void *)oob_buf;
+
+ ret = mtd->read_oob(mtd, ENV_OFFSET_SIZE, &ops);
+ if (ret) {
+ printf("error reading OOB block 0\n");
+ return ret;
+ }
+
+ if (oob_buf[0] == ENV_OOB_MARKER) {
+ *result = oob_buf[1] * mtd->erasesize;
+ } else if (oob_buf[0] == ENV_OOB_MARKER_OLD) {
+ *result = oob_buf[1];
+ } else {
+ printf("No dynamic environment marker in OOB block 0\n");
+ return -ENOENT;
+ }
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_ENV_OFFSET_REDUND
+void env_relocate_spec(void)
+{
+#if !defined(ENV_IS_EMBEDDED)
+ int read1_fail = 0, read2_fail = 0;
+ env_t *tmp_env1, *tmp_env2;
+
+ tmp_env1 = (env_t *)malloc(CONFIG_ENV_SIZE);
+ tmp_env2 = (env_t *)malloc(CONFIG_ENV_SIZE);
+ if (tmp_env1 == NULL || tmp_env2 == NULL) {
+ puts("Can't allocate buffers for environment\n");
+ set_default_env("!malloc() failed");
+ goto done;
+ }
+
+ read1_fail = readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1);
+ read2_fail = readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2);
+
+ if (read1_fail && read2_fail)
+ puts("*** Error - No Valid Environment Area found\n");
+ else if (read1_fail || read2_fail)
+ puts("*** Warning - some problems detected "
+ "reading environment; recovered successfully\n");
+
+ if (read1_fail && read2_fail) {
+ set_default_env("!bad env area");
+ goto done;
+ } else if (!read1_fail && read2_fail) {
+ gd->env_valid = 1;
+ env_import((char *)tmp_env1, 1);
+ } else if (read1_fail && !read2_fail) {
+ gd->env_valid = 2;
+ env_import((char *)tmp_env2, 1);
+ } else {
+ env_import_redund((char *)tmp_env1, (char *)tmp_env2);
+ }
+
+done:
+ free(tmp_env1);
+ free(tmp_env2);
+
+#endif /* ! ENV_IS_EMBEDDED */
+}
+#else /* ! CONFIG_ENV_OFFSET_REDUND */
+/*
+ * The legacy NAND code saved the environment in the first NAND
+ * device i.e., nand_dev_desc + 0. This is also the behaviour using
+ * the new NAND code.
+ */
+void env_relocate_spec(void)
+{
+#if !defined(ENV_IS_EMBEDDED)
+ int ret;
+ ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
+
+#if defined(CONFIG_ENV_OFFSET_OOB)
+ struct mtd_info *mtd = get_nand_dev_by_index(0);
+ /*
+ * If unable to read environment offset from NAND OOB then fall through
+ * to the normal environment reading code below
+ */
+ if (mtd && !get_nand_env_oob(mtd, &nand_env_oob_offset)) {
+ printf("Found Environment offset in OOB..\n");
+ } else {
+ set_default_env("!no env offset in OOB");
+ return;
+ }
+#endif
+
+ ret = readenv(CONFIG_ENV_OFFSET, (u_char *)buf);
+ if (ret) {
+ set_default_env("!readenv() failed");
+ return;
+ }
+
+ env_import(buf, 1);
+#endif /* ! ENV_IS_EMBEDDED */
+}
+#endif /* CONFIG_ENV_OFFSET_REDUND */
diff --git a/env/nowhere.c b/env/nowhere.c
new file mode 100644
index 0000000000..bdc1ed5e67
--- /dev/null
+++ b/env/nowhere.c
@@ -0,0 +1,35 @@
+/*
+ * (C) Copyright 2000-2010
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Andreas Heppel <aheppel@sysgo.de>
+
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <command.h>
+#include <environment.h>
+#include <linux/stddef.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+env_t *env_ptr;
+
+void env_relocate_spec(void)
+{
+}
+
+/*
+ * Initialize Environment use
+ *
+ * We are still running from ROM, so data use is limited
+ */
+int env_init(void)
+{
+ gd->env_addr = (ulong)&default_environment[0];
+ gd->env_valid = 0;
+
+ return 0;
+}
diff --git a/env/nvram.c b/env/nvram.c
new file mode 100644
index 0000000000..524f07d5f8
--- /dev/null
+++ b/env/nvram.c
@@ -0,0 +1,114 @@
+/*
+ * (C) Copyright 2000-2010
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Andreas Heppel <aheppel@sysgo.de>
+
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/*
+ * 09-18-2001 Andreas Heppel, Sysgo RTS GmbH <aheppel@sysgo.de>
+ *
+ * It might not be possible in all cases to use 'memcpy()' to copy
+ * the environment to NVRAM, as the NVRAM might not be mapped into
+ * the memory space. (I.e. this is the case for the BAB750). In those
+ * cases it might be possible to access the NVRAM using a different
+ * method. For example, the RTC on the BAB750 is accessible in IO
+ * space using its address and data registers. To enable usage of
+ * NVRAM in those cases I invented the functions 'nvram_read()' and
+ * 'nvram_write()', which will be activated upon the configuration
+ * #define CONFIG_SYS_NVRAM_ACCESS_ROUTINE. Note, that those functions are
+ * strongly dependent on the used HW, and must be redefined for each
+ * board that wants to use them.
+ */
+
+#include <common.h>
+#include <command.h>
+#include <environment.h>
+#include <linux/stddef.h>
+#include <search.h>
+#include <errno.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#ifdef CONFIG_SYS_NVRAM_ACCESS_ROUTINE
+extern void *nvram_read(void *dest, const long src, size_t count);
+extern void nvram_write(long dest, const void *src, size_t count);
+env_t *env_ptr;
+#else
+env_t *env_ptr = (env_t *)CONFIG_ENV_ADDR;
+#endif
+
+char *env_name_spec = "NVRAM";
+
+#ifdef CONFIG_SYS_NVRAM_ACCESS_ROUTINE
+uchar env_get_char_spec(int index)
+{
+ uchar c;
+
+ nvram_read(&c, CONFIG_ENV_ADDR + index, 1);
+
+ return c;
+}
+#endif
+
+void env_relocate_spec(void)
+{
+ char buf[CONFIG_ENV_SIZE];
+
+#if defined(CONFIG_SYS_NVRAM_ACCESS_ROUTINE)
+ nvram_read(buf, CONFIG_ENV_ADDR, CONFIG_ENV_SIZE);
+#else
+ memcpy(buf, (void *)CONFIG_ENV_ADDR, CONFIG_ENV_SIZE);
+#endif
+ env_import(buf, 1);
+}
+
+int saveenv(void)
+{
+ env_t env_new;
+ int rcode = 0;
+
+ rcode = env_export(&env_new);
+ if (rcode)
+ return rcode;
+
+#ifdef CONFIG_SYS_NVRAM_ACCESS_ROUTINE
+ nvram_write(CONFIG_ENV_ADDR, &env_new, CONFIG_ENV_SIZE);
+#else
+ if (memcpy((char *)CONFIG_ENV_ADDR, &env_new, CONFIG_ENV_SIZE) == NULL)
+ rcode = 1;
+#endif
+ return rcode;
+}
+
+/*
+ * Initialize Environment use
+ *
+ * We are still running from ROM, so data use is limited
+ */
+int env_init(void)
+{
+#if defined(CONFIG_SYS_NVRAM_ACCESS_ROUTINE)
+ ulong crc;
+ uchar data[ENV_SIZE];
+
+ nvram_read(&crc, CONFIG_ENV_ADDR, sizeof(ulong));
+ nvram_read(data, CONFIG_ENV_ADDR + sizeof(ulong), ENV_SIZE);
+
+ if (crc32(0, data, ENV_SIZE) == crc) {
+ gd->env_addr = (ulong)CONFIG_ENV_ADDR + sizeof(long);
+#else
+ if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) {
+ gd->env_addr = (ulong)&env_ptr->data;
+#endif
+ gd->env_valid = 1;
+ } else {
+ gd->env_addr = (ulong)&default_environment[0];
+ gd->env_valid = 0;
+ }
+
+ return 0;
+}
diff --git a/env/onenand.c b/env/onenand.c
new file mode 100644
index 0000000000..cc3d670de8
--- /dev/null
+++ b/env/onenand.c
@@ -0,0 +1,116 @@
+/*
+ * (C) Copyright 2010 DENX Software Engineering
+ * Wolfgang Denk <wd@denx.de>
+ *
+ * (C) Copyright 2005-2009 Samsung Electronics
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <command.h>
+#include <environment.h>
+#include <linux/stddef.h>
+#include <malloc.h>
+#include <search.h>
+#include <errno.h>
+#include <onenand_uboot.h>
+
+#include <linux/compat.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/onenand.h>
+
+char *env_name_spec = "OneNAND";
+
+#define ONENAND_MAX_ENV_SIZE CONFIG_ENV_SIZE
+#define ONENAND_ENV_SIZE(mtd) (ONENAND_MAX_ENV_SIZE - ENV_HEADER_SIZE)
+
+DECLARE_GLOBAL_DATA_PTR;
+
+void env_relocate_spec(void)
+{
+ struct mtd_info *mtd = &onenand_mtd;
+#ifdef CONFIG_ENV_ADDR_FLEX
+ struct onenand_chip *this = &onenand_chip;
+#endif
+ int rc;
+ size_t retlen;
+#ifdef ENV_IS_EMBEDDED
+ char *buf = (char *)&environment;
+#else
+ loff_t env_addr = CONFIG_ENV_ADDR;
+ char onenand_env[ONENAND_MAX_ENV_SIZE];
+ char *buf = (char *)&onenand_env[0];
+#endif /* ENV_IS_EMBEDDED */
+
+#ifndef ENV_IS_EMBEDDED
+# ifdef CONFIG_ENV_ADDR_FLEX
+ if (FLEXONENAND(this))
+ env_addr = CONFIG_ENV_ADDR_FLEX;
+# endif
+ /* Check OneNAND exist */
+ if (mtd->writesize)
+ /* Ignore read fail */
+ mtd_read(mtd, env_addr, ONENAND_MAX_ENV_SIZE,
+ &retlen, (u_char *)buf);
+ else
+ mtd->writesize = MAX_ONENAND_PAGESIZE;
+#endif /* !ENV_IS_EMBEDDED */
+
+ rc = env_import(buf, 1);
+ if (rc)
+ gd->env_valid = 1;
+}
+
+int saveenv(void)
+{
+ env_t env_new;
+ int ret;
+ struct mtd_info *mtd = &onenand_mtd;
+#ifdef CONFIG_ENV_ADDR_FLEX
+ struct onenand_chip *this = &onenand_chip;
+#endif
+ loff_t env_addr = CONFIG_ENV_ADDR;
+ size_t retlen;
+ struct erase_info instr = {
+ .callback = NULL,
+ };
+
+ ret = env_export(&env_new);
+ if (ret)
+ return ret;
+
+ instr.len = CONFIG_ENV_SIZE;
+#ifdef CONFIG_ENV_ADDR_FLEX
+ if (FLEXONENAND(this)) {
+ env_addr = CONFIG_ENV_ADDR_FLEX;
+ instr.len = CONFIG_ENV_SIZE_FLEX;
+ instr.len <<= onenand_mtd.eraseregions[0].numblocks == 1 ?
+ 1 : 0;
+ }
+#endif
+ instr.addr = env_addr;
+ instr.mtd = mtd;
+ if (mtd_erase(mtd, &instr)) {
+ printf("OneNAND: erase failed at 0x%08llx\n", env_addr);
+ return 1;
+ }
+
+ if (mtd_write(mtd, env_addr, ONENAND_MAX_ENV_SIZE, &retlen,
+ (u_char *)&env_new)) {
+ printf("OneNAND: write failed at 0x%llx\n", instr.addr);
+ return 2;
+ }
+
+ return 0;
+}
+
+int env_init(void)
+{
+ /* use default */
+ gd->env_addr = (ulong)&default_environment[0];
+ gd->env_valid = 1;
+
+ return 0;
+}
diff --git a/env/remote.c b/env/remote.c
new file mode 100644
index 0000000000..eb977ee1fe
--- /dev/null
+++ b/env/remote.c
@@ -0,0 +1,58 @@
+/*
+ * (C) Copyright 2011-2012 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/* #define DEBUG */
+
+#include <common.h>
+#include <command.h>
+#include <environment.h>
+#include <linux/stddef.h>
+
+char *env_name_spec = "Remote";
+
+#ifdef ENV_IS_EMBEDDED
+env_t *env_ptr = &environment;
+#else /* ! ENV_IS_EMBEDDED */
+env_t *env_ptr = (env_t *)CONFIG_ENV_ADDR;
+#endif /* ENV_IS_EMBEDDED */
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#if !defined(CONFIG_ENV_OFFSET)
+#define CONFIG_ENV_OFFSET 0
+#endif
+
+int env_init(void)
+{
+ if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) {
+ gd->env_addr = (ulong)&(env_ptr->data);
+ gd->env_valid = 1;
+ return 0;
+ }
+
+ gd->env_addr = (ulong)default_environment;
+ gd->env_valid = 0;
+ return 0;
+}
+
+#ifdef CONFIG_CMD_SAVEENV
+int saveenv(void)
+{
+#ifdef CONFIG_SRIO_PCIE_BOOT_SLAVE
+ printf("Can not support the 'saveenv' when boot from SRIO or PCIE!\n");
+ return 1;
+#else
+ return 0;
+#endif
+}
+#endif /* CONFIG_CMD_SAVEENV */
+
+void env_relocate_spec(void)
+{
+#ifndef ENV_IS_EMBEDDED
+ env_import((char *)env_ptr, 1);
+#endif
+}
diff --git a/env/sata.c b/env/sata.c
new file mode 100644
index 0000000000..b0cee35a60
--- /dev/null
+++ b/env/sata.c
@@ -0,0 +1,127 @@
+/*
+ * (C) Copyright 2010-2016 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/* #define DEBUG */
+
+#include <common.h>
+
+#include <command.h>
+#include <environment.h>
+#include <linux/stddef.h>
+#include <errno.h>
+#include <memalign.h>
+#include <sata.h>
+#include <search.h>
+
+#if defined(CONFIG_ENV_SIZE_REDUND) || defined(CONFIG_ENV_OFFSET_REDUND)
+#error ENV REDUND not supported
+#endif
+
+#if !defined(CONFIG_ENV_OFFSET) || !defined(CONFIG_ENV_SIZE)
+#error CONFIG_ENV_OFFSET or CONFIG_ENV_SIZE not defined
+#endif
+
+char *env_name_spec = "SATA";
+
+DECLARE_GLOBAL_DATA_PTR;
+
+__weak int sata_get_env_dev(void)
+{
+ return CONFIG_SYS_SATA_ENV_DEV;
+}
+
+int env_init(void)
+{
+ /* use default */
+ gd->env_addr = (ulong)&default_environment[0];
+ gd->env_valid = 1;
+
+ return 0;
+}
+
+#ifdef CONFIG_CMD_SAVEENV
+static inline int write_env(struct blk_desc *sata, unsigned long size,
+ unsigned long offset, void *buffer)
+{
+ uint blk_start, blk_cnt, n;
+
+ blk_start = ALIGN(offset, sata->blksz) / sata->blksz;
+ blk_cnt = ALIGN(size, sata->blksz) / sata->blksz;
+
+ n = blk_dwrite(sata, blk_start, blk_cnt, buffer);
+
+ return (n == blk_cnt) ? 0 : -1;
+}
+
+int saveenv(void)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
+ struct blk_desc *sata = NULL;
+ int env_sata, ret;
+
+ if (sata_initialize())
+ return 1;
+
+ env_sata = sata_get_env_dev();
+
+ sata = sata_get_dev(env_sata);
+ if (sata == NULL) {
+ printf("Unknown SATA(%d) device for environment!\n",
+ env_sata);
+ return 1;
+ }
+
+ ret = env_export(env_new);
+ if (ret)
+ return 1;
+
+ printf("Writing to SATA(%d)...", env_sata);
+ if (write_env(sata, CONFIG_ENV_SIZE, CONFIG_ENV_OFFSET, &env_new)) {
+ puts("failed\n");
+ return 1;
+ }
+
+ puts("done\n");
+ return 0;
+}
+#endif /* CONFIG_CMD_SAVEENV */
+
+static inline int read_env(struct blk_desc *sata, unsigned long size,
+ unsigned long offset, void *buffer)
+{
+ uint blk_start, blk_cnt, n;
+
+ blk_start = ALIGN(offset, sata->blksz) / sata->blksz;
+ blk_cnt = ALIGN(size, sata->blksz) / sata->blksz;
+
+ n = blk_dread(sata, blk_start, blk_cnt, buffer);
+
+ return (n == blk_cnt) ? 0 : -1;
+}
+
+void env_relocate_spec(void)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
+ struct blk_desc *sata = NULL;
+ int env_sata;
+
+ if (sata_initialize())
+ return;
+
+ env_sata = sata_get_env_dev();
+
+ sata = sata_get_dev(env_sata);
+ if (sata == NULL) {
+ printf("Unknown SATA(%d) device for environment!\n",
+ env_sata);
+ return;
+ }
+
+ if (read_env(sata, CONFIG_ENV_SIZE, CONFIG_ENV_OFFSET, buf))
+ return set_default_env(NULL);
+
+ env_import(buf, 1);
+}
diff --git a/env/sf.c b/env/sf.c
new file mode 100644
index 0000000000..45f441a042
--- /dev/null
+++ b/env/sf.c
@@ -0,0 +1,344 @@
+/*
+ * (C) Copyright 2000-2010
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Andreas Heppel <aheppel@sysgo.de>
+ *
+ * (C) Copyright 2008 Atmel Corporation
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+#include <common.h>
+#include <dm.h>
+#include <environment.h>
+#include <malloc.h>
+#include <spi.h>
+#include <spi_flash.h>
+#include <search.h>
+#include <errno.h>
+#include <dm/device-internal.h>
+
+#ifndef CONFIG_ENV_SPI_BUS
+# define CONFIG_ENV_SPI_BUS CONFIG_SF_DEFAULT_BUS
+#endif
+#ifndef CONFIG_ENV_SPI_CS
+# define CONFIG_ENV_SPI_CS CONFIG_SF_DEFAULT_CS
+#endif
+#ifndef CONFIG_ENV_SPI_MAX_HZ
+# define CONFIG_ENV_SPI_MAX_HZ CONFIG_SF_DEFAULT_SPEED
+#endif
+#ifndef CONFIG_ENV_SPI_MODE
+# define CONFIG_ENV_SPI_MODE CONFIG_SF_DEFAULT_MODE
+#endif
+
+#ifdef CONFIG_ENV_OFFSET_REDUND
+static ulong env_offset = CONFIG_ENV_OFFSET;
+static ulong env_new_offset = CONFIG_ENV_OFFSET_REDUND;
+
+#define ACTIVE_FLAG 1
+#define OBSOLETE_FLAG 0
+#endif /* CONFIG_ENV_OFFSET_REDUND */
+
+DECLARE_GLOBAL_DATA_PTR;
+
+char *env_name_spec = "SPI Flash";
+
+static struct spi_flash *env_flash;
+
+static int setup_flash_device(void)
+{
+#ifdef CONFIG_DM_SPI_FLASH
+ struct udevice *new;
+ int ret;
+
+ /* speed and mode will be read from DT */
+ ret = spi_flash_probe_bus_cs(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS,
+ 0, 0, &new);
+ if (ret) {
+ set_default_env("!spi_flash_probe_bus_cs() failed");
+ return 1;
+ }
+
+ env_flash = dev_get_uclass_priv(new);
+#else
+
+ if (!env_flash) {
+ env_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS,
+ CONFIG_ENV_SPI_CS,
+ CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE);
+ if (!env_flash) {
+ set_default_env("!spi_flash_probe() failed");
+ return 1;
+ }
+ }
+#endif
+ return 0;
+}
+
+#if defined(CONFIG_ENV_OFFSET_REDUND)
+int saveenv(void)
+{
+ env_t env_new;
+ char *saved_buffer = NULL, flag = OBSOLETE_FLAG;
+ u32 saved_size, saved_offset, sector;
+ int ret;
+
+ ret = setup_flash_device();
+ if (ret)
+ return ret;
+
+ ret = env_export(&env_new);
+ if (ret)
+ return ret;
+ env_new.flags = ACTIVE_FLAG;
+
+ if (gd->env_valid == 1) {
+ env_new_offset = CONFIG_ENV_OFFSET_REDUND;
+ env_offset = CONFIG_ENV_OFFSET;
+ } else {
+ env_new_offset = CONFIG_ENV_OFFSET;
+ env_offset = CONFIG_ENV_OFFSET_REDUND;
+ }
+
+ /* Is the sector larger than the env (i.e. embedded) */
+ if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
+ saved_size = CONFIG_ENV_SECT_SIZE - CONFIG_ENV_SIZE;
+ saved_offset = env_new_offset + CONFIG_ENV_SIZE;
+ saved_buffer = memalign(ARCH_DMA_MINALIGN, saved_size);
+ if (!saved_buffer) {
+ ret = 1;
+ goto done;
+ }
+ ret = spi_flash_read(env_flash, saved_offset,
+ saved_size, saved_buffer);
+ if (ret)
+ goto done;
+ }
+
+ sector = DIV_ROUND_UP(CONFIG_ENV_SIZE, CONFIG_ENV_SECT_SIZE);
+
+ puts("Erasing SPI flash...");
+ ret = spi_flash_erase(env_flash, env_new_offset,
+ sector * CONFIG_ENV_SECT_SIZE);
+ if (ret)
+ goto done;
+
+ puts("Writing to SPI flash...");
+
+ ret = spi_flash_write(env_flash, env_new_offset,
+ CONFIG_ENV_SIZE, &env_new);
+ if (ret)
+ goto done;
+
+ if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
+ ret = spi_flash_write(env_flash, saved_offset,
+ saved_size, saved_buffer);
+ if (ret)
+ goto done;
+ }
+
+ ret = spi_flash_write(env_flash, env_offset + offsetof(env_t, flags),
+ sizeof(env_new.flags), &flag);
+ if (ret)
+ goto done;
+
+ puts("done\n");
+
+ gd->env_valid = gd->env_valid == 2 ? 1 : 2;
+
+ printf("Valid environment: %d\n", (int)gd->env_valid);
+
+ done:
+ if (saved_buffer)
+ free(saved_buffer);
+
+ return ret;
+}
+
+void env_relocate_spec(void)
+{
+ int ret;
+ int crc1_ok = 0, crc2_ok = 0;
+ env_t *tmp_env1 = NULL;
+ env_t *tmp_env2 = NULL;
+ env_t *ep = NULL;
+
+ tmp_env1 = (env_t *)memalign(ARCH_DMA_MINALIGN,
+ CONFIG_ENV_SIZE);
+ tmp_env2 = (env_t *)memalign(ARCH_DMA_MINALIGN,
+ CONFIG_ENV_SIZE);
+ if (!tmp_env1 || !tmp_env2) {
+ set_default_env("!malloc() failed");
+ goto out;
+ }
+
+ ret = setup_flash_device();
+ if (ret)
+ goto out;
+
+ ret = spi_flash_read(env_flash, CONFIG_ENV_OFFSET,
+ CONFIG_ENV_SIZE, tmp_env1);
+ if (ret) {
+ set_default_env("!spi_flash_read() failed");
+ goto err_read;
+ }
+
+ if (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc)
+ crc1_ok = 1;
+
+ ret = spi_flash_read(env_flash, CONFIG_ENV_OFFSET_REDUND,
+ CONFIG_ENV_SIZE, tmp_env2);
+ if (!ret) {
+ if (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc)
+ crc2_ok = 1;
+ }
+
+ if (!crc1_ok && !crc2_ok) {
+ set_default_env("!bad CRC");
+ goto err_read;
+ } else if (crc1_ok && !crc2_ok) {
+ gd->env_valid = 1;
+ } else if (!crc1_ok && crc2_ok) {
+ gd->env_valid = 2;
+ } else if (tmp_env1->flags == ACTIVE_FLAG &&
+ tmp_env2->flags == OBSOLETE_FLAG) {
+ gd->env_valid = 1;
+ } else if (tmp_env1->flags == OBSOLETE_FLAG &&
+ tmp_env2->flags == ACTIVE_FLAG) {
+ gd->env_valid = 2;
+ } else if (tmp_env1->flags == tmp_env2->flags) {
+ gd->env_valid = 1;
+ } else if (tmp_env1->flags == 0xFF) {
+ gd->env_valid = 1;
+ } else if (tmp_env2->flags == 0xFF) {
+ gd->env_valid = 2;
+ } else {
+ /*
+ * this differs from code in env_flash.c, but I think a sane
+ * default path is desirable.
+ */
+ gd->env_valid = 1;
+ }
+
+ if (gd->env_valid == 1)
+ ep = tmp_env1;
+ else
+ ep = tmp_env2;
+
+ ret = env_import((char *)ep, 0);
+ if (!ret) {
+ error("Cannot import environment: errno = %d\n", errno);
+ set_default_env("!env_import failed");
+ }
+
+err_read:
+ spi_flash_free(env_flash);
+ env_flash = NULL;
+out:
+ free(tmp_env1);
+ free(tmp_env2);
+}
+#else
+int saveenv(void)
+{
+ u32 saved_size, saved_offset, sector;
+ char *saved_buffer = NULL;
+ int ret = 1;
+ env_t env_new;
+
+ ret = setup_flash_device();
+ if (ret)
+ return ret;
+
+ /* Is the sector larger than the env (i.e. embedded) */
+ if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
+ saved_size = CONFIG_ENV_SECT_SIZE - CONFIG_ENV_SIZE;
+ saved_offset = CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE;
+ saved_buffer = malloc(saved_size);
+ if (!saved_buffer)
+ goto done;
+
+ ret = spi_flash_read(env_flash, saved_offset,
+ saved_size, saved_buffer);
+ if (ret)
+ goto done;
+ }
+
+ ret = env_export(&env_new);
+ if (ret)
+ goto done;
+
+ sector = DIV_ROUND_UP(CONFIG_ENV_SIZE, CONFIG_ENV_SECT_SIZE);
+
+ puts("Erasing SPI flash...");
+ ret = spi_flash_erase(env_flash, CONFIG_ENV_OFFSET,
+ sector * CONFIG_ENV_SECT_SIZE);
+ if (ret)
+ goto done;
+
+ puts("Writing to SPI flash...");
+ ret = spi_flash_write(env_flash, CONFIG_ENV_OFFSET,
+ CONFIG_ENV_SIZE, &env_new);
+ if (ret)
+ goto done;
+
+ if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
+ ret = spi_flash_write(env_flash, saved_offset,
+ saved_size, saved_buffer);
+ if (ret)
+ goto done;
+ }
+
+ ret = 0;
+ puts("done\n");
+
+ done:
+ if (saved_buffer)
+ free(saved_buffer);
+
+ return ret;
+}
+
+void env_relocate_spec(void)
+{
+ int ret;
+ char *buf = NULL;
+
+ buf = (char *)memalign(ARCH_DMA_MINALIGN, CONFIG_ENV_SIZE);
+ if (!buf) {
+ set_default_env("!malloc() failed");
+ return;
+ }
+
+ ret = setup_flash_device();
+ if (ret)
+ goto out;
+
+ ret = spi_flash_read(env_flash,
+ CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, buf);
+ if (ret) {
+ set_default_env("!spi_flash_read() failed");
+ goto err_read;
+ }
+
+ ret = env_import(buf, 1);
+ if (ret)
+ gd->env_valid = 1;
+
+err_read:
+ spi_flash_free(env_flash);
+ env_flash = NULL;
+out:
+ free(buf);
+}
+#endif
+
+int env_init(void)
+{
+ /* SPI flash isn't usable before relocation */
+ gd->env_addr = (ulong)&default_environment[0];
+ gd->env_valid = 1;
+
+ return 0;
+}
diff --git a/env/ubi.c b/env/ubi.c
new file mode 100644
index 0000000000..95b527ddca
--- /dev/null
+++ b/env/ubi.c
@@ -0,0 +1,179 @@
+/*
+ * (c) Copyright 2012 by National Instruments,
+ * Joe Hershberger <joe.hershberger@ni.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+
+#include <command.h>
+#include <environment.h>
+#include <errno.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <search.h>
+#include <ubi_uboot.h>
+#undef crc32
+
+char *env_name_spec = "UBI";
+
+env_t *env_ptr;
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int env_init(void)
+{
+ /* use default */
+ gd->env_addr = (ulong)&default_environment[0];
+ gd->env_valid = 1;
+
+ return 0;
+}
+
+#ifdef CONFIG_CMD_SAVEENV
+#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
+int saveenv(void)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
+ int ret;
+
+ ret = env_export(env_new);
+ if (ret)
+ return ret;
+
+ if (ubi_part(CONFIG_ENV_UBI_PART, NULL)) {
+ printf("\n** Cannot find mtd partition \"%s\"\n",
+ CONFIG_ENV_UBI_PART);
+ return 1;
+ }
+
+ if (gd->env_valid == 1) {
+ puts("Writing to redundant UBI... ");
+ if (ubi_volume_write(CONFIG_ENV_UBI_VOLUME_REDUND,
+ (void *)env_new, CONFIG_ENV_SIZE)) {
+ printf("\n** Unable to write env to %s:%s **\n",
+ CONFIG_ENV_UBI_PART,
+ CONFIG_ENV_UBI_VOLUME_REDUND);
+ return 1;
+ }
+ } else {
+ puts("Writing to UBI... ");
+ if (ubi_volume_write(CONFIG_ENV_UBI_VOLUME,
+ (void *)env_new, CONFIG_ENV_SIZE)) {
+ printf("\n** Unable to write env to %s:%s **\n",
+ CONFIG_ENV_UBI_PART,
+ CONFIG_ENV_UBI_VOLUME);
+ return 1;
+ }
+ }
+
+ puts("done\n");
+
+ gd->env_valid = gd->env_valid == 2 ? 1 : 2;
+
+ return 0;
+}
+#else /* ! CONFIG_SYS_REDUNDAND_ENVIRONMENT */
+int saveenv(void)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
+ int ret;
+
+ ret = env_export(env_new);
+ if (ret)
+ return ret;
+
+ if (ubi_part(CONFIG_ENV_UBI_PART, NULL)) {
+ printf("\n** Cannot find mtd partition \"%s\"\n",
+ CONFIG_ENV_UBI_PART);
+ return 1;
+ }
+
+ if (ubi_volume_write(CONFIG_ENV_UBI_VOLUME, (void *)env_new,
+ CONFIG_ENV_SIZE)) {
+ printf("\n** Unable to write env to %s:%s **\n",
+ CONFIG_ENV_UBI_PART, CONFIG_ENV_UBI_VOLUME);
+ return 1;
+ }
+
+ puts("done\n");
+ return 0;
+}
+#endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */
+#endif /* CONFIG_CMD_SAVEENV */
+
+#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
+void env_relocate_spec(void)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(char, env1_buf, CONFIG_ENV_SIZE);
+ ALLOC_CACHE_ALIGN_BUFFER(char, env2_buf, CONFIG_ENV_SIZE);
+ env_t *tmp_env1, *tmp_env2;
+
+ /*
+ * In case we have restarted u-boot there is a chance that buffer
+ * contains old environment (from the previous boot).
+ * If UBI volume is zero size, ubi_volume_read() doesn't modify the
+ * buffer.
+ * We need to clear buffer manually here, so the invalid CRC will
+ * cause setting default environment as expected.
+ */
+ memset(env1_buf, 0x0, CONFIG_ENV_SIZE);
+ memset(env2_buf, 0x0, CONFIG_ENV_SIZE);
+
+ tmp_env1 = (env_t *)env1_buf;
+ tmp_env2 = (env_t *)env2_buf;
+
+ if (ubi_part(CONFIG_ENV_UBI_PART, NULL)) {
+ printf("\n** Cannot find mtd partition \"%s\"\n",
+ CONFIG_ENV_UBI_PART);
+ set_default_env(NULL);
+ return;
+ }
+
+ if (ubi_volume_read(CONFIG_ENV_UBI_VOLUME, (void *)tmp_env1,
+ CONFIG_ENV_SIZE)) {
+ printf("\n** Unable to read env from %s:%s **\n",
+ CONFIG_ENV_UBI_PART, CONFIG_ENV_UBI_VOLUME);
+ }
+
+ if (ubi_volume_read(CONFIG_ENV_UBI_VOLUME_REDUND, (void *)tmp_env2,
+ CONFIG_ENV_SIZE)) {
+ printf("\n** Unable to read redundant env from %s:%s **\n",
+ CONFIG_ENV_UBI_PART, CONFIG_ENV_UBI_VOLUME_REDUND);
+ }
+
+ env_import_redund((char *)tmp_env1, (char *)tmp_env2);
+}
+#else /* ! CONFIG_SYS_REDUNDAND_ENVIRONMENT */
+void env_relocate_spec(void)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
+
+ /*
+ * In case we have restarted u-boot there is a chance that buffer
+ * contains old environment (from the previous boot).
+ * If UBI volume is zero size, ubi_volume_read() doesn't modify the
+ * buffer.
+ * We need to clear buffer manually here, so the invalid CRC will
+ * cause setting default environment as expected.
+ */
+ memset(buf, 0x0, CONFIG_ENV_SIZE);
+
+ if (ubi_part(CONFIG_ENV_UBI_PART, NULL)) {
+ printf("\n** Cannot find mtd partition \"%s\"\n",
+ CONFIG_ENV_UBI_PART);
+ set_default_env(NULL);
+ return;
+ }
+
+ if (ubi_volume_read(CONFIG_ENV_UBI_VOLUME, buf, CONFIG_ENV_SIZE)) {
+ printf("\n** Unable to read env from %s:%s **\n",
+ CONFIG_ENV_UBI_PART, CONFIG_ENV_UBI_VOLUME);
+ set_default_env(NULL);
+ return;
+ }
+
+ env_import(buf, 1);
+}
+#endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */