| /* | 
 |  * nftldump.c: Dumping the content of NFTL partitions on a "Physical Disk" | 
 |  * | 
 |  * This program is free software; you can redistribute it and/or modify | 
 |  * it under the terms of the GNU General Public License as published by | 
 |  * the Free Software Foundation; either version 2 of the License, or | 
 |  * (at your option) any later version. | 
 |  * | 
 |  * This program is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |  * GNU General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU General Public License | 
 |  * along with this program; if not, write to the Free Software | 
 |  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
 |  * | 
 |  * ToDo: | 
 |  *	1. UnitSizeFactor != 0xFF cases | 
 |  *	2. test, test, and test !!! | 
 |  */ | 
 |  | 
 | #define PROGRAM_NAME "nftldump" | 
 |  | 
 | #define _XOPEN_SOURCE 500 /* For pread */ | 
 |  | 
 | #include <unistd.h> | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <sys/types.h> | 
 | #include <fcntl.h> | 
 | #include <sys/stat.h> | 
 | #include <errno.h> | 
 |  | 
 | #include <sys/ioctl.h> | 
 | #include <asm/types.h> | 
 | #include <mtd/mtd-user.h> | 
 | #include <mtd/nftl-user.h> | 
 | #include <mtd_swab.h> | 
 |  | 
 | static struct NFTLMediaHeader MedHead[2]; | 
 | static mtd_info_t meminfo; | 
 |  | 
 | static struct nftl_oob oobbuf; | 
 | static struct mtd_oob_buf oob = {0, 16, (unsigned char *)&oobbuf}; | 
 |  | 
 | static int fd, ofd = -1;; | 
 | static int NumMedHeads; | 
 |  | 
 | static unsigned char BadUnitTable[MAX_ERASE_ZONES]; | 
 |  | 
 | #define SWAP16(x) do { x = le16_to_cpu(x); } while(0) | 
 | #define SWAP32(x) do { x = le32_to_cpu(x); } while(0) | 
 |  | 
 | /* VUCtable, store the Erase Unit Number of the first Erase Unit in the chain */ | 
 | static unsigned short *VUCtable; | 
 |  | 
 | /* FixMe: make this dynamic allocated */ | 
 | #define ERASESIZE 0x2000 | 
 | #define NUMVUNITS ((40*1024*1024) / ERASESIZE) | 
 | static union nftl_uci UCItable[NUMVUNITS][3]; | 
 |  | 
 | static unsigned short nextEUN(unsigned short curEUN) | 
 | { | 
 | 	return UCItable[curEUN][0].a.ReplUnitNum; | 
 | } | 
 |  | 
 | static unsigned int find_media_headers(void) | 
 | { | 
 | 	int i; | 
 | 	static unsigned long ofs = 0; | 
 |  | 
 | 	NumMedHeads = 0; | 
 | 	while (ofs < meminfo.size) { | 
 | 		pread(fd, &MedHead[NumMedHeads], sizeof(struct NFTLMediaHeader), ofs); | 
 | 		if (!strncmp(MedHead[NumMedHeads].DataOrgID, "ANAND", 6)) { | 
 | 			SWAP16(MedHead[NumMedHeads].NumEraseUnits); | 
 | 			SWAP16(MedHead[NumMedHeads].FirstPhysicalEUN); | 
 | 			SWAP32(MedHead[NumMedHeads].FormattedSize); | 
 |  | 
 | 			if (NumMedHeads == 0) { | 
 | 				printf("NFTL Media Header found at offset 0x%08lx:\n", ofs); | 
 | 				printf("NumEraseUnits:    %d\n", | 
 | 						MedHead[NumMedHeads].NumEraseUnits); | 
 | 				printf("FirstPhysicalEUN: %d\n", | 
 | 						MedHead[NumMedHeads].FirstPhysicalEUN); | 
 | 				printf("Formatted Size:   %d\n", | 
 | 						MedHead[NumMedHeads].FormattedSize); | 
 | 				printf("UnitSizeFactor:   0x%x\n", | 
 | 						MedHead[NumMedHeads].UnitSizeFactor); | 
 |  | 
 | 				/* read BadUnitTable, I don't know why pread() does not work for | 
 | 				   larger (7680 bytes) chunks */ | 
 | 				for (i = 0; i < MAX_ERASE_ZONES; i += 512) | 
 | 					pread(fd, &BadUnitTable[i], 512, ofs + 512 + i); | 
 | 			} else | 
 | 				printf("Second NFTL Media Header found at offset 0x%08lx\n",ofs); | 
 | 			NumMedHeads++; | 
 | 		} | 
 |  | 
 | 		ofs += meminfo.erasesize; | 
 | 		if (NumMedHeads == 2) { | 
 | 			if (strncmp((char *)&MedHead[0], (char *)&MedHead[1], sizeof(struct NFTLMediaHeader)) != 0) { | 
 | 				printf("warning: NFTL Media Header is not consistent with " | 
 | 						"Spare NFTL Media Header\n"); | 
 | 			} | 
 | 			break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* allocate Virtual Unit Chain table for this NFTL partition */ | 
 | 	VUCtable = calloc(MedHead[0].NumEraseUnits, sizeof(unsigned short)); | 
 | 	return NumMedHeads; | 
 | } | 
 |  | 
 | static void dump_erase_units(void) | 
 | { | 
 | 	int i, j; | 
 | 	unsigned long ofs; | 
 |  | 
 | 	for (i = MedHead[0].FirstPhysicalEUN; i < MedHead[0].FirstPhysicalEUN + | 
 | 			MedHead[0].NumEraseUnits; i++) { | 
 | 		/* For each Erase Unit */ | 
 | 		ofs = i * meminfo.erasesize; | 
 |  | 
 | 		/* read the Unit Control Information */ | 
 | 		for (j = 0; j < 3; j++) { | 
 | 			oob.start = ofs + (j * 512); | 
 | 			if (ioctl(fd, MEMREADOOB, &oob)) | 
 | 				printf("MEMREADOOB at %lx: %s\n", | 
 | 						(unsigned long) oob.start, strerror(errno)); | 
 | 			memcpy(&UCItable[i][j], &oobbuf.u, 8); | 
 | 		} | 
 | 		if (UCItable[i][1].b.EraseMark != cpu_to_le16(0x3c69)) { | 
 | 			printf("EraseMark not present in unit %d: %x\n", | 
 | 					i, UCItable[i][1].b.EraseMark); | 
 | 		} else { | 
 | 			/* a properly formatted unit */ | 
 | 			SWAP16(UCItable[i][0].a.VirtUnitNum); | 
 | 			SWAP16(UCItable[i][0].a.ReplUnitNum); | 
 | 			SWAP16(UCItable[i][0].a.SpareVirtUnitNum); | 
 | 			SWAP16(UCItable[i][0].a.SpareReplUnitNum); | 
 | 			SWAP32(UCItable[i][1].b.WearInfo); | 
 | 			SWAP16(UCItable[i][1].b.EraseMark); | 
 | 			SWAP16(UCItable[i][1].b.EraseMark1); | 
 | 			SWAP16(UCItable[i][2].c.FoldMark); | 
 | 			SWAP16(UCItable[i][2].c.FoldMark1); | 
 |  | 
 | 			if (!(UCItable[i][0].a.VirtUnitNum & 0x8000)) { | 
 | 				/* If this is the first in a chain, store the EUN in the VUC table */ | 
 | 				if (VUCtable[UCItable[i][0].a.VirtUnitNum & 0x7fff]) { | 
 | 					printf("Duplicate start of chain for VUC %d: " | 
 | 							"Unit %d replaces Unit %d\n", | 
 | 							UCItable[i][0].a.VirtUnitNum & 0x7fff, | 
 | 							i, VUCtable[UCItable[i][0].a.VirtUnitNum & 0x7fff]); | 
 | 				} | 
 | 				VUCtable[UCItable[i][0].a.VirtUnitNum & 0x7fff] = i; | 
 | 			} | 
 | 		} | 
 |  | 
 | 		switch (BadUnitTable[i]) { | 
 | 			case ZONE_BAD_ORIGINAL: | 
 | 				printf("Unit %d is marked as ZONE_BAD_ORIGINAL\n", i); | 
 | 				continue; | 
 | 			case ZONE_BAD_MARKED: | 
 | 				printf("Unit %d is marked as ZONE_BAD_MARKED\n", i); | 
 | 				continue; | 
 | 		} | 
 |  | 
 | 		/* ZONE_GOOD */ | 
 | 		if (UCItable[i][0].a.VirtUnitNum == 0xffff) | 
 | 			printf("Unit %d is free\n", i); | 
 | 		else | 
 | 			printf("Unit %d is in chain %d and %s a replacement\n", i, | 
 | 					UCItable[i][0].a.VirtUnitNum & 0x7fff, | 
 | 					UCItable[i][0].a.VirtUnitNum & 0x8000 ? "is" : "is not"); | 
 | 	} | 
 | } | 
 |  | 
 | static void dump_virtual_units(void) | 
 | { | 
 | 	int i, j; | 
 | 	char readbuf[512]; | 
 |  | 
 | 	for (i = 0; i < (MedHead[0].FormattedSize / meminfo.erasesize); i++) { | 
 | 		unsigned short curEUN = VUCtable[i]; | 
 |  | 
 | 		printf("Virtual Unit #%d: ", i); | 
 | 		if (!curEUN) { | 
 | 			printf("Not present\n"); | 
 | 			continue; | 
 | 		} | 
 | 		printf("%d", curEUN); | 
 |  | 
 | 		/* walk through the Virtual Unit Chain */ | 
 | 		while ((curEUN = nextEUN(curEUN)) != 0xffff) { | 
 | 			printf(", %d", curEUN & 0x7fff); | 
 | 		} | 
 | 		printf("\n"); | 
 |  | 
 | 		if (ofd != -1) { | 
 | 			/* Actually write out the data */ | 
 | 			for (j = 0; j < meminfo.erasesize / 512; j++) { | 
 | 				/* For each sector in the block */ | 
 | 				unsigned short lastgoodEUN = 0xffff, thisEUN = VUCtable[i]; | 
 | 				unsigned int status; | 
 |  | 
 | 				if (thisEUN == 0xffff) thisEUN = 0; | 
 |  | 
 | 				while (thisEUN && (thisEUN & 0x7fff) != 0x7fff) { | 
 | 					oob.start = (thisEUN * ERASESIZE) + (j * 512); | 
 | 					ioctl(fd, MEMREADOOB, &oob); | 
 | 					status = oobbuf.b.Status | oobbuf.b.Status1; | 
 |  | 
 | 					switch (status) { | 
 | 						case SECTOR_FREE: | 
 | 							/* This is still free. Don't look any more */ | 
 | 							thisEUN = 0; | 
 | 							break; | 
 |  | 
 | 						case SECTOR_USED: | 
 | 							/* SECTOR_USED. This is a good one. */ | 
 | 							lastgoodEUN = thisEUN; | 
 | 							break; | 
 | 					} | 
 |  | 
 | 					/* Find the next erase unit in this chain, if any */ | 
 | 					if (thisEUN) | 
 | 						thisEUN = nextEUN(thisEUN) & 0x7fff; | 
 | 				} | 
 |  | 
 | 				if (lastgoodEUN == 0xffff) | 
 | 					memset(readbuf, 0, 512); | 
 | 				else | 
 | 					pread(fd, readbuf, 512, | 
 | 							(lastgoodEUN * ERASESIZE) + (j * 512)); | 
 |  | 
 | 				write(ofd, readbuf, 512); | 
 | 			} | 
 |  | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | int main(int argc, char **argv) | 
 | { | 
 | 	if (argc < 2) { | 
 | 		printf("Usage: %s <device> [<outfile>]\n", PROGRAM_NAME); | 
 | 		exit(1); | 
 | 	} | 
 | 	fd = open(argv[1], O_RDONLY); | 
 | 	if (fd == -1) { | 
 | 		perror("open flash"); | 
 | 		exit (1); | 
 | 	} | 
 |  | 
 | 	if (argc > 2) { | 
 | 		ofd = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, 0644); | 
 | 		if (ofd == -1) | 
 | 			perror ("open outfile"); | 
 | 	} | 
 |  | 
 | 	/* get size information of the MTD device */ | 
 | 	if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { | 
 | 		perror("ioctl(MEMGETINFO)"); | 
 | 		close(fd); | 
 | 		return 1; | 
 | 	} | 
 |  | 
 | 	while (find_media_headers() != 0) { | 
 | 		dump_erase_units(); | 
 | 		dump_virtual_units(); | 
 | 		free(VUCtable); | 
 | 	} | 
 |  | 
 | 	exit(0); | 
 | } |