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