static void pad(int size) { if ((ofs % size == 0) && (ofs < erasesize)) return; if (ofs < erasesize) { memset(buf + ofs, 0xff, (size - (ofs % size))); ofs += (size - (ofs % size)); } ofs = ofs % erasesize; if (ofs == 0) { while (mtd_block_is_bad(outfd, mtdofs) && (mtdofs < mtdsize)) { if (!quiet) fprintf(stderr, "\nSkipping bad block at 0x%08x ", mtdofs); mtdofs += erasesize; /* Move the file pointer along over the bad block. */ lseek(outfd, erasesize, SEEK_CUR); } mtd_erase_block(outfd, mtdofs); write(outfd, buf, erasesize); mtdofs += erasesize; } }
static int mtd_erase(const char *mtd) { int fd; struct erase_info_user mtdEraseInfo; if (quiet < 2) fprintf(stderr, "Erasing %s ...\n", mtd); fd = mtd_check_open(mtd); if(fd < 0) { fprintf(stderr, "Could not open mtd device: %s\n", mtd); exit(1); } mtdEraseInfo.length = erasesize; for (mtdEraseInfo.start = 0; mtdEraseInfo.start < mtdsize; mtdEraseInfo.start += erasesize) { if (mtd_block_is_bad(fd, mtdEraseInfo.start)) { if (!quiet) fprintf(stderr, "\nSkipping bad block at 0x%x ", mtdEraseInfo.start); } else { ioctl(fd, MEMUNLOCK, &mtdEraseInfo); if(ioctl(fd, MEMERASE, &mtdEraseInfo)) fprintf(stderr, "Failed to erase block on %s at 0x%x\n", mtd, mtdEraseInfo.start); } } close(fd); return 0; }
static int mtd_dump(const char *mtd, int part_offset, int size) { int ret = 0, offset = 0; int fd; char *buf; if (quiet < 2) fprintf(stderr, "Dumping %s ...\n", mtd); fd = mtd_check_open(mtd); if(fd < 0) { fprintf(stderr, "Could not open mtd device: %s\n", mtd); return -1; } if (!size) size = mtdsize; if (part_offset) lseek(fd, part_offset, SEEK_SET); buf = malloc(erasesize); if (!buf) return -1; do { int len = (size > erasesize) ? (erasesize) : (size); int rlen = read(fd, buf, len); if (rlen < 0) { if (errno == EINTR) continue; ret = -1; goto out; } if (!rlen || rlen != len) break; if (mtd_block_is_bad(fd, offset)) { fprintf(stderr, "skipping bad block at 0x%08x\n", offset); } else { size -= rlen; write(1, buf, rlen); } offset += rlen; } while (size > 0); out: close(fd); return ret; }
static int mtd_write(int imagefd, const char *mtd, char *fis_layout, size_t part_offset) { char *next = NULL; char *str = NULL; int fd, result; ssize_t r, w, e; ssize_t skip = 0; uint32_t offset = 0; int jffs2_replaced = 0; int skip_bad_blocks = 0; #ifdef FIS_SUPPORT static struct fis_part new_parts[MAX_ARGS]; static struct fis_part old_parts[MAX_ARGS]; int n_new = 0, n_old = 0; if (fis_layout) { const char *tmp = mtd; char *word, *brkt; int ret; memset(&old_parts, 0, sizeof(old_parts)); memset(&new_parts, 0, sizeof(new_parts)); do { next = strchr(tmp, ':'); if (!next) next = (char *) tmp + strlen(tmp); memcpy(old_parts[n_old].name, tmp, next - tmp); n_old++; tmp = next + 1; } while(*next); for (word = strtok_r(fis_layout, ",", &brkt); word; word = strtok_r(NULL, ",", &brkt)) { tmp = strtok(word, ":"); strncpy((char *) new_parts[n_new].name, tmp, sizeof(new_parts[n_new].name) - 1); tmp = strtok(NULL, ":"); if (!tmp) goto next; new_parts[n_new].size = strtoul(tmp, NULL, 0); tmp = strtok(NULL, ":"); if (!tmp) goto next; new_parts[n_new].loadaddr = strtoul(tmp, NULL, 16); next: n_new++; } ret = fis_validate(old_parts, n_old, new_parts, n_new); if (ret < 0) { fprintf(stderr, "Failed to validate the new FIS partition table\n"); exit(1); } if (ret == 0) fis_layout = NULL; } #endif if (strchr(mtd, ':')) { str = strdup(mtd); mtd = str; } r = 0; resume: next = strchr(mtd, ':'); if (next) { *next = 0; next++; } fd = mtd_check_open(mtd); if(fd < 0) { fprintf(stderr, "Could not open mtd device: %s\n", mtd); exit(1); } if (part_offset > 0) { fprintf(stderr, "Seeking on mtd device '%s' to: %zu\n", mtd, part_offset); lseek(fd, part_offset, SEEK_SET); } indicate_writing(mtd); w = e = 0; for (;;) { /* buffer may contain data already (from trx check or last mtd partition write attempt) */ while (buflen < erasesize) { r = read(imagefd, buf + buflen, erasesize - buflen); if (r < 0) { if ((errno == EINTR) || (errno == EAGAIN)) continue; else { perror("read"); break; } } if (r == 0) break; buflen += r; } if (buflen == 0) break; if (buflen < erasesize) { /* Pad block to eraseblock size */ memset(&buf[buflen], 0xff, erasesize - buflen); buflen = erasesize; } if (skip > 0) { skip -= buflen; buflen = 0; if (skip <= 0) indicate_writing(mtd); continue; } if (jffs2file && w >= jffs2_skip_bytes) { if (memcmp(buf, JFFS2_EOF, sizeof(JFFS2_EOF) - 1) == 0) { if (!quiet) fprintf(stderr, "\b\b\b "); if (quiet < 2) fprintf(stderr, "\nAppending jffs2 data from %s to %s...", jffs2file, mtd); /* got an EOF marker - this is the place to add some jffs2 data */ skip = mtd_replace_jffs2(mtd, fd, e, jffs2file); jffs2_replaced = 1; /* don't add it again */ jffs2file = NULL; w += skip; e += skip; skip -= buflen; buflen = 0; offset = 0; continue; } /* no EOF marker, make sure we figure out the last inode number * before appending some data */ mtd_parse_jffs2data(buf, jffs2dir); } /* need to erase the next block before writing data to it */ if(!no_erase) { while (w + buflen > e - skip_bad_blocks) { if (!quiet) fprintf(stderr, "\b\b\b[e]"); if (mtd_block_is_bad(fd, e)) { if (!quiet) fprintf(stderr, "\nSkipping bad block at 0x%08zx ", e); skip_bad_blocks += erasesize; e += erasesize; // Move the file pointer along over the bad block. lseek(fd, erasesize, SEEK_CUR); continue; } if (mtd_erase_block(fd, e) < 0) { if (next) { if (w < e) { write(fd, buf + offset, e - w); offset = e - w; } w = 0; e = 0; close(fd); mtd = next; fprintf(stderr, "\b\b\b \n"); goto resume; } else { fprintf(stderr, "Failed to erase block\n"); exit(1); } } /* erase the chunk */ e += erasesize; } } if (!quiet) fprintf(stderr, "\b\b\b[w]"); if ((result = write(fd, buf + offset, buflen)) < buflen) { if (result < 0) { fprintf(stderr, "Error writing image.\n"); exit(1); } else { fprintf(stderr, "Insufficient space.\n"); exit(1); } } w += buflen; buflen = 0; offset = 0; } if (jffs2_replaced && trx_fixup) { trx_fixup(fd, mtd); } if (!quiet) fprintf(stderr, "\b\b\b\b "); if (quiet < 2) fprintf(stderr, "\n"); #ifdef FIS_SUPPORT if (fis_layout) { if (fis_remap(old_parts, n_old, new_parts, n_new) < 0) fprintf(stderr, "Failed to update the FIS partition table\n"); } #endif close(fd); return 0; }
int flash_mtd_write(const char *mtd_part, int offset, unsigned char *buf, size_t count) { int fd, ret, cnt, len, ofs, ofs_align, bad_shift; unsigned char *ptr, *tmp = NULL, *tmp_ubi = NULL; struct mtd_info mi; struct erase_info_user ei; if (!mtd_part || !buf || offset < 0 || count < 1) return -1; memset(&mi, 0, sizeof(mi)); fd = mtd_dev_open(mtd_part, O_RDWR|O_SYNC, &mi); if (fd < 0) { fprintf(stderr, "%s: failed to open MTD partition %s\n", __func__, mtd_part); return -2; } ret = 0; if (((size_t)offset + count) > mi.size) { fprintf(stderr, "%s: out of MTD partition (offset: 0x%x, count: 0x%x, %s size: 0x%x)\n", __func__, offset, count, mi.dev, mi.size); ret = -3; goto out_err; } ptr = (unsigned char*) buf; /* align offset and length to write size of UBI volume backed MTD device. */ if (mi.type == MTD_UBIVOLUME) { int old_offset = offset; size_t tmp_count, old_count = count; tmp_count = ROUNDUP((size_t)offset + count, mi.writesize); offset = ROUNDDOWN(offset, (int)mi.writesize); count = tmp_count - offset; if (offset != old_offset || count != old_count) { if (((size_t)offset + count) > mi.size) { fprintf(stderr, "%s: out of UBI partition. (aligned offset: 0x%x, count: 0x%x, %s size: 0x%x)\n", __func__, offset, count, mi.dev, mi.size); ret = -3; goto out_err; } tmp_ubi = malloc(count); if (!tmp_ubi) { fprintf(stderr, "%s: failed to alloc memory for %u bytes\n", __func__, count); ret = -1; goto out_err; } if (flash_mtd_read(mtd_part, offset, tmp_ubi, count) < 0) { ret = -4; goto out_err; } memcpy(tmp_ubi + (old_offset - offset), buf, old_count); ptr = tmp_ubi; } } tmp = (unsigned char *)malloc(mi.erasesize); if (!tmp) { fprintf(stderr, "%s: failed to alloc memory for %u bytes\n", __func__, mi.erasesize); ret = -1; goto out_err; } cnt = (int)count; ofs = offset; bad_shift = 0; while (cnt > 0) { ofs_align = ofs & ~(mi.erasesize - 1); /* aligned to erase boundary */ len = (int)mi.erasesize - (ofs - ofs_align); if (cnt < len) len = cnt; ei.start = ofs_align; ei.start += bad_shift; ei.length = mi.erasesize; /* check bad block */ if (mtd_block_is_bad(fd, mi.type, ei.start)) { bad_shift += mi.erasesize; continue; } /* backup */ if (lseek(fd, ei.start, SEEK_SET) < 0) { fprintf(stderr, "%s: failed to seek 0x%x on %s (errno: %d)\n", __func__, ei.start, mi.dev, errno); ret = -4; break; } if (read(fd, tmp, mi.erasesize) != mi.erasesize) { fprintf(stderr, "%s: failed to read %u bytes from %s (errno: %d)\n", __func__, mi.erasesize, mi.dev, errno); ret = -4; break; } /* erase */ ioctl(fd, MEMUNLOCK, &ei); if (ioctl(fd, MEMERASE, &ei) < 0) { fprintf(stderr, "%s: failed to erase %s at start 0x%x, length 0x%x (errno: %d)\n", __func__, mi.dev, ei.start, ei.length, errno); ret = -5; break; } /* write */ if (lseek(fd, ei.start, SEEK_SET) < 0) { fprintf(stderr, "%s: failed to seek 0x%x on %s (errno: %d)\n", __func__, ei.start, mi.dev, errno); ret = -6; break; } memcpy(tmp + (ofs - ofs_align), ptr, len); if (write(fd, tmp, mi.erasesize) != mi.erasesize) { fprintf(stderr, "%s: failed to write %u bytes to %s (errno: %d)\n", __func__, mi.erasesize, mi.dev, errno); ret = -6; break; } ptr += len; ofs += len; cnt -= len; } free(tmp); /* verify after write */ if (cnt == 0 && mi.type != MTD_UBIVOLUME) { tmp = (unsigned char *)malloc(count); if (tmp) { if (flash_mtd_read(mtd_part, offset, tmp, count) == 0) { if (memcmp(tmp, buf, count) != 0) { fprintf(stderr, "%s: failed to verify after write to %s - data mismatch!\n", __func__, mi.dev); ret = -7; } } free(tmp); } } out_err: if (tmp_ubi) free(tmp_ubi); close(fd); return ret; }