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