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