blob: 6d72acd7bef1dc1eb6e59040d3471c35f82a9976 [file] [log] [blame]
/*
* 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 _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];
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define SWAP16(x) do { ; } while(0)
#define SWAP32(x) do { ; } while(0)
#else
#define SWAP16(x) do { x = swab16(x); } while(0)
#define SWAP32(x) do { x = swab32(x); } while(0)
#endif
/* 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", argv[0]);
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);
}