diff options
| author | nbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2009-09-21 09:58:41 +0000 | 
|---|---|---|
| committer | nbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2009-09-21 09:58:41 +0000 | 
| commit | 3a0a80e1294f903c0fc271b1e71dba59007b5f61 (patch) | |
| tree | d6828f3e5f71dfcf14d10029c93f330058433642 | |
| parent | b93baae0249cc7cac36138c0733dcd9d9a717c7c (diff) | |
mtd: add support for rewriting the fis table layout on redboot based systems
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@17659 3c298f89-4303-0410-b956-a3cf2f4a3e73
| -rw-r--r-- | package/mtd/Makefile | 14 | ||||
| -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 | 
5 files changed, 451 insertions, 56 deletions
| diff --git a/package/mtd/Makefile b/package/mtd/Makefile index adba9dd5e..ce538c6ca 100644 --- a/package/mtd/Makefile +++ b/package/mtd/Makefile @@ -12,6 +12,7 @@ PKG_NAME:=mtd  PKG_RELEASE:=9  PKG_BUILD_DIR := $(KERNEL_BUILD_DIR)/$(PKG_NAME) +STAMP_PREPARED := $(STAMP_PREPARED)_$(call confvar,CONFIG_MTD_REDBOOT_PARTS)  include $(INCLUDE_DIR)/package.mk @@ -33,12 +34,13 @@ endef  target=$(firstword $(subst -, ,$(BOARD))) -define Build/Compile -	$(MAKE) -C $(PKG_BUILD_DIR) \ -		$(TARGET_CONFIGURE_OPTS) \ -		TARGET=$(target) \ -		CFLAGS="$(TARGET_CFLAGS) -Dtarget_$(target)=1 -Wall" -endef +MAKE_FLAGS += TARGET="$(target)" +TARGET_CFLAGS += -Dtarget_$(target)=1 -Wall + +ifdef CONFIG_MTD_REDBOOT_PARTS +  MAKE_FLAGS += FIS_SUPPORT=1 +  TARGET_CFLAGS += -DFIS_SUPPORT=1 +endif  define Package/mtd/install  	$(INSTALL_DIR) $(1)/sbin 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(); | 
