aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Rini2013-02-22 15:55:19 -0600
committerTom Rini2013-03-12 13:22:31 -0500
commitd8699dca33973ba7813949b9e0eea292eeec7293 (patch)
treea767f6834c18b17feec4abb9a928b16cd326d646
parentc79739dd35caaf9fd66c007aff5d32f2bfb6ac15 (diff)
downloadti-u-boot-d8699dca33973ba7813949b9e0eea292eeec7293.tar.gz
ti-u-boot-d8699dca33973ba7813949b9e0eea292eeec7293.tar.xz
ti-u-boot-d8699dca33973ba7813949b9e0eea292eeec7293.zip
nand: Extend nand_(read|write)_skip_bad with *actual and limit parameters
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 count not just complete blocks used but partial ones as well. 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. Signed-off-by: Pantelis Antoniou <panto@antoniou-consulting.com> Signed-off-by: Tom Rini <trini@ti.com>
-rw-r--r--common/cmd_nand.c53
-rw-r--r--common/env_nand.c3
-rw-r--r--drivers/mtd/nand/nand_util.c68
-rw-r--r--include/nand.h4
4 files changed, 95 insertions, 33 deletions
diff --git a/common/cmd_nand.c b/common/cmd_nand.c
index 1568594ca4..54107933bf 100644
--- a/common/cmd_nand.c
+++ b/common/cmd_nand.c
@@ -137,7 +137,8 @@ static inline int str2long(const char *p, ulong *num)
137 return *p != '\0' && *endptr == '\0'; 137 return *p != '\0' && *endptr == '\0';
138} 138}
139 139
140static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size) 140static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size,
141 loff_t *maxsize)
141{ 142{
142#ifdef CONFIG_CMD_MTDPARTS 143#ifdef CONFIG_CMD_MTDPARTS
143 struct mtd_device *dev; 144 struct mtd_device *dev;
@@ -160,6 +161,7 @@ static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size)
160 161
161 *off = part->offset; 162 *off = part->offset;
162 *size = part->size; 163 *size = part->size;
164 *maxsize = part->size;
163 *idx = dev->id->num; 165 *idx = dev->id->num;
164 166
165 ret = set_dev(*idx); 167 ret = set_dev(*idx);
@@ -173,10 +175,11 @@ static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size)
173#endif 175#endif
174} 176}
175 177
176static int arg_off(const char *arg, int *idx, loff_t *off, loff_t *maxsize) 178static int arg_off(const char *arg, int *idx, loff_t *off, loff_t *size,
179 loff_t *maxsize)
177{ 180{
178 if (!str2off(arg, off)) 181 if (!str2off(arg, off))
179 return get_part(arg, idx, off, maxsize); 182 return get_part(arg, idx, off, size, maxsize);
180 183
181 if (*off >= nand_info[*idx].size) { 184 if (*off >= nand_info[*idx].size) {
182 puts("Offset exceeds device limit\n"); 185 puts("Offset exceeds device limit\n");
@@ -184,36 +187,35 @@ static int arg_off(const char *arg, int *idx, loff_t *off, loff_t *maxsize)
184 } 187 }
185 188
186 *maxsize = nand_info[*idx].size - *off; 189 *maxsize = nand_info[*idx].size - *off;
190 *size = *maxsize;
187 return 0; 191 return 0;
188} 192}
189 193
190static int arg_off_size(int argc, char *const argv[], int *idx, 194static int arg_off_size(int argc, char *const argv[], int *idx,
191 loff_t *off, loff_t *size) 195 loff_t *off, loff_t *size, loff_t *maxsize)
192{ 196{
193 int ret; 197 int ret;
194 loff_t maxsize = 0;
195 198
196 if (argc == 0) { 199 if (argc == 0) {
197 *off = 0; 200 *off = 0;
198 *size = nand_info[*idx].size; 201 *size = nand_info[*idx].size;
202 *maxsize = *size;
199 goto print; 203 goto print;
200 } 204 }
201 205
202 ret = arg_off(argv[0], idx, off, &maxsize); 206 ret = arg_off(argv[0], idx, off, size, maxsize);
203 if (ret) 207 if (ret)
204 return ret; 208 return ret;
205 209
206 if (argc == 1) { 210 if (argc == 1)
207 *size = maxsize;
208 goto print; 211 goto print;
209 }
210 212
211 if (!str2off(argv[1], size)) { 213 if (!str2off(argv[1], size)) {
212 printf("'%s' is not a number\n", argv[1]); 214 printf("'%s' is not a number\n", argv[1]);
213 return -1; 215 return -1;
214 } 216 }
215 217
216 if (*size > maxsize) { 218 if (*size > *maxsize) {
217 puts("Size exceeds partition or device limit\n"); 219 puts("Size exceeds partition or device limit\n");
218 return -1; 220 return -1;
219 } 221 }
@@ -307,7 +309,8 @@ int do_nand_env_oob(cmd_tbl_t *cmdtp, int argc, char *const argv[])
307 if (argc < 3) 309 if (argc < 3)
308 goto usage; 310 goto usage;
309 311
310 if (arg_off(argv[2], &idx, &addr, &maxsize)) { 312 /* We don't care about size, or maxsize. */
313 if (arg_off(argv[2], &idx, &addr, &maxsize, &maxsize)) {
311 puts("Offset or partition name expected\n"); 314 puts("Offset or partition name expected\n");
312 return 1; 315 return 1;
313 } 316 }
@@ -432,7 +435,7 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
432{ 435{
433 int i, ret = 0; 436 int i, ret = 0;
434 ulong addr; 437 ulong addr;
435 loff_t off, size; 438 loff_t off, size, maxsize;
436 char *cmd, *s; 439 char *cmd, *s;
437 nand_info_t *nand; 440 nand_info_t *nand;
438#ifdef CONFIG_SYS_NAND_QUIET 441#ifdef CONFIG_SYS_NAND_QUIET
@@ -557,7 +560,8 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
557 560
558 printf("\nNAND %s: ", cmd); 561 printf("\nNAND %s: ", cmd);
559 /* skip first two or three arguments, look for offset and size */ 562 /* skip first two or three arguments, look for offset and size */
560 if (arg_off_size(argc - o, argv + o, &dev, &off, &size) != 0) 563 if (arg_off_size(argc - o, argv + o, &dev, &off, &size,
564 &maxsize) != 0)
561 return 1; 565 return 1;
562 566
563 nand = &nand_info[dev]; 567 nand = &nand_info[dev];
@@ -625,7 +629,7 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
625 if (s && !strcmp(s, ".raw")) { 629 if (s && !strcmp(s, ".raw")) {
626 raw = 1; 630 raw = 1;
627 631
628 if (arg_off(argv[3], &dev, &off, &size)) 632 if (arg_off(argv[3], &dev, &off, &size, &maxsize))
629 return 1; 633 return 1;
630 634
631 if (argc > 4 && !str2long(argv[4], &pagecount)) { 635 if (argc > 4 && !str2long(argv[4], &pagecount)) {
@@ -641,7 +645,7 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
641 rwsize = pagecount * (nand->writesize + nand->oobsize); 645 rwsize = pagecount * (nand->writesize + nand->oobsize);
642 } else { 646 } else {
643 if (arg_off_size(argc - 3, argv + 3, &dev, 647 if (arg_off_size(argc - 3, argv + 3, &dev,
644 &off, &size) != 0) 648 &off, &size, &maxsize) != 0)
645 return 1; 649 return 1;
646 650
647 rwsize = size; 651 rwsize = size;
@@ -651,9 +655,11 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
651 !strcmp(s, ".e") || !strcmp(s, ".i")) { 655 !strcmp(s, ".e") || !strcmp(s, ".i")) {
652 if (read) 656 if (read)
653 ret = nand_read_skip_bad(nand, off, &rwsize, 657 ret = nand_read_skip_bad(nand, off, &rwsize,
658 NULL, maxsize,
654 (u_char *)addr); 659 (u_char *)addr);
655 else 660 else
656 ret = nand_write_skip_bad(nand, off, &rwsize, 661 ret = nand_write_skip_bad(nand, off, &rwsize,
662 NULL, maxsize,
657 (u_char *)addr, 0); 663 (u_char *)addr, 0);
658#ifdef CONFIG_CMD_NAND_TRIMFFS 664#ifdef CONFIG_CMD_NAND_TRIMFFS
659 } else if (!strcmp(s, ".trimffs")) { 665 } else if (!strcmp(s, ".trimffs")) {
@@ -661,8 +667,8 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
661 printf("Unknown nand command suffix '%s'\n", s); 667 printf("Unknown nand command suffix '%s'\n", s);
662 return 1; 668 return 1;
663 } 669 }
664 ret = nand_write_skip_bad(nand, off, &rwsize, 670 ret = nand_write_skip_bad(nand, off, &rwsize, NULL,
665 (u_char *)addr, 671 maxsize, (u_char *)addr,
666 WITH_DROP_FFS); 672 WITH_DROP_FFS);
667#endif 673#endif
668#ifdef CONFIG_CMD_NAND_YAFFS 674#ifdef CONFIG_CMD_NAND_YAFFS
@@ -671,8 +677,8 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
671 printf("Unknown nand command suffix '%s'.\n", s); 677 printf("Unknown nand command suffix '%s'.\n", s);
672 return 1; 678 return 1;
673 } 679 }
674 ret = nand_write_skip_bad(nand, off, &rwsize, 680 ret = nand_write_skip_bad(nand, off, &rwsize, NULL,
675 (u_char *)addr, 681 maxsize, (u_char *)addr,
676 WITH_INLINE_OOB); 682 WITH_INLINE_OOB);
677#endif 683#endif
678 } else if (!strcmp(s, ".oob")) { 684 } else if (!strcmp(s, ".oob")) {
@@ -781,7 +787,8 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
781 if (s && !strcmp(s, ".allexcept")) 787 if (s && !strcmp(s, ".allexcept"))
782 allexcept = 1; 788 allexcept = 1;
783 789
784 if (arg_off_size(argc - 2, argv + 2, &dev, &off, &size) < 0) 790 if (arg_off_size(argc - 2, argv + 2, &dev, &off, &size,
791 &maxsize) < 0)
785 return 1; 792 return 1;
786 793
787 if (!nand_unlock(&nand_info[dev], off, size, allexcept)) { 794 if (!nand_unlock(&nand_info[dev], off, size, allexcept)) {
@@ -879,7 +886,8 @@ static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand,
879 printf("\nLoading from %s, offset 0x%lx\n", nand->name, offset); 886 printf("\nLoading from %s, offset 0x%lx\n", nand->name, offset);
880 887
881 cnt = nand->writesize; 888 cnt = nand->writesize;
882 r = nand_read_skip_bad(nand, offset, &cnt, (u_char *) addr); 889 r = nand_read_skip_bad(nand, offset, &cnt, NULL, nand->size,
890 (u_char *) addr);
883 if (r) { 891 if (r) {
884 puts("** Read error\n"); 892 puts("** Read error\n");
885 bootstage_error(BOOTSTAGE_ID_NAND_HDR_READ); 893 bootstage_error(BOOTSTAGE_ID_NAND_HDR_READ);
@@ -911,7 +919,8 @@ static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand,
911 } 919 }
912 bootstage_mark(BOOTSTAGE_ID_NAND_TYPE); 920 bootstage_mark(BOOTSTAGE_ID_NAND_TYPE);
913 921
914 r = nand_read_skip_bad(nand, offset, &cnt, (u_char *) addr); 922 r = nand_read_skip_bad(nand, offset, &cnt, NULL, nand->size,
923 (u_char *) addr);
915 if (r) { 924 if (r) {
916 puts("** Read error\n"); 925 puts("** Read error\n");
917 bootstage_error(BOOTSTAGE_ID_NAND_READ); 926 bootstage_error(BOOTSTAGE_ID_NAND_READ);
diff --git a/common/env_nand.c b/common/env_nand.c
index 22e72a20b0..123bcc7273 100644
--- a/common/env_nand.c
+++ b/common/env_nand.c
@@ -281,7 +281,8 @@ int readenv(size_t offset, u_char *buf)
281 } else { 281 } else {
282 char_ptr = &buf[amount_loaded]; 282 char_ptr = &buf[amount_loaded];
283 if (nand_read_skip_bad(&nand_info[0], offset, 283 if (nand_read_skip_bad(&nand_info[0], offset,
284 &len, char_ptr)) 284 &len, NULL,
285 nand_info[0].size, char_ptr))
285 return 1; 286 return 1;
286 287
287 offset += blocksize; 288 offset += blocksize;
diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c
index 2ba0c5ef95..a5c91f833a 100644
--- a/drivers/mtd/nand/nand_util.c
+++ b/drivers/mtd/nand/nand_util.c
@@ -399,11 +399,13 @@ int nand_unlock(struct mtd_info *mtd, loff_t start, size_t length,
399 * @param nand NAND device 399 * @param nand NAND device
400 * @param offset offset in flash 400 * @param offset offset in flash
401 * @param length image length 401 * @param length image length
402 * @param used length of flash needed for the requested length
402 * @return 0 if the image fits and there are no bad blocks 403 * @return 0 if the image fits and there are no bad blocks
403 * 1 if the image fits, but there are bad blocks 404 * 1 if the image fits, but there are bad blocks
404 * -1 if the image does not fit 405 * -1 if the image does not fit
405 */ 406 */
406static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length) 407static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length,
408 size_t *used)
407{ 409{
408 size_t len_excl_bad = 0; 410 size_t len_excl_bad = 0;
409 int ret = 0; 411 int ret = 0;
@@ -425,8 +427,13 @@ static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length)
425 ret = 1; 427 ret = 1;
426 428
427 offset += block_len; 429 offset += block_len;
430 *used += block_len;
428 } 431 }
429 432
433 /* If the length is not a multiple of block_len, adjust. */
434 if (len_excl_bad > length)
435 *used -= (len_excl_bad - length);
436
430 return ret; 437 return ret;
431} 438}
432 439
@@ -459,23 +466,36 @@ static size_t drop_ffs(const nand_info_t *nand, const u_char *buf,
459 * Write image to NAND flash. 466 * Write image to NAND flash.
460 * Blocks that are marked bad are skipped and the is written to the next 467 * Blocks that are marked bad are skipped and the is written to the next
461 * block instead as long as the image is short enough to fit even after 468 * block instead as long as the image is short enough to fit even after
462 * skipping the bad blocks. 469 * skipping the bad blocks. Due to bad blocks we may not be able to
470 * perform the requested write. In the case where the write would
471 * extend beyond the end of the NAND device, both length and actual (if
472 * not NULL) are set to 0. In the case where the write would extend
473 * beyond the limit we are passed, length is set to 0 and actual is set
474 * to the required length.
463 * 475 *
464 * @param nand NAND device 476 * @param nand NAND device
465 * @param offset offset in flash 477 * @param offset offset in flash
466 * @param length buffer length 478 * @param length buffer length
479 * @param actual set to size required to write length worth of
480 * buffer or 0 on error, if not NULL
481 * @param lim maximum size that actual may be in order to not
482 * exceed the buffer
467 * @param buffer buffer to read from 483 * @param buffer buffer to read from
468 * @param flags flags modifying the behaviour of the write to NAND 484 * @param flags flags modifying the behaviour of the write to NAND
469 * @return 0 in case of success 485 * @return 0 in case of success
470 */ 486 */
471int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, 487int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
472 u_char *buffer, int flags) 488 size_t *actual, loff_t lim, u_char *buffer, int flags)
473{ 489{
474 int rval = 0, blocksize; 490 int rval = 0, blocksize;
475 size_t left_to_write = *length; 491 size_t left_to_write = *length;
492 size_t used_for_write = 0;
476 u_char *p_buffer = buffer; 493 u_char *p_buffer = buffer;
477 int need_skip; 494 int need_skip;
478 495
496 if (actual)
497 *actual = 0;
498
479#ifdef CONFIG_CMD_NAND_YAFFS 499#ifdef CONFIG_CMD_NAND_YAFFS
480 if (flags & WITH_YAFFS_OOB) { 500 if (flags & WITH_YAFFS_OOB) {
481 if (flags & ~WITH_YAFFS_OOB) 501 if (flags & ~WITH_YAFFS_OOB)
@@ -512,13 +532,23 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
512 return -EINVAL; 532 return -EINVAL;
513 } 533 }
514 534
515 need_skip = check_skip_len(nand, offset, *length); 535 need_skip = check_skip_len(nand, offset, *length, &used_for_write);
536
537 if (actual)
538 *actual = used_for_write;
539
516 if (need_skip < 0) { 540 if (need_skip < 0) {
517 printf("Attempt to write outside the flash area\n"); 541 printf("Attempt to write outside the flash area\n");
518 *length = 0; 542 *length = 0;
519 return -EINVAL; 543 return -EINVAL;
520 } 544 }
521 545
546 if (used_for_write > lim) {
547 puts("Size of write exceeds partition or device limit\n");
548 *length = 0;
549 return -EFBIG;
550 }
551
522 if (!need_skip && !(flags & WITH_DROP_FFS)) { 552 if (!need_skip && !(flags & WITH_DROP_FFS)) {
523 rval = nand_write(nand, offset, length, buffer); 553 rval = nand_write(nand, offset, length, buffer);
524 if (rval == 0) 554 if (rval == 0)
@@ -609,36 +639,58 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
609 * 639 *
610 * Read image from NAND flash. 640 * Read image from NAND flash.
611 * Blocks that are marked bad are skipped and the next block is read 641 * Blocks that are marked bad are skipped and the next block is read
612 * instead as long as the image is short enough to fit even after skipping the 642 * instead as long as the image is short enough to fit even after
613 * bad blocks. 643 * skipping the bad blocks. Due to bad blocks we may not be able to
644 * perform the requested read. In the case where the read would extend
645 * beyond the end of the NAND device, both length and actual (if not
646 * NULL) are set to 0. In the case where the read would extend beyond
647 * the limit we are passed, length is set to 0 and actual is set to the
648 * required length.
614 * 649 *
615 * @param nand NAND device 650 * @param nand NAND device
616 * @param offset offset in flash 651 * @param offset offset in flash
617 * @param length buffer length, on return holds number of read bytes 652 * @param length buffer length, on return holds number of read bytes
653 * @param actual set to size required to read length worth of buffer or 0
654 * on error, if not NULL
655 * @param lim maximum size that actual may be in order to not exceed the
656 * buffer
618 * @param buffer buffer to write to 657 * @param buffer buffer to write to
619 * @return 0 in case of success 658 * @return 0 in case of success
620 */ 659 */
621int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, 660int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
622 u_char *buffer) 661 size_t *actual, loff_t lim, u_char *buffer)
623{ 662{
624 int rval; 663 int rval;
625 size_t left_to_read = *length; 664 size_t left_to_read = *length;
665 size_t used_for_read = 0;
626 u_char *p_buffer = buffer; 666 u_char *p_buffer = buffer;
627 int need_skip; 667 int need_skip;
628 668
629 if ((offset & (nand->writesize - 1)) != 0) { 669 if ((offset & (nand->writesize - 1)) != 0) {
630 printf("Attempt to read non page-aligned data\n"); 670 printf("Attempt to read non page-aligned data\n");
631 *length = 0; 671 *length = 0;
672 if (actual)
673 *actual = 0;
632 return -EINVAL; 674 return -EINVAL;
633 } 675 }
634 676
635 need_skip = check_skip_len(nand, offset, *length); 677 need_skip = check_skip_len(nand, offset, *length, &used_for_read);
678
679 if (actual)
680 *actual = used_for_read;
681
636 if (need_skip < 0) { 682 if (need_skip < 0) {
637 printf("Attempt to read outside the flash area\n"); 683 printf("Attempt to read outside the flash area\n");
638 *length = 0; 684 *length = 0;
639 return -EINVAL; 685 return -EINVAL;
640 } 686 }
641 687
688 if (used_for_read > lim) {
689 puts("Size of read exceeds partition or device limit\n");
690 *length = 0;
691 return -EFBIG;
692 }
693
642 if (!need_skip) { 694 if (!need_skip) {
643 rval = nand_read(nand, offset, length, buffer); 695 rval = nand_read(nand, offset, length, buffer);
644 if (!rval || rval == -EUCLEAN) 696 if (!rval || rval == -EUCLEAN)
diff --git a/include/nand.h b/include/nand.h
index dded4e27f0..f0f3bf94b5 100644
--- a/include/nand.h
+++ b/include/nand.h
@@ -129,7 +129,7 @@ struct nand_erase_options {
129typedef struct nand_erase_options nand_erase_options_t; 129typedef struct nand_erase_options nand_erase_options_t;
130 130
131int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, 131int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
132 u_char *buffer); 132 size_t *actual, loff_t lim, u_char *buffer);
133 133
134#define WITH_YAFFS_OOB (1 << 0) /* whether write with yaffs format. This flag 134#define WITH_YAFFS_OOB (1 << 0) /* whether write with yaffs format. This flag
135 * is a 'mode' meaning it cannot be mixed with 135 * 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,
137#define WITH_DROP_FFS (1 << 1) /* drop trailing all-0xff pages */ 137#define WITH_DROP_FFS (1 << 1) /* drop trailing all-0xff pages */
138 138
139int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, 139int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
140 u_char *buffer, int flags); 140 size_t *actual, loff_t lim, u_char *buffer, int flags);
141int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts); 141int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts);
142int nand_torture(nand_info_t *nand, loff_t offset); 142int nand_torture(nand_info_t *nand, loff_t offset);
143 143