18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * inftlmount.c -- INFTL mount code with extensive checks.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author: Greg Ungerer (gerg@snapgear.com)
68c2ecf20Sopenharmony_ci * Copyright © 2002-2003, Greg Ungerer (gerg@snapgear.com)
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Based heavily on the nftlmount.c code which is:
98c2ecf20Sopenharmony_ci * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
108c2ecf20Sopenharmony_ci * Copyright © 2000 Netgem S.A.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <asm/errno.h>
168c2ecf20Sopenharmony_ci#include <asm/io.h>
178c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
188c2ecf20Sopenharmony_ci#include <linux/delay.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h>
218c2ecf20Sopenharmony_ci#include <linux/mtd/nftl.h>
228c2ecf20Sopenharmony_ci#include <linux/mtd/inftl.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/*
258c2ecf20Sopenharmony_ci * find_boot_record: Find the INFTL Media Header and its Spare copy which
268c2ecf20Sopenharmony_ci *	contains the various device information of the INFTL partition and
278c2ecf20Sopenharmony_ci *	Bad Unit Table. Update the PUtable[] table according to the Bad
288c2ecf20Sopenharmony_ci *	Unit Table. PUtable[] is used for management of Erase Unit in
298c2ecf20Sopenharmony_ci *	other routines in inftlcore.c and inftlmount.c.
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_cistatic int find_boot_record(struct INFTLrecord *inftl)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	struct inftl_unittail h1;
348c2ecf20Sopenharmony_ci	//struct inftl_oob oob;
358c2ecf20Sopenharmony_ci	unsigned int i, block;
368c2ecf20Sopenharmony_ci	u8 buf[SECTORSIZE];
378c2ecf20Sopenharmony_ci	struct INFTLMediaHeader *mh = &inftl->MediaHdr;
388c2ecf20Sopenharmony_ci	struct mtd_info *mtd = inftl->mbd.mtd;
398c2ecf20Sopenharmony_ci	struct INFTLPartition *ip;
408c2ecf20Sopenharmony_ci	size_t retlen;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	pr_debug("INFTL: find_boot_record(inftl=%p)\n", inftl);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci        /*
458c2ecf20Sopenharmony_ci	 * Assume logical EraseSize == physical erasesize for starting the
468c2ecf20Sopenharmony_ci	 * scan. We'll sort it out later if we find a MediaHeader which says
478c2ecf20Sopenharmony_ci	 * otherwise.
488c2ecf20Sopenharmony_ci	 */
498c2ecf20Sopenharmony_ci	inftl->EraseSize = inftl->mbd.mtd->erasesize;
508c2ecf20Sopenharmony_ci        inftl->nb_blocks = (u32)inftl->mbd.mtd->size / inftl->EraseSize;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	inftl->MediaUnit = BLOCK_NIL;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	/* Search for a valid boot record */
558c2ecf20Sopenharmony_ci	for (block = 0; block < inftl->nb_blocks; block++) {
568c2ecf20Sopenharmony_ci		int ret;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci		/*
598c2ecf20Sopenharmony_ci		 * Check for BNAND header first. Then whinge if it's found
608c2ecf20Sopenharmony_ci		 * but later checks fail.
618c2ecf20Sopenharmony_ci		 */
628c2ecf20Sopenharmony_ci		ret = mtd_read(mtd, block * inftl->EraseSize, SECTORSIZE,
638c2ecf20Sopenharmony_ci			       &retlen, buf);
648c2ecf20Sopenharmony_ci		/* We ignore ret in case the ECC of the MediaHeader is invalid
658c2ecf20Sopenharmony_ci		   (which is apparently acceptable) */
668c2ecf20Sopenharmony_ci		if (retlen != SECTORSIZE) {
678c2ecf20Sopenharmony_ci			static int warncount = 5;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci			if (warncount) {
708c2ecf20Sopenharmony_ci				printk(KERN_WARNING "INFTL: block read at 0x%x "
718c2ecf20Sopenharmony_ci					"of mtd%d failed: %d\n",
728c2ecf20Sopenharmony_ci					block * inftl->EraseSize,
738c2ecf20Sopenharmony_ci					inftl->mbd.mtd->index, ret);
748c2ecf20Sopenharmony_ci				if (!--warncount)
758c2ecf20Sopenharmony_ci					printk(KERN_WARNING "INFTL: further "
768c2ecf20Sopenharmony_ci						"failures for this block will "
778c2ecf20Sopenharmony_ci						"not be printed\n");
788c2ecf20Sopenharmony_ci			}
798c2ecf20Sopenharmony_ci			continue;
808c2ecf20Sopenharmony_ci		}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci		if (retlen < 6 || memcmp(buf, "BNAND", 6)) {
838c2ecf20Sopenharmony_ci			/* BNAND\0 not found. Continue */
848c2ecf20Sopenharmony_ci			continue;
858c2ecf20Sopenharmony_ci		}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci		/* To be safer with BIOS, also use erase mark as discriminant */
888c2ecf20Sopenharmony_ci		ret = inftl_read_oob(mtd,
898c2ecf20Sopenharmony_ci				     block * inftl->EraseSize + SECTORSIZE + 8,
908c2ecf20Sopenharmony_ci				     8, &retlen,(char *)&h1);
918c2ecf20Sopenharmony_ci		if (ret < 0) {
928c2ecf20Sopenharmony_ci			printk(KERN_WARNING "INFTL: ANAND header found at "
938c2ecf20Sopenharmony_ci				"0x%x in mtd%d, but OOB data read failed "
948c2ecf20Sopenharmony_ci				"(err %d)\n", block * inftl->EraseSize,
958c2ecf20Sopenharmony_ci				inftl->mbd.mtd->index, ret);
968c2ecf20Sopenharmony_ci			continue;
978c2ecf20Sopenharmony_ci		}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci		/*
1018c2ecf20Sopenharmony_ci		 * This is the first we've seen.
1028c2ecf20Sopenharmony_ci		 * Copy the media header structure into place.
1038c2ecf20Sopenharmony_ci		 */
1048c2ecf20Sopenharmony_ci		memcpy(mh, buf, sizeof(struct INFTLMediaHeader));
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci		/* Read the spare media header at offset 4096 */
1078c2ecf20Sopenharmony_ci		mtd_read(mtd, block * inftl->EraseSize + 4096, SECTORSIZE,
1088c2ecf20Sopenharmony_ci			 &retlen, buf);
1098c2ecf20Sopenharmony_ci		if (retlen != SECTORSIZE) {
1108c2ecf20Sopenharmony_ci			printk(KERN_WARNING "INFTL: Unable to read spare "
1118c2ecf20Sopenharmony_ci			       "Media Header\n");
1128c2ecf20Sopenharmony_ci			return -1;
1138c2ecf20Sopenharmony_ci		}
1148c2ecf20Sopenharmony_ci		/* Check if this one is the same as the first one we found. */
1158c2ecf20Sopenharmony_ci		if (memcmp(mh, buf, sizeof(struct INFTLMediaHeader))) {
1168c2ecf20Sopenharmony_ci			printk(KERN_WARNING "INFTL: Primary and spare Media "
1178c2ecf20Sopenharmony_ci			       "Headers disagree.\n");
1188c2ecf20Sopenharmony_ci			return -1;
1198c2ecf20Sopenharmony_ci		}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci		mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks);
1228c2ecf20Sopenharmony_ci		mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions);
1238c2ecf20Sopenharmony_ci		mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions);
1248c2ecf20Sopenharmony_ci		mh->BlockMultiplierBits = le32_to_cpu(mh->BlockMultiplierBits);
1258c2ecf20Sopenharmony_ci		mh->FormatFlags = le32_to_cpu(mh->FormatFlags);
1268c2ecf20Sopenharmony_ci		mh->PercentUsed = le32_to_cpu(mh->PercentUsed);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci		pr_debug("INFTL: Media Header ->\n"
1298c2ecf20Sopenharmony_ci			 "    bootRecordID          = %s\n"
1308c2ecf20Sopenharmony_ci			 "    NoOfBootImageBlocks   = %d\n"
1318c2ecf20Sopenharmony_ci			 "    NoOfBinaryPartitions  = %d\n"
1328c2ecf20Sopenharmony_ci			 "    NoOfBDTLPartitions    = %d\n"
1338c2ecf20Sopenharmony_ci			 "    BlockMultiplierBits   = %d\n"
1348c2ecf20Sopenharmony_ci			 "    FormatFlgs            = %d\n"
1358c2ecf20Sopenharmony_ci			 "    OsakVersion           = 0x%x\n"
1368c2ecf20Sopenharmony_ci			 "    PercentUsed           = %d\n",
1378c2ecf20Sopenharmony_ci			 mh->bootRecordID, mh->NoOfBootImageBlocks,
1388c2ecf20Sopenharmony_ci			 mh->NoOfBinaryPartitions,
1398c2ecf20Sopenharmony_ci			 mh->NoOfBDTLPartitions,
1408c2ecf20Sopenharmony_ci			 mh->BlockMultiplierBits, mh->FormatFlags,
1418c2ecf20Sopenharmony_ci			 mh->OsakVersion, mh->PercentUsed);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		if (mh->NoOfBDTLPartitions == 0) {
1448c2ecf20Sopenharmony_ci			printk(KERN_WARNING "INFTL: Media Header sanity check "
1458c2ecf20Sopenharmony_ci				"failed: NoOfBDTLPartitions (%d) == 0, "
1468c2ecf20Sopenharmony_ci				"must be at least 1\n", mh->NoOfBDTLPartitions);
1478c2ecf20Sopenharmony_ci			return -1;
1488c2ecf20Sopenharmony_ci		}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci		if ((mh->NoOfBDTLPartitions + mh->NoOfBinaryPartitions) > 4) {
1518c2ecf20Sopenharmony_ci			printk(KERN_WARNING "INFTL: Media Header sanity check "
1528c2ecf20Sopenharmony_ci				"failed: Total Partitions (%d) > 4, "
1538c2ecf20Sopenharmony_ci				"BDTL=%d Binary=%d\n", mh->NoOfBDTLPartitions +
1548c2ecf20Sopenharmony_ci				mh->NoOfBinaryPartitions,
1558c2ecf20Sopenharmony_ci				mh->NoOfBDTLPartitions,
1568c2ecf20Sopenharmony_ci				mh->NoOfBinaryPartitions);
1578c2ecf20Sopenharmony_ci			return -1;
1588c2ecf20Sopenharmony_ci		}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci		if (mh->BlockMultiplierBits > 1) {
1618c2ecf20Sopenharmony_ci			printk(KERN_WARNING "INFTL: sorry, we don't support "
1628c2ecf20Sopenharmony_ci				"UnitSizeFactor 0x%02x\n",
1638c2ecf20Sopenharmony_ci				mh->BlockMultiplierBits);
1648c2ecf20Sopenharmony_ci			return -1;
1658c2ecf20Sopenharmony_ci		} else if (mh->BlockMultiplierBits == 1) {
1668c2ecf20Sopenharmony_ci			printk(KERN_WARNING "INFTL: support for INFTL with "
1678c2ecf20Sopenharmony_ci				"UnitSizeFactor 0x%02x is experimental\n",
1688c2ecf20Sopenharmony_ci				mh->BlockMultiplierBits);
1698c2ecf20Sopenharmony_ci			inftl->EraseSize = inftl->mbd.mtd->erasesize <<
1708c2ecf20Sopenharmony_ci				mh->BlockMultiplierBits;
1718c2ecf20Sopenharmony_ci			inftl->nb_blocks = (u32)inftl->mbd.mtd->size / inftl->EraseSize;
1728c2ecf20Sopenharmony_ci			block >>= mh->BlockMultiplierBits;
1738c2ecf20Sopenharmony_ci		}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci		/* Scan the partitions */
1768c2ecf20Sopenharmony_ci		for (i = 0; (i < 4); i++) {
1778c2ecf20Sopenharmony_ci			ip = &mh->Partitions[i];
1788c2ecf20Sopenharmony_ci			ip->virtualUnits = le32_to_cpu(ip->virtualUnits);
1798c2ecf20Sopenharmony_ci			ip->firstUnit = le32_to_cpu(ip->firstUnit);
1808c2ecf20Sopenharmony_ci			ip->lastUnit = le32_to_cpu(ip->lastUnit);
1818c2ecf20Sopenharmony_ci			ip->flags = le32_to_cpu(ip->flags);
1828c2ecf20Sopenharmony_ci			ip->spareUnits = le32_to_cpu(ip->spareUnits);
1838c2ecf20Sopenharmony_ci			ip->Reserved0 = le32_to_cpu(ip->Reserved0);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci			pr_debug("    PARTITION[%d] ->\n"
1868c2ecf20Sopenharmony_ci				 "        virtualUnits    = %d\n"
1878c2ecf20Sopenharmony_ci				 "        firstUnit       = %d\n"
1888c2ecf20Sopenharmony_ci				 "        lastUnit        = %d\n"
1898c2ecf20Sopenharmony_ci				 "        flags           = 0x%x\n"
1908c2ecf20Sopenharmony_ci				 "        spareUnits      = %d\n",
1918c2ecf20Sopenharmony_ci				 i, ip->virtualUnits, ip->firstUnit,
1928c2ecf20Sopenharmony_ci				 ip->lastUnit, ip->flags,
1938c2ecf20Sopenharmony_ci				 ip->spareUnits);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci			if (ip->Reserved0 != ip->firstUnit) {
1968c2ecf20Sopenharmony_ci				struct erase_info *instr = &inftl->instr;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci				/*
1998c2ecf20Sopenharmony_ci				 * 	Most likely this is using the
2008c2ecf20Sopenharmony_ci				 * 	undocumented qiuck mount feature.
2018c2ecf20Sopenharmony_ci				 * 	We don't support that, we will need
2028c2ecf20Sopenharmony_ci				 * 	to erase the hidden block for full
2038c2ecf20Sopenharmony_ci				 * 	compatibility.
2048c2ecf20Sopenharmony_ci				 */
2058c2ecf20Sopenharmony_ci				instr->addr = ip->Reserved0 * inftl->EraseSize;
2068c2ecf20Sopenharmony_ci				instr->len = inftl->EraseSize;
2078c2ecf20Sopenharmony_ci				mtd_erase(mtd, instr);
2088c2ecf20Sopenharmony_ci			}
2098c2ecf20Sopenharmony_ci			if ((ip->lastUnit - ip->firstUnit + 1) < ip->virtualUnits) {
2108c2ecf20Sopenharmony_ci				printk(KERN_WARNING "INFTL: Media Header "
2118c2ecf20Sopenharmony_ci					"Partition %d sanity check failed\n"
2128c2ecf20Sopenharmony_ci					"    firstUnit %d : lastUnit %d  >  "
2138c2ecf20Sopenharmony_ci					"virtualUnits %d\n", i, ip->lastUnit,
2148c2ecf20Sopenharmony_ci					ip->firstUnit, ip->Reserved0);
2158c2ecf20Sopenharmony_ci				return -1;
2168c2ecf20Sopenharmony_ci			}
2178c2ecf20Sopenharmony_ci			if (ip->Reserved1 != 0) {
2188c2ecf20Sopenharmony_ci				printk(KERN_WARNING "INFTL: Media Header "
2198c2ecf20Sopenharmony_ci					"Partition %d sanity check failed: "
2208c2ecf20Sopenharmony_ci					"Reserved1 %d != 0\n",
2218c2ecf20Sopenharmony_ci					i, ip->Reserved1);
2228c2ecf20Sopenharmony_ci				return -1;
2238c2ecf20Sopenharmony_ci			}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci			if (ip->flags & INFTL_BDTL)
2268c2ecf20Sopenharmony_ci				break;
2278c2ecf20Sopenharmony_ci		}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci		if (i >= 4) {
2308c2ecf20Sopenharmony_ci			printk(KERN_WARNING "INFTL: Media Header Partition "
2318c2ecf20Sopenharmony_ci				"sanity check failed:\n       No partition "
2328c2ecf20Sopenharmony_ci				"marked as Disk Partition\n");
2338c2ecf20Sopenharmony_ci			return -1;
2348c2ecf20Sopenharmony_ci		}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci		inftl->nb_boot_blocks = ip->firstUnit;
2378c2ecf20Sopenharmony_ci		inftl->numvunits = ip->virtualUnits;
2388c2ecf20Sopenharmony_ci		if (inftl->numvunits > (inftl->nb_blocks -
2398c2ecf20Sopenharmony_ci		    inftl->nb_boot_blocks - 2)) {
2408c2ecf20Sopenharmony_ci			printk(KERN_WARNING "INFTL: Media Header sanity check "
2418c2ecf20Sopenharmony_ci				"failed:\n        numvunits (%d) > nb_blocks "
2428c2ecf20Sopenharmony_ci				"(%d) - nb_boot_blocks(%d) - 2\n",
2438c2ecf20Sopenharmony_ci				inftl->numvunits, inftl->nb_blocks,
2448c2ecf20Sopenharmony_ci				inftl->nb_boot_blocks);
2458c2ecf20Sopenharmony_ci			return -1;
2468c2ecf20Sopenharmony_ci		}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci		inftl->mbd.size  = inftl->numvunits *
2498c2ecf20Sopenharmony_ci			(inftl->EraseSize / SECTORSIZE);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci		/*
2528c2ecf20Sopenharmony_ci		 * Block count is set to last used EUN (we won't need to keep
2538c2ecf20Sopenharmony_ci		 * any meta-data past that point).
2548c2ecf20Sopenharmony_ci		 */
2558c2ecf20Sopenharmony_ci		inftl->firstEUN = ip->firstUnit;
2568c2ecf20Sopenharmony_ci		inftl->lastEUN = ip->lastUnit;
2578c2ecf20Sopenharmony_ci		inftl->nb_blocks = ip->lastUnit + 1;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci		/* Memory alloc */
2608c2ecf20Sopenharmony_ci		inftl->PUtable = kmalloc_array(inftl->nb_blocks, sizeof(u16),
2618c2ecf20Sopenharmony_ci					       GFP_KERNEL);
2628c2ecf20Sopenharmony_ci		if (!inftl->PUtable) {
2638c2ecf20Sopenharmony_ci			printk(KERN_WARNING "INFTL: allocation of PUtable "
2648c2ecf20Sopenharmony_ci				"failed (%zd bytes)\n",
2658c2ecf20Sopenharmony_ci				inftl->nb_blocks * sizeof(u16));
2668c2ecf20Sopenharmony_ci			return -ENOMEM;
2678c2ecf20Sopenharmony_ci		}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci		inftl->VUtable = kmalloc_array(inftl->nb_blocks, sizeof(u16),
2708c2ecf20Sopenharmony_ci					       GFP_KERNEL);
2718c2ecf20Sopenharmony_ci		if (!inftl->VUtable) {
2728c2ecf20Sopenharmony_ci			kfree(inftl->PUtable);
2738c2ecf20Sopenharmony_ci			printk(KERN_WARNING "INFTL: allocation of VUtable "
2748c2ecf20Sopenharmony_ci				"failed (%zd bytes)\n",
2758c2ecf20Sopenharmony_ci				inftl->nb_blocks * sizeof(u16));
2768c2ecf20Sopenharmony_ci			return -ENOMEM;
2778c2ecf20Sopenharmony_ci		}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci		/* Mark the blocks before INFTL MediaHeader as reserved */
2808c2ecf20Sopenharmony_ci		for (i = 0; i < inftl->nb_boot_blocks; i++)
2818c2ecf20Sopenharmony_ci			inftl->PUtable[i] = BLOCK_RESERVED;
2828c2ecf20Sopenharmony_ci		/* Mark all remaining blocks as potentially containing data */
2838c2ecf20Sopenharmony_ci		for (; i < inftl->nb_blocks; i++)
2848c2ecf20Sopenharmony_ci			inftl->PUtable[i] = BLOCK_NOTEXPLORED;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci		/* Mark this boot record (NFTL MediaHeader) block as reserved */
2878c2ecf20Sopenharmony_ci		inftl->PUtable[block] = BLOCK_RESERVED;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci		/* Read Bad Erase Unit Table and modify PUtable[] accordingly */
2908c2ecf20Sopenharmony_ci		for (i = 0; i < inftl->nb_blocks; i++) {
2918c2ecf20Sopenharmony_ci			int physblock;
2928c2ecf20Sopenharmony_ci			/* If any of the physical eraseblocks are bad, don't
2938c2ecf20Sopenharmony_ci			   use the unit. */
2948c2ecf20Sopenharmony_ci			for (physblock = 0; physblock < inftl->EraseSize; physblock += inftl->mbd.mtd->erasesize) {
2958c2ecf20Sopenharmony_ci				if (mtd_block_isbad(inftl->mbd.mtd,
2968c2ecf20Sopenharmony_ci						    i * inftl->EraseSize + physblock))
2978c2ecf20Sopenharmony_ci					inftl->PUtable[i] = BLOCK_RESERVED;
2988c2ecf20Sopenharmony_ci			}
2998c2ecf20Sopenharmony_ci		}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci		inftl->MediaUnit = block;
3028c2ecf20Sopenharmony_ci		return 0;
3038c2ecf20Sopenharmony_ci	}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	/* Not found. */
3068c2ecf20Sopenharmony_ci	return -1;
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic int memcmpb(void *a, int c, int n)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	int i;
3128c2ecf20Sopenharmony_ci	for (i = 0; i < n; i++) {
3138c2ecf20Sopenharmony_ci		if (c != ((unsigned char *)a)[i])
3148c2ecf20Sopenharmony_ci			return 1;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci	return 0;
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci/*
3208c2ecf20Sopenharmony_ci * check_free_sector: check if a free sector is actually FREE,
3218c2ecf20Sopenharmony_ci *	i.e. All 0xff in data and oob area.
3228c2ecf20Sopenharmony_ci */
3238c2ecf20Sopenharmony_cistatic int check_free_sectors(struct INFTLrecord *inftl, unsigned int address,
3248c2ecf20Sopenharmony_ci	int len, int check_oob)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	struct mtd_info *mtd = inftl->mbd.mtd;
3278c2ecf20Sopenharmony_ci	size_t retlen;
3288c2ecf20Sopenharmony_ci	int i, ret;
3298c2ecf20Sopenharmony_ci	u8 *buf;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	buf = kmalloc(SECTORSIZE + mtd->oobsize, GFP_KERNEL);
3328c2ecf20Sopenharmony_ci	if (!buf)
3338c2ecf20Sopenharmony_ci		return -1;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	ret = -1;
3368c2ecf20Sopenharmony_ci	for (i = 0; i < len; i += SECTORSIZE) {
3378c2ecf20Sopenharmony_ci		if (mtd_read(mtd, address, SECTORSIZE, &retlen, buf))
3388c2ecf20Sopenharmony_ci			goto out;
3398c2ecf20Sopenharmony_ci		if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
3408c2ecf20Sopenharmony_ci			goto out;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci		if (check_oob) {
3438c2ecf20Sopenharmony_ci			if(inftl_read_oob(mtd, address, mtd->oobsize,
3448c2ecf20Sopenharmony_ci					  &retlen, &buf[SECTORSIZE]) < 0)
3458c2ecf20Sopenharmony_ci				goto out;
3468c2ecf20Sopenharmony_ci			if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0)
3478c2ecf20Sopenharmony_ci				goto out;
3488c2ecf20Sopenharmony_ci		}
3498c2ecf20Sopenharmony_ci		address += SECTORSIZE;
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	ret = 0;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ciout:
3558c2ecf20Sopenharmony_ci	kfree(buf);
3568c2ecf20Sopenharmony_ci	return ret;
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci/*
3608c2ecf20Sopenharmony_ci * INFTL_format: format a Erase Unit by erasing ALL Erase Zones in the Erase
3618c2ecf20Sopenharmony_ci *		 Unit and Update INFTL metadata. Each erase operation is
3628c2ecf20Sopenharmony_ci *		 checked with check_free_sectors.
3638c2ecf20Sopenharmony_ci *
3648c2ecf20Sopenharmony_ci * Return: 0 when succeed, -1 on error.
3658c2ecf20Sopenharmony_ci *
3668c2ecf20Sopenharmony_ci * ToDo: 1. Is it necessary to check_free_sector after erasing ??
3678c2ecf20Sopenharmony_ci */
3688c2ecf20Sopenharmony_ciint INFTL_formatblock(struct INFTLrecord *inftl, int block)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	size_t retlen;
3718c2ecf20Sopenharmony_ci	struct inftl_unittail uci;
3728c2ecf20Sopenharmony_ci	struct erase_info *instr = &inftl->instr;
3738c2ecf20Sopenharmony_ci	struct mtd_info *mtd = inftl->mbd.mtd;
3748c2ecf20Sopenharmony_ci	int physblock;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	pr_debug("INFTL: INFTL_formatblock(inftl=%p,block=%d)\n", inftl, block);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	memset(instr, 0, sizeof(struct erase_info));
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	/* FIXME: Shouldn't we be setting the 'discarded' flag to zero
3818c2ecf20Sopenharmony_ci	   _first_? */
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	/* Use async erase interface, test return code */
3848c2ecf20Sopenharmony_ci	instr->addr = block * inftl->EraseSize;
3858c2ecf20Sopenharmony_ci	instr->len = inftl->mbd.mtd->erasesize;
3868c2ecf20Sopenharmony_ci	/* Erase one physical eraseblock at a time, even though the NAND api
3878c2ecf20Sopenharmony_ci	   allows us to group them.  This way we if we have a failure, we can
3888c2ecf20Sopenharmony_ci	   mark only the failed block in the bbt. */
3898c2ecf20Sopenharmony_ci	for (physblock = 0; physblock < inftl->EraseSize;
3908c2ecf20Sopenharmony_ci	     physblock += instr->len, instr->addr += instr->len) {
3918c2ecf20Sopenharmony_ci		int ret;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci		ret = mtd_erase(inftl->mbd.mtd, instr);
3948c2ecf20Sopenharmony_ci		if (ret) {
3958c2ecf20Sopenharmony_ci			printk(KERN_WARNING "INFTL: error while formatting block %d\n",
3968c2ecf20Sopenharmony_ci				block);
3978c2ecf20Sopenharmony_ci			goto fail;
3988c2ecf20Sopenharmony_ci		}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci		/*
4018c2ecf20Sopenharmony_ci		 * Check the "freeness" of Erase Unit before updating metadata.
4028c2ecf20Sopenharmony_ci		 * FixMe: is this check really necessary? Since we have check
4038c2ecf20Sopenharmony_ci		 * the return code after the erase operation.
4048c2ecf20Sopenharmony_ci		 */
4058c2ecf20Sopenharmony_ci		if (check_free_sectors(inftl, instr->addr, instr->len, 1) != 0)
4068c2ecf20Sopenharmony_ci			goto fail;
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	uci.EraseMark = cpu_to_le16(ERASE_MARK);
4108c2ecf20Sopenharmony_ci	uci.EraseMark1 = cpu_to_le16(ERASE_MARK);
4118c2ecf20Sopenharmony_ci	uci.Reserved[0] = 0;
4128c2ecf20Sopenharmony_ci	uci.Reserved[1] = 0;
4138c2ecf20Sopenharmony_ci	uci.Reserved[2] = 0;
4148c2ecf20Sopenharmony_ci	uci.Reserved[3] = 0;
4158c2ecf20Sopenharmony_ci	instr->addr = block * inftl->EraseSize + SECTORSIZE * 2;
4168c2ecf20Sopenharmony_ci	if (inftl_write_oob(mtd, instr->addr + 8, 8, &retlen, (char *)&uci) < 0)
4178c2ecf20Sopenharmony_ci		goto fail;
4188c2ecf20Sopenharmony_ci	return 0;
4198c2ecf20Sopenharmony_cifail:
4208c2ecf20Sopenharmony_ci	/* could not format, update the bad block table (caller is responsible
4218c2ecf20Sopenharmony_ci	   for setting the PUtable to BLOCK_RESERVED on failure) */
4228c2ecf20Sopenharmony_ci	mtd_block_markbad(inftl->mbd.mtd, instr->addr);
4238c2ecf20Sopenharmony_ci	return -1;
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci/*
4278c2ecf20Sopenharmony_ci * format_chain: Format an invalid Virtual Unit chain. It frees all the Erase
4288c2ecf20Sopenharmony_ci *	Units in a Virtual Unit Chain, i.e. all the units are disconnected.
4298c2ecf20Sopenharmony_ci *
4308c2ecf20Sopenharmony_ci *	Since the chain is invalid then we will have to erase it from its
4318c2ecf20Sopenharmony_ci *	head (normally for INFTL we go from the oldest). But if it has a
4328c2ecf20Sopenharmony_ci *	loop then there is no oldest...
4338c2ecf20Sopenharmony_ci */
4348c2ecf20Sopenharmony_cistatic void format_chain(struct INFTLrecord *inftl, unsigned int first_block)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	unsigned int block = first_block, block1;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	printk(KERN_WARNING "INFTL: formatting chain at block %d\n",
4398c2ecf20Sopenharmony_ci		first_block);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	for (;;) {
4428c2ecf20Sopenharmony_ci		block1 = inftl->PUtable[block];
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci		printk(KERN_WARNING "INFTL: formatting block %d\n", block);
4458c2ecf20Sopenharmony_ci		if (INFTL_formatblock(inftl, block) < 0) {
4468c2ecf20Sopenharmony_ci			/*
4478c2ecf20Sopenharmony_ci			 * Cannot format !!!! Mark it as Bad Unit,
4488c2ecf20Sopenharmony_ci			 */
4498c2ecf20Sopenharmony_ci			inftl->PUtable[block] = BLOCK_RESERVED;
4508c2ecf20Sopenharmony_ci		} else {
4518c2ecf20Sopenharmony_ci			inftl->PUtable[block] = BLOCK_FREE;
4528c2ecf20Sopenharmony_ci		}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci		/* Goto next block on the chain */
4558c2ecf20Sopenharmony_ci		block = block1;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci		if (block == BLOCK_NIL || block >= inftl->lastEUN)
4588c2ecf20Sopenharmony_ci			break;
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_civoid INFTL_dumptables(struct INFTLrecord *s)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	int i;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	pr_debug("-------------------------------------------"
4678c2ecf20Sopenharmony_ci		"----------------------------------\n");
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	pr_debug("VUtable[%d] ->", s->nb_blocks);
4708c2ecf20Sopenharmony_ci	for (i = 0; i < s->nb_blocks; i++) {
4718c2ecf20Sopenharmony_ci		if ((i % 8) == 0)
4728c2ecf20Sopenharmony_ci			pr_debug("\n%04x: ", i);
4738c2ecf20Sopenharmony_ci		pr_debug("%04x ", s->VUtable[i]);
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	pr_debug("\n-------------------------------------------"
4778c2ecf20Sopenharmony_ci		"----------------------------------\n");
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	pr_debug("PUtable[%d-%d=%d] ->", s->firstEUN, s->lastEUN, s->nb_blocks);
4808c2ecf20Sopenharmony_ci	for (i = 0; i <= s->lastEUN; i++) {
4818c2ecf20Sopenharmony_ci		if ((i % 8) == 0)
4828c2ecf20Sopenharmony_ci			pr_debug("\n%04x: ", i);
4838c2ecf20Sopenharmony_ci		pr_debug("%04x ", s->PUtable[i]);
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	pr_debug("\n-------------------------------------------"
4878c2ecf20Sopenharmony_ci		"----------------------------------\n");
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	pr_debug("INFTL ->\n"
4908c2ecf20Sopenharmony_ci		"  EraseSize       = %d\n"
4918c2ecf20Sopenharmony_ci		"  h/s/c           = %d/%d/%d\n"
4928c2ecf20Sopenharmony_ci		"  numvunits       = %d\n"
4938c2ecf20Sopenharmony_ci		"  firstEUN        = %d\n"
4948c2ecf20Sopenharmony_ci		"  lastEUN         = %d\n"
4958c2ecf20Sopenharmony_ci		"  numfreeEUNs     = %d\n"
4968c2ecf20Sopenharmony_ci		"  LastFreeEUN     = %d\n"
4978c2ecf20Sopenharmony_ci		"  nb_blocks       = %d\n"
4988c2ecf20Sopenharmony_ci		"  nb_boot_blocks  = %d",
4998c2ecf20Sopenharmony_ci		s->EraseSize, s->heads, s->sectors, s->cylinders,
5008c2ecf20Sopenharmony_ci		s->numvunits, s->firstEUN, s->lastEUN, s->numfreeEUNs,
5018c2ecf20Sopenharmony_ci		s->LastFreeEUN, s->nb_blocks, s->nb_boot_blocks);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	pr_debug("\n-------------------------------------------"
5048c2ecf20Sopenharmony_ci		"----------------------------------\n");
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_civoid INFTL_dumpVUchains(struct INFTLrecord *s)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	int logical, block, i;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	pr_debug("-------------------------------------------"
5128c2ecf20Sopenharmony_ci		"----------------------------------\n");
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	pr_debug("INFTL Virtual Unit Chains:\n");
5158c2ecf20Sopenharmony_ci	for (logical = 0; logical < s->nb_blocks; logical++) {
5168c2ecf20Sopenharmony_ci		block = s->VUtable[logical];
5178c2ecf20Sopenharmony_ci		if (block >= s->nb_blocks)
5188c2ecf20Sopenharmony_ci			continue;
5198c2ecf20Sopenharmony_ci		pr_debug("  LOGICAL %d --> %d ", logical, block);
5208c2ecf20Sopenharmony_ci		for (i = 0; i < s->nb_blocks; i++) {
5218c2ecf20Sopenharmony_ci			if (s->PUtable[block] == BLOCK_NIL)
5228c2ecf20Sopenharmony_ci				break;
5238c2ecf20Sopenharmony_ci			block = s->PUtable[block];
5248c2ecf20Sopenharmony_ci			pr_debug("%d ", block);
5258c2ecf20Sopenharmony_ci		}
5268c2ecf20Sopenharmony_ci		pr_debug("\n");
5278c2ecf20Sopenharmony_ci	}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	pr_debug("-------------------------------------------"
5308c2ecf20Sopenharmony_ci		"----------------------------------\n");
5318c2ecf20Sopenharmony_ci}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ciint INFTL_mount(struct INFTLrecord *s)
5348c2ecf20Sopenharmony_ci{
5358c2ecf20Sopenharmony_ci	struct mtd_info *mtd = s->mbd.mtd;
5368c2ecf20Sopenharmony_ci	unsigned int block, first_block, prev_block, last_block;
5378c2ecf20Sopenharmony_ci	unsigned int first_logical_block, logical_block, erase_mark;
5388c2ecf20Sopenharmony_ci	int chain_length, do_format_chain;
5398c2ecf20Sopenharmony_ci	struct inftl_unithead1 h0;
5408c2ecf20Sopenharmony_ci	struct inftl_unittail h1;
5418c2ecf20Sopenharmony_ci	size_t retlen;
5428c2ecf20Sopenharmony_ci	int i;
5438c2ecf20Sopenharmony_ci	u8 *ANACtable, ANAC;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	pr_debug("INFTL: INFTL_mount(inftl=%p)\n", s);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	/* Search for INFTL MediaHeader and Spare INFTL Media Header */
5488c2ecf20Sopenharmony_ci	if (find_boot_record(s) < 0) {
5498c2ecf20Sopenharmony_ci		printk(KERN_WARNING "INFTL: could not find valid boot record?\n");
5508c2ecf20Sopenharmony_ci		return -ENXIO;
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	/* Init the logical to physical table */
5548c2ecf20Sopenharmony_ci	for (i = 0; i < s->nb_blocks; i++)
5558c2ecf20Sopenharmony_ci		s->VUtable[i] = BLOCK_NIL;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	logical_block = block = BLOCK_NIL;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	/* Temporary buffer to store ANAC numbers. */
5608c2ecf20Sopenharmony_ci	ANACtable = kcalloc(s->nb_blocks, sizeof(u8), GFP_KERNEL);
5618c2ecf20Sopenharmony_ci	if (!ANACtable) {
5628c2ecf20Sopenharmony_ci		printk(KERN_WARNING "INFTL: allocation of ANACtable "
5638c2ecf20Sopenharmony_ci				"failed (%zd bytes)\n",
5648c2ecf20Sopenharmony_ci				s->nb_blocks * sizeof(u8));
5658c2ecf20Sopenharmony_ci		return -ENOMEM;
5668c2ecf20Sopenharmony_ci	}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	/*
5698c2ecf20Sopenharmony_ci	 * First pass is to explore each physical unit, and construct the
5708c2ecf20Sopenharmony_ci	 * virtual chains that exist (newest physical unit goes into VUtable).
5718c2ecf20Sopenharmony_ci	 * Any block that is in any way invalid will be left in the
5728c2ecf20Sopenharmony_ci	 * NOTEXPLORED state. Then at the end we will try to format it and
5738c2ecf20Sopenharmony_ci	 * mark it as free.
5748c2ecf20Sopenharmony_ci	 */
5758c2ecf20Sopenharmony_ci	pr_debug("INFTL: pass 1, explore each unit\n");
5768c2ecf20Sopenharmony_ci	for (first_block = s->firstEUN; first_block <= s->lastEUN; first_block++) {
5778c2ecf20Sopenharmony_ci		if (s->PUtable[first_block] != BLOCK_NOTEXPLORED)
5788c2ecf20Sopenharmony_ci			continue;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci		do_format_chain = 0;
5818c2ecf20Sopenharmony_ci		first_logical_block = BLOCK_NIL;
5828c2ecf20Sopenharmony_ci		last_block = BLOCK_NIL;
5838c2ecf20Sopenharmony_ci		block = first_block;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci		for (chain_length = 0; ; chain_length++) {
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci			if ((chain_length == 0) &&
5888c2ecf20Sopenharmony_ci			    (s->PUtable[block] != BLOCK_NOTEXPLORED)) {
5898c2ecf20Sopenharmony_ci				/* Nothing to do here, onto next block */
5908c2ecf20Sopenharmony_ci				break;
5918c2ecf20Sopenharmony_ci			}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci			if (inftl_read_oob(mtd, block * s->EraseSize + 8,
5948c2ecf20Sopenharmony_ci					   8, &retlen, (char *)&h0) < 0 ||
5958c2ecf20Sopenharmony_ci			    inftl_read_oob(mtd, block * s->EraseSize +
5968c2ecf20Sopenharmony_ci					   2 * SECTORSIZE + 8, 8, &retlen,
5978c2ecf20Sopenharmony_ci					   (char *)&h1) < 0) {
5988c2ecf20Sopenharmony_ci				/* Should never happen? */
5998c2ecf20Sopenharmony_ci				do_format_chain++;
6008c2ecf20Sopenharmony_ci				break;
6018c2ecf20Sopenharmony_ci			}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci			logical_block = le16_to_cpu(h0.virtualUnitNo);
6048c2ecf20Sopenharmony_ci			prev_block = le16_to_cpu(h0.prevUnitNo);
6058c2ecf20Sopenharmony_ci			erase_mark = le16_to_cpu((h1.EraseMark | h1.EraseMark1));
6068c2ecf20Sopenharmony_ci			ANACtable[block] = h0.ANAC;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci			/* Previous block is relative to start of Partition */
6098c2ecf20Sopenharmony_ci			if (prev_block < s->nb_blocks)
6108c2ecf20Sopenharmony_ci				prev_block += s->firstEUN;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci			/* Already explored partial chain? */
6138c2ecf20Sopenharmony_ci			if (s->PUtable[block] != BLOCK_NOTEXPLORED) {
6148c2ecf20Sopenharmony_ci				/* Check if chain for this logical */
6158c2ecf20Sopenharmony_ci				if (logical_block == first_logical_block) {
6168c2ecf20Sopenharmony_ci					if (last_block != BLOCK_NIL)
6178c2ecf20Sopenharmony_ci						s->PUtable[last_block] = block;
6188c2ecf20Sopenharmony_ci				}
6198c2ecf20Sopenharmony_ci				break;
6208c2ecf20Sopenharmony_ci			}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci			/* Check for invalid block */
6238c2ecf20Sopenharmony_ci			if (erase_mark != ERASE_MARK) {
6248c2ecf20Sopenharmony_ci				printk(KERN_WARNING "INFTL: corrupt block %d "
6258c2ecf20Sopenharmony_ci					"in chain %d, chain length %d, erase "
6268c2ecf20Sopenharmony_ci					"mark 0x%x?\n", block, first_block,
6278c2ecf20Sopenharmony_ci					chain_length, erase_mark);
6288c2ecf20Sopenharmony_ci				/*
6298c2ecf20Sopenharmony_ci				 * Assume end of chain, probably incomplete
6308c2ecf20Sopenharmony_ci				 * fold/erase...
6318c2ecf20Sopenharmony_ci				 */
6328c2ecf20Sopenharmony_ci				if (chain_length == 0)
6338c2ecf20Sopenharmony_ci					do_format_chain++;
6348c2ecf20Sopenharmony_ci				break;
6358c2ecf20Sopenharmony_ci			}
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci			/* Check for it being free already then... */
6388c2ecf20Sopenharmony_ci			if ((logical_block == BLOCK_FREE) ||
6398c2ecf20Sopenharmony_ci			    (logical_block == BLOCK_NIL)) {
6408c2ecf20Sopenharmony_ci				s->PUtable[block] = BLOCK_FREE;
6418c2ecf20Sopenharmony_ci				break;
6428c2ecf20Sopenharmony_ci			}
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci			/* Sanity checks on block numbers */
6458c2ecf20Sopenharmony_ci			if ((logical_block >= s->nb_blocks) ||
6468c2ecf20Sopenharmony_ci			    ((prev_block >= s->nb_blocks) &&
6478c2ecf20Sopenharmony_ci			     (prev_block != BLOCK_NIL))) {
6488c2ecf20Sopenharmony_ci				if (chain_length > 0) {
6498c2ecf20Sopenharmony_ci					printk(KERN_WARNING "INFTL: corrupt "
6508c2ecf20Sopenharmony_ci						"block %d in chain %d?\n",
6518c2ecf20Sopenharmony_ci						block, first_block);
6528c2ecf20Sopenharmony_ci					do_format_chain++;
6538c2ecf20Sopenharmony_ci				}
6548c2ecf20Sopenharmony_ci				break;
6558c2ecf20Sopenharmony_ci			}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci			if (first_logical_block == BLOCK_NIL) {
6588c2ecf20Sopenharmony_ci				first_logical_block = logical_block;
6598c2ecf20Sopenharmony_ci			} else {
6608c2ecf20Sopenharmony_ci				if (first_logical_block != logical_block) {
6618c2ecf20Sopenharmony_ci					/* Normal for folded chain... */
6628c2ecf20Sopenharmony_ci					break;
6638c2ecf20Sopenharmony_ci				}
6648c2ecf20Sopenharmony_ci			}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci			/*
6678c2ecf20Sopenharmony_ci			 * Current block is valid, so if we followed a virtual
6688c2ecf20Sopenharmony_ci			 * chain to get here then we can set the previous
6698c2ecf20Sopenharmony_ci			 * block pointer in our PUtable now. Then move onto
6708c2ecf20Sopenharmony_ci			 * the previous block in the chain.
6718c2ecf20Sopenharmony_ci			 */
6728c2ecf20Sopenharmony_ci			s->PUtable[block] = BLOCK_NIL;
6738c2ecf20Sopenharmony_ci			if (last_block != BLOCK_NIL)
6748c2ecf20Sopenharmony_ci				s->PUtable[last_block] = block;
6758c2ecf20Sopenharmony_ci			last_block = block;
6768c2ecf20Sopenharmony_ci			block = prev_block;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci			/* Check for end of chain */
6798c2ecf20Sopenharmony_ci			if (block == BLOCK_NIL)
6808c2ecf20Sopenharmony_ci				break;
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci			/* Validate next block before following it... */
6838c2ecf20Sopenharmony_ci			if (block > s->lastEUN) {
6848c2ecf20Sopenharmony_ci				printk(KERN_WARNING "INFTL: invalid previous "
6858c2ecf20Sopenharmony_ci					"block %d in chain %d?\n", block,
6868c2ecf20Sopenharmony_ci					first_block);
6878c2ecf20Sopenharmony_ci				do_format_chain++;
6888c2ecf20Sopenharmony_ci				break;
6898c2ecf20Sopenharmony_ci			}
6908c2ecf20Sopenharmony_ci		}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci		if (do_format_chain) {
6938c2ecf20Sopenharmony_ci			format_chain(s, first_block);
6948c2ecf20Sopenharmony_ci			continue;
6958c2ecf20Sopenharmony_ci		}
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci		/*
6988c2ecf20Sopenharmony_ci		 * Looks like a valid chain then. It may not really be the
6998c2ecf20Sopenharmony_ci		 * newest block in the chain, but it is the newest we have
7008c2ecf20Sopenharmony_ci		 * found so far. We might update it in later iterations of
7018c2ecf20Sopenharmony_ci		 * this loop if we find something newer.
7028c2ecf20Sopenharmony_ci		 */
7038c2ecf20Sopenharmony_ci		s->VUtable[first_logical_block] = first_block;
7048c2ecf20Sopenharmony_ci		logical_block = BLOCK_NIL;
7058c2ecf20Sopenharmony_ci	}
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	INFTL_dumptables(s);
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	/*
7108c2ecf20Sopenharmony_ci	 * Second pass, check for infinite loops in chains. These are
7118c2ecf20Sopenharmony_ci	 * possible because we don't update the previous pointers when
7128c2ecf20Sopenharmony_ci	 * we fold chains. No big deal, just fix them up in PUtable.
7138c2ecf20Sopenharmony_ci	 */
7148c2ecf20Sopenharmony_ci	pr_debug("INFTL: pass 2, validate virtual chains\n");
7158c2ecf20Sopenharmony_ci	for (logical_block = 0; logical_block < s->numvunits; logical_block++) {
7168c2ecf20Sopenharmony_ci		block = s->VUtable[logical_block];
7178c2ecf20Sopenharmony_ci		last_block = BLOCK_NIL;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci		/* Check for free/reserved/nil */
7208c2ecf20Sopenharmony_ci		if (block >= BLOCK_RESERVED)
7218c2ecf20Sopenharmony_ci			continue;
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci		ANAC = ANACtable[block];
7248c2ecf20Sopenharmony_ci		for (i = 0; i < s->numvunits; i++) {
7258c2ecf20Sopenharmony_ci			if (s->PUtable[block] == BLOCK_NIL)
7268c2ecf20Sopenharmony_ci				break;
7278c2ecf20Sopenharmony_ci			if (s->PUtable[block] > s->lastEUN) {
7288c2ecf20Sopenharmony_ci				printk(KERN_WARNING "INFTL: invalid prev %d, "
7298c2ecf20Sopenharmony_ci					"in virtual chain %d\n",
7308c2ecf20Sopenharmony_ci					s->PUtable[block], logical_block);
7318c2ecf20Sopenharmony_ci				s->PUtable[block] = BLOCK_NIL;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci			}
7348c2ecf20Sopenharmony_ci			if (ANACtable[block] != ANAC) {
7358c2ecf20Sopenharmony_ci				/*
7368c2ecf20Sopenharmony_ci				 * Chain must point back to itself. This is ok,
7378c2ecf20Sopenharmony_ci				 * but we will need adjust the tables with this
7388c2ecf20Sopenharmony_ci				 * newest block and oldest block.
7398c2ecf20Sopenharmony_ci				 */
7408c2ecf20Sopenharmony_ci				s->VUtable[logical_block] = block;
7418c2ecf20Sopenharmony_ci				s->PUtable[last_block] = BLOCK_NIL;
7428c2ecf20Sopenharmony_ci				break;
7438c2ecf20Sopenharmony_ci			}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci			ANAC--;
7468c2ecf20Sopenharmony_ci			last_block = block;
7478c2ecf20Sopenharmony_ci			block = s->PUtable[block];
7488c2ecf20Sopenharmony_ci		}
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci		if (i >= s->nb_blocks) {
7518c2ecf20Sopenharmony_ci			/*
7528c2ecf20Sopenharmony_ci			 * Uhoo, infinite chain with valid ANACS!
7538c2ecf20Sopenharmony_ci			 * Format whole chain...
7548c2ecf20Sopenharmony_ci			 */
7558c2ecf20Sopenharmony_ci			format_chain(s, first_block);
7568c2ecf20Sopenharmony_ci		}
7578c2ecf20Sopenharmony_ci	}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	INFTL_dumptables(s);
7608c2ecf20Sopenharmony_ci	INFTL_dumpVUchains(s);
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	/*
7638c2ecf20Sopenharmony_ci	 * Third pass, format unreferenced blocks and init free block count.
7648c2ecf20Sopenharmony_ci	 */
7658c2ecf20Sopenharmony_ci	s->numfreeEUNs = 0;
7668c2ecf20Sopenharmony_ci	s->LastFreeEUN = BLOCK_NIL;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	pr_debug("INFTL: pass 3, format unused blocks\n");
7698c2ecf20Sopenharmony_ci	for (block = s->firstEUN; block <= s->lastEUN; block++) {
7708c2ecf20Sopenharmony_ci		if (s->PUtable[block] == BLOCK_NOTEXPLORED) {
7718c2ecf20Sopenharmony_ci			printk("INFTL: unreferenced block %d, formatting it\n",
7728c2ecf20Sopenharmony_ci				block);
7738c2ecf20Sopenharmony_ci			if (INFTL_formatblock(s, block) < 0)
7748c2ecf20Sopenharmony_ci				s->PUtable[block] = BLOCK_RESERVED;
7758c2ecf20Sopenharmony_ci			else
7768c2ecf20Sopenharmony_ci				s->PUtable[block] = BLOCK_FREE;
7778c2ecf20Sopenharmony_ci		}
7788c2ecf20Sopenharmony_ci		if (s->PUtable[block] == BLOCK_FREE) {
7798c2ecf20Sopenharmony_ci			s->numfreeEUNs++;
7808c2ecf20Sopenharmony_ci			if (s->LastFreeEUN == BLOCK_NIL)
7818c2ecf20Sopenharmony_ci				s->LastFreeEUN = block;
7828c2ecf20Sopenharmony_ci		}
7838c2ecf20Sopenharmony_ci	}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	kfree(ANACtable);
7868c2ecf20Sopenharmony_ci	return 0;
7878c2ecf20Sopenharmony_ci}
788