aboutsummaryrefslogtreecommitdiff
path: root/nand-utils/nftldump.c
diff options
context:
space:
mode:
Diffstat (limited to 'nand-utils/nftldump.c')
-rw-r--r--nand-utils/nftldump.c278
1 files changed, 278 insertions, 0 deletions
diff --git a/nand-utils/nftldump.c b/nand-utils/nftldump.c
new file mode 100644
index 0000000..32f4f2f
--- /dev/null
+++ b/nand-utils/nftldump.c
@@ -0,0 +1,278 @@
+/*
+ * 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);
+}