197894cda5773e59bd13e87b72077751099419a9fThomas Gleixner/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * NFTL mount code with extensive checks
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
497894cda5773e59bd13e87b72077751099419a9fThomas Gleixner * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
5a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * Copyright © 2000 Netgem S.A.
6a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org>
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it under the terms of the GNU General Public License as published by
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation; either version 2 of the License, or
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (at your option) any later version.
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful,
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * GNU General Public License for more details.
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU General Public License
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/errno.h>
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/mtd.h>
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/nand.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/nftl.h>
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define SECTORSIZE 512
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	various device information of the NFTL partition and Bad Unit Table. Update
3592394b5c2be774425f255b5c7afbd8b19978fe12Brian Norris *	the ReplUnitTable[] table according to the Bad Unit Table. ReplUnitTable[]
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	is used for management of Erase Unit in other routines in nftl.c and nftlmount.c
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int find_boot_record(struct NFTLrecord *nftl)
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct nftl_uci1 h1;
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int block, boot_record_count = 0;
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size_t retlen;
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 buf[SECTORSIZE];
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct NFTLMediaHeader *mh = &nftl->MediaHdr;
45f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner	struct mtd_info *mtd = nftl->mbd.mtd;
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int i;
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4897894cda5773e59bd13e87b72077751099419a9fThomas Gleixner        /* Assume logical EraseSize == physical erasesize for starting the scan.
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   We'll sort it out later if we find a MediaHeader which says otherwise */
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Actually, we won't.  The new DiskOnChip driver has already scanned
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   the MediaHeader and adjusted the virtual erasesize it presents in
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   the mtd device accordingly.  We could even get rid of
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   nftl->EraseSize if there were any point in doing so. */
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nftl->EraseSize = nftl->mbd.mtd->erasesize;
5569423d99fc182a81f3c5db3eb5c140acc6fc64beAdrian Hunter        nftl->nb_blocks = (u32)nftl->mbd.mtd->size / nftl->EraseSize;
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nftl->MediaUnit = BLOCK_NIL;
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nftl->SpareMediaUnit = BLOCK_NIL;
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* search for a valid boot record */
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (block = 0; block < nftl->nb_blocks; block++) {
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int ret;
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Check for ANAND header first. Then can whinge if it's found but later
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   checks fail */
66329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy		ret = mtd_read(mtd, block * nftl->EraseSize, SECTORSIZE,
67329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy			       &retlen, buf);
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* We ignore ret in case the ECC of the MediaHeader is invalid
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   (which is apparently acceptable) */
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (retlen != SECTORSIZE) {
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			static int warncount = 5;
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (warncount) {
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				printk(KERN_WARNING "Block read at 0x%x of mtd%d failed: %d\n",
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				       block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (!--warncount)
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					printk(KERN_WARNING "Further failures for this block will not be printed\n");
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (retlen < 6 || memcmp(buf, "ANAND", 6)) {
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* ANAND\0 not found. Continue */
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
8597894cda5773e59bd13e87b72077751099419a9fThomas Gleixner			printk(KERN_DEBUG "ANAND header not found at 0x%x in mtd%d\n",
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       block * nftl->EraseSize, nftl->mbd.mtd->index);
8797894cda5773e59bd13e87b72077751099419a9fThomas Gleixner#endif
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* To be safer with BIOS, also use erase mark as discriminant */
928593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner		if ((ret = nftl_read_oob(mtd, block * nftl->EraseSize +
93f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner					 SECTORSIZE + 8, 8, &retlen,
94f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner					 (char *)&h1) < 0)) {
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n",
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0 /* Some people seem to have devices without ECC or erase marks
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 on the Media Header blocks. There are enough other sanity
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 checks in here that we can probably do without it.
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      */
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (le16_to_cpu(h1.EraseMark | h1.EraseMark1) != ERASE_MARK) {
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but erase mark not present (0x%04x,0x%04x instead)\n",
10697894cda5773e59bd13e87b72077751099419a9fThomas Gleixner			       block * nftl->EraseSize, nftl->mbd.mtd->index,
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       le16_to_cpu(h1.EraseMark), le16_to_cpu(h1.EraseMark1));
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Finally reread to check ECC */
112f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner		if ((ret = mtd->read(mtd, block * nftl->EraseSize, SECTORSIZE,
113f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner				     &retlen, buf) < 0)) {
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n",
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Paranoia. Check the ANAND header is still there after the ECC read */
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (memcmp(buf, "ANAND", 6)) {
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but went away on reread!\n",
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       block * nftl->EraseSize, nftl->mbd.mtd->index);
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_NOTICE "New data are: %02x %02x %02x %02x %02x %02x\n",
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* OK, we like it. */
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (boot_record_count) {
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* We've already processed one. So we just check if
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   this one is the same as the first one we found */
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (memcmp(mh, buf, sizeof(struct NFTLMediaHeader))) {
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				printk(KERN_NOTICE "NFTL Media Headers at 0x%x and 0x%x disagree.\n",
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				       nftl->MediaUnit * nftl->EraseSize, block * nftl->EraseSize);
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* if (debug) Print both side by side */
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (boot_record_count < 2) {
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					/* We haven't yet seen two real ones */
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					return -1;
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				continue;
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (boot_record_count == 1)
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				nftl->SpareMediaUnit = block;
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* Mark this boot record (NFTL MediaHeader) block as reserved */
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			nftl->ReplUnitTable[block] = BLOCK_RESERVED;
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			boot_record_count++;
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* This is the first we've seen. Copy the media header structure into place */
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(mh, buf, sizeof(struct NFTLMediaHeader));
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Do some sanity checks on it */
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsThe new DiskOnChip driver scans the MediaHeader itself, and presents a virtual
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserasesize based on UnitSizeFactor.  So the erasesize we read from the mtd
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdevice is already correct.
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (mh->UnitSizeFactor == 0) {
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_NOTICE "NFTL: UnitSizeFactor 0x00 detected. This violates the spec but we think we know what it means...\n");
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else if (mh->UnitSizeFactor < 0xfc) {
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_NOTICE "Sorry, we don't support UnitSizeFactor 0x%02x\n",
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       mh->UnitSizeFactor);
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -1;
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else if (mh->UnitSizeFactor != 0xff) {
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_NOTICE "WARNING: Support for NFTL with UnitSizeFactor 0x%02x is experimental\n",
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       mh->UnitSizeFactor);
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			nftl->EraseSize = nftl->mbd.mtd->erasesize << (0xff - mh->UnitSizeFactor);
17269423d99fc182a81f3c5db3eb5c140acc6fc64beAdrian Hunter			nftl->nb_blocks = (u32)nftl->mbd.mtd->size / nftl->EraseSize;
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN);
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) {
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_NOTICE "NFTL Media Header sanity check failed:\n");
17897894cda5773e59bd13e87b72077751099419a9fThomas Gleixner			printk(KERN_NOTICE "nb_boot_blocks (%d) + 2 > nb_blocks (%d)\n",
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       nftl->nb_boot_blocks, nftl->nb_blocks);
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -1;
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		nftl->numvunits = le32_to_cpu(mh->FormattedSize) / nftl->EraseSize;
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (nftl->numvunits > (nftl->nb_blocks - nftl->nb_boot_blocks - 2)) {
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_NOTICE "NFTL Media Header sanity check failed:\n");
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_NOTICE "numvunits (%d) > nb_blocks (%d) - nb_boot_blocks(%d) - 2\n",
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       nftl->numvunits, nftl->nb_blocks, nftl->nb_boot_blocks);
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -1;
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
19097894cda5773e59bd13e87b72077751099419a9fThomas Gleixner
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		nftl->mbd.size  = nftl->numvunits * (nftl->EraseSize / SECTORSIZE);
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* If we're not using the last sectors in the device for some reason,
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   reduce nb_blocks accordingly so we forget they're there */
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		nftl->nb_blocks = le16_to_cpu(mh->NumEraseUnits) + le16_to_cpu(mh->FirstPhysicalEUN);
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* XXX: will be suppressed */
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		nftl->lastEUN = nftl->nb_blocks - 1;
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* memory alloc */
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		nftl->EUNtable = kmalloc(nftl->nb_blocks * sizeof(u16), GFP_KERNEL);
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!nftl->EUNtable) {
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_NOTICE "NFTL: allocation of EUNtable failed\n");
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENOMEM;
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		nftl->ReplUnitTable = kmalloc(nftl->nb_blocks * sizeof(u16), GFP_KERNEL);
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!nftl->ReplUnitTable) {
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kfree(nftl->EUNtable);
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_NOTICE "NFTL: allocation of ReplUnitTable failed\n");
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENOMEM;
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
21397894cda5773e59bd13e87b72077751099419a9fThomas Gleixner
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* mark the bios blocks (blocks before NFTL MediaHeader) as reserved */
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < nftl->nb_boot_blocks; i++)
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			nftl->ReplUnitTable[i] = BLOCK_RESERVED;
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* mark all remaining blocks as potentially containing data */
21897894cda5773e59bd13e87b72077751099419a9fThomas Gleixner		for (; i < nftl->nb_blocks; i++) {
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			nftl->ReplUnitTable[i] = BLOCK_NOTEXPLORED;
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Mark this boot record (NFTL MediaHeader) block as reserved */
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		nftl->ReplUnitTable[block] = BLOCK_RESERVED;
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* read the Bad Erase Unit Table and modify ReplUnitTable[] accordingly */
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < nftl->nb_blocks; i++) {
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsThe new DiskOnChip driver already scanned the bad block table.  Just query it.
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if ((i & (SECTORSIZE - 1)) == 0) {
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* read one sector for every SECTORSIZE of blocks */
231f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner				if ((ret = mtd->read(nftl->mbd.mtd, block * nftl->EraseSize +
232f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner						     i + SECTORSIZE, SECTORSIZE, &retlen,
233f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner						     buf)) < 0) {
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n",
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					       ret);
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					kfree(nftl->ReplUnitTable);
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					kfree(nftl->EUNtable);
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					return -1;
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* mark the Bad Erase Unit as RESERVED in ReplUnitTable */
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (buf[i & (SECTORSIZE - 1)] != 0xff)
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				nftl->ReplUnitTable[i] = BLOCK_RESERVED;
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
2457086c19d07429d697057587caf1e5e0345442d16Artem Bityutskiy			if (mtd_block_isbad(nftl->mbd.mtd,
2467086c19d07429d697057587caf1e5e0345442d16Artem Bityutskiy					    i * nftl->EraseSize))
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				nftl->ReplUnitTable[i] = BLOCK_RESERVED;
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
24997894cda5773e59bd13e87b72077751099419a9fThomas Gleixner
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		nftl->MediaUnit = block;
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		boot_record_count++;
25297894cda5773e59bd13e87b72077751099419a9fThomas Gleixner
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} /* foreach (block) */
25497894cda5773e59bd13e87b72077751099419a9fThomas Gleixner
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return boot_record_count?0:-1;
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int memcmpb(void *a, int c, int n)
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < n; i++) {
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (c != ((unsigned char *)a)[i])
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return 1;
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* check_free_sector: check if a free sector is actually FREE, i.e. All 0xff in data and oob area */
26997894cda5773e59bd13e87b72077751099419a9fThomas Gleixnerstatic int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int len,
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			      int check_oob)
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 buf[SECTORSIZE + nftl->mbd.mtd->oobsize];
2739223a456da8ed357bf7e0b128c853e2c8bd54614Thomas Gleixner	struct mtd_info *mtd = nftl->mbd.mtd;
2749223a456da8ed357bf7e0b128c853e2c8bd54614Thomas Gleixner	size_t retlen;
2759223a456da8ed357bf7e0b128c853e2c8bd54614Thomas Gleixner	int i;
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < len; i += SECTORSIZE) {
278329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy		if (mtd_read(mtd, address, SECTORSIZE, &retlen, buf))
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -1;
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -1;
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (check_oob) {
2848593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner			if(nftl_read_oob(mtd, address, mtd->oobsize,
2859223a456da8ed357bf7e0b128c853e2c8bd54614Thomas Gleixner					 &retlen, &buf[SECTORSIZE]) < 0)
2869223a456da8ed357bf7e0b128c853e2c8bd54614Thomas Gleixner				return -1;
2879223a456da8ed357bf7e0b128c853e2c8bd54614Thomas Gleixner			if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0)
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return -1;
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		address += SECTORSIZE;
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* NFTL_format: format a Erase Unit by erasing ALL Erase Zones in the Erase Unit and
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *              Update NFTL metadata. Each erase operation is checked with check_free_sectors
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Return: 0 when succeed, -1 on error.
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
30192394b5c2be774425f255b5c7afbd8b19978fe12Brian Norris *  ToDo: 1. Is it necessary to check_free_sector after erasing ??
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint NFTL_formatblock(struct NFTLrecord *nftl, int block)
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size_t retlen;
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int nb_erases, erase_mark;
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct nftl_uci1 uci;
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct erase_info *instr = &nftl->instr;
309f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner	struct mtd_info *mtd = nftl->mbd.mtd;
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Read the Unit Control Information #1 for Wear-Leveling */
3128593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8,
313f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner			  8, &retlen, (char *)&uci) < 0)
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto default_uci1;
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	erase_mark = le16_to_cpu ((uci.EraseMark | uci.EraseMark1));
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (erase_mark != ERASE_MARK) {
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default_uci1:
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uci.EraseMark = cpu_to_le16(ERASE_MARK);
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uci.EraseMark1 = cpu_to_le16(ERASE_MARK);
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uci.WearInfo = cpu_to_le32(0);
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset(instr, 0, sizeof(struct erase_info));
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* XXX: use async erase interface, XXX: test return code */
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	instr->mtd = nftl->mbd.mtd;
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	instr->addr = block * nftl->EraseSize;
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	instr->len = nftl->EraseSize;
3307e1f0dc0551b99acb5e8fa161a7ac401994d57d8Artem Bityutskiy	mtd_erase(mtd, instr);
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (instr->state == MTD_ERASE_FAILED) {
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("Error while formatting block %d\n", block);
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto fail;
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* increase and write Wear-Leveling info */
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		nb_erases = le32_to_cpu(uci.WearInfo);
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		nb_erases++;
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
34192394b5c2be774425f255b5c7afbd8b19978fe12Brian Norris		/* wrap (almost impossible with current flash) or free block */
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (nb_erases == 0)
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			nb_erases = 1;
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* check the "freeness" of Erase Unit before updating metadata
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * FixMe:  is this check really necessary ? since we have check the
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 *         return code after the erase operation. */
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (check_free_sectors(nftl, instr->addr, nftl->EraseSize, 1) != 0)
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto fail;
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		uci.WearInfo = le32_to_cpu(nb_erases);
3528593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner		if (nftl_write_oob(mtd, block * nftl->EraseSize + SECTORSIZE +
353f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner				   8, 8, &retlen, (char *)&uci) < 0)
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto fail;
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfail:
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* could not format, update the bad block table (caller is responsible
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   for setting the ReplUnitTable to BLOCK_RESERVED on failure) */
3595942ddbc500d1c9b75e571b656be97f65b26adfeArtem Bityutskiy	mtd_block_markbad(nftl->mbd.mtd, instr->addr);
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -1;
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* check_sectors_in_chain: Check that each sector of a Virtual Unit Chain is correct.
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Mark as 'IGNORE' each incorrect sector. This check is only done if the chain
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	was being folded when NFTL was interrupted.
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
36792394b5c2be774425f255b5c7afbd8b19978fe12Brian Norris *	The check_free_sectors in this function is necessary. There is a possible
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	situation that after writing the Data area, the Block Control Information is
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	not updated according (due to power failure or something) which leaves the block
37092394b5c2be774425f255b5c7afbd8b19978fe12Brian Norris *	in an inconsistent state. So we have to check if a block is really FREE in this
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	case. */
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_block)
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
374f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner	struct mtd_info *mtd = nftl->mbd.mtd;
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int block, i, status;
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct nftl_bci bci;
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int sectors_per_block;
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size_t retlen;
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sectors_per_block = nftl->EraseSize / SECTORSIZE;
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	block = first_block;
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (;;) {
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < sectors_per_block; i++) {
3848593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner			if (nftl_read_oob(mtd,
385f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner					  block * nftl->EraseSize + i * SECTORSIZE,
386f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner					  8, &retlen, (char *)&bci) < 0)
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				status = SECTOR_IGNORE;
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			else
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				status = bci.Status | bci.Status1;
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			switch(status) {
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case SECTOR_FREE:
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* verify that the sector is really free. If not, mark
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   as ignore */
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (memcmpb(&bci, 0xff, 8) != 0 ||
39697894cda5773e59bd13e87b72077751099419a9fThomas Gleixner				    check_free_sectors(nftl, block * nftl->EraseSize + i * SECTORSIZE,
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						       SECTORSIZE, 0) != 0) {
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					printk("Incorrect free sector %d in block %d: "
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					       "marking it as ignored\n",
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					       i, block);
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					/* sector not free actually : mark it as SECTOR_IGNORE  */
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					bci.Status = SECTOR_IGNORE;
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					bci.Status1 = SECTOR_IGNORE;
4058593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner					nftl_write_oob(mtd, block *
406f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner						       nftl->EraseSize +
407f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner						       i * SECTORSIZE, 8,
408f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner						       &retlen, (char *)&bci);
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			default:
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* proceed to next Erase Unit on the chain */
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		block = nftl->ReplUnitTable[block];
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!(block == BLOCK_NIL || block < nftl->nb_blocks))
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk("incorrect ReplUnitTable[] : %d\n", block);
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (block == BLOCK_NIL || block >= nftl->nb_blocks)
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
425efad798b9f01300565f65058b153250cc49d58f2Paulius Zaleckas/* calc_chain_length: Walk through a Virtual Unit Chain and estimate chain length */
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int calc_chain_length(struct NFTLrecord *nftl, unsigned int first_block)
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int length = 0, block = first_block;
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (;;) {
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		length++;
43292394b5c2be774425f255b5c7afbd8b19978fe12Brian Norris		/* avoid infinite loops, although this is guaranteed not to
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   happen because of the previous checks */
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (length >= nftl->nb_blocks) {
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk("nftl: length too long %d !\n", length);
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		block = nftl->ReplUnitTable[block];
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!(block == BLOCK_NIL || block < nftl->nb_blocks))
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk("incorrect ReplUnitTable[] : %d\n", block);
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (block == BLOCK_NIL || block >= nftl->nb_blocks)
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return length;
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* format_chain: Format an invalid Virtual Unit chain. It frees all the Erase Units in a
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Virtual Unit Chain, i.e. all the units are disconnected.
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
45192394b5c2be774425f255b5c7afbd8b19978fe12Brian Norris *	It is not strictly correct to begin from the first block of the chain because
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	if we stop the code, we may see again a valid chain if there was a first_block
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	flag in a block inside it. But is it really a problem ?
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
45592394b5c2be774425f255b5c7afbd8b19978fe12Brian Norris * FixMe: Figure out what the last statement means. What if power failure when we are
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	in the for (;;) loop formatting blocks ??
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void format_chain(struct NFTLrecord *nftl, unsigned int first_block)
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int block = first_block, block1;
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("Formatting chain at block %d\n", first_block);
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (;;) {
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		block1 = nftl->ReplUnitTable[block];
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("Formatting block %d\n", block);
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (NFTL_formatblock(nftl, block) < 0) {
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* cannot format !!!! Mark it as Bad Unit */
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			nftl->ReplUnitTable[block] = BLOCK_RESERVED;
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			nftl->ReplUnitTable[block] = BLOCK_FREE;
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* goto next block on the chain */
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		block = block1;
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!(block == BLOCK_NIL || block < nftl->nb_blocks))
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk("incorrect ReplUnitTable[] : %d\n", block);
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (block == BLOCK_NIL || block >= nftl->nb_blocks)
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* check_and_mark_free_block: Verify that a block is free in the NFTL sense (valid erase mark) or
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	totally free (only 0xff).
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Definition: Free Erase Unit -- A properly erased/formatted Free Erase Unit should have meet the
48992394b5c2be774425f255b5c7afbd8b19978fe12Brian Norris *	following criteria:
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	1. */
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int check_and_mark_free_block(struct NFTLrecord *nftl, int block)
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
493f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner	struct mtd_info *mtd = nftl->mbd.mtd;
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct nftl_uci1 h1;
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int erase_mark;
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size_t retlen;
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* check erase mark. */
4998593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
500f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner			  &retlen, (char *)&h1) < 0)
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -1;
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1));
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (erase_mark != ERASE_MARK) {
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* if no erase mark, the block must be totally free. This is
50692394b5c2be774425f255b5c7afbd8b19978fe12Brian Norris		   possible in two cases : empty filesystem or interrupted erase (very unlikely) */
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (check_free_sectors (nftl, block * nftl->EraseSize, nftl->EraseSize, 1) != 0)
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -1;
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* free block : write erase mark */
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		h1.EraseMark = cpu_to_le16(ERASE_MARK);
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		h1.EraseMark1 = cpu_to_le16(ERASE_MARK);
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		h1.WearInfo = cpu_to_le32(0);
5148593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner		if (nftl_write_oob(mtd,
515f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner				   block * nftl->EraseSize + SECTORSIZE + 8, 8,
516f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner				   &retlen, (char *)&h1) < 0)
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -1;
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* if erase mark present, need to skip it when doing check */
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < nftl->EraseSize; i += SECTORSIZE) {
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* check free sector */
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (check_free_sectors (nftl, block * nftl->EraseSize + i,
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						SECTORSIZE, 0) != 0)
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return -1;
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5278593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner			if (nftl_read_oob(mtd, block * nftl->EraseSize + i,
528f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner					  16, &retlen, buf) < 0)
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return -1;
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (i == SECTORSIZE) {
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* skip erase mark */
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (memcmpb(buf, 0xff, 8))
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					return -1;
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			} else {
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (memcmpb(buf, 0xff, 16))
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					return -1;
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* get_fold_mark: Read fold mark from Unit Control Information #2, we use FOLD_MARK_IN_PROGRESS
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	to indicate that we are in the progression of a Virtual Unit Chain folding. If the UCI #2
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	is FOLD_MARK_IN_PROGRESS when mounting the NFTL, the (previous) folding process is interrupted
54892394b5c2be774425f255b5c7afbd8b19978fe12Brian Norris *	for some reason. A clean up/check of the VUC is necessary in this case.
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * WARNING: return 0 if read error
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int get_fold_mark(struct NFTLrecord *nftl, unsigned int block)
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
554f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner	struct mtd_info *mtd = nftl->mbd.mtd;
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct nftl_uci2 uci;
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size_t retlen;
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5588593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	if (nftl_read_oob(mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8,
559f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner			  8, &retlen, (char *)&uci) < 0)
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return le16_to_cpu((uci.FoldMark | uci.FoldMark1));
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint NFTL_mount(struct NFTLrecord *s)
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int first_logical_block, logical_block, rep_block, nb_erases, erase_mark;
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int block, first_block, is_first_block;
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int chain_length, do_format_chain;
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct nftl_uci0 h0;
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct nftl_uci1 h1;
573f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner	struct mtd_info *mtd = s->mbd.mtd;
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size_t retlen;
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* search for NFTL MediaHeader and Spare NFTL Media Header */
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (find_boot_record(s) < 0) {
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("Could not find valid boot record\n");
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -1;
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* init the logical to physical table */
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < s->nb_blocks; i++) {
5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		s->EUNtable[i] = BLOCK_NIL;
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* first pass : explore each block chain */
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	first_logical_block = 0;
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (first_block = 0; first_block < s->nb_blocks; first_block++) {
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* if the block was not already explored, we can look at it */
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (s->ReplUnitTable[first_block] == BLOCK_NOTEXPLORED) {
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			block = first_block;
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			chain_length = 0;
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			do_format_chain = 0;
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			for (;;) {
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* read the block header. If error, we format the chain */
5988593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner				if (nftl_read_oob(mtd,
599f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner						  block * s->EraseSize + 8, 8,
600f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner						  &retlen, (char *)&h0) < 0 ||
6018593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner				    nftl_read_oob(mtd,
602f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner						  block * s->EraseSize +
603f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner						  SECTORSIZE + 8, 8,
604f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner						  &retlen, (char *)&h1) < 0) {
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					s->ReplUnitTable[block] = BLOCK_NIL;
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					do_format_chain = 1;
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				logical_block = le16_to_cpu ((h0.VirtUnitNum | h0.SpareVirtUnitNum));
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				rep_block = le16_to_cpu ((h0.ReplUnitNum | h0.SpareReplUnitNum));
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				nb_erases = le32_to_cpu (h1.WearInfo);
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1));
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				is_first_block = !(logical_block >> 15);
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				logical_block = logical_block & 0x7fff;
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* invalid/free block test */
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (erase_mark != ERASE_MARK || logical_block >= s->nb_blocks) {
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					if (chain_length == 0) {
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						/* if not currently in a chain, we can handle it safely */
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						if (check_and_mark_free_block(s, block) < 0) {
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							/* not really free: format it */
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							printk("Formatting block %d\n", block);
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							if (NFTL_formatblock(s, block) < 0) {
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds								/* could not format: reserve the block */
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds								s->ReplUnitTable[block] = BLOCK_RESERVED;
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							} else {
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds								s->ReplUnitTable[block] = BLOCK_FREE;
6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							}
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						} else {
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							/* free block: mark it */
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							s->ReplUnitTable[block] = BLOCK_FREE;
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						}
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						/* directly examine the next block. */
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						goto examine_ReplUnitTable;
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					} else {
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						/* the block was in a chain : this is bad. We
6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						   must format all the chain */
6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						printk("Block %d: free but referenced in chain %d\n",
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						       block, first_block);
6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						s->ReplUnitTable[block] = BLOCK_NIL;
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						do_format_chain = 1;
6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						break;
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					}
6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* we accept only first blocks here */
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (chain_length == 0) {
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					/* this block is not the first block in chain :
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					   ignore it, it will be included in a chain
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					   later, or marked as not explored */
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					if (!is_first_block)
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						goto examine_ReplUnitTable;
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					first_logical_block = logical_block;
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				} else {
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					if (logical_block != first_logical_block) {
65897894cda5773e59bd13e87b72077751099419a9fThomas Gleixner						printk("Block %d: incorrect logical block: %d expected: %d\n",
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						       block, logical_block, first_logical_block);
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						/* the chain is incorrect : we must format it,
66192394b5c2be774425f255b5c7afbd8b19978fe12Brian Norris						   but we need to read it completely */
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						do_format_chain = 1;
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					}
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					if (is_first_block) {
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						/* we accept that a block is marked as first
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						   block while being last block in a chain
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						   only if the chain is being folded */
6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						if (get_fold_mark(s, block) != FOLD_MARK_IN_PROGRESS ||
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						    rep_block != 0xffff) {
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							printk("Block %d: incorrectly marked as first block in chain\n",
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							       block);
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							/* the chain is incorrect : we must format it,
67392394b5c2be774425f255b5c7afbd8b19978fe12Brian Norris							   but we need to read it completely */
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							do_format_chain = 1;
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						} else {
6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							printk("Block %d: folding in progress - ignoring first block flag\n",
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							       block);
6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						}
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					}
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				chain_length++;
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (rep_block == 0xffff) {
6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					/* no more blocks after */
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					s->ReplUnitTable[block] = BLOCK_NIL;
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				} else if (rep_block >= s->nb_blocks) {
68797894cda5773e59bd13e87b72077751099419a9fThomas Gleixner					printk("Block %d: referencing invalid block %d\n",
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					       block, rep_block);
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					do_format_chain = 1;
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					s->ReplUnitTable[block] = BLOCK_NIL;
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				} else if (s->ReplUnitTable[rep_block] != BLOCK_NOTEXPLORED) {
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					/* same problem as previous 'is_first_block' test:
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					   we accept that the last block of a chain has
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					   the first_block flag set if folding is in
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					   progress. We handle here the case where the
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					   last block appeared first */
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					if (s->ReplUnitTable[rep_block] == BLOCK_NIL &&
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					    s->EUNtable[first_logical_block] == rep_block &&
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					    get_fold_mark(s, first_block) == FOLD_MARK_IN_PROGRESS) {
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						/* EUNtable[] will be set after */
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						printk("Block %d: folding in progress - ignoring first block flag\n",
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						       rep_block);
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						s->ReplUnitTable[block] = rep_block;
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						s->EUNtable[first_logical_block] = BLOCK_NIL;
7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					} else {
70797894cda5773e59bd13e87b72077751099419a9fThomas Gleixner						printk("Block %d: referencing block %d already in another chain\n",
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						       block, rep_block);
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						/* XXX: should handle correctly fold in progress chains */
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						do_format_chain = 1;
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						s->ReplUnitTable[block] = BLOCK_NIL;
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					}
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				} else {
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					/* this is OK */
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					s->ReplUnitTable[block] = rep_block;
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					block = rep_block;
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* the chain was completely explored. Now we can decide
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   what to do with it */
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (do_format_chain) {
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* invalid chain : format it */
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				format_chain(s, first_block);
7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			} else {
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				unsigned int first_block1, chain_to_format, chain_length1;
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				int fold_mark;
72997894cda5773e59bd13e87b72077751099419a9fThomas Gleixner
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* valid chain : get foldmark */
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				fold_mark = get_fold_mark(s, first_block);
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (fold_mark == 0) {
7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					/* cannot get foldmark : format the chain */
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					printk("Could read foldmark at block %d\n", first_block);
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					format_chain(s, first_block);
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				} else {
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					if (fold_mark == FOLD_MARK_IN_PROGRESS)
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						check_sectors_in_chain(s, first_block);
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					/* now handle the case where we find two chains at the
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					   same virtual address : we select the longer one,
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					   because the shorter one is the one which was being
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					   folded if the folding was not done in place */
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					first_block1 = s->EUNtable[first_logical_block];
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					if (first_block1 != BLOCK_NIL) {
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						/* XXX: what to do if same length ? */
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						chain_length1 = calc_chain_length(s, first_block1);
74897894cda5773e59bd13e87b72077751099419a9fThomas Gleixner						printk("Two chains at blocks %d (len=%d) and %d (len=%d)\n",
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						       first_block1, chain_length1, first_block, chain_length);
75097894cda5773e59bd13e87b72077751099419a9fThomas Gleixner
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						if (chain_length >= chain_length1) {
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							chain_to_format = first_block1;
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							s->EUNtable[first_logical_block] = first_block;
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						} else {
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							chain_to_format = first_block;
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						}
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						format_chain(s, chain_to_format);
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					} else {
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						s->EUNtable[first_logical_block] = first_block;
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					}
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	examine_ReplUnitTable:;
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* second pass to format unreferenced blocks  and init free block count */
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	s->numfreeEUNs = 0;
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	s->LastFreeEUN = le16_to_cpu(s->MediaHdr.FirstPhysicalEUN);
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (block = 0; block < s->nb_blocks; block++) {
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (s->ReplUnitTable[block] == BLOCK_NOTEXPLORED) {
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk("Unreferenced block %d, formatting it\n", block);
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (NFTL_formatblock(s, block) < 0)
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				s->ReplUnitTable[block] = BLOCK_RESERVED;
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			else
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				s->ReplUnitTable[block] = BLOCK_FREE;
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (s->ReplUnitTable[block] == BLOCK_FREE) {
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			s->numfreeEUNs++;
7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			s->LastFreeEUN = block;
7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
787