X-Git-Url: https://git.archive.openwrt.org/?p=openwrt.git;a=blobdiff_plain;f=package%2Fsystem%2Fmtd%2Fsrc%2Fmtd.c;h=dae0514a0b8fecb74f09298843ffe9058874d20a;hp=2ec02a871b1be84b65d9893ad9d94896a36b7461;hb=891755fa71eba8375d3152e5f35ff69619a9c723;hpb=09e51af7eb7114e27b11520b89322b77d04cd639 diff --git a/package/system/mtd/src/mtd.c b/package/system/mtd/src/mtd.c index 2ec02a871b..dae0514a0b 100644 --- a/package/system/mtd/src/mtd.c +++ b/package/system/mtd/src/mtd.c @@ -21,6 +21,8 @@ * The code is based on the linux-mtd examples. */ +#define _GNU_SOURCE +#include #include #include #include @@ -44,11 +46,37 @@ #include "fis.h" #include "mtd.h" +#include + #define MAX_ARGS 8 #define JFFS2_DEFAULT_DIR "" /* directory name without /, empty means root dir */ +#define TRX_MAGIC 0x48445230 /* "HDR0" */ +#define SEAMA_MAGIC 0x5ea3a417 + +#if !defined(__BYTE_ORDER) +#error "Unknown byte order" +#endif + +#if __BYTE_ORDER == __BIG_ENDIAN +#define cpu_to_be32(x) (x) +#define be32_to_cpu(x) (x) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +#define cpu_to_be32(x) bswap_32(x) +#define be32_to_cpu(x) bswap_32(x) +#else +#error "Unsupported endianness" +#endif + +enum mtd_image_format { + MTD_IMAGE_FORMAT_UNKNOWN, + MTD_IMAGE_FORMAT_TRX, + MTD_IMAGE_FORMAT_SEAMA, +}; + static char *buf = NULL; static char *imagefile = NULL; +static enum mtd_image_format imageformat = MTD_IMAGE_FORMAT_UNKNOWN; static char *jffs2file = NULL, *jffs2dir = JFFS2_DEFAULT_DIR; static int buflen = 0; int quiet; @@ -56,6 +84,7 @@ int no_erase; int mtdsize = 0; int erasesize = 0; int jffs2_skip_bytes=0; +int mtdtype = 0; int mtd_open(const char *mtd, bool block) { @@ -64,10 +93,12 @@ int mtd_open(const char *mtd, bool block) int i; int ret; int flags = O_RDWR | O_SYNC; + char name[PATH_MAX]; + snprintf(name, sizeof(name), "\"%s\"", mtd); if ((fp = fopen("/proc/mtd", "r"))) { while (fgets(dev, sizeof(dev), fp)) { - if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) { + if (sscanf(dev, "mtd%d:", &i) && strstr(dev, name)) { snprintf(dev, sizeof(dev), "/dev/mtd%s/%d", (block ? "block" : ""), i); if ((ret=open(dev, flags))<0) { snprintf(dev, sizeof(dev), "/dev/mtd%s%d", (block ? "block" : ""), i); @@ -101,10 +132,28 @@ int mtd_check_open(const char *mtd) } mtdsize = mtdInfo.size; erasesize = mtdInfo.erasesize; + mtdtype = mtdInfo.type; return fd; } +int mtd_block_is_bad(int fd, int offset) +{ + int r = 0; + loff_t o = offset; + + if (mtdtype == MTD_NANDFLASH) + { + r = ioctl(fd, MEMGETBADBLOCK, &o); + if (r < 0) + { + fprintf(stderr, "Failed to get erase block status\n"); + exit(1); + } + } + return r; +} + int mtd_erase_block(int fd, int offset) { struct erase_info_user mtdEraseInfo; @@ -125,13 +174,46 @@ int mtd_write_buffer(int fd, const char *buf, int offset, int length) return 0; } - static int image_check(int imagefd, const char *mtd) { + uint32_t magic; int ret = 1; - if (trx_check) { - ret = trx_check(imagefd, mtd, buf, &buflen); + int bufread; + + while (buflen < sizeof(magic)) { + bufread = read(imagefd, buf + buflen, sizeof(magic) - buflen); + if (bufread < 1) + break; + + buflen += bufread; + } + + if (buflen < sizeof(magic)) { + fprintf(stdout, "Could not get image magic\n"); + return 0; + } + + magic = ((uint32_t *)buf)[0]; + + if (be32_to_cpu(magic) == TRX_MAGIC) + imageformat = MTD_IMAGE_FORMAT_TRX; + else if (be32_to_cpu(magic) == SEAMA_MAGIC) + imageformat = MTD_IMAGE_FORMAT_SEAMA; + + switch (imageformat) { + case MTD_IMAGE_FORMAT_TRX: + if (trx_check) + ret = trx_check(imagefd, mtd, buf, &buflen); + break; + case MTD_IMAGE_FORMAT_SEAMA: + break; + default: +#ifdef target_brcm + if (!strcmp(mtd, "firmware")) + ret = 0; +#endif + break; } return ret; @@ -234,10 +316,14 @@ mtd_erase(const char *mtd) for (mtdEraseInfo.start = 0; mtdEraseInfo.start < mtdsize; mtdEraseInfo.start += erasesize) { - - ioctl(fd, MEMUNLOCK, &mtdEraseInfo); - if(ioctl(fd, MEMERASE, &mtdEraseInfo)) - fprintf(stderr, "Failed to erase block on %s at 0x%x\n", mtd, mtdEraseInfo.start); + 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); @@ -245,6 +331,115 @@ mtd_erase(const char *mtd) } +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_verify(const char *mtd, char *file) +{ + uint32_t f_md5[4], m_md5[4]; + struct stat s; + md5_ctx_t ctx; + int ret = 0; + int fd; + + if (quiet < 2) + fprintf(stderr, "Verifying %s against %s ...\n", mtd, file); + + if (stat(file, &s) || md5sum(file, f_md5) < 0) { + fprintf(stderr, "Failed to hash %s\n", file); + return -1; + } + + fd = mtd_check_open(mtd); + if(fd < 0) { + fprintf(stderr, "Could not open mtd device: %s\n", mtd); + return -1; + } + + md5_begin(&ctx); + do { + char buf[256]; + int len = (s.st_size > sizeof(buf)) ? (sizeof(buf)) : (s.st_size); + int rlen = read(fd, buf, len); + + if (rlen < 0) { + if (errno == EINTR) + continue; + ret = -1; + goto out; + } + if (!rlen) + break; + md5_hash(buf, rlen, &ctx); + s.st_size -= rlen; + } while (s.st_size > 0); + + md5_end(m_md5, &ctx); + + fprintf(stderr, "%08x%08x%08x%08x - %s\n", m_md5[0], m_md5[1], m_md5[2], m_md5[3], mtd); + fprintf(stderr, "%08x%08x%08x%08x - %s\n", f_md5[0], f_md5[1], f_md5[2], f_md5[3], file); + + ret = memcmp(f_md5, m_md5, sizeof(m_md5)); + if (!ret) + fprintf(stderr, "Success\n"); + else + fprintf(stderr, "Failed\n"); + +out: + close(fd); + return ret; +} + static void indicate_writing(const char *mtd) { @@ -265,6 +460,7 @@ mtd_write(int imagefd, const char *mtd, char *fis_layout, size_t part_offset) 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]; @@ -341,7 +537,7 @@ resume: exit(1); } if (part_offset > 0) { - fprintf(stderr, "Seeking on mtd device '%s' to: %u\n", mtd, part_offset); + fprintf(stderr, "Seeking on mtd device '%s' to: %zu\n", mtd, part_offset); lseek(fd, part_offset, SEEK_SET); } @@ -370,6 +566,12 @@ resume: 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; @@ -407,10 +609,21 @@ resume: /* need to erase the next block before writing data to it */ if(!no_erase) { - while (w + buflen > e) { + 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) { @@ -453,14 +666,24 @@ resume: offset = 0; } - if (jffs2_replaced && trx_fixup) { - trx_fixup(fd, mtd); + if (jffs2_replaced) { + switch (imageformat) { + case MTD_IMAGE_FORMAT_TRX: + if (trx_fixup) + trx_fixup(fd, mtd); + break; + case MTD_IMAGE_FORMAT_SEAMA: + if (mtd_fixseama) + mtd_fixseama(mtd, 0); + break; + default: + break; + } } if (!quiet) fprintf(stderr, "\b\b\b\b "); -done: if (quiet < 2) fprintf(stderr, "\n"); @@ -483,8 +706,13 @@ static void usage(void) " unlock unlock the device\n" " refresh refresh mtd partition\n" " erase erase all data on device\n" + " verify |- verify (use - for stdin) to device\n" " write |- write (use - for stdin) to device\n" " jffs2write append to the jffs2 partition on the device\n"); + if (mtd_resetbc) { + fprintf(stderr, + " resetbc reset the uboot boot counter\n"); + } if (mtd_fixtrx) { fprintf(stderr, " fixtrx fix the checksum in a trx header on first boot\n"); @@ -493,7 +721,7 @@ static void usage(void) fprintf(stderr, " fixseama fix the checksum in a seama header on first boot\n"); } - fprintf(stderr, + fprintf(stderr, "Following options are available:\n" " -q quiet mode (once: no [w] on writing,\n" " twice: no status messages)\n" @@ -504,11 +732,12 @@ static void usage(void) " -d directory for jffs2write, defaults to \"tmp\"\n" " -j integrate into jffs2 data when writing an image\n" " -s skip the first n bytes when appending data to the jffs2 partiton, defaults to \"0\"\n" - " -p write beginning at partition offset\n"); + " -p write beginning at partition offset\n" + " -l the length of data that we want to dump\n"); if (mtd_fixtrx) { fprintf(stderr, " -o offset offset of the image header in the partition(for fixtrx)\n"); - } + } fprintf(stderr, #ifdef FIS_SUPPORT " -F [:[:]][,...]\n" @@ -540,7 +769,7 @@ int main (int argc, char **argv) int ch, i, boot, imagefd = 0, force, unlocked; char *erase[MAX_ARGS], *device = NULL; char *fis_layout = NULL; - size_t offset = 0, part_offset = 0; + size_t offset = 0, part_offset = 0, dump_len = 0; enum { CMD_ERASE, CMD_WRITE, @@ -548,6 +777,9 @@ int main (int argc, char **argv) CMD_JFFS2WRITE, CMD_FIXTRX, CMD_FIXSEAMA, + CMD_VERIFY, + CMD_DUMP, + CMD_RESETBC, } cmd = -1; erase[0] = NULL; @@ -561,7 +793,7 @@ int main (int argc, char **argv) #ifdef FIS_SUPPORT "F:" #endif - "frnqe:d:s:j:p:o:")) != -1) + "frnqe:d:s:j:p:o:l:")) != -1) switch (ch) { case 'f': force = 1; @@ -605,11 +837,15 @@ int main (int argc, char **argv) usage(); } break; - case 'o': - if (!mtd_fixtrx) { - fprintf(stderr, "-o: is not available on this platform\n"); + case 'l': + errno = 0; + dump_len = strtoul(optarg, 0, 0); + if (errno) { + fprintf(stderr, "-l: illegal numeric string\n"); usage(); } + break; + case 'o': errno = 0; offset = strtoul(optarg, 0, 0); if (errno) { @@ -638,12 +874,22 @@ int main (int argc, char **argv) } else if ((strcmp(argv[0], "erase") == 0) && (argc == 2)) { cmd = CMD_ERASE; device = argv[1]; + } else if (((strcmp(argv[0], "resetbc") == 0) && (argc == 2)) && mtd_resetbc) { + cmd = CMD_RESETBC; + device = argv[1]; } else if (((strcmp(argv[0], "fixtrx") == 0) && (argc == 2)) && mtd_fixtrx) { cmd = CMD_FIXTRX; device = argv[1]; } else if (((strcmp(argv[0], "fixseama") == 0) && (argc == 2)) && mtd_fixseama) { cmd = CMD_FIXSEAMA; device = argv[1]; + } else if ((strcmp(argv[0], "verify") == 0) && (argc == 3)) { + cmd = CMD_VERIFY; + imagefile = argv[1]; + device = argv[2]; + } else if ((strcmp(argv[0], "dump") == 0) && (argc == 2)) { + cmd = CMD_DUMP; + device = argv[1]; } else if ((strcmp(argv[0], "write") == 0) && (argc == 3)) { cmd = CMD_WRITE; device = argv[2]; @@ -698,6 +944,12 @@ int main (int argc, char **argv) if (!unlocked) mtd_unlock(device); break; + case CMD_VERIFY: + mtd_verify(device, imagefile); + break; + case CMD_DUMP: + mtd_dump(device, offset, dump_len); + break; case CMD_ERASE: if (!unlocked) mtd_unlock(device); @@ -717,6 +969,10 @@ int main (int argc, char **argv) if (mtd_fixtrx) { mtd_fixtrx(device, offset); } + case CMD_RESETBC: + if (mtd_resetbc) { + mtd_resetbc(device); + } case CMD_FIXSEAMA: if (mtd_fixseama) mtd_fixseama(device, 0);