[PATCH v1 07/12] mtd: env, cmd: add EN75 NAND remap handling

AK Sharma <[email protected]>
Newsgroups org.u-boot-project.lists.u-boot
Message-ID <[email protected]>
Honour the EcoNet/Airoha NAND "remap" region attribute when reading and
writing MTD partitions from the env and the mtd command, matching the
vendor block layout.

Signed-off-by: AK Sharma <[email protected]>
---
 cmd/mtd.c   | 30 ++++++++++++++--
 env/Kconfig |  2 +-
 env/mtd.c   | 98 +++++++++++++++++++++++++++++++++--------------------
 3 files changed, 89 insertions(+), 41 deletions(-)

diff --git a/cmd/mtd.c b/cmd/mtd.c
index 7f251440..d7b8fa3b 100644
--- a/cmd/mtd.c
+++ b/cmd/mtd.c
@@ -519,6 +519,13 @@ static int do_mtd_io(struct cmd_tbl *cmdtp, int flag, int argc,
 		goto out_put_mtd;
 	}
 
+	if (start_off >= mtd->size) {
+		printf("Offset 0x%llx is past the end of %s (size 0x%llx)\n",
+		       start_off, mtd->name, mtd->size);
+		ret = CMD_RET_FAILURE;
+		goto out_put_mtd;
+	}
+
 	default_len = dump ? mtd->writesize : mtd->size;
 	len = argc > 1 ? hextoul(argv[1], NULL) : default_len;
 	if (!mtd_is_aligned_with_min_io_size(mtd, len)) {
@@ -557,11 +564,21 @@ static int do_mtd_io(struct cmd_tbl *cmdtp, int flag, int argc,
 	io_op.datbuf = buf;
 	io_op.oobbuf = woob ? &buf[len] : NULL;
 
-	/* Search for the first good block after the given offset */
+	/*
+	 * Search for the first good block after the given offset.
+	 * Bounded: mtd_block_isbad() returns a negative errno past the end of
+	 * the device, which is nonzero and would otherwise spin forever.
+	 */
 	off = start_off;
-	while (mtd_block_isbad(mtd, off))
+	while (off < mtd->size && mtd_block_isbad(mtd, off) > 0)
 		off += mtd->erasesize;
 
+	if (off >= mtd->size) {
+		printf("No good block found after offset 0x%llx\n", start_off);
+		ret = CMD_RET_FAILURE;
+		goto out_put_mtd;
+	}
+
 	led_activity_blink();
 
 	if (benchmark)
@@ -569,9 +586,16 @@ static int do_mtd_io(struct cmd_tbl *cmdtp, int flag, int argc,
 
 	/* Loop over the pages to do the actual read/write */
 	while (remaining) {
+		if (off >= mtd->size) {
+			printf("Reached the end of %s before completing\n",
+			       mtd->name);
+			ret = -EIO;
+			break;
+		}
+
 		/* Skip the block if it is bad */
 		if (mtd_is_aligned_with_block_size(mtd, off) &&
-		    mtd_block_isbad(mtd, off)) {
+		    mtd_block_isbad(mtd, off) > 0) {
 			off += mtd->erasesize;
 			continue;
 		}
diff --git a/env/Kconfig b/env/Kconfig
index 7abd82ab..ba45d194 100644
--- a/env/Kconfig
+++ b/env/Kconfig
@@ -652,7 +652,7 @@ config ENV_OFFSET_RELATIVE_END
 config ENV_OFFSET_REDUND
 	hex "Redundant environment offset"
 	depends on (ENV_IS_IN_EEPROM || ENV_IS_IN_MMC || ENV_IS_IN_NAND || \
-		    ENV_IS_IN_SPI_FLASH) && ENV_REDUNDANT
+		    ENV_IS_IN_SPI_FLASH || ENV_IS_IN_MTD) && ENV_REDUNDANT
 	default 0x10C0000 if MICROBLAZE
 	default 0x0
 	help
diff --git a/env/mtd.c b/env/mtd.c
index b26ee809..bf9fa516 100644
--- a/env/mtd.c
+++ b/env/mtd.c
@@ -2,6 +2,7 @@
 /*
  *  Author: Christian Marangi <[email protected]>
  */
+#include <env.h>
 #include <env_internal.h>
 #include <errno.h>
 #include <malloc.h>
@@ -9,6 +10,7 @@
 #include <asm/cache.h>
 #include <asm/global_data.h>
 #include <linux/mtd/mtd.h>
+#include <memalign.h>
 #include <u-boot/crc.h>
 
 DECLARE_GLOBAL_DATA_PTR;
@@ -30,6 +32,29 @@ static int setup_mtd_device(struct mtd_info **mtd_env)
 	return 0;
 }
 
+static int env_mtd_read_buf(struct mtd_info *mtd, u32 offset, void *buf, int len)
+{
+	u32 sect_size = mtd->erasesize;
+	size_t ret_len;
+	char *tmp = buf;
+	int remaining = len;
+	int ret;
+
+	while (remaining) {
+		if (!(offset % sect_size) && mtd_block_isbad(mtd, offset)) {
+			offset += sect_size;
+			continue;
+		}
+		ret = mtd_read(mtd, offset, mtd->writesize, &ret_len, tmp);
+		if (ret)
+			return ret;
+		tmp += ret_len;
+		offset += ret_len;
+		remaining -= ret_len;
+	}
+	return 0;
+}
+
 static int env_mtd_save(void)
 {
 	char *saved_buf = NULL, *write_buf, *tmp;
@@ -40,7 +65,7 @@ static int env_mtd_save(void)
 	u32 write_size;
 	env_t env_new;
 	int remaining;
-	u32 offset;
+	u32 offset, save_off;
 	int ret;
 
 	ret = setup_mtd_device(&mtd_env);
@@ -48,6 +73,12 @@ static int env_mtd_save(void)
 		return ret;
 
 	sect_size = mtd_env->erasesize;
+#ifdef CONFIG_ENV_OFFSET_REDUND
+	save_off = (gd->env_valid == ENV_VALID) ?
+		CONFIG_ENV_OFFSET_REDUND : CONFIG_ENV_OFFSET;
+#else
+	save_off = CONFIG_ENV_OFFSET;
+#endif
 
 	/* Is the sector larger than the env (i.e. embedded) */
 	if (sect_size > CONFIG_ENV_SIZE) {
@@ -57,7 +88,7 @@ static int env_mtd_save(void)
 			goto done;
 		}
 
-		offset = CONFIG_ENV_OFFSET;
+		offset = save_off;
 		remaining = sect_size;
 		tmp = saved_buf;
 
@@ -87,7 +118,7 @@ static int env_mtd_save(void)
 	sect_num = DIV_ROUND_UP(CONFIG_ENV_SIZE, sect_size);
 
 	ei.mtd = mtd_env;
-	ei.addr = CONFIG_ENV_OFFSET;
+	ei.addr = save_off;
 	ei.len = sect_num * sect_size;
 
 	puts("Erasing MTD...");
@@ -104,7 +135,7 @@ static int env_mtd_save(void)
 		write_buf = (char *)&env_new;
 	}
 
-	offset = CONFIG_ENV_OFFSET;
+	offset = save_off;
 	remaining = write_size;
 	tmp = write_buf;
 
@@ -129,6 +160,9 @@ static int env_mtd_save(void)
 
 	ret = 0;
 	puts("done\n");
+#ifdef CONFIG_ENV_OFFSET_REDUND
+	gd->env_valid = gd->env_valid == ENV_VALID ? ENV_REDUND : ENV_VALID;
+#endif
 
 done:
 	put_mtd_device(mtd_env);
@@ -142,12 +176,8 @@ done:
 static int env_mtd_load(void)
 {
 	struct mtd_info *mtd_env;
-	char *buf, *tmp;
-	size_t ret_len;
-	int remaining;
-	u32 sect_size;
-	u32 offset;
-	int ret;
+	char *buf, *buf2 = NULL;
+	int ret, read1_fail, read2_fail = 1;
 
 	buf = (char *)memalign(ARCH_DMA_MINALIGN, CONFIG_ENV_SIZE);
 	if (!buf) {
@@ -159,40 +189,34 @@ static int env_mtd_load(void)
 	if (ret)
 		goto out;
 
-	sect_size = mtd_env->erasesize;
-
-	offset = CONFIG_ENV_OFFSET;
-	remaining = CONFIG_ENV_SIZE;
-	tmp = buf;
-
-	while (remaining) {
-		/* Skip the block if it is bad */
-		if (!(offset % sect_size) &&
-		    mtd_block_isbad(mtd_env, offset)) {
-			offset += sect_size;
-			continue;
-		}
-
-		ret = mtd_read(mtd_env, offset, mtd_env->writesize,
-			       &ret_len, tmp);
-		if (ret) {
-			env_set_default("mtd_read() failed", 1);
-			goto out;
-		}
-
-		tmp += ret_len;
-		offset += ret_len;
-		remaining -= ret_len;
+	read1_fail = env_mtd_read_buf(mtd_env, CONFIG_ENV_OFFSET, buf,
+				      CONFIG_ENV_SIZE);
+#ifdef CONFIG_ENV_OFFSET_REDUND
+	buf2 = (char *)memalign(ARCH_DMA_MINALIGN, CONFIG_ENV_SIZE);
+	if (!buf2) {
+		env_set_default("memalign() failed", 0);
+		ret = -EIO;
+		goto out_dev;
+	}
+	read2_fail = env_mtd_read_buf(mtd_env, CONFIG_ENV_OFFSET_REDUND, buf2,
+				      CONFIG_ENV_SIZE);
+	ret = env_import_redund(buf, read1_fail, buf2, read2_fail, H_EXTERNAL);
+#else
+	if (read1_fail) {
+		env_set_default("mtd_read() failed", 1);
+		ret = read1_fail;
+		goto out_dev;
 	}
-
 	ret = env_import(buf, 1, H_EXTERNAL);
 	if (!ret)
 		gd->env_valid = ENV_VALID;
+#endif
 
-out:
+out_dev:
 	put_mtd_device(mtd_env);
-
+out:
 	free(buf);
+	free(buf2);
 
 	return ret;
 }
-- 
2.53.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.