[U-Boot] [PATCH v2 1/4] nand: Extend nand_(read|write)_skip_bad with *actual and limit parameters
Tom Rini
trini at ti.com
Tue Feb 26 16:56:08 CET 2013
We make these two functions take a size_t pointer to how much space
was used on NAND to read or write the buffer (when reads/writes happen)
so that bad blocks can be accounted for. We also make them take an
loff_t limit on how much data can be read or written. This means that
we can now catch the case of when writing to a partition would exceed
the partition size due to bad blocks. To do this we also need to make
check_skip_len care about total actual size used rather than block_size
chunks used. All callers of nand_(read|write)_skip_bad are adjusted to
call these with the most sensible limits available.
The changes were started by Pantelis and finished by Tom.
Cc: Scott Wood <scottwood at freescale.com>
Signed-off-by: Pantelis Antoniou <panto at antoniou-consulting.com>
Signed-off-by: Tom Rini <trini at ti.com>
---
common/cmd_nand.c | 61 +++++++++++++++++++++--------------------
common/env_nand.c | 5 ++--
drivers/mtd/nand/nand_util.c | 62 +++++++++++++++++++++++++++++++-----------
include/nand.h | 4 +--
4 files changed, 82 insertions(+), 50 deletions(-)
diff --git a/common/cmd_nand.c b/common/cmd_nand.c
index 1568594..e091e02 100644
--- a/common/cmd_nand.c
+++ b/common/cmd_nand.c
@@ -137,7 +137,8 @@ static inline int str2long(const char *p, ulong *num)
return *p != '\0' && *endptr == '\0';
}
-static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size)
+static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size,
+ loff_t *maxsize)
{
#ifdef CONFIG_CMD_MTDPARTS
struct mtd_device *dev;
@@ -160,6 +161,7 @@ static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size)
*off = part->offset;
*size = part->size;
+ *maxsize = part->offset + part->size;
*idx = dev->id->num;
ret = set_dev(*idx);
@@ -173,10 +175,11 @@ static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size)
#endif
}
-static int arg_off(const char *arg, int *idx, loff_t *off, loff_t *maxsize)
+static int arg_off(const char *arg, int *idx, loff_t *off, loff_t *size,
+ loff_t *maxsize)
{
if (!str2off(arg, off))
- return get_part(arg, idx, off, maxsize);
+ return get_part(arg, idx, off, size, maxsize);
if (*off >= nand_info[*idx].size) {
puts("Offset exceeds device limit\n");
@@ -188,36 +191,29 @@ static int arg_off(const char *arg, int *idx, loff_t *off, loff_t *maxsize)
}
static int arg_off_size(int argc, char *const argv[], int *idx,
- loff_t *off, loff_t *size)
+ loff_t *off, loff_t *size, loff_t *maxsize)
{
int ret;
- loff_t maxsize = 0;
if (argc == 0) {
*off = 0;
*size = nand_info[*idx].size;
+ *maxsize = *size;
goto print;
}
- ret = arg_off(argv[0], idx, off, &maxsize);
+ ret = arg_off(argv[0], idx, off, size, maxsize);
if (ret)
return ret;
- if (argc == 1) {
- *size = maxsize;
+ if (argc == 1)
goto print;
- }
if (!str2off(argv[1], size)) {
printf("'%s' is not a number\n", argv[1]);
return -1;
}
- if (*size > maxsize) {
- puts("Size exceeds partition or device limit\n");
- return -1;
- }
-
print:
printf("device %d ", *idx);
if (*size == nand_info[*idx].size)
@@ -299,15 +295,14 @@ int do_nand_env_oob(cmd_tbl_t *cmdtp, int argc, char *const argv[])
printf("0x%08lx\n", nand_env_oob_offset);
} else if (!strcmp(cmd, "set")) {
- loff_t addr;
- loff_t maxsize;
+ loff_t addr, size, maxsize;
struct mtd_oob_ops ops;
int idx = 0;
if (argc < 3)
goto usage;
- if (arg_off(argv[2], &idx, &addr, &maxsize)) {
+ if (arg_off(argv[2], &idx, &addr, &size, &maxsize)) {
puts("Offset or partition name expected\n");
return 1;
}
@@ -432,7 +427,7 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
int i, ret = 0;
ulong addr;
- loff_t off, size;
+ loff_t off, size, maxsize;
char *cmd, *s;
nand_info_t *nand;
#ifdef CONFIG_SYS_NAND_QUIET
@@ -557,7 +552,8 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
printf("\nNAND %s: ", cmd);
/* skip first two or three arguments, look for offset and size */
- if (arg_off_size(argc - o, argv + o, &dev, &off, &size) != 0)
+ if (arg_off_size(argc - o, argv + o, &dev, &off, &size,
+ &maxsize) != 0)
return 1;
nand = &nand_info[dev];
@@ -605,7 +601,7 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
}
if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {
- size_t rwsize;
+ size_t rwsize, actual;
ulong pagecount = 1;
int read;
int raw;
@@ -625,7 +621,7 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
if (s && !strcmp(s, ".raw")) {
raw = 1;
- if (arg_off(argv[3], &dev, &off, &size))
+ if (arg_off(argv[3], &dev, &off, &size, &maxsize))
return 1;
if (argc > 4 && !str2long(argv[4], &pagecount)) {
@@ -641,7 +637,7 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
rwsize = pagecount * (nand->writesize + nand->oobsize);
} else {
if (arg_off_size(argc - 3, argv + 3, &dev,
- &off, &size) != 0)
+ &off, &size, &maxsize) != 0)
return 1;
rwsize = size;
@@ -651,9 +647,11 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
!strcmp(s, ".e") || !strcmp(s, ".i")) {
if (read)
ret = nand_read_skip_bad(nand, off, &rwsize,
+ &actual, maxsize,
(u_char *)addr);
else
ret = nand_write_skip_bad(nand, off, &rwsize,
+ &actual, maxsize,
(u_char *)addr, 0);
#ifdef CONFIG_CMD_NAND_TRIMFFS
} else if (!strcmp(s, ".trimffs")) {
@@ -661,8 +659,8 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
printf("Unknown nand command suffix '%s'\n", s);
return 1;
}
- ret = nand_write_skip_bad(nand, off, &rwsize,
- (u_char *)addr,
+ ret = nand_write_skip_bad(nand, off, &rwsize, &actual,
+ maxsize, (u_char *)addr,
WITH_DROP_FFS);
#endif
#ifdef CONFIG_CMD_NAND_YAFFS
@@ -671,8 +669,8 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
printf("Unknown nand command suffix '%s'.\n", s);
return 1;
}
- ret = nand_write_skip_bad(nand, off, &rwsize,
- (u_char *)addr,
+ ret = nand_write_skip_bad(nand, off, &rwsize, &actual,
+ maxsize, (u_char *)addr,
WITH_INLINE_OOB);
#endif
} else if (!strcmp(s, ".oob")) {
@@ -781,7 +779,8 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
if (s && !strcmp(s, ".allexcept"))
allexcept = 1;
- if (arg_off_size(argc - 2, argv + 2, &dev, &off, &size) < 0)
+ if (arg_off_size(argc - 2, argv + 2, &dev, &off, &size,
+ &maxsize) < 0)
return 1;
if (!nand_unlock(&nand_info[dev], off, size, allexcept)) {
@@ -862,7 +861,7 @@ static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand,
{
int r;
char *s;
- size_t cnt;
+ size_t cnt, actual;
image_header_t *hdr;
#if defined(CONFIG_FIT)
const void *fit_hdr = NULL;
@@ -879,7 +878,8 @@ static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand,
printf("\nLoading from %s, offset 0x%lx\n", nand->name, offset);
cnt = nand->writesize;
- r = nand_read_skip_bad(nand, offset, &cnt, (u_char *) addr);
+ r = nand_read_skip_bad(nand, offset, &cnt, &actual, nand->size,
+ (u_char *) addr);
if (r) {
puts("** Read error\n");
bootstage_error(BOOTSTAGE_ID_NAND_HDR_READ);
@@ -911,7 +911,8 @@ static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand,
}
bootstage_mark(BOOTSTAGE_ID_NAND_TYPE);
- r = nand_read_skip_bad(nand, offset, &cnt, (u_char *) addr);
+ r = nand_read_skip_bad(nand, offset, &cnt, &actual, nand->size,
+ (u_char *) addr);
if (r) {
puts("** Read error\n");
bootstage_error(BOOTSTAGE_ID_NAND_READ);
diff --git a/common/env_nand.c b/common/env_nand.c
index 22e72a2..05efbf5 100644
--- a/common/env_nand.c
+++ b/common/env_nand.c
@@ -266,7 +266,7 @@ int readenv(size_t offset, u_char *buf)
{
size_t end = offset + CONFIG_ENV_RANGE;
size_t amount_loaded = 0;
- size_t blocksize, len;
+ size_t blocksize, len, actual;
u_char *char_ptr;
blocksize = nand_info[0].erasesize;
@@ -281,7 +281,8 @@ int readenv(size_t offset, u_char *buf)
} else {
char_ptr = &buf[amount_loaded];
if (nand_read_skip_bad(&nand_info[0], offset,
- &len, char_ptr))
+ &len, &actual,
+ nand_info[0].size, char_ptr))
return 1;
offset += blocksize;
diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c
index 2ba0c5e..5ed5b1d 100644
--- a/drivers/mtd/nand/nand_util.c
+++ b/drivers/mtd/nand/nand_util.c
@@ -397,34 +397,38 @@ int nand_unlock(struct mtd_info *mtd, loff_t start, size_t length,
* blocks fits into device
*
* @param nand NAND device
- * @param offset offset in flash
+ * @param offsetp offset in flash (on exit offset where it's ending)
* @param length image length
* @return 0 if the image fits and there are no bad blocks
* 1 if the image fits, but there are bad blocks
* -1 if the image does not fit
*/
-static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length)
+static int check_skip_len(nand_info_t *nand, loff_t *offset, size_t length)
{
- size_t len_excl_bad = 0;
int ret = 0;
- while (len_excl_bad < length) {
+ while (length > 0) {
size_t block_len, block_off;
loff_t block_start;
- if (offset >= nand->size)
+ if (*offset >= nand->size)
return -1;
- block_start = offset & ~(loff_t)(nand->erasesize - 1);
- block_off = offset & (nand->erasesize - 1);
+ block_start = *offset & ~(loff_t)(nand->erasesize - 1);
+ block_off = *offset & (nand->erasesize - 1);
block_len = nand->erasesize - block_off;
- if (!nand_block_isbad(nand, block_start))
- len_excl_bad += block_len;
- else
+ if (!nand_block_isbad(nand, block_start)) {
+ if (block_len > length) {
+ /* Final chunk is smaller than block. */
+ *offset += length;
+ return ret;
+ } else
+ length -= block_len;
+ } else
ret = 1;
- offset += block_len;
+ *offset += block_len;
}
return ret;
@@ -459,22 +463,26 @@ static size_t drop_ffs(const nand_info_t *nand, const u_char *buf,
* Write image to NAND flash.
* Blocks that are marked bad are skipped and the is written to the next
* block instead as long as the image is short enough to fit even after
- * skipping the bad blocks.
+ * skipping the bad blocks. Note that the actual size needed may exceed
+ * both the length and available NAND due to bad blocks.
*
* @param nand NAND device
* @param offset offset in flash
* @param length buffer length
+ * @param actual size required to write length worth of buffer
+ * @param lim end location of where data in the buffer may be written.
* @param buffer buffer to read from
* @param flags flags modifying the behaviour of the write to NAND
* @return 0 in case of success
*/
int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
- u_char *buffer, int flags)
+ size_t *actual, loff_t lim, u_char *buffer, int flags)
{
int rval = 0, blocksize;
size_t left_to_write = *length;
u_char *p_buffer = buffer;
int need_skip;
+ loff_t tmp_offset;
#ifdef CONFIG_CMD_NAND_YAFFS
if (flags & WITH_YAFFS_OOB) {
@@ -509,16 +517,25 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
if ((offset & (nand->writesize - 1)) != 0) {
printf("Attempt to write non page-aligned data\n");
*length = 0;
+ *actual = 0;
return -EINVAL;
}
- need_skip = check_skip_len(nand, offset, *length);
+ tmp_offset = offset;
+ need_skip = check_skip_len(nand, &tmp_offset, *length);
+ *actual = tmp_offset;
if (need_skip < 0) {
printf("Attempt to write outside the flash area\n");
*length = 0;
return -EINVAL;
}
+ if (*actual > lim) {
+ puts("Size of write exceeds partition or device limit\n");
+ *length = 0;
+ return -EFBIG;
+ }
+
if (!need_skip && !(flags & WITH_DROP_FFS)) {
rval = nand_write(nand, offset, length, buffer);
if (rval == 0)
@@ -610,35 +627,48 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
* Read image from NAND flash.
* Blocks that are marked bad are skipped and the next block is read
* instead as long as the image is short enough to fit even after skipping the
+ * bad blocks. Note that the actual size needed may exceed the length due to
* bad blocks.
*
* @param nand NAND device
* @param offset offset in flash
* @param length buffer length, on return holds number of read bytes
+ * @param actual size required to read length worth of buffer
+ * @param lim end location of where data in the buffer may be written.
* @param buffer buffer to write to
* @return 0 in case of success
*/
int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
- u_char *buffer)
+ size_t *actual, loff_t lim, u_char *buffer)
{
int rval;
size_t left_to_read = *length;
u_char *p_buffer = buffer;
int need_skip;
+ loff_t tmp_offset;
if ((offset & (nand->writesize - 1)) != 0) {
printf("Attempt to read non page-aligned data\n");
*length = 0;
+ *actual = 0;
return -EINVAL;
}
- need_skip = check_skip_len(nand, offset, *length);
+ tmp_offset = offset;
+ need_skip = check_skip_len(nand, &tmp_offset, *length);
+ *actual = tmp_offset;
if (need_skip < 0) {
printf("Attempt to read outside the flash area\n");
*length = 0;
return -EINVAL;
}
+ if (*actual > lim) {
+ puts("Size of read exceeds partition or device limit\n");
+ *length = 0;
+ return -EFBIG;
+ }
+
if (!need_skip) {
rval = nand_read(nand, offset, length, buffer);
if (!rval || rval == -EUCLEAN)
diff --git a/include/nand.h b/include/nand.h
index dded4e2..f0f3bf9 100644
--- a/include/nand.h
+++ b/include/nand.h
@@ -129,7 +129,7 @@ struct nand_erase_options {
typedef struct nand_erase_options nand_erase_options_t;
int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
- u_char *buffer);
+ size_t *actual, loff_t lim, u_char *buffer);
#define WITH_YAFFS_OOB (1 << 0) /* whether write with yaffs format. This flag
* is a 'mode' meaning it cannot be mixed with
@@ -137,7 +137,7 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
#define WITH_DROP_FFS (1 << 1) /* drop trailing all-0xff pages */
int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
- u_char *buffer, int flags);
+ size_t *actual, loff_t lim, u_char *buffer, int flags);
int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts);
int nand_torture(nand_info_t *nand, loff_t offset);
--
1.7.9.5
More information about the U-Boot
mailing list