xref: /openbmc/linux/drivers/mtd/nftlmount.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * NFTL mount code with extensive checks
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
6a1452a37SDavid Woodhouse  * Copyright © 2000 Netgem S.A.
7a1452a37SDavid Woodhouse  * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org>
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds 
101da177e4SLinus Torvalds #include <linux/kernel.h>
111da177e4SLinus Torvalds #include <asm/errno.h>
121da177e4SLinus Torvalds #include <linux/delay.h>
131da177e4SLinus Torvalds #include <linux/slab.h>
141da177e4SLinus Torvalds #include <linux/mtd/mtd.h>
15d4092d76SBoris Brezillon #include <linux/mtd/rawnand.h>
161da177e4SLinus Torvalds #include <linux/mtd/nftl.h>
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds #define SECTORSIZE 512
191da177e4SLinus Torvalds 
201da177e4SLinus Torvalds /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the
211da177e4SLinus Torvalds  *	various device information of the NFTL partition and Bad Unit Table. Update
2292394b5cSBrian Norris  *	the ReplUnitTable[] table according to the Bad Unit Table. ReplUnitTable[]
231da177e4SLinus Torvalds  *	is used for management of Erase Unit in other routines in nftl.c and nftlmount.c
241da177e4SLinus Torvalds  */
find_boot_record(struct NFTLrecord * nftl)251da177e4SLinus Torvalds static int find_boot_record(struct NFTLrecord *nftl)
261da177e4SLinus Torvalds {
271da177e4SLinus Torvalds 	struct nftl_uci1 h1;
281da177e4SLinus Torvalds 	unsigned int block, boot_record_count = 0;
291da177e4SLinus Torvalds 	size_t retlen;
301da177e4SLinus Torvalds 	u8 buf[SECTORSIZE];
311da177e4SLinus Torvalds 	struct NFTLMediaHeader *mh = &nftl->MediaHdr;
32f4a43cfcSThomas Gleixner 	struct mtd_info *mtd = nftl->mbd.mtd;
331da177e4SLinus Torvalds 	unsigned int i;
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds         /* Assume logical EraseSize == physical erasesize for starting the scan.
361da177e4SLinus Torvalds 	   We'll sort it out later if we find a MediaHeader which says otherwise */
371da177e4SLinus Torvalds 	/* Actually, we won't.  The new DiskOnChip driver has already scanned
381da177e4SLinus Torvalds 	   the MediaHeader and adjusted the virtual erasesize it presents in
391da177e4SLinus Torvalds 	   the mtd device accordingly.  We could even get rid of
401da177e4SLinus Torvalds 	   nftl->EraseSize if there were any point in doing so. */
411da177e4SLinus Torvalds 	nftl->EraseSize = nftl->mbd.mtd->erasesize;
4269423d99SAdrian Hunter         nftl->nb_blocks = (u32)nftl->mbd.mtd->size / nftl->EraseSize;
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds 	nftl->MediaUnit = BLOCK_NIL;
451da177e4SLinus Torvalds 	nftl->SpareMediaUnit = BLOCK_NIL;
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds 	/* search for a valid boot record */
481da177e4SLinus Torvalds 	for (block = 0; block < nftl->nb_blocks; block++) {
491da177e4SLinus Torvalds 		int ret;
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds 		/* Check for ANAND header first. Then can whinge if it's found but later
521da177e4SLinus Torvalds 		   checks fail */
53329ad399SArtem Bityutskiy 		ret = mtd_read(mtd, block * nftl->EraseSize, SECTORSIZE,
54f4a43cfcSThomas Gleixner 			       &retlen, buf);
551da177e4SLinus Torvalds 		/* We ignore ret in case the ECC of the MediaHeader is invalid
561da177e4SLinus Torvalds 		   (which is apparently acceptable) */
571da177e4SLinus Torvalds 		if (retlen != SECTORSIZE) {
581da177e4SLinus Torvalds 			static int warncount = 5;
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds 			if (warncount) {
611da177e4SLinus Torvalds 				printk(KERN_WARNING "Block read at 0x%x of mtd%d failed: %d\n",
621da177e4SLinus Torvalds 				       block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
631da177e4SLinus Torvalds 				if (!--warncount)
641da177e4SLinus Torvalds 					printk(KERN_WARNING "Further failures for this block will not be printed\n");
651da177e4SLinus Torvalds 			}
661da177e4SLinus Torvalds 			continue;
671da177e4SLinus Torvalds 		}
681da177e4SLinus Torvalds 
691da177e4SLinus Torvalds 		if (retlen < 6 || memcmp(buf, "ANAND", 6)) {
701da177e4SLinus Torvalds 			/* ANAND\0 not found. Continue */
711da177e4SLinus Torvalds #if 0
721da177e4SLinus Torvalds 			printk(KERN_DEBUG "ANAND header not found at 0x%x in mtd%d\n",
731da177e4SLinus Torvalds 			       block * nftl->EraseSize, nftl->mbd.mtd->index);
741da177e4SLinus Torvalds #endif
751da177e4SLinus Torvalds 			continue;
761da177e4SLinus Torvalds 		}
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds 		/* To be safer with BIOS, also use erase mark as discriminant */
79768c57c8SAndy Shevchenko 		ret = nftl_read_oob(mtd, block * nftl->EraseSize +
80f4a43cfcSThomas Gleixner 					 SECTORSIZE + 8, 8, &retlen,
81768c57c8SAndy Shevchenko 					 (char *)&h1);
82768c57c8SAndy Shevchenko 		if (ret < 0) {
831da177e4SLinus Torvalds 			printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n",
841da177e4SLinus Torvalds 			       block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
851da177e4SLinus Torvalds 			continue;
861da177e4SLinus Torvalds 		}
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds #if 0 /* Some people seem to have devices without ECC or erase marks
891da177e4SLinus Torvalds 	 on the Media Header blocks. There are enough other sanity
901da177e4SLinus Torvalds 	 checks in here that we can probably do without it.
911da177e4SLinus Torvalds       */
921da177e4SLinus Torvalds 		if (le16_to_cpu(h1.EraseMark | h1.EraseMark1) != ERASE_MARK) {
931da177e4SLinus Torvalds 			printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but erase mark not present (0x%04x,0x%04x instead)\n",
941da177e4SLinus Torvalds 			       block * nftl->EraseSize, nftl->mbd.mtd->index,
951da177e4SLinus Torvalds 			       le16_to_cpu(h1.EraseMark), le16_to_cpu(h1.EraseMark1));
961da177e4SLinus Torvalds 			continue;
971da177e4SLinus Torvalds 		}
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds 		/* Finally reread to check ECC */
100768c57c8SAndy Shevchenko 		ret = mtd->read(mtd, block * nftl->EraseSize, SECTORSIZE,
101768c57c8SAndy Shevchenko 				&retlen, buf);
102768c57c8SAndy Shevchenko 		if (ret < 0) {
1031da177e4SLinus Torvalds 			printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n",
1041da177e4SLinus Torvalds 			       block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
1051da177e4SLinus Torvalds 			continue;
1061da177e4SLinus Torvalds 		}
1071da177e4SLinus Torvalds 
1081da177e4SLinus Torvalds 		/* Paranoia. Check the ANAND header is still there after the ECC read */
1091da177e4SLinus Torvalds 		if (memcmp(buf, "ANAND", 6)) {
1101da177e4SLinus Torvalds 			printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but went away on reread!\n",
1111da177e4SLinus Torvalds 			       block * nftl->EraseSize, nftl->mbd.mtd->index);
112ac9cd36cSAntonio Cardace 			printk(KERN_NOTICE "New data are: %6ph\n", buf);
1131da177e4SLinus Torvalds 			continue;
1141da177e4SLinus Torvalds 		}
1151da177e4SLinus Torvalds #endif
1161da177e4SLinus Torvalds 		/* OK, we like it. */
1171da177e4SLinus Torvalds 
1181da177e4SLinus Torvalds 		if (boot_record_count) {
1191da177e4SLinus Torvalds 			/* We've already processed one. So we just check if
1201da177e4SLinus Torvalds 			   this one is the same as the first one we found */
1211da177e4SLinus Torvalds 			if (memcmp(mh, buf, sizeof(struct NFTLMediaHeader))) {
1221da177e4SLinus Torvalds 				printk(KERN_NOTICE "NFTL Media Headers at 0x%x and 0x%x disagree.\n",
1231da177e4SLinus Torvalds 				       nftl->MediaUnit * nftl->EraseSize, block * nftl->EraseSize);
1241da177e4SLinus Torvalds 				/* if (debug) Print both side by side */
1251da177e4SLinus Torvalds 				if (boot_record_count < 2) {
1261da177e4SLinus Torvalds 					/* We haven't yet seen two real ones */
1271da177e4SLinus Torvalds 					return -1;
1281da177e4SLinus Torvalds 				}
1291da177e4SLinus Torvalds 				continue;
1301da177e4SLinus Torvalds 			}
1311da177e4SLinus Torvalds 			if (boot_record_count == 1)
1321da177e4SLinus Torvalds 				nftl->SpareMediaUnit = block;
1331da177e4SLinus Torvalds 
1341da177e4SLinus Torvalds 			/* Mark this boot record (NFTL MediaHeader) block as reserved */
1351da177e4SLinus Torvalds 			nftl->ReplUnitTable[block] = BLOCK_RESERVED;
1361da177e4SLinus Torvalds 
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds 			boot_record_count++;
1391da177e4SLinus Torvalds 			continue;
1401da177e4SLinus Torvalds 		}
1411da177e4SLinus Torvalds 
1421da177e4SLinus Torvalds 		/* This is the first we've seen. Copy the media header structure into place */
1431da177e4SLinus Torvalds 		memcpy(mh, buf, sizeof(struct NFTLMediaHeader));
1441da177e4SLinus Torvalds 
1451da177e4SLinus Torvalds 		/* Do some sanity checks on it */
1461da177e4SLinus Torvalds #if 0
1471da177e4SLinus Torvalds The new DiskOnChip driver scans the MediaHeader itself, and presents a virtual
1481da177e4SLinus Torvalds erasesize based on UnitSizeFactor.  So the erasesize we read from the mtd
1491da177e4SLinus Torvalds device is already correct.
1501da177e4SLinus Torvalds 		if (mh->UnitSizeFactor == 0) {
1511da177e4SLinus Torvalds 			printk(KERN_NOTICE "NFTL: UnitSizeFactor 0x00 detected. This violates the spec but we think we know what it means...\n");
1521da177e4SLinus Torvalds 		} else if (mh->UnitSizeFactor < 0xfc) {
1531da177e4SLinus Torvalds 			printk(KERN_NOTICE "Sorry, we don't support UnitSizeFactor 0x%02x\n",
1541da177e4SLinus Torvalds 			       mh->UnitSizeFactor);
1551da177e4SLinus Torvalds 			return -1;
1561da177e4SLinus Torvalds 		} else if (mh->UnitSizeFactor != 0xff) {
1571da177e4SLinus Torvalds 			printk(KERN_NOTICE "WARNING: Support for NFTL with UnitSizeFactor 0x%02x is experimental\n",
1581da177e4SLinus Torvalds 			       mh->UnitSizeFactor);
1591da177e4SLinus Torvalds 			nftl->EraseSize = nftl->mbd.mtd->erasesize << (0xff - mh->UnitSizeFactor);
16069423d99SAdrian Hunter 			nftl->nb_blocks = (u32)nftl->mbd.mtd->size / nftl->EraseSize;
1611da177e4SLinus Torvalds 		}
1621da177e4SLinus Torvalds #endif
1631da177e4SLinus Torvalds 		nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN);
1641da177e4SLinus Torvalds 		if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) {
1651da177e4SLinus Torvalds 			printk(KERN_NOTICE "NFTL Media Header sanity check failed:\n");
1661da177e4SLinus Torvalds 			printk(KERN_NOTICE "nb_boot_blocks (%d) + 2 > nb_blocks (%d)\n",
1671da177e4SLinus Torvalds 			       nftl->nb_boot_blocks, nftl->nb_blocks);
1681da177e4SLinus Torvalds 			return -1;
1691da177e4SLinus Torvalds 		}
1701da177e4SLinus Torvalds 
1711da177e4SLinus Torvalds 		nftl->numvunits = le32_to_cpu(mh->FormattedSize) / nftl->EraseSize;
1721da177e4SLinus Torvalds 		if (nftl->numvunits > (nftl->nb_blocks - nftl->nb_boot_blocks - 2)) {
1731da177e4SLinus Torvalds 			printk(KERN_NOTICE "NFTL Media Header sanity check failed:\n");
1741da177e4SLinus Torvalds 			printk(KERN_NOTICE "numvunits (%d) > nb_blocks (%d) - nb_boot_blocks(%d) - 2\n",
1751da177e4SLinus Torvalds 			       nftl->numvunits, nftl->nb_blocks, nftl->nb_boot_blocks);
1761da177e4SLinus Torvalds 			return -1;
1771da177e4SLinus Torvalds 		}
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds 		nftl->mbd.size  = nftl->numvunits * (nftl->EraseSize / SECTORSIZE);
1801da177e4SLinus Torvalds 
1811da177e4SLinus Torvalds 		/* If we're not using the last sectors in the device for some reason,
1821da177e4SLinus Torvalds 		   reduce nb_blocks accordingly so we forget they're there */
1831da177e4SLinus Torvalds 		nftl->nb_blocks = le16_to_cpu(mh->NumEraseUnits) + le16_to_cpu(mh->FirstPhysicalEUN);
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds 		/* XXX: will be suppressed */
1861da177e4SLinus Torvalds 		nftl->lastEUN = nftl->nb_blocks - 1;
1871da177e4SLinus Torvalds 
1881da177e4SLinus Torvalds 		/* memory alloc */
1896da2ec56SKees Cook 		nftl->EUNtable = kmalloc_array(nftl->nb_blocks, sizeof(u16),
1906da2ec56SKees Cook 					       GFP_KERNEL);
191*8ef02913SZhen Lei 		if (!nftl->EUNtable)
1921da177e4SLinus Torvalds 			return -ENOMEM;
1931da177e4SLinus Torvalds 
1946da2ec56SKees Cook 		nftl->ReplUnitTable = kmalloc_array(nftl->nb_blocks,
1956da2ec56SKees Cook 						    sizeof(u16),
1966da2ec56SKees Cook 						    GFP_KERNEL);
1971da177e4SLinus Torvalds 		if (!nftl->ReplUnitTable) {
1981da177e4SLinus Torvalds 			kfree(nftl->EUNtable);
1991da177e4SLinus Torvalds 			return -ENOMEM;
2001da177e4SLinus Torvalds 		}
2011da177e4SLinus Torvalds 
2021da177e4SLinus Torvalds 		/* mark the bios blocks (blocks before NFTL MediaHeader) as reserved */
2031da177e4SLinus Torvalds 		for (i = 0; i < nftl->nb_boot_blocks; i++)
2041da177e4SLinus Torvalds 			nftl->ReplUnitTable[i] = BLOCK_RESERVED;
2051da177e4SLinus Torvalds 		/* mark all remaining blocks as potentially containing data */
2061da177e4SLinus Torvalds 		for (; i < nftl->nb_blocks; i++) {
2071da177e4SLinus Torvalds 			nftl->ReplUnitTable[i] = BLOCK_NOTEXPLORED;
2081da177e4SLinus Torvalds 		}
2091da177e4SLinus Torvalds 
2101da177e4SLinus Torvalds 		/* Mark this boot record (NFTL MediaHeader) block as reserved */
2111da177e4SLinus Torvalds 		nftl->ReplUnitTable[block] = BLOCK_RESERVED;
2121da177e4SLinus Torvalds 
2131da177e4SLinus Torvalds 		/* read the Bad Erase Unit Table and modify ReplUnitTable[] accordingly */
2141da177e4SLinus Torvalds 		for (i = 0; i < nftl->nb_blocks; i++) {
2151da177e4SLinus Torvalds #if 0
2161da177e4SLinus Torvalds The new DiskOnChip driver already scanned the bad block table.  Just query it.
2171da177e4SLinus Torvalds 			if ((i & (SECTORSIZE - 1)) == 0) {
2181da177e4SLinus Torvalds 				/* read one sector for every SECTORSIZE of blocks */
219768c57c8SAndy Shevchenko 				ret = mtd->read(nftl->mbd.mtd,
220768c57c8SAndy Shevchenko 						block * nftl->EraseSize + i +
221768c57c8SAndy Shevchenko 						SECTORSIZE, SECTORSIZE,
222768c57c8SAndy Shevchenko 						&retlen, buf);
223768c57c8SAndy Shevchenko 				if (ret < 0) {
2241da177e4SLinus Torvalds 					printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n",
2251da177e4SLinus Torvalds 					       ret);
2261da177e4SLinus Torvalds 					kfree(nftl->ReplUnitTable);
2271da177e4SLinus Torvalds 					kfree(nftl->EUNtable);
2281da177e4SLinus Torvalds 					return -1;
2291da177e4SLinus Torvalds 				}
2301da177e4SLinus Torvalds 			}
2311da177e4SLinus Torvalds 			/* mark the Bad Erase Unit as RESERVED in ReplUnitTable */
2321da177e4SLinus Torvalds 			if (buf[i & (SECTORSIZE - 1)] != 0xff)
2331da177e4SLinus Torvalds 				nftl->ReplUnitTable[i] = BLOCK_RESERVED;
2341da177e4SLinus Torvalds #endif
2357086c19dSArtem Bityutskiy 			if (mtd_block_isbad(nftl->mbd.mtd,
2367086c19dSArtem Bityutskiy 					    i * nftl->EraseSize))
2371da177e4SLinus Torvalds 				nftl->ReplUnitTable[i] = BLOCK_RESERVED;
2381da177e4SLinus Torvalds 		}
2391da177e4SLinus Torvalds 
2401da177e4SLinus Torvalds 		nftl->MediaUnit = block;
2411da177e4SLinus Torvalds 		boot_record_count++;
2421da177e4SLinus Torvalds 
2431da177e4SLinus Torvalds 	} /* foreach (block) */
2441da177e4SLinus Torvalds 
2451da177e4SLinus Torvalds 	return boot_record_count?0:-1;
2461da177e4SLinus Torvalds }
2471da177e4SLinus Torvalds 
memcmpb(void * a,int c,int n)2481da177e4SLinus Torvalds static int memcmpb(void *a, int c, int n)
2491da177e4SLinus Torvalds {
2501da177e4SLinus Torvalds 	int i;
2511da177e4SLinus Torvalds 	for (i = 0; i < n; i++) {
2521da177e4SLinus Torvalds 		if (c != ((unsigned char *)a)[i])
2531da177e4SLinus Torvalds 			return 1;
2541da177e4SLinus Torvalds 	}
2551da177e4SLinus Torvalds 	return 0;
2561da177e4SLinus Torvalds }
2571da177e4SLinus Torvalds 
2581da177e4SLinus Torvalds /* check_free_sector: check if a free sector is actually FREE, i.e. All 0xff in data and oob area */
check_free_sectors(struct NFTLrecord * nftl,unsigned int address,int len,int check_oob)2591da177e4SLinus Torvalds static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int len,
2601da177e4SLinus Torvalds 			      int check_oob)
2611da177e4SLinus Torvalds {
2629223a456SThomas Gleixner 	struct mtd_info *mtd = nftl->mbd.mtd;
2639223a456SThomas Gleixner 	size_t retlen;
26427ab41e2SKees Cook 	int i, ret;
26527ab41e2SKees Cook 	u8 *buf;
2661da177e4SLinus Torvalds 
26727ab41e2SKees Cook 	buf = kmalloc(SECTORSIZE + mtd->oobsize, GFP_KERNEL);
26827ab41e2SKees Cook 	if (!buf)
2699c5b19c2SYang Li 		return -ENOMEM;
27027ab41e2SKees Cook 
27127ab41e2SKees Cook 	ret = -1;
2721da177e4SLinus Torvalds 	for (i = 0; i < len; i += SECTORSIZE) {
273329ad399SArtem Bityutskiy 		if (mtd_read(mtd, address, SECTORSIZE, &retlen, buf))
27427ab41e2SKees Cook 			goto out;
2751da177e4SLinus Torvalds 		if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
27627ab41e2SKees Cook 			goto out;
2771da177e4SLinus Torvalds 
2781da177e4SLinus Torvalds 		if (check_oob) {
2798593fbc6SThomas Gleixner 			if(nftl_read_oob(mtd, address, mtd->oobsize,
2809223a456SThomas Gleixner 					 &retlen, &buf[SECTORSIZE]) < 0)
28127ab41e2SKees Cook 				goto out;
2829223a456SThomas Gleixner 			if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0)
28327ab41e2SKees Cook 				goto out;
2841da177e4SLinus Torvalds 		}
2851da177e4SLinus Torvalds 		address += SECTORSIZE;
2861da177e4SLinus Torvalds 	}
2871da177e4SLinus Torvalds 
28827ab41e2SKees Cook 	ret = 0;
28927ab41e2SKees Cook 
29027ab41e2SKees Cook out:
29127ab41e2SKees Cook 	kfree(buf);
29227ab41e2SKees Cook 	return ret;
2931da177e4SLinus Torvalds }
2941da177e4SLinus Torvalds 
2951da177e4SLinus Torvalds /* NFTL_format: format a Erase Unit by erasing ALL Erase Zones in the Erase Unit and
2961da177e4SLinus Torvalds  *              Update NFTL metadata. Each erase operation is checked with check_free_sectors
2971da177e4SLinus Torvalds  *
2981da177e4SLinus Torvalds  * Return: 0 when succeed, -1 on error.
2991da177e4SLinus Torvalds  *
30092394b5cSBrian Norris  *  ToDo: 1. Is it necessary to check_free_sector after erasing ??
3011da177e4SLinus Torvalds  */
NFTL_formatblock(struct NFTLrecord * nftl,int block)3021da177e4SLinus Torvalds int NFTL_formatblock(struct NFTLrecord *nftl, int block)
3031da177e4SLinus Torvalds {
3041da177e4SLinus Torvalds 	size_t retlen;
3051da177e4SLinus Torvalds 	unsigned int nb_erases, erase_mark;
3061da177e4SLinus Torvalds 	struct nftl_uci1 uci;
3071da177e4SLinus Torvalds 	struct erase_info *instr = &nftl->instr;
308f4a43cfcSThomas Gleixner 	struct mtd_info *mtd = nftl->mbd.mtd;
3091da177e4SLinus Torvalds 
3101da177e4SLinus Torvalds 	/* Read the Unit Control Information #1 for Wear-Leveling */
3118593fbc6SThomas Gleixner 	if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8,
3121da177e4SLinus Torvalds 			  8, &retlen, (char *)&uci) < 0)
3131da177e4SLinus Torvalds 		goto default_uci1;
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds 	erase_mark = le16_to_cpu ((uci.EraseMark | uci.EraseMark1));
3161da177e4SLinus Torvalds 	if (erase_mark != ERASE_MARK) {
3171da177e4SLinus Torvalds 	default_uci1:
3181da177e4SLinus Torvalds 		uci.EraseMark = cpu_to_le16(ERASE_MARK);
3191da177e4SLinus Torvalds 		uci.EraseMark1 = cpu_to_le16(ERASE_MARK);
3201da177e4SLinus Torvalds 		uci.WearInfo = cpu_to_le32(0);
3211da177e4SLinus Torvalds 	}
3221da177e4SLinus Torvalds 
3231da177e4SLinus Torvalds 	memset(instr, 0, sizeof(struct erase_info));
3241da177e4SLinus Torvalds 
3251da177e4SLinus Torvalds 	/* XXX: use async erase interface, XXX: test return code */
3261da177e4SLinus Torvalds 	instr->addr = block * nftl->EraseSize;
3271da177e4SLinus Torvalds 	instr->len = nftl->EraseSize;
328884cfd90SBoris Brezillon 	if (mtd_erase(mtd, instr)) {
3291da177e4SLinus Torvalds 		printk("Error while formatting block %d\n", block);
3301da177e4SLinus Torvalds 		goto fail;
3311da177e4SLinus Torvalds 	}
3321da177e4SLinus Torvalds 
3331da177e4SLinus Torvalds 	/* increase and write Wear-Leveling info */
3341da177e4SLinus Torvalds 	nb_erases = le32_to_cpu(uci.WearInfo);
3351da177e4SLinus Torvalds 	nb_erases++;
3361da177e4SLinus Torvalds 
33792394b5cSBrian Norris 	/* wrap (almost impossible with current flash) or free block */
3381da177e4SLinus Torvalds 	if (nb_erases == 0)
3391da177e4SLinus Torvalds 		nb_erases = 1;
3401da177e4SLinus Torvalds 
3411da177e4SLinus Torvalds 	/* check the "freeness" of Erase Unit before updating metadata
3421da177e4SLinus Torvalds 	 * FixMe:  is this check really necessary ? since we have check the
3434845a077SColin Ian King 	 *         return code after the erase operation.
3444845a077SColin Ian King 	 */
3451da177e4SLinus Torvalds 	if (check_free_sectors(nftl, instr->addr, nftl->EraseSize, 1) != 0)
3461da177e4SLinus Torvalds 		goto fail;
3471da177e4SLinus Torvalds 
3481da177e4SLinus Torvalds 	uci.WearInfo = le32_to_cpu(nb_erases);
3498593fbc6SThomas Gleixner 	if (nftl_write_oob(mtd, block * nftl->EraseSize + SECTORSIZE +
350f4a43cfcSThomas Gleixner 			   8, 8, &retlen, (char *)&uci) < 0)
3511da177e4SLinus Torvalds 		goto fail;
3521da177e4SLinus Torvalds 	return 0;
3531da177e4SLinus Torvalds fail:
3541da177e4SLinus Torvalds 	/* could not format, update the bad block table (caller is responsible
3551da177e4SLinus Torvalds 	   for setting the ReplUnitTable to BLOCK_RESERVED on failure) */
3565942ddbcSArtem Bityutskiy 	mtd_block_markbad(nftl->mbd.mtd, instr->addr);
3571da177e4SLinus Torvalds 	return -1;
3581da177e4SLinus Torvalds }
3591da177e4SLinus Torvalds 
3601da177e4SLinus Torvalds /* check_sectors_in_chain: Check that each sector of a Virtual Unit Chain is correct.
3611da177e4SLinus Torvalds  *	Mark as 'IGNORE' each incorrect sector. This check is only done if the chain
3621da177e4SLinus Torvalds  *	was being folded when NFTL was interrupted.
3631da177e4SLinus Torvalds  *
36492394b5cSBrian Norris  *	The check_free_sectors in this function is necessary. There is a possible
3651da177e4SLinus Torvalds  *	situation that after writing the Data area, the Block Control Information is
3661da177e4SLinus Torvalds  *	not updated according (due to power failure or something) which leaves the block
36792394b5cSBrian Norris  *	in an inconsistent state. So we have to check if a block is really FREE in this
3681da177e4SLinus Torvalds  *	case. */
check_sectors_in_chain(struct NFTLrecord * nftl,unsigned int first_block)3691da177e4SLinus Torvalds static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_block)
3701da177e4SLinus Torvalds {
371f4a43cfcSThomas Gleixner 	struct mtd_info *mtd = nftl->mbd.mtd;
3721da177e4SLinus Torvalds 	unsigned int block, i, status;
3731da177e4SLinus Torvalds 	struct nftl_bci bci;
3741da177e4SLinus Torvalds 	int sectors_per_block;
3751da177e4SLinus Torvalds 	size_t retlen;
3761da177e4SLinus Torvalds 
3771da177e4SLinus Torvalds 	sectors_per_block = nftl->EraseSize / SECTORSIZE;
3781da177e4SLinus Torvalds 	block = first_block;
3791da177e4SLinus Torvalds 	for (;;) {
3801da177e4SLinus Torvalds 		for (i = 0; i < sectors_per_block; i++) {
3818593fbc6SThomas Gleixner 			if (nftl_read_oob(mtd,
382f4a43cfcSThomas Gleixner 					  block * nftl->EraseSize + i * SECTORSIZE,
3831da177e4SLinus Torvalds 					  8, &retlen, (char *)&bci) < 0)
3841da177e4SLinus Torvalds 				status = SECTOR_IGNORE;
3851da177e4SLinus Torvalds 			else
3861da177e4SLinus Torvalds 				status = bci.Status | bci.Status1;
3871da177e4SLinus Torvalds 
3881da177e4SLinus Torvalds 			switch(status) {
3891da177e4SLinus Torvalds 			case SECTOR_FREE:
3901da177e4SLinus Torvalds 				/* verify that the sector is really free. If not, mark
3911da177e4SLinus Torvalds 				   as ignore */
3921da177e4SLinus Torvalds 				if (memcmpb(&bci, 0xff, 8) != 0 ||
3931da177e4SLinus Torvalds 				    check_free_sectors(nftl, block * nftl->EraseSize + i * SECTORSIZE,
3941da177e4SLinus Torvalds 						       SECTORSIZE, 0) != 0) {
3951da177e4SLinus Torvalds 					printk("Incorrect free sector %d in block %d: "
3961da177e4SLinus Torvalds 					       "marking it as ignored\n",
3971da177e4SLinus Torvalds 					       i, block);
3981da177e4SLinus Torvalds 
3991da177e4SLinus Torvalds 					/* sector not free actually : mark it as SECTOR_IGNORE  */
4001da177e4SLinus Torvalds 					bci.Status = SECTOR_IGNORE;
4011da177e4SLinus Torvalds 					bci.Status1 = SECTOR_IGNORE;
4028593fbc6SThomas Gleixner 					nftl_write_oob(mtd, block *
403f4a43cfcSThomas Gleixner 						       nftl->EraseSize +
404f4a43cfcSThomas Gleixner 						       i * SECTORSIZE, 8,
405f4a43cfcSThomas Gleixner 						       &retlen, (char *)&bci);
4061da177e4SLinus Torvalds 				}
4071da177e4SLinus Torvalds 				break;
4081da177e4SLinus Torvalds 			default:
4091da177e4SLinus Torvalds 				break;
4101da177e4SLinus Torvalds 			}
4111da177e4SLinus Torvalds 		}
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds 		/* proceed to next Erase Unit on the chain */
4141da177e4SLinus Torvalds 		block = nftl->ReplUnitTable[block];
4151da177e4SLinus Torvalds 		if (!(block == BLOCK_NIL || block < nftl->nb_blocks))
4161da177e4SLinus Torvalds 			printk("incorrect ReplUnitTable[] : %d\n", block);
4171da177e4SLinus Torvalds 		if (block == BLOCK_NIL || block >= nftl->nb_blocks)
4181da177e4SLinus Torvalds 			break;
4191da177e4SLinus Torvalds 	}
4201da177e4SLinus Torvalds }
4211da177e4SLinus Torvalds 
422efad798bSPaulius Zaleckas /* calc_chain_length: Walk through a Virtual Unit Chain and estimate chain length */
calc_chain_length(struct NFTLrecord * nftl,unsigned int first_block)4231da177e4SLinus Torvalds static int calc_chain_length(struct NFTLrecord *nftl, unsigned int first_block)
4241da177e4SLinus Torvalds {
4251da177e4SLinus Torvalds 	unsigned int length = 0, block = first_block;
4261da177e4SLinus Torvalds 
4271da177e4SLinus Torvalds 	for (;;) {
4281da177e4SLinus Torvalds 		length++;
42992394b5cSBrian Norris 		/* avoid infinite loops, although this is guaranteed not to
4301da177e4SLinus Torvalds 		   happen because of the previous checks */
4311da177e4SLinus Torvalds 		if (length >= nftl->nb_blocks) {
4321da177e4SLinus Torvalds 			printk("nftl: length too long %d !\n", length);
4331da177e4SLinus Torvalds 			break;
4341da177e4SLinus Torvalds 		}
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds 		block = nftl->ReplUnitTable[block];
4371da177e4SLinus Torvalds 		if (!(block == BLOCK_NIL || block < nftl->nb_blocks))
4381da177e4SLinus Torvalds 			printk("incorrect ReplUnitTable[] : %d\n", block);
4391da177e4SLinus Torvalds 		if (block == BLOCK_NIL || block >= nftl->nb_blocks)
4401da177e4SLinus Torvalds 			break;
4411da177e4SLinus Torvalds 	}
4421da177e4SLinus Torvalds 	return length;
4431da177e4SLinus Torvalds }
4441da177e4SLinus Torvalds 
4451da177e4SLinus Torvalds /* format_chain: Format an invalid Virtual Unit chain. It frees all the Erase Units in a
4461da177e4SLinus Torvalds  *	Virtual Unit Chain, i.e. all the units are disconnected.
4471da177e4SLinus Torvalds  *
44892394b5cSBrian Norris  *	It is not strictly correct to begin from the first block of the chain because
4491da177e4SLinus Torvalds  *	if we stop the code, we may see again a valid chain if there was a first_block
4501da177e4SLinus Torvalds  *	flag in a block inside it. But is it really a problem ?
4511da177e4SLinus Torvalds  *
45292394b5cSBrian Norris  * FixMe: Figure out what the last statement means. What if power failure when we are
4531da177e4SLinus Torvalds  *	in the for (;;) loop formatting blocks ??
4541da177e4SLinus Torvalds  */
format_chain(struct NFTLrecord * nftl,unsigned int first_block)4551da177e4SLinus Torvalds static void format_chain(struct NFTLrecord *nftl, unsigned int first_block)
4561da177e4SLinus Torvalds {
4571da177e4SLinus Torvalds 	unsigned int block = first_block, block1;
4581da177e4SLinus Torvalds 
4591da177e4SLinus Torvalds 	printk("Formatting chain at block %d\n", first_block);
4601da177e4SLinus Torvalds 
4611da177e4SLinus Torvalds 	for (;;) {
4621da177e4SLinus Torvalds 		block1 = nftl->ReplUnitTable[block];
4631da177e4SLinus Torvalds 
4641da177e4SLinus Torvalds 		printk("Formatting block %d\n", block);
4651da177e4SLinus Torvalds 		if (NFTL_formatblock(nftl, block) < 0) {
4661da177e4SLinus Torvalds 			/* cannot format !!!! Mark it as Bad Unit */
4671da177e4SLinus Torvalds 			nftl->ReplUnitTable[block] = BLOCK_RESERVED;
4681da177e4SLinus Torvalds 		} else {
4691da177e4SLinus Torvalds 			nftl->ReplUnitTable[block] = BLOCK_FREE;
4701da177e4SLinus Torvalds 		}
4711da177e4SLinus Torvalds 
4721da177e4SLinus Torvalds 		/* goto next block on the chain */
4731da177e4SLinus Torvalds 		block = block1;
4741da177e4SLinus Torvalds 
4751da177e4SLinus Torvalds 		if (!(block == BLOCK_NIL || block < nftl->nb_blocks))
4761da177e4SLinus Torvalds 			printk("incorrect ReplUnitTable[] : %d\n", block);
4771da177e4SLinus Torvalds 		if (block == BLOCK_NIL || block >= nftl->nb_blocks)
4781da177e4SLinus Torvalds 			break;
4791da177e4SLinus Torvalds 	}
4801da177e4SLinus Torvalds }
4811da177e4SLinus Torvalds 
4821da177e4SLinus Torvalds /* check_and_mark_free_block: Verify that a block is free in the NFTL sense (valid erase mark) or
4831da177e4SLinus Torvalds  *	totally free (only 0xff).
4841da177e4SLinus Torvalds  *
4851da177e4SLinus Torvalds  * Definition: Free Erase Unit -- A properly erased/formatted Free Erase Unit should have meet the
48692394b5cSBrian Norris  *	following criteria:
4871da177e4SLinus Torvalds  *	1. */
check_and_mark_free_block(struct NFTLrecord * nftl,int block)4881da177e4SLinus Torvalds static int check_and_mark_free_block(struct NFTLrecord *nftl, int block)
4891da177e4SLinus Torvalds {
490f4a43cfcSThomas Gleixner 	struct mtd_info *mtd = nftl->mbd.mtd;
4911da177e4SLinus Torvalds 	struct nftl_uci1 h1;
4921da177e4SLinus Torvalds 	unsigned int erase_mark;
4931da177e4SLinus Torvalds 	size_t retlen;
4941da177e4SLinus Torvalds 
4951da177e4SLinus Torvalds 	/* check erase mark. */
4968593fbc6SThomas Gleixner 	if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
4971da177e4SLinus Torvalds 			  &retlen, (char *)&h1) < 0)
4981da177e4SLinus Torvalds 		return -1;
4991da177e4SLinus Torvalds 
5001da177e4SLinus Torvalds 	erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1));
5011da177e4SLinus Torvalds 	if (erase_mark != ERASE_MARK) {
5021da177e4SLinus Torvalds 		/* if no erase mark, the block must be totally free. This is
50392394b5cSBrian Norris 		   possible in two cases : empty filesystem or interrupted erase (very unlikely) */
5041da177e4SLinus Torvalds 		if (check_free_sectors (nftl, block * nftl->EraseSize, nftl->EraseSize, 1) != 0)
5051da177e4SLinus Torvalds 			return -1;
5061da177e4SLinus Torvalds 
5071da177e4SLinus Torvalds 		/* free block : write erase mark */
5081da177e4SLinus Torvalds 		h1.EraseMark = cpu_to_le16(ERASE_MARK);
5091da177e4SLinus Torvalds 		h1.EraseMark1 = cpu_to_le16(ERASE_MARK);
5101da177e4SLinus Torvalds 		h1.WearInfo = cpu_to_le32(0);
5118593fbc6SThomas Gleixner 		if (nftl_write_oob(mtd,
512f4a43cfcSThomas Gleixner 				   block * nftl->EraseSize + SECTORSIZE + 8, 8,
5131da177e4SLinus Torvalds 				   &retlen, (char *)&h1) < 0)
5141da177e4SLinus Torvalds 			return -1;
5151da177e4SLinus Torvalds 	} else {
5161da177e4SLinus Torvalds #if 0
5171da177e4SLinus Torvalds 		/* if erase mark present, need to skip it when doing check */
5181da177e4SLinus Torvalds 		for (i = 0; i < nftl->EraseSize; i += SECTORSIZE) {
5191da177e4SLinus Torvalds 			/* check free sector */
5201da177e4SLinus Torvalds 			if (check_free_sectors (nftl, block * nftl->EraseSize + i,
5211da177e4SLinus Torvalds 						SECTORSIZE, 0) != 0)
5221da177e4SLinus Torvalds 				return -1;
5231da177e4SLinus Torvalds 
5248593fbc6SThomas Gleixner 			if (nftl_read_oob(mtd, block * nftl->EraseSize + i,
5251da177e4SLinus Torvalds 					  16, &retlen, buf) < 0)
5261da177e4SLinus Torvalds 				return -1;
5271da177e4SLinus Torvalds 			if (i == SECTORSIZE) {
5281da177e4SLinus Torvalds 				/* skip erase mark */
5291da177e4SLinus Torvalds 				if (memcmpb(buf, 0xff, 8))
5301da177e4SLinus Torvalds 					return -1;
5311da177e4SLinus Torvalds 			} else {
5321da177e4SLinus Torvalds 				if (memcmpb(buf, 0xff, 16))
5331da177e4SLinus Torvalds 					return -1;
5341da177e4SLinus Torvalds 			}
5351da177e4SLinus Torvalds 		}
5361da177e4SLinus Torvalds #endif
5371da177e4SLinus Torvalds 	}
5381da177e4SLinus Torvalds 
5391da177e4SLinus Torvalds 	return 0;
5401da177e4SLinus Torvalds }
5411da177e4SLinus Torvalds 
5421da177e4SLinus Torvalds /* get_fold_mark: Read fold mark from Unit Control Information #2, we use FOLD_MARK_IN_PROGRESS
5431da177e4SLinus Torvalds  *	to indicate that we are in the progression of a Virtual Unit Chain folding. If the UCI #2
5441da177e4SLinus Torvalds  *	is FOLD_MARK_IN_PROGRESS when mounting the NFTL, the (previous) folding process is interrupted
54592394b5cSBrian Norris  *	for some reason. A clean up/check of the VUC is necessary in this case.
5461da177e4SLinus Torvalds  *
5471da177e4SLinus Torvalds  * WARNING: return 0 if read error
5481da177e4SLinus Torvalds  */
get_fold_mark(struct NFTLrecord * nftl,unsigned int block)5491da177e4SLinus Torvalds static int get_fold_mark(struct NFTLrecord *nftl, unsigned int block)
5501da177e4SLinus Torvalds {
551f4a43cfcSThomas Gleixner 	struct mtd_info *mtd = nftl->mbd.mtd;
5521da177e4SLinus Torvalds 	struct nftl_uci2 uci;
5531da177e4SLinus Torvalds 	size_t retlen;
5541da177e4SLinus Torvalds 
5558593fbc6SThomas Gleixner 	if (nftl_read_oob(mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8,
5561da177e4SLinus Torvalds 			  8, &retlen, (char *)&uci) < 0)
5571da177e4SLinus Torvalds 		return 0;
5581da177e4SLinus Torvalds 
5591da177e4SLinus Torvalds 	return le16_to_cpu((uci.FoldMark | uci.FoldMark1));
5601da177e4SLinus Torvalds }
5611da177e4SLinus Torvalds 
NFTL_mount(struct NFTLrecord * s)5621da177e4SLinus Torvalds int NFTL_mount(struct NFTLrecord *s)
5631da177e4SLinus Torvalds {
5641da177e4SLinus Torvalds 	int i;
565fb60e87dSColin Ian King 	unsigned int first_logical_block, logical_block, rep_block, erase_mark;
5661da177e4SLinus Torvalds 	unsigned int block, first_block, is_first_block;
5671da177e4SLinus Torvalds 	int chain_length, do_format_chain;
5681da177e4SLinus Torvalds 	struct nftl_uci0 h0;
5691da177e4SLinus Torvalds 	struct nftl_uci1 h1;
570f4a43cfcSThomas Gleixner 	struct mtd_info *mtd = s->mbd.mtd;
5711da177e4SLinus Torvalds 	size_t retlen;
5721da177e4SLinus Torvalds 
5731da177e4SLinus Torvalds 	/* search for NFTL MediaHeader and Spare NFTL Media Header */
5741da177e4SLinus Torvalds 	if (find_boot_record(s) < 0) {
5751da177e4SLinus Torvalds 		printk("Could not find valid boot record\n");
5761da177e4SLinus Torvalds 		return -1;
5771da177e4SLinus Torvalds 	}
5781da177e4SLinus Torvalds 
5791da177e4SLinus Torvalds 	/* init the logical to physical table */
5801da177e4SLinus Torvalds 	for (i = 0; i < s->nb_blocks; i++) {
5811da177e4SLinus Torvalds 		s->EUNtable[i] = BLOCK_NIL;
5821da177e4SLinus Torvalds 	}
5831da177e4SLinus Torvalds 
5841da177e4SLinus Torvalds 	/* first pass : explore each block chain */
5851da177e4SLinus Torvalds 	first_logical_block = 0;
5861da177e4SLinus Torvalds 	for (first_block = 0; first_block < s->nb_blocks; first_block++) {
5871da177e4SLinus Torvalds 		/* if the block was not already explored, we can look at it */
5881da177e4SLinus Torvalds 		if (s->ReplUnitTable[first_block] == BLOCK_NOTEXPLORED) {
5891da177e4SLinus Torvalds 			block = first_block;
5901da177e4SLinus Torvalds 			chain_length = 0;
5911da177e4SLinus Torvalds 			do_format_chain = 0;
5921da177e4SLinus Torvalds 
5931da177e4SLinus Torvalds 			for (;;) {
5941da177e4SLinus Torvalds 				/* read the block header. If error, we format the chain */
5958593fbc6SThomas Gleixner 				if (nftl_read_oob(mtd,
596f4a43cfcSThomas Gleixner 						  block * s->EraseSize + 8, 8,
5971da177e4SLinus Torvalds 						  &retlen, (char *)&h0) < 0 ||
5988593fbc6SThomas Gleixner 				    nftl_read_oob(mtd,
599f4a43cfcSThomas Gleixner 						  block * s->EraseSize +
600f4a43cfcSThomas Gleixner 						  SECTORSIZE + 8, 8,
6011da177e4SLinus Torvalds 						  &retlen, (char *)&h1) < 0) {
6021da177e4SLinus Torvalds 					s->ReplUnitTable[block] = BLOCK_NIL;
6031da177e4SLinus Torvalds 					do_format_chain = 1;
6041da177e4SLinus Torvalds 					break;
6051da177e4SLinus Torvalds 				}
6061da177e4SLinus Torvalds 
6071da177e4SLinus Torvalds 				logical_block = le16_to_cpu ((h0.VirtUnitNum | h0.SpareVirtUnitNum));
6081da177e4SLinus Torvalds 				rep_block = le16_to_cpu ((h0.ReplUnitNum | h0.SpareReplUnitNum));
6091da177e4SLinus Torvalds 				erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1));
6101da177e4SLinus Torvalds 
6111da177e4SLinus Torvalds 				is_first_block = !(logical_block >> 15);
6121da177e4SLinus Torvalds 				logical_block = logical_block & 0x7fff;
6131da177e4SLinus Torvalds 
6141da177e4SLinus Torvalds 				/* invalid/free block test */
6151da177e4SLinus Torvalds 				if (erase_mark != ERASE_MARK || logical_block >= s->nb_blocks) {
6161da177e4SLinus Torvalds 					if (chain_length == 0) {
6171da177e4SLinus Torvalds 						/* if not currently in a chain, we can handle it safely */
6181da177e4SLinus Torvalds 						if (check_and_mark_free_block(s, block) < 0) {
6191da177e4SLinus Torvalds 							/* not really free: format it */
6201da177e4SLinus Torvalds 							printk("Formatting block %d\n", block);
6211da177e4SLinus Torvalds 							if (NFTL_formatblock(s, block) < 0) {
6221da177e4SLinus Torvalds 								/* could not format: reserve the block */
6231da177e4SLinus Torvalds 								s->ReplUnitTable[block] = BLOCK_RESERVED;
6241da177e4SLinus Torvalds 							} else {
6251da177e4SLinus Torvalds 								s->ReplUnitTable[block] = BLOCK_FREE;
6261da177e4SLinus Torvalds 							}
6271da177e4SLinus Torvalds 						} else {
6281da177e4SLinus Torvalds 							/* free block: mark it */
6291da177e4SLinus Torvalds 							s->ReplUnitTable[block] = BLOCK_FREE;
6301da177e4SLinus Torvalds 						}
6311da177e4SLinus Torvalds 						/* directly examine the next block. */
6321da177e4SLinus Torvalds 						goto examine_ReplUnitTable;
6331da177e4SLinus Torvalds 					} else {
6341da177e4SLinus Torvalds 						/* the block was in a chain : this is bad. We
6351da177e4SLinus Torvalds 						   must format all the chain */
6361da177e4SLinus Torvalds 						printk("Block %d: free but referenced in chain %d\n",
6371da177e4SLinus Torvalds 						       block, first_block);
6381da177e4SLinus Torvalds 						s->ReplUnitTable[block] = BLOCK_NIL;
6391da177e4SLinus Torvalds 						do_format_chain = 1;
6401da177e4SLinus Torvalds 						break;
6411da177e4SLinus Torvalds 					}
6421da177e4SLinus Torvalds 				}
6431da177e4SLinus Torvalds 
6441da177e4SLinus Torvalds 				/* we accept only first blocks here */
6451da177e4SLinus Torvalds 				if (chain_length == 0) {
6461da177e4SLinus Torvalds 					/* this block is not the first block in chain :
6471da177e4SLinus Torvalds 					   ignore it, it will be included in a chain
6481da177e4SLinus Torvalds 					   later, or marked as not explored */
6491da177e4SLinus Torvalds 					if (!is_first_block)
6501da177e4SLinus Torvalds 						goto examine_ReplUnitTable;
6511da177e4SLinus Torvalds 					first_logical_block = logical_block;
6521da177e4SLinus Torvalds 				} else {
6531da177e4SLinus Torvalds 					if (logical_block != first_logical_block) {
6541da177e4SLinus Torvalds 						printk("Block %d: incorrect logical block: %d expected: %d\n",
6551da177e4SLinus Torvalds 						       block, logical_block, first_logical_block);
6561da177e4SLinus Torvalds 						/* the chain is incorrect : we must format it,
65792394b5cSBrian Norris 						   but we need to read it completely */
6581da177e4SLinus Torvalds 						do_format_chain = 1;
6591da177e4SLinus Torvalds 					}
6601da177e4SLinus Torvalds 					if (is_first_block) {
6611da177e4SLinus Torvalds 						/* we accept that a block is marked as first
6621da177e4SLinus Torvalds 						   block while being last block in a chain
6631da177e4SLinus Torvalds 						   only if the chain is being folded */
6641da177e4SLinus Torvalds 						if (get_fold_mark(s, block) != FOLD_MARK_IN_PROGRESS ||
6651da177e4SLinus Torvalds 						    rep_block != 0xffff) {
6661da177e4SLinus Torvalds 							printk("Block %d: incorrectly marked as first block in chain\n",
6671da177e4SLinus Torvalds 							       block);
6681da177e4SLinus Torvalds 							/* the chain is incorrect : we must format it,
66992394b5cSBrian Norris 							   but we need to read it completely */
6701da177e4SLinus Torvalds 							do_format_chain = 1;
6711da177e4SLinus Torvalds 						} else {
6721da177e4SLinus Torvalds 							printk("Block %d: folding in progress - ignoring first block flag\n",
6731da177e4SLinus Torvalds 							       block);
6741da177e4SLinus Torvalds 						}
6751da177e4SLinus Torvalds 					}
6761da177e4SLinus Torvalds 				}
6771da177e4SLinus Torvalds 				chain_length++;
6781da177e4SLinus Torvalds 				if (rep_block == 0xffff) {
6791da177e4SLinus Torvalds 					/* no more blocks after */
6801da177e4SLinus Torvalds 					s->ReplUnitTable[block] = BLOCK_NIL;
6811da177e4SLinus Torvalds 					break;
6821da177e4SLinus Torvalds 				} else if (rep_block >= s->nb_blocks) {
6831da177e4SLinus Torvalds 					printk("Block %d: referencing invalid block %d\n",
6841da177e4SLinus Torvalds 					       block, rep_block);
6851da177e4SLinus Torvalds 					do_format_chain = 1;
6861da177e4SLinus Torvalds 					s->ReplUnitTable[block] = BLOCK_NIL;
6871da177e4SLinus Torvalds 					break;
6881da177e4SLinus Torvalds 				} else if (s->ReplUnitTable[rep_block] != BLOCK_NOTEXPLORED) {
6891da177e4SLinus Torvalds 					/* same problem as previous 'is_first_block' test:
6901da177e4SLinus Torvalds 					   we accept that the last block of a chain has
6911da177e4SLinus Torvalds 					   the first_block flag set if folding is in
6921da177e4SLinus Torvalds 					   progress. We handle here the case where the
6931da177e4SLinus Torvalds 					   last block appeared first */
6941da177e4SLinus Torvalds 					if (s->ReplUnitTable[rep_block] == BLOCK_NIL &&
6951da177e4SLinus Torvalds 					    s->EUNtable[first_logical_block] == rep_block &&
6961da177e4SLinus Torvalds 					    get_fold_mark(s, first_block) == FOLD_MARK_IN_PROGRESS) {
6971da177e4SLinus Torvalds 						/* EUNtable[] will be set after */
6981da177e4SLinus Torvalds 						printk("Block %d: folding in progress - ignoring first block flag\n",
6991da177e4SLinus Torvalds 						       rep_block);
7001da177e4SLinus Torvalds 						s->ReplUnitTable[block] = rep_block;
7011da177e4SLinus Torvalds 						s->EUNtable[first_logical_block] = BLOCK_NIL;
7021da177e4SLinus Torvalds 					} else {
7031da177e4SLinus Torvalds 						printk("Block %d: referencing block %d already in another chain\n",
7041da177e4SLinus Torvalds 						       block, rep_block);
7051da177e4SLinus Torvalds 						/* XXX: should handle correctly fold in progress chains */
7061da177e4SLinus Torvalds 						do_format_chain = 1;
7071da177e4SLinus Torvalds 						s->ReplUnitTable[block] = BLOCK_NIL;
7081da177e4SLinus Torvalds 					}
7091da177e4SLinus Torvalds 					break;
7101da177e4SLinus Torvalds 				} else {
7111da177e4SLinus Torvalds 					/* this is OK */
7121da177e4SLinus Torvalds 					s->ReplUnitTable[block] = rep_block;
7131da177e4SLinus Torvalds 					block = rep_block;
7141da177e4SLinus Torvalds 				}
7151da177e4SLinus Torvalds 			}
7161da177e4SLinus Torvalds 
7171da177e4SLinus Torvalds 			/* the chain was completely explored. Now we can decide
7181da177e4SLinus Torvalds 			   what to do with it */
7191da177e4SLinus Torvalds 			if (do_format_chain) {
7201da177e4SLinus Torvalds 				/* invalid chain : format it */
7211da177e4SLinus Torvalds 				format_chain(s, first_block);
7221da177e4SLinus Torvalds 			} else {
7231da177e4SLinus Torvalds 				unsigned int first_block1, chain_to_format, chain_length1;
7241da177e4SLinus Torvalds 				int fold_mark;
7251da177e4SLinus Torvalds 
7261da177e4SLinus Torvalds 				/* valid chain : get foldmark */
7271da177e4SLinus Torvalds 				fold_mark = get_fold_mark(s, first_block);
7281da177e4SLinus Torvalds 				if (fold_mark == 0) {
7291da177e4SLinus Torvalds 					/* cannot get foldmark : format the chain */
7301da177e4SLinus Torvalds 					printk("Could read foldmark at block %d\n", first_block);
7311da177e4SLinus Torvalds 					format_chain(s, first_block);
7321da177e4SLinus Torvalds 				} else {
7331da177e4SLinus Torvalds 					if (fold_mark == FOLD_MARK_IN_PROGRESS)
7341da177e4SLinus Torvalds 						check_sectors_in_chain(s, first_block);
7351da177e4SLinus Torvalds 
7361da177e4SLinus Torvalds 					/* now handle the case where we find two chains at the
7371da177e4SLinus Torvalds 					   same virtual address : we select the longer one,
7381da177e4SLinus Torvalds 					   because the shorter one is the one which was being
7391da177e4SLinus Torvalds 					   folded if the folding was not done in place */
7401da177e4SLinus Torvalds 					first_block1 = s->EUNtable[first_logical_block];
7411da177e4SLinus Torvalds 					if (first_block1 != BLOCK_NIL) {
7421da177e4SLinus Torvalds 						/* XXX: what to do if same length ? */
7431da177e4SLinus Torvalds 						chain_length1 = calc_chain_length(s, first_block1);
7441da177e4SLinus Torvalds 						printk("Two chains at blocks %d (len=%d) and %d (len=%d)\n",
7451da177e4SLinus Torvalds 						       first_block1, chain_length1, first_block, chain_length);
7461da177e4SLinus Torvalds 
7471da177e4SLinus Torvalds 						if (chain_length >= chain_length1) {
7481da177e4SLinus Torvalds 							chain_to_format = first_block1;
7491da177e4SLinus Torvalds 							s->EUNtable[first_logical_block] = first_block;
7501da177e4SLinus Torvalds 						} else {
7511da177e4SLinus Torvalds 							chain_to_format = first_block;
7521da177e4SLinus Torvalds 						}
7531da177e4SLinus Torvalds 						format_chain(s, chain_to_format);
7541da177e4SLinus Torvalds 					} else {
7551da177e4SLinus Torvalds 						s->EUNtable[first_logical_block] = first_block;
7561da177e4SLinus Torvalds 					}
7571da177e4SLinus Torvalds 				}
7581da177e4SLinus Torvalds 			}
7591da177e4SLinus Torvalds 		}
7601da177e4SLinus Torvalds 	examine_ReplUnitTable:;
7611da177e4SLinus Torvalds 	}
7621da177e4SLinus Torvalds 
7631da177e4SLinus Torvalds 	/* second pass to format unreferenced blocks  and init free block count */
7641da177e4SLinus Torvalds 	s->numfreeEUNs = 0;
7651da177e4SLinus Torvalds 	s->LastFreeEUN = le16_to_cpu(s->MediaHdr.FirstPhysicalEUN);
7661da177e4SLinus Torvalds 
7671da177e4SLinus Torvalds 	for (block = 0; block < s->nb_blocks; block++) {
7681da177e4SLinus Torvalds 		if (s->ReplUnitTable[block] == BLOCK_NOTEXPLORED) {
7691da177e4SLinus Torvalds 			printk("Unreferenced block %d, formatting it\n", block);
7701da177e4SLinus Torvalds 			if (NFTL_formatblock(s, block) < 0)
7711da177e4SLinus Torvalds 				s->ReplUnitTable[block] = BLOCK_RESERVED;
7721da177e4SLinus Torvalds 			else
7731da177e4SLinus Torvalds 				s->ReplUnitTable[block] = BLOCK_FREE;
7741da177e4SLinus Torvalds 		}
7751da177e4SLinus Torvalds 		if (s->ReplUnitTable[block] == BLOCK_FREE) {
7761da177e4SLinus Torvalds 			s->numfreeEUNs++;
7771da177e4SLinus Torvalds 			s->LastFreeEUN = block;
7781da177e4SLinus Torvalds 		}
7791da177e4SLinus Torvalds 	}
7801da177e4SLinus Torvalds 
7811da177e4SLinus Torvalds 	return 0;
7821da177e4SLinus Torvalds }
783