diff options
Diffstat (limited to 'package/mtd/src')
-rw-r--r-- | package/mtd/src/Makefile | 4 | ||||
-rw-r--r-- | package/mtd/src/fis.c | 226 | ||||
-rw-r--r-- | package/mtd/src/fis.h | 14 | ||||
-rw-r--r-- | package/mtd/src/mtd.c | 249 |
4 files changed, 443 insertions, 50 deletions
diff --git a/package/mtd/src/Makefile b/package/mtd/src/Makefile index 68a632d5a..bb509be8b 100644 --- a/package/mtd/src/Makefile +++ b/package/mtd/src/Makefile @@ -5,6 +5,10 @@ obj = mtd.o jffs2.o crc32.o obj.brcm = trx.o obj.brcm47xx = $(obj.brcm) +ifdef FIS_SUPPORT + obj += fis.o +endif + mtd: $(obj) $(obj.$(TARGET)) clean: rm -f *.o jffs2 diff --git a/package/mtd/src/fis.c b/package/mtd/src/fis.c new file mode 100644 index 000000000..e774e62a6 --- /dev/null +++ b/package/mtd/src/fis.c @@ -0,0 +1,226 @@ +#include <sys/mman.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> +#include "crc32.h" +#include "mtd.h" +#include "fis.h" + +struct fis_image_hdr { + unsigned char name[16]; + uint32_t flash_base; + uint32_t mem_base; + uint32_t size; + uint32_t entry_point; + uint32_t data_length; +} __attribute__((packed)); + +struct fis_image_crc { + uint32_t desc; + uint32_t file; +} __attribute__((packed)); + +struct fis_image_desc { + struct fis_image_hdr hdr; + char _pad[256 - sizeof(struct fis_image_hdr) - sizeof(struct fis_image_crc)]; + struct fis_image_crc crc; +} __attribute__((packed)); + +static int fis_fd = -1; +static struct fis_image_desc *fis_desc; +static int fis_erasesize = 0; + +static void +fis_close(void) +{ + if (fis_desc) + munmap(fis_desc, fis_erasesize); + + if (fis_fd >= 0) + close(fis_fd); + + fis_fd = -1; + fis_desc = NULL; +} + +static struct fis_image_desc * +fis_open(void) +{ + struct fis_image_desc *desc; + + if (fis_fd >= 0) + fis_close(); + + fis_fd = mtd_check_open("FIS directory"); + if (fis_fd < 0) + goto error; + + close(fis_fd); + fis_fd = mtd_open("FIS directory", true); + if (fis_fd < 0) + goto error; + + fis_erasesize = erasesize; + desc = mmap(NULL, erasesize, PROT_READ|PROT_WRITE, MAP_SHARED, fis_fd, 0); + if (desc == MAP_FAILED) + goto error; + + fis_desc = desc; + return desc; + +error: + fis_close(); + return NULL; +} + +int +fis_validate(struct fis_part *old, int n_old, struct fis_part *new, int n_new) +{ + struct fis_image_desc *desc; + void *end; + int found = 0; + int i; + + desc = fis_open(); + if (!desc) + return 0; + + for (i = 0; i < n_new - 1; i++) { + if (!new[i].size) { + fprintf(stderr, "FIS error: only the last partition can detect the size automatically\n"); + i = -1; + goto done; + } + } + + end = desc; + end = (char *) end + fis_erasesize; + while ((void *) desc < end) { + if (!desc->hdr.name[0] || (desc->hdr.name[0] == 0xff)) + break; + + for (i = 0; i < n_old; i++) { + if (!strncmp((char *) desc->hdr.name, (char *) old[i].name, sizeof(desc->hdr.name))) { + found++; + goto next; + } + } +next: + desc++; + continue; + } + + if (found == n_old) + i = 1; + else + i = -1; + +done: + fis_close(); + return i; +} + +int +fis_remap(struct fis_part *old, int n_old, struct fis_part *new, int n_new) +{ + struct fis_image_desc *fisdir = NULL; + struct fis_image_desc *redboot = NULL; + struct fis_image_desc *first = NULL; + struct fis_image_desc *last = NULL; + struct fis_image_desc *desc; + struct fis_part *part; + uint32_t offset = 0, size = 0; + char *end, *tmp; + int i; + + desc = fis_open(); + if (!desc) + return -1; + + if (!quiet) + fprintf(stderr, "Updating FIS table... \n"); + + end = (char *) desc + fis_erasesize; + while ((char *) desc < end) { + if (!desc->hdr.name[0] || (desc->hdr.name[0] == 0xff)) + break; + + if (!strcmp((char *) desc->hdr.name, "FIS directory")) + fisdir = desc; + + if (!strcmp((char *) desc->hdr.name, "RedBoot")) + redboot = desc; + + for (i = 0; i < n_old; i++) { + if (!strncmp((char *) desc->hdr.name, (char *) old[i].name, sizeof(desc->hdr.name))) { + size += desc->hdr.size; + last = desc; + if (!first) + first = desc; + break; + } + } + desc++; + } + desc--; + + if (desc == last) { + desc = fisdir; + } + + /* size fixup */ + if (desc && (last->hdr.flash_base < desc->hdr.flash_base - last->hdr.size)) + size += (desc->hdr.flash_base - last->hdr.flash_base) - last->hdr.size; + +#ifdef notyet + desc = first - 1; + if (redboot && (desc >= redboot)) { + if (first->hdr.flash_base - desc->hdr.size > desc->hdr.flash_base) { + int delta = first->hdr.flash_base - desc->hdr.size - desc->hdr.flash_base; + + offset -= delta; + size += delta; + } + } +#endif + + last++; + desc = first + n_new; + offset = first->hdr.flash_base; + + if (desc != last) { + if (desc > last) + tmp = (char *) desc; + else + tmp = (char *) last; + + memmove(desc, last, end - tmp); + if (desc < last) { + tmp = end - (last - desc) * sizeof(struct fis_image_desc); + memset(tmp, 0xff, tmp - end); + } + } + + for (part = new, desc = first; desc < first + n_new; desc++, part++) { + memset(desc, 0, sizeof(struct fis_image_desc)); + memcpy(desc->hdr.name, part->name, sizeof(desc->hdr.name)); + desc->crc.desc = 0; + desc->crc.file = 0; + + desc->hdr.flash_base = offset; + desc->hdr.mem_base = part->loadaddr; + desc->hdr.entry_point = part->loadaddr; + desc->hdr.size = (part->size > 0) ? part->size : size; + desc->hdr.data_length = desc->hdr.size; + + offset += desc->hdr.size; + size -= desc->hdr.size; + } + + msync(fis_desc, fis_erasesize, MS_SYNC|MS_INVALIDATE); + fis_close(); + + return 0; +} diff --git a/package/mtd/src/fis.h b/package/mtd/src/fis.h new file mode 100644 index 000000000..bdf1103d8 --- /dev/null +++ b/package/mtd/src/fis.h @@ -0,0 +1,14 @@ +#ifndef __FIS_H +#define __FIS_H + +struct fis_part { + unsigned char name[16]; + uint32_t offset; + uint32_t loadaddr; + uint32_t size; +}; + +int fis_validate(struct fis_part *old, int n_old, struct fis_part *new, int n_new); +int fis_remap(struct fis_part *old, int n_old, struct fis_part *new, int n_new); + +#endif diff --git a/package/mtd/src/mtd.c b/package/mtd/src/mtd.c index be4c1be7f..b9b2763af 100644 --- a/package/mtd/src/mtd.c +++ b/package/mtd/src/mtd.c @@ -43,6 +43,7 @@ #include <sys/reboot.h> #include <linux/reboot.h> #include "mtd-api.h" +#include "fis.h" #include "mtd.h" #define MAX_ARGS 8 @@ -119,10 +120,9 @@ int mtd_erase_block(int fd, int offset) mtdEraseInfo.start = offset; mtdEraseInfo.length = erasesize; ioctl(fd, MEMUNLOCK, &mtdEraseInfo); - if (ioctl (fd, MEMERASE, &mtdEraseInfo) < 0) { - fprintf(stderr, "Erasing mtd failed.\n"); - exit(1); - } + if (ioctl (fd, MEMERASE, &mtdEraseInfo) < 0) + return -1; + return 0; } @@ -146,42 +146,78 @@ image_check(int imagefd, const char *mtd) static int mtd_check(const char *mtd) { + char *next = NULL; + char *str = NULL; int fd; - fd = mtd_check_open(mtd); - if (!fd) - return 0; + if (strchr(mtd, ':')) { + str = strdup(mtd); + mtd = str; + } - if (!buf) - buf = malloc(erasesize); + do { + next = strchr(mtd, ':'); + if (next) { + *next = 0; + next++; + } + + fd = mtd_check_open(mtd); + if (!fd) + return 0; + + if (!buf) + buf = malloc(erasesize); + + close(fd); + mtd = next; + } while (next); + + if (str) + free(str); - close(fd); return 1; } static int mtd_unlock(const char *mtd) { - int fd; struct erase_info_user mtdLockInfo; + char *next = NULL; + char *str = NULL; + int fd; - fd = mtd_check_open(mtd); - if(fd <= 0) { - fprintf(stderr, "Could not open mtd device: %s\n", mtd); - exit(1); + if (strchr(mtd, ':')) { + str = strdup(mtd); + mtd = str; } - if (quiet < 2) - fprintf(stderr, "Unlocking %s ...\n", mtd); + do { + 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); + } - mtdLockInfo.start = 0; - mtdLockInfo.length = mtdsize; - if(ioctl(fd, MEMUNLOCK, &mtdLockInfo)) { + if (quiet < 2) + fprintf(stderr, "Unlocking %s ...\n", mtd); + + mtdLockInfo.start = 0; + mtdLockInfo.length = mtdsize; + ioctl(fd, MEMUNLOCK, &mtdLockInfo); close(fd); - return 0; - } - - close(fd); + mtd = next; + } while (next); + + if (str) + free(str); + return 0; } @@ -205,11 +241,11 @@ 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); - } + } close(fd); return 0; @@ -244,27 +280,99 @@ mtd_refresh(const char *mtd) } static int -mtd_write(int imagefd, const char *mtd) +mtd_write(int imagefd, const char *mtd, char *fis_layout) { + char *next = NULL; + char *str = NULL; int fd, result; ssize_t r, w, e; + uint32_t offset = 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 (quiet < 2) fprintf(stderr, "Writing from %s to %s ... ", imagefile, mtd); - r = w = e = 0; + w = e = 0; if (!quiet) fprintf(stderr, " [ ]"); for (;;) { - /* buffer may contain data already (from trx check) */ - do { + /* 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)) @@ -279,7 +387,7 @@ mtd_write(int imagefd, const char *mtd) break; buflen += r; - } while (buflen < erasesize); + } if (buflen == 0) break; @@ -304,16 +412,33 @@ mtd_write(int imagefd, const char *mtd) if (!quiet) fprintf(stderr, "\b\b\b[e]"); - mtd_erase_block(fd, e); + + 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, buflen)) < buflen) { + + if ((result = write(fd, buf + offset, buflen)) < buflen) { if (result < 0) { fprintf(stderr, "Error writing image.\n"); exit(1); @@ -325,21 +450,30 @@ mtd_write(int imagefd, const char *mtd) w += buflen; buflen = 0; + offset = 0; } + if (!quiet) - fprintf(stderr, "\b\b\b\b"); + fprintf(stderr, "\b\b\b\b "); done: 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; } static void usage(void) { - fprintf(stderr, "Usage: mtd [<options> ...] <command> [<arguments> ...] <device>\n\n" + fprintf(stderr, "Usage: mtd [<options> ...] <command> [<arguments> ...] <device>[:<device>...]\n\n" "The device is in the format of mtdX (eg: mtd4) or its label.\n" "mtd recognizes these commands:\n" " unlock unlock the device\n" @@ -355,6 +489,12 @@ static void usage(void) " -e <device> erase <device> before executing the command\n" " -d <name> directory for jffs2write, defaults to \"tmp\"\n" " -j <name> integrate <file> into jffs2 data when writing an image\n" +#ifdef FIS_SUPPORT + " -F <part>[:<size>[:<entrypoint>]][,<part>...]\n" + " alter the fis partition table to create new partitions replacing\n" + " the partitions provided as argument to the write command\n" + " (only valid together with the write command)\n" +#endif "\n" "Example: To write linux.trx to mtd4 labeled as linux and reboot afterwards\n" " mtd -r write linux.trx linux\n\n"); @@ -378,6 +518,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; enum { CMD_ERASE, CMD_WRITE, @@ -385,14 +526,18 @@ int main (int argc, char **argv) CMD_REFRESH, CMD_JFFS2WRITE } cmd = -1; - + erase[0] = NULL; boot = 0; force = 0; buflen = 0; quiet = 0; - while ((ch = getopt(argc, argv, "frqe:d:j:")) != -1) + while ((ch = getopt(argc, argv, +#ifdef FIS_SUPPORT + "F:" +#endif + "frqe:d:j:")) != -1) switch (ch) { case 'f': force = 1; @@ -410,20 +555,25 @@ int main (int argc, char **argv) i = 0; while ((erase[i] != NULL) && ((i + 1) < MAX_ARGS)) i++; - + erase[i++] = optarg; erase[i] = NULL; break; case 'd': jffs2dir = optarg; break; +#ifdef FIS_SUPPORT + case 'F': + fis_layout = optarg; + break; +#endif case '?': default: usage(); } argc -= optind; argv += optind; - + if (argc < 2) usage(); @@ -439,7 +589,7 @@ int main (int argc, char **argv) } else if ((strcmp(argv[0], "write") == 0) && (argc == 3)) { cmd = CMD_WRITE; device = argv[2]; - + if (strcmp(argv[1], "-") == 0) { imagefile = "<stdin>"; imagefd = 0; @@ -450,7 +600,7 @@ int main (int argc, char **argv) exit(1); } } - + if (!mtd_check(device)) { fprintf(stderr, "Can't open device for writing!\n"); exit(1); @@ -463,7 +613,7 @@ int main (int argc, char **argv) } else if ((strcmp(argv[0], "jffs2write") == 0) && (argc == 3)) { cmd = CMD_JFFS2WRITE; device = argv[2]; - + imagefile = argv[1]; if (!mtd_check(device)) { fprintf(stderr, "Can't open device for writing!\n"); @@ -474,7 +624,7 @@ int main (int argc, char **argv) } sync(); - + i = 0; unlocked = 0; while (erase[i] != NULL) { @@ -484,8 +634,7 @@ int main (int argc, char **argv) unlocked = 1; i++; } - - + switch (cmd) { case CMD_UNLOCK: if (!unlocked) @@ -499,7 +648,7 @@ int main (int argc, char **argv) case CMD_WRITE: if (!unlocked) mtd_unlock(device); - mtd_write(imagefd, device); + mtd_write(imagefd, device, fis_layout); break; case CMD_JFFS2WRITE: if (!unlocked) @@ -512,7 +661,7 @@ int main (int argc, char **argv) } sync(); - + if (boot) do_reboot(); |