From 40462e541d740e6af634fc793164e49317b38a11 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Wed, 4 Sep 2013 15:16:56 +0100 Subject: mtd: driver _read() returns max_bitflips; mtd_read() returns -EUCLEAN Linux modified the MTD driver interface in commit edbc4540 (with the same name as this commit). The effect is that calls to mtd_read will not return -EUCLEAN if the number of ECC-corrected bit errors is below a certain threshold, which defaults to the strength of the ECC. This allows -EUCLEAN to stop indicating "some bits were corrected" and begin indicating "a large number of bits were corrected, the data held in this region of flash may be lost soon". UBI makes use of this and when -EUCLEAN is returned from mtd_read it will move data to another block of flash. Without adopting this interface change UBI on U-boot attempts to move data between blocks every time a single bit is corrected using the ECC, which is a very common occurance on some devices. For some devices where bit errors are common enough, UBI can get stuck constantly moving data around because each block it attempts to use has a single bit error. This condition is hit when wear_leveling_worker attempts to move data from one PEB to another in response to an -EUCLEAN/UBI_IO_BITFLIPS error. When this happens ubi_eba_copy_leb is called to perform the data copy, and after the data is written it is read back to check its validity. If that read returns UBI_IO_BITFLIPS (in response to an MTD -EUCLEAN) then ubi_eba_copy_leb returns 1 to wear_leveling worker, which then proceeds to schedule the destination PEB for erasure. This leads to erase_worker running on the PEB, and following a successful erase wear_leveling_worker is called which begins this whole cycle all over again. The end result is that (without UBI debug output enabled) the boot appears to simply hang whilst in reality U-boot busily works away at destroying a block of the NAND flash. Debug output from this situation: UBI DBG: ensure_wear_leveling: schedule scrubbing UBI DBG: wear_leveling_worker: scrub PEB 1027 to PEB 4083 UBI DBG: ubi_io_read_vid_hdr: read VID header from PEB 1027 UBI DBG: ubi_io_read: read 4096 bytes from PEB 1027:4096 UBI DBG: ubi_eba_copy_leb: copy LEB 0:0, PEB 1027 to PEB 4083 UBI DBG: ubi_eba_copy_leb: read 1040384 bytes of data UBI DBG: ubi_io_read: read 1040384 bytes from PEB 1027:8192 UBI: fixable bit-flip detected at PEB 1027 UBI DBG: ubi_io_write_vid_hdr: write VID header to PEB 4083 UBI DBG: ubi_io_write: write 4096 bytes to PEB 4083:4096 UBI DBG: ubi_io_read_vid_hdr: read VID header from PEB 4083 UBI DBG: ubi_io_read: read 4096 bytes from PEB 4083:4096 UBI DBG: ubi_io_write: write 4096 bytes to PEB 4083:8192 UBI DBG: ubi_io_read: read 4096 bytes from PEB 4083:8192 UBI: fixable bit-flip detected at PEB 4083 UBI DBG: schedule_erase: schedule erasure of PEB 4083, EC 55, torture 0 UBI DBG: erase_worker: erase PEB 4083 EC 55 UBI DBG: sync_erase: erase PEB 4083, old EC 55 UBI DBG: do_sync_erase: erase PEB 4083 UBI DBG: sync_erase: erased PEB 4083, new EC 56 UBI DBG: ubi_io_write_ec_hdr: write EC header to PEB 4083 UBI DBG: ubi_io_write: write 4096 bytes to PEB 4083:0 UBI DBG: ensure_wear_leveling: schedule scrubbing UBI DBG: wear_leveling_worker: scrub PEB 1027 to PEB 4083 ... This patch adopts the interface change as in Linux commit edbc4540 in order to avoid such situations. Given that none of the drivers under drivers/mtd return -EUCLEAN, this should only affect those using software ECC. I have tested that it works on a board which is currently out of tree, but which I hope to be able to begin upstreaming soon. Signed-off-by: Paul Burton Acked-by: Stefan Roese diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 49c0814..deda5f2 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -217,11 +217,23 @@ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr) int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { + int ret_code; if (from < 0 || from > mtd->size || len > mtd->size - from) return -EINVAL; if (!len) return 0; - return mtd->_read(mtd, from, len, retlen, buf); + + /* + * In the absence of an error, drivers return a non-negative integer + * representing the maximum number of bitflips that were corrected on + * any one ecc region (if applicable; zero otherwise). + */ + ret_code = mtd->_read(mtd, from, len, retlen, buf); + if (unlikely(ret_code < 0)) + return ret_code; + if (mtd->ecc_strength == 0) + return 0; /* device lacks ecc */ + return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0; } int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 9dfe7bb..146ce11 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -53,12 +53,12 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len, stats = part->master->ecc_stats; res = mtd_read(part->master, from + part->offset, len, retlen, buf); - if (unlikely(res)) { - if (mtd_is_bitflip(res)) - mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected; - if (mtd_is_eccerr(res)) - mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed; - } + if (unlikely(mtd_is_eccerr(res))) + mtd->ecc_stats.failed += + part->master->ecc_stats.failed - stats.failed; + else + mtd->ecc_stats.corrected += + part->master->ecc_stats.corrected - stats.corrected; return res; } diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 9e05cef..d4d586c 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1238,6 +1238,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, mtd->oobavail : mtd->oobsize; uint8_t *bufpoi, *oob, *buf; + unsigned int max_bitflips = 0; stats = mtd->ecc_stats; @@ -1265,7 +1266,10 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); - /* Now read the page into the buffer */ + /* + * Now read the page into the buffer. Absent an error, + * the read methods return max bitflips per ecc step. + */ if (unlikely(ops->mode == MTD_OPS_RAW)) ret = chip->ecc.read_page_raw(mtd, chip, bufpoi, oob_required, @@ -1284,15 +1288,19 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, break; } + max_bitflips = max_t(unsigned int, max_bitflips, ret); + /* Transfer not aligned data */ if (!aligned) { if (!NAND_HAS_SUBPAGE_READ(chip) && !oob && !(mtd->ecc_stats.failed - stats.failed) && - (ops->mode != MTD_OPS_RAW)) + (ops->mode != MTD_OPS_RAW)) { chip->pagebuf = realpage; - else + chip->pagebuf_bitflips = ret; + } else { /* Invalidate page cache */ chip->pagebuf = -1; + } memcpy(buf, chip->buffers->databuf + col, bytes); } @@ -1310,6 +1318,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, } else { memcpy(buf, chip->buffers->databuf + col, bytes); buf += bytes; + max_bitflips = max_t(unsigned int, max_bitflips, + chip->pagebuf_bitflips); } readlen -= bytes; @@ -1341,7 +1351,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; - return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; + return max_bitflips; } /** diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index ddfe7e7..067f8ef 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -969,7 +969,8 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; - return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; + /* return max bitflips per ecc step; ONENANDs correct 1 bit only */ + return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0; } /** diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 2055584..0546565 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -464,6 +464,8 @@ struct nand_buffers { * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 * @pagebuf: [INTERN] holds the pagenumber which is currently in * data_buf. + * @pagebuf_bitflips: [INTERN] holds the bitflip count for the page which is + * currently in data_buf. * @subpagesize: [INTERN] holds the subpagesize * @onfi_version: [INTERN] holds the chip ONFI version (BCD encoded), * non 0 if ONFI supported. @@ -531,6 +533,7 @@ struct nand_chip { uint64_t chipsize; int pagemask; int pagebuf; + unsigned int pagebuf_bitflips; int subpagesize; uint8_t cellinfo; int badblockpos; -- cgit v0.10.2 From 39ac34473f3c96e77cbe03a49141771ed1639486 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Wed, 4 Sep 2013 15:16:57 +0100 Subject: cmd_mtdparts: use 64 bits for flash size, partition size & offset This matches the 64 bit size in struct mtd_info and allows the mtdparts command to function correctly with a flash >= 4GiB. Format specifiers for size & offset are given the ll length, matching its use in drivers/mtd in absence of something like inttypes.h/PRIx64. Signed-off-by: Paul Burton Acked-by: Stefan Roese diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index f791372..40b6333 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -93,13 +93,13 @@ DECLARE_GLOBAL_DATA_PTR; /* special size referring to all the remaining space in a partition */ -#define SIZE_REMAINING 0xFFFFFFFF +#define SIZE_REMAINING (~0llu) /* special offset value, it is used when not provided by user * * this value is used temporarily during parsing, later such offests * are recalculated */ -#define OFFSET_NOT_SPECIFIED 0xFFFFFFFF +#define OFFSET_NOT_SPECIFIED (~0llu) /* minimum partition size */ #define MIN_PART_SIZE 4096 @@ -160,9 +160,9 @@ static int device_del(struct mtd_device *dev); * @param retptr output pointer to next char after parse completes (output) * @return resulting unsigned int */ -static unsigned long memsize_parse (const char *const ptr, const char **retptr) +static u64 memsize_parse (const char *const ptr, const char **retptr) { - unsigned long ret = simple_strtoul(ptr, (char **)retptr, 0); + u64 ret = simple_strtoull(ptr, (char **)retptr, 0); switch (**retptr) { case 'G': @@ -193,20 +193,20 @@ static unsigned long memsize_parse (const char *const ptr, const char **retptr) * @param buf output buffer * @param size size to be converted to string */ -static void memsize_format(char *buf, u32 size) +static void memsize_format(char *buf, u64 size) { #define SIZE_GB ((u32)1024*1024*1024) #define SIZE_MB ((u32)1024*1024) #define SIZE_KB ((u32)1024) if ((size % SIZE_GB) == 0) - sprintf(buf, "%ug", size/SIZE_GB); + sprintf(buf, "%llug", size/SIZE_GB); else if ((size % SIZE_MB) == 0) - sprintf(buf, "%um", size/SIZE_MB); + sprintf(buf, "%llum", size/SIZE_MB); else if (size % SIZE_KB == 0) - sprintf(buf, "%uk", size/SIZE_KB); + sprintf(buf, "%lluk", size/SIZE_KB); else - sprintf(buf, "%u", size); + sprintf(buf, "%llu", size); } /** @@ -310,6 +310,7 @@ static int part_validate_eraseblock(struct mtdids *id, struct part_info *part) struct mtd_info *mtd = NULL; int i, j; ulong start; + u64 offset, size; if (get_mtd_info(id->type, id->num, &mtd)) return 1; @@ -321,14 +322,16 @@ static int part_validate_eraseblock(struct mtdids *id, struct part_info *part) * Only one eraseregion (NAND, OneNAND or uniform NOR), * checking for alignment is easy here */ - if ((unsigned long)part->offset % mtd->erasesize) { + offset = part->offset; + if (do_div(offset, mtd->erasesize)) { printf("%s%d: partition (%s) start offset" "alignment incorrect\n", MTD_DEV_TYPE(id->type), id->num, part->name); return 1; } - if (part->size % mtd->erasesize) { + size = part->size; + if (do_div(size, mtd->erasesize)) { printf("%s%d: partition (%s) size alignment incorrect\n", MTD_DEV_TYPE(id->type), id->num, part->name); return 1; @@ -395,7 +398,7 @@ static int part_validate(struct mtdids *id, struct part_info *part) part->size = id->size - part->offset; if (part->offset > id->size) { - printf("%s: offset %08x beyond flash size %08x\n", + printf("%s: offset %08llx beyond flash size %08llx\n", id->mtd_id, part->offset, id->size); return 1; } @@ -578,8 +581,8 @@ static int part_add(struct mtd_device *dev, struct part_info *part) static int part_parse(const char *const partdef, const char **ret, struct part_info **retpart) { struct part_info *part; - unsigned long size; - unsigned long offset; + u64 size; + u64 offset; const char *name; int name_len; unsigned int mask_flags; @@ -598,7 +601,7 @@ static int part_parse(const char *const partdef, const char **ret, struct part_i } else { size = memsize_parse(p, &p); if (size < MIN_PART_SIZE) { - printf("partition size too small (%lx)\n", size); + printf("partition size too small (%llx)\n", size); return 1; } } @@ -670,14 +673,14 @@ static int part_parse(const char *const partdef, const char **ret, struct part_i part->auto_name = 0; } else { /* auto generated name in form of size@offset */ - sprintf(part->name, "0x%08lx@0x%08lx", size, offset); + sprintf(part->name, "0x%08llx@0x%08llx", size, offset); part->auto_name = 1; } part->name[name_len - 1] = '\0'; INIT_LIST_HEAD(&part->link); - debug("+ partition: name %-22s size 0x%08x offset 0x%08x mask flags %d\n", + debug("+ partition: name %-22s size 0x%08llx offset 0x%08llx mask flags %d\n", part->name, part->size, part->offset, part->mask_flags); @@ -693,7 +696,7 @@ static int part_parse(const char *const partdef, const char **ret, struct part_i * @param size a pointer to the size of the mtd device (output) * @return 0 if device is valid, 1 otherwise */ -static int mtd_device_validate(u8 type, u8 num, u32 *size) +static int mtd_device_validate(u8 type, u8 num, u64 *size) { struct mtd_info *mtd = NULL; @@ -826,7 +829,7 @@ static int device_parse(const char *const mtd_dev, const char **ret, struct mtd_ LIST_HEAD(tmp_list); struct list_head *entry, *n; u16 num_parts; - u32 offset; + u64 offset; int err = 1; debug("===device_parse===\n"); @@ -1071,7 +1074,8 @@ static int generate_mtdparts(char *buf, u32 buflen) struct part_info *part, *prev_part; char *p = buf; char tmpbuf[32]; - u32 size, offset, len, part_cnt; + u64 size, offset; + u32 len, part_cnt; u32 maxlen = buflen - 1; debug("--- generate_mtdparts ---\n"); @@ -1270,7 +1274,7 @@ static void print_partition_table(void) list_for_each(pentry, &dev->parts) { part = list_entry(pentry, struct part_info, link); - printf("%2d: %-20s0x%08x\t0x%08x\t%d\n", + printf("%2d: %-20s0x%08llx\t0x%08llx\t%d\n", part_num, part->name, part->size, part->offset, part->mask_flags); #endif /* defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */ @@ -1297,7 +1301,7 @@ static void list_partitions(void) if (current_mtd_dev) { part = mtd_part_info(current_mtd_dev, current_mtd_partnum); if (part) { - printf("\nactive partition: %s%d,%d - (%s) 0x%08x @ 0x%08x\n", + printf("\nactive partition: %s%d,%d - (%s) 0x%08llx @ 0x%08llx\n", MTD_DEV_TYPE(current_mtd_dev->id->type), current_mtd_dev->id->num, current_mtd_partnum, part->name, part->size, part->offset); @@ -1397,7 +1401,7 @@ static int delete_partition(const char *id) if (find_dev_and_part(id, &dev, &pnum, &part) == 0) { - debug("delete_partition: device = %s%d, partition %d = (%s) 0x%08x@0x%08x\n", + debug("delete_partition: device = %s%d, partition %d = (%s) 0x%08llx@0x%08llx\n", MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum, part->name, part->size, part->offset); @@ -1589,7 +1593,7 @@ static int parse_mtdids(const char *const ids) struct list_head *entry, *n; struct mtdids *id_tmp; u8 type, num; - u32 size; + u64 size; int ret = 1; debug("\n---parse_mtdids---\nmtdids = %s\n\n", ids); @@ -1663,7 +1667,7 @@ static int parse_mtdids(const char *const ids) id->mtd_id[mtd_id_len - 1] = '\0'; INIT_LIST_HEAD(&id->link); - debug("+ id %s%d\t%16d bytes\t%s\n", + debug("+ id %s%d\t%16lld bytes\t%s\n", MTD_DEV_TYPE(id->type), id->num, id->size, id->mtd_id); diff --git a/include/jffs2/load_kernel.h b/include/jffs2/load_kernel.h index e1943e5..dd0d23f 100644 --- a/include/jffs2/load_kernel.h +++ b/include/jffs2/load_kernel.h @@ -32,8 +32,8 @@ struct part_info { struct list_head link; char *name; /* partition name */ u8 auto_name; /* set to 1 for generated name */ - u32 size; /* total size of the partition */ - u32 offset; /* offset within device */ + u64 size; /* total size of the partition */ + u64 offset; /* offset within device */ void *jffs2_priv; /* used internaly by jffs2 */ u32 mask_flags; /* kernel MTD mask flags */ u32 sector_size; /* size of sector */ @@ -44,7 +44,7 @@ struct mtdids { struct list_head link; u8 type; /* device type */ u8 num; /* device number */ - u32 size; /* device size */ + u64 size; /* device size */ char *mtd_id; /* linux kernel device id */ }; -- cgit v0.10.2 From dd7185f1764fb8ed93940c2ac44cd6c400ebae7e Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Wed, 4 Sep 2013 15:16:58 +0100 Subject: cmd_ubi: use int64_t volume size for 'ubi create' int64_t matches the bytes field in struct ubi_mkvol_req to which the size is assigned. With the prior signed 32 bit integer, volumes were restricted to being less than 2GiB in size. Signed-off-by: Paul Burton Acked-by: Stefan Roese diff --git a/common/cmd_ubi.c b/common/cmd_ubi.c index 5ba4feb..f11cb61 100644 --- a/common/cmd_ubi.c +++ b/common/cmd_ubi.c @@ -167,7 +167,7 @@ bad: return err; } -static int ubi_create_vol(char *volume, int size, int dynamic) +static int ubi_create_vol(char *volume, int64_t size, int dynamic) { struct ubi_mkvol_req req; int err; @@ -191,7 +191,7 @@ static int ubi_create_vol(char *volume, int size, int dynamic) printf("verify_mkvol_req failed %d\n", err); return err; } - printf("Creating %s volume %s of size %d\n", + printf("Creating %s volume %s of size %lld\n", dynamic ? "dynamic" : "static", volume, size); /* Call real ubi create volume */ return ubi_create_volume(ubi, &req); @@ -498,7 +498,7 @@ int ubi_part(char *part_name, const char *vid_header_offset) static int do_ubi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - size_t size = 0; + int64_t size = 0; ulong addr = 0; if (argc < 2) @@ -558,13 +558,13 @@ static int do_ubi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } /* E.g., create volume size */ if (argc == 4) { - size = simple_strtoul(argv[3], NULL, 16); + size = simple_strtoull(argv[3], NULL, 16); argc--; } /* Use maximum available size */ if (!size) { - size = ubi->avail_pebs * ubi->leb_size; - printf("No size specified -> Using max size (%u)\n", size); + size = (int64_t)ubi->avail_pebs * ubi->leb_size; + printf("No size specified -> Using max size (%lld)\n", size); } /* E.g., create volume */ if (argc == 3) @@ -590,7 +590,7 @@ static int do_ubi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ret = ubi_volume_write(argv[3], (void *)addr, size); if (!ret) { - printf("%d bytes written to volume %s\n", size, + printf("%lld bytes written to volume %s\n", size, argv[3]); } @@ -613,7 +613,7 @@ static int do_ubi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } if (argc == 3) { - printf("Read %d bytes from volume %s to %lx\n", size, + printf("Read %lld bytes from volume %s to %lx\n", size, argv[3], addr); return ubi_volume_read(argv[3], (char *)addr, size); -- cgit v0.10.2 From cc734f5ab26134e5e8d57c34edc257c89ac5b1d2 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Wed, 4 Sep 2013 15:16:59 +0100 Subject: cmd_ubi: add write.part command, to write a volume in multiple parts This allows you to write data to an UBI volume when the amount of memory available to write that data from is less than the total size of the data. For example, you may split a root filesystem UBIFS image into parts, provide the total size of the image to the first write.part command and then use multiple write.part commands to write the subsequent parts of the volume. This results in a sequence of commands akin to: ext4load mmc 0:1 0x80000000 rootfs.ubifs.0 ubi write.part 0x80000000 root 0x08000000 0x18000000 ext4load mmc 0:1 0x80000000 rootfs.ubifs.1 ubi write.part 0x80000000 root 0x08000000 ext4load mmc 0:1 0x80000000 rootfs.ubifs.2 ubi write.part 0x80000000 root 0x08000000 This would write 384MiB of data to the UBI volume 'root' whilst only requiring 128MiB of said data to be held in memory at a time. Signed-off-by: Paul Burton Acked-by: Stefan Roese diff --git a/common/cmd_ubi.c b/common/cmd_ubi.c index f11cb61..122ba7e 100644 --- a/common/cmd_ubi.c +++ b/common/cmd_ubi.c @@ -266,28 +266,15 @@ out_err: return err; } -int ubi_volume_write(char *volume, void *buf, size_t size) +int ubi_volume_continue_write(char *volume, void *buf, size_t size) { int err = 1; - int rsvd_bytes = 0; struct ubi_volume *vol; vol = ubi_find_volume(volume); if (vol == NULL) return ENODEV; - rsvd_bytes = vol->reserved_pebs * (ubi->leb_size - vol->data_pad); - if (size < 0 || size > rsvd_bytes) { - printf("size > volume size! Aborting!\n"); - return EINVAL; - } - - err = ubi_start_update(ubi, vol, size); - if (err < 0) { - printf("Cannot start volume update\n"); - return -err; - } - err = ubi_more_update_data(ubi, vol, buf, size); if (err < 0) { printf("Couldnt or partially wrote data\n"); @@ -314,6 +301,37 @@ int ubi_volume_write(char *volume, void *buf, size_t size) return 0; } +int ubi_volume_begin_write(char *volume, void *buf, size_t size, + size_t full_size) +{ + int err = 1; + int rsvd_bytes = 0; + struct ubi_volume *vol; + + vol = ubi_find_volume(volume); + if (vol == NULL) + return ENODEV; + + rsvd_bytes = vol->reserved_pebs * (ubi->leb_size - vol->data_pad); + if (size < 0 || size > rsvd_bytes) { + printf("size > volume size! Aborting!\n"); + return EINVAL; + } + + err = ubi_start_update(ubi, vol, full_size); + if (err < 0) { + printf("Cannot start volume update\n"); + return -err; + } + + return ubi_volume_continue_write(volume, buf, size); +} + +int ubi_volume_write(char *volume, void *buf, size_t size) +{ + return ubi_volume_begin_write(volume, buf, size, size); +} + int ubi_volume_read(char *volume, char *buf, size_t size) { int err, lnum, off, len, tbuf_size; @@ -588,7 +606,20 @@ static int do_ubi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) addr = simple_strtoul(argv[2], NULL, 16); size = simple_strtoul(argv[4], NULL, 16); - ret = ubi_volume_write(argv[3], (void *)addr, size); + if (strlen(argv[1]) == 10 && + strncmp(argv[1] + 5, ".part", 5) == 0) { + if (argc < 6) { + ret = ubi_volume_continue_write(argv[3], + (void *)addr, size); + } else { + size_t full_size; + full_size = simple_strtoul(argv[5], NULL, 16); + ret = ubi_volume_begin_write(argv[3], + (void *)addr, size, full_size); + } + } else { + ret = ubi_volume_write(argv[3], (void *)addr, size); + } if (!ret) { printf("%lld bytes written to volume %s\n", size, argv[3]); @@ -636,6 +667,8 @@ U_BOOT_CMD( " - create volume name with size\n" "ubi write[vol] address volume size" " - Write volume from address with size\n" + "ubi write.part address volume size [fullsize]\n" + " - Write part of a volume from address\n" "ubi read[vol] address volume [size]" " - Read volume to address with size\n" "ubi remove[vol] volume" diff --git a/doc/README.ubi b/doc/README.ubi index 3cf4ef2..d82c75c 100644 --- a/doc/README.ubi +++ b/doc/README.ubi @@ -14,6 +14,8 @@ ubi part [part] [offset] ubi info [l[ayout]] - Display volume and ubi layout information ubi create[vol] volume [size] [type] - create volume name with size ubi write[vol] address volume size - Write volume from address with size +ubi write.part address volume size [fullsize] + - Write part of a volume from address ubi read[vol] address volume [size] - Read volume to address with size ubi remove[vol] volume - Remove volume [Legends] @@ -77,6 +79,7 @@ ubi createvol Create UBI volume on UBI device ubi removevol Remove UBI volume from UBI device ubi read Read data from UBI volume to memory ubi write Write data from memory to UBI volume +ubi write.part Write data from memory to UBI volume, in parts Here a few examples on the usage: -- cgit v0.10.2