Пример #1
0
static int NFTL_readblock(struct NFTLrecord *nftl, unsigned block, char *buffer)
{
	u16 lastgoodEUN;
	u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
	unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
        unsigned int status;
	int silly = MAX_LOOPS;
        size_t retlen;
        struct nftl_bci bci;

	lastgoodEUN = BLOCK_NIL;

        if (thisEUN != BLOCK_NIL) {
		while (thisEUN < nftl->nb_blocks) {
			if (MTD_READOOB(nftl->mtd, (thisEUN * nftl->EraseSize) + blockofs,
					8, &retlen, (char *)&bci) < 0)
				status = SECTOR_IGNORE;
			else
				status = bci.Status | bci.Status1;

			switch (status) {
			case SECTOR_FREE:
				/* no modification of a sector should follow a free sector */
				goto the_end;
			case SECTOR_DELETED:
				lastgoodEUN = BLOCK_NIL;
				break;
			case SECTOR_USED:
				lastgoodEUN = thisEUN;
				break;
			case SECTOR_IGNORE:
				break;
			default:
				printk("Unknown status for block %d in EUN %d: %x\n",
				       block, thisEUN, status);
				break;
			}

			if (!silly--) {
				printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
				       block / (nftl->EraseSize / 512));
				return 1;
			}
			thisEUN = nftl->ReplUnitTable[thisEUN];
		}
        }

 the_end:
	if (lastgoodEUN == BLOCK_NIL) {
		/* the requested block is not on the media, return all 0x00 */
		memset(buffer, 0, 512);
	} else {
		loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
		size_t retlen;
		u_char eccbuf[6];
		if (MTD_READECC(nftl->mtd, ptr, 512, &retlen, buffer, eccbuf, NAND_ECC_DISKONCHIP))
			return -EIO;
	}
	return 0;
}
Пример #2
0
static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
{
	u16 BlockMap[MAX_SECTORS_PER_UNIT];
	unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
	unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
	unsigned int thisEUN;
	int block;
	int silly;
	unsigned int targetEUN;
	struct nftl_oob oob;
	int inplace = 1;
        size_t retlen;

	memset(BlockMap, 0xff, sizeof(BlockMap));
	memset(BlockFreeFound, 0, sizeof(BlockFreeFound));

	thisEUN = nftl->EUNtable[thisVUC];

	if (thisEUN == BLOCK_NIL) {
		printk(KERN_WARNING "Trying to fold non-existent "
		       "Virtual Unit Chain %d!\n", thisVUC);
		return BLOCK_NIL;
	}
	
	/* Scan to find the Erase Unit which holds the actual data for each
	   512-byte block within the Chain.
	*/
        silly = MAX_LOOPS;
	targetEUN = BLOCK_NIL;
	while (thisEUN <= nftl->lastEUN ) {
                unsigned int status, foldmark;

		targetEUN = thisEUN;
		for (block = 0; block < nftl->EraseSize / 512; block ++) {
			MTD_READOOB(nftl->mtd,
				    (thisEUN * nftl->EraseSize) + (block * 512),
				    16 , &retlen, (char *)&oob);
			if (block == 2) {
                                foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
                                if (foldmark == FOLD_MARK_IN_PROGRESS) {
                                        DEBUG(MTD_DEBUG_LEVEL1, 
                                              "Write Inhibited on EUN %d\n", thisEUN);
					inplace = 0;
				} else {
					/* There's no other reason not to do inplace,
					   except ones that come later. So we don't need
					   to preserve inplace */
					inplace = 1;
				}
			}
                        status = oob.b.Status | oob.b.Status1;
			BlockLastState[block] = status;

			switch(status) {
			case SECTOR_FREE:
				BlockFreeFound[block] = 1;
				break;

			case SECTOR_USED:
				if (!BlockFreeFound[block])
					BlockMap[block] = thisEUN;
				else
					printk(KERN_WARNING 
					       "SECTOR_USED found after SECTOR_FREE "
					       "in Virtual Unit Chain %d for block %d\n",
					       thisVUC, block);
				break;
			case SECTOR_DELETED:
				if (!BlockFreeFound[block])
					BlockMap[block] = BLOCK_NIL;
				else
					printk(KERN_WARNING 
					       "SECTOR_DELETED found after SECTOR_FREE "
					       "in Virtual Unit Chain %d for block %d\n",
					       thisVUC, block);
				break;

			case SECTOR_IGNORE:
				break;
			default:
				printk("Unknown status for block %d in EUN %d: %x\n",
				       block, thisEUN, status);
			}
		}

		if (!silly--) {
			printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
			       thisVUC);
			return BLOCK_NIL;
		}
		
		thisEUN = nftl->ReplUnitTable[thisEUN];
	}

	if (inplace) {
		/* We're being asked to be a fold-in-place. Check
		   that all blocks which actually have data associated
		   with them (i.e. BlockMap[block] != BLOCK_NIL) are 
		   either already present or SECTOR_FREE in the target
		   block. If not, we're going to have to fold out-of-place
		   anyway.
		*/
		for (block = 0; block < nftl->EraseSize / 512 ; block++) {
			if (BlockLastState[block] != SECTOR_FREE &&
			    BlockMap[block] != BLOCK_NIL &&
			    BlockMap[block] != targetEUN) {
				DEBUG(MTD_DEBUG_LEVEL1, "Setting inplace to 0. VUC %d, "
				      "block %d was %x lastEUN, "
				      "and is in EUN %d (%s) %d\n",
				      thisVUC, block, BlockLastState[block],
				      BlockMap[block], 
				      BlockMap[block]== targetEUN ? "==" : "!=",
				      targetEUN);
				inplace = 0;
				break;
			}
		}

		if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
		    pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
		    BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
		    SECTOR_FREE) {
			DEBUG(MTD_DEBUG_LEVEL1, "Pending write not free in EUN %d. "
			      "Folding out of place.\n", targetEUN);
			inplace = 0;
		}
	}
	
	if (!inplace) {
		DEBUG(MTD_DEBUG_LEVEL1, "Cannot fold Virtual Unit Chain %d in place. "
		      "Trying out-of-place\n", thisVUC);
		/* We need to find a targetEUN to fold into. */
		targetEUN = NFTL_findfreeblock(nftl, 1);
		if (targetEUN == BLOCK_NIL) {
			/* Ouch. Now we're screwed. We need to do a 
			   fold-in-place of another chain to make room
			   for this one. We need a better way of selecting
			   which chain to fold, because makefreeblock will 
			   only ask us to fold the same one again.
			*/
			printk(KERN_WARNING
			       "NFTL_findfreeblock(desperate) returns 0xffff.\n");
			return BLOCK_NIL;
		}
	} else {
            /* We put a fold mark in the chain we are folding only if
               we fold in place to help the mount check code. If we do
               not fold in place, it is possible to find the valid
               chain by selecting the longer one */
            oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
            oob.u.c.unused = 0xffffffff;
            MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, 
                         8, &retlen, (char *)&oob.u);
        }

	/* OK. We now know the location of every block in the Virtual Unit Chain,
	   and the Erase Unit into which we are supposed to be copying.
	   Go for it.
	*/
	DEBUG(MTD_DEBUG_LEVEL1,"Folding chain %d into unit %d\n", thisVUC, targetEUN);
	for (block = 0; block < nftl->EraseSize / 512 ; block++) {
		unsigned char movebuf[512];
		int ret;

		/* If it's in the target EUN already, or if it's pending write, do nothing */
		if (BlockMap[block] == targetEUN ||
		    (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
			continue;
		}

                /* copy only in non free block (free blocks can only
                   happen in case of media errors or deleted blocks) */
                if (BlockMap[block] == BLOCK_NIL)
                        continue;
                
                ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
				  512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); 
                if (ret < 0) {
                    ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block])
                                      + (block * 512), 512, &retlen,
                                      movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); 
                    if (ret != -EIO) 
                        printk("Error went away on retry.\n");
                }
                MTD_WRITEECC(nftl->mtd, (nftl->EraseSize * targetEUN) + (block * 512),
                             512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP);
	}
        
        /* add the header so that it is now a valid chain */
        oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum
                = cpu_to_le16(thisVUC);
        oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
        
        MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 8, 
                     8, &retlen, (char *)&oob.u);

	/* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */

	/* At this point, we have two different chains for this Virtual Unit, and no way to tell 
	   them apart. If we crash now, we get confused. However, both contain the same data, so we
	   shouldn't actually lose data in this case. It's just that when we load up on a medium which
	   has duplicate chains, we need to free one of the chains because it's not necessary any more.
	*/
	thisEUN = nftl->EUNtable[thisVUC];
	DEBUG(MTD_DEBUG_LEVEL1,"Want to erase\n");

	/* For each block in the old chain (except the targetEUN of course), 
	   free it and make it available for future use */
	while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
		unsigned int EUNtmp;

                EUNtmp = nftl->ReplUnitTable[thisEUN];

                if (NFTL_formatblock(nftl, thisEUN) < 0) {
			/* could not erase : mark block as reserved
			 * FixMe: Update Bad Unit Table on disk
			 */
			nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
                } else {
			/* correctly erased : mark it as free */
			nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
			nftl->numfreeEUNs++;
                }
                thisEUN = EUNtmp;
	}
	
	/* Make this the new start of chain for thisVUC */
	nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
	nftl->EUNtable[thisVUC] = targetEUN;

	return targetEUN;
}
/*
 * find_boot_record: Find the INFTL Media Header and its Spare copy which
 *	contains the various device information of the INFTL partition and
 *	Bad Unit Table. Update the PUtable[] table according to the Bad
 *	Unit Table. PUtable[] is used for management of Erase Unit in
 *	other routines in inftlcore.c and inftlmount.c.
 */
static int find_boot_record(struct INFTLrecord *inftl)
{
	struct inftl_unittail h1;
	//struct inftl_oob oob;
	unsigned int i, block, boot_record_count = 0;
	u8 buf[SECTORSIZE];
	struct INFTLMediaHeader *mh = &inftl->MediaHdr;
	struct INFTLPartition *ip;
	int retlen;

	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: find_boot_record(inftl=0x%x)\n",
		(int)inftl);

        /*
	 * Assume logical EraseSize == physical erasesize for starting the
	 * scan. We'll sort it out later if we find a MediaHeader which says
	 * otherwise.
	 */
	inftl->EraseSize = inftl->mtd->erasesize;
        inftl->nb_blocks = inftl->mtd->size / inftl->EraseSize;

	inftl->MediaUnit = BLOCK_NIL;
	inftl->SpareMediaUnit = BLOCK_NIL;

	/* Search for a valid boot record */
	for (block = 0; block < inftl->nb_blocks; block++) {
		int ret;

		/*
		 * Check for BNAND header first. Then whinge if it's found
		 * but later checks fail.
		 */
		if ((ret = MTD_READ(inftl->mtd, block * inftl->EraseSize,
		    SECTORSIZE, &retlen, buf))) {
			static int warncount = 5;

			if (warncount) {
				printk(KERN_WARNING "INFTL: block read at 0x%x "
					"of mtd%d failed: %d\n",
					block * inftl->EraseSize,
					inftl->mtd->index, ret);
				if (!--warncount)
					printk(KERN_WARNING "INFTL: further "
						"failures for this block will "
						"not be printed\n");
			}
			continue;
		}

		if (retlen < 6 || memcmp(buf, "BNAND", 6)) {
			/* BNAND\0 not found. Continue */
			continue;
		}

		/* To be safer with BIOS, also use erase mark as discriminant */
		if ((ret = MTD_READOOB(inftl->mtd, block * inftl->EraseSize +
		    SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0)) {
			printk(KERN_WARNING "INFTL: ANAND header found at "
				"0x%x in mtd%d, but OOB data read failed "
				"(err %d)\n", block * inftl->EraseSize,
				inftl->mtd->index, ret);
			continue;
		}

		if (boot_record_count) {
			/*
			 * We've already processed one. So we just check if
			 * this one is the same as the first one we found.
			 */
			if (memcmp(mh, buf, sizeof(struct INFTLMediaHeader))) {
				printk(KERN_WARNING "INFTL: Media Headers at "
					"0x%x and 0x%x disagree.\n",
					inftl->MediaUnit * inftl->EraseSize,
					block * inftl->EraseSize);
				return -1;
			}
			if (boot_record_count == 1)
				inftl->SpareMediaUnit = block;

			/*
			 * Mark this boot record (INFTL MediaHeader) block as
			 * reserved.
			 */
			inftl->PUtable[block] = BLOCK_RESERVED;

			boot_record_count++;
			continue;
		}

		/*
		 * This is the first we've seen.
		 * Copy the media header structure into place.
		 */
		memcpy(mh, buf, sizeof(struct INFTLMediaHeader));
		mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks);
		mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions);
		mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions);
		mh->BlockMultiplierBits = le32_to_cpu(mh->BlockMultiplierBits);
		mh->FormatFlags = le32_to_cpu(mh->FormatFlags);
		mh->PercentUsed = le32_to_cpu(mh->PercentUsed);

#ifdef CONFIG_MTD_DEBUG_VERBOSE
		if (CONFIG_MTD_DEBUG_VERBOSE >= 2) {
			printk("INFTL: Media Header ->\n"
				"    bootRecordID          = %s\n"
				"    NoOfBootImageBlocks   = %d\n"
				"    NoOfBinaryPartitions  = %d\n"
				"    NoOfBDTLPartitions    = %d\n"
				"    BlockMultiplerBits    = %d\n"
				"    FormatFlgs            = %d\n"
				"    OsakVersion           = 0x%x\n"
				"    PercentUsed           = %d\n",
				mh->bootRecordID, mh->NoOfBootImageBlocks,
				mh->NoOfBinaryPartitions,
				mh->NoOfBDTLPartitions,
				mh->BlockMultiplierBits, mh->FormatFlags,
				mh->OsakVersion, mh->PercentUsed);
		}
#endif

		if (mh->NoOfBDTLPartitions == 0) {
			printk(KERN_WARNING "INFTL: Media Header sanity check "
				"failed: NoOfBDTLPartitions (%d) == 0, "
				"must be at least 1\n", mh->NoOfBDTLPartitions);
			return -1;
		}

		if ((mh->NoOfBDTLPartitions + mh->NoOfBinaryPartitions) > 4) {
			printk(KERN_WARNING "INFTL: Media Header sanity check "
				"failed: Total Partitions (%d) > 4, "
				"BDTL=%d Binary=%d\n", mh->NoOfBDTLPartitions +
				mh->NoOfBinaryPartitions,
				mh->NoOfBDTLPartitions,
				mh->NoOfBinaryPartitions);
			return -1;
		}

		if (mh->BlockMultiplierBits > 1) {
			printk(KERN_WARNING "INFTL: sorry, we don't support "
				"UnitSizeFactor 0x%02x\n",
				mh->BlockMultiplierBits);
			return -1;
		} else if (mh->BlockMultiplierBits == 1) {
			printk(KERN_WARNING "INFTL: support for INFTL with "
				"UnitSizeFactor 0x%02x is experimental\n",
				mh->BlockMultiplierBits);
			inftl->EraseSize = inftl->mtd->erasesize <<
				(0xff - mh->BlockMultiplierBits);
			inftl->nb_blocks = inftl->mtd->size / inftl->EraseSize;
		}

		/* Scan the partitions */
		for (i = 0; (i < 4); i++) {
			ip = &mh->Partitions[i];
			ip->virtualUnits = le32_to_cpu(ip->virtualUnits);
			ip->firstUnit = le32_to_cpu(ip->firstUnit);
			ip->lastUnit = le32_to_cpu(ip->lastUnit);
			ip->flags = le32_to_cpu(ip->flags);
			ip->spareUnits = le32_to_cpu(ip->spareUnits);
			ip->Reserved0 = le32_to_cpu(ip->Reserved0);

#ifdef CONFIG_MTD_DEBUG_VERBOSE
			if (CONFIG_MTD_DEBUG_VERBOSE >= 2) {
				printk("    PARTITION[%d] ->\n"
					"        virtualUnits    = %d\n"
					"        firstUnit       = %d\n"
					"        lastUnit        = %d\n"
					"        flags           = 0x%x\n"
					"        spareUnits      = %d\n",
					i, ip->virtualUnits, ip->firstUnit,
					ip->lastUnit, ip->flags,
					ip->spareUnits);
			}
#endif

			if (ip->Reserved0 != ip->firstUnit) {
				struct erase_info *instr = &inftl->instr;

				/*
				 * 	Most likely this is using the
				 * 	undocumented qiuck mount feature.
				 * 	We don't support that, we will need
				 * 	to erase the hidden block for full
				 * 	compatibility.
				 */
				instr->addr = ip->Reserved0 * inftl->EraseSize;
				instr->len = inftl->EraseSize;
				MTD_ERASE(inftl->mtd, instr);
			}
			if ((ip->lastUnit - ip->firstUnit + 1) < ip->virtualUnits) {
				printk(KERN_WARNING "INFTL: Media Header "
					"Partition %d sanity check failed\n"
					"    firstUnit %d : lastUnit %d  >  "
					"virtualUnits %d\n", i, ip->lastUnit,
					ip->firstUnit, ip->Reserved0);
				return -1;
			}
			if (ip->Reserved1 != 0) {
				printk(KERN_WARNING "INFTL: Media Header "
					"Partition %d sanity check failed: "
					"Reserved1 %d != 0\n",
					i, ip->Reserved1);
				return -1;
			}

			if (ip->flags & INFTL_BDTL)
				break;
		}

		if (i >= 4) {
			printk(KERN_WARNING "INFTL: Media Header Partition "
				"sanity check failed:\n       No partition "
				"marked as Disk Partition\n");
			return -1;
		}

		inftl->nb_boot_blocks = ip->firstUnit;
		inftl->numvunits = ip->virtualUnits;
		if (inftl->numvunits > (inftl->nb_blocks -
		    inftl->nb_boot_blocks - 2)) {
			printk(KERN_WARNING "INFTL: Media Header sanity check "
				"failed:\n        numvunits (%d) > nb_blocks "
				"(%d) - nb_boot_blocks(%d) - 2\n",
				inftl->numvunits, inftl->nb_blocks,
				inftl->nb_boot_blocks);
			return -1;
		}
		
		inftl->nr_sects  = inftl->numvunits *
			(inftl->EraseSize / SECTORSIZE);

		/*
		 * Block count is set to last used EUN (we won't need to keep
		 * any meta-data past that point).
		 */
		inftl->firstEUN = ip->firstUnit;
		inftl->lastEUN = ip->lastUnit;
		inftl->nb_blocks = ip->lastUnit + 1;

		/* Memory alloc */
		inftl->PUtable = kmalloc(inftl->nb_blocks * sizeof(u16), GFP_KERNEL);
		if (!inftl->PUtable) {
			printk(KERN_WARNING "INFTL: allocation of PUtable "
				"failed (%d bytes)\n",
				inftl->nb_blocks * sizeof(u16));
			return -ENOMEM;
		}

		inftl->VUtable = kmalloc(inftl->nb_blocks * sizeof(u16), GFP_KERNEL);
		if (!inftl->VUtable) {
			kfree(inftl->PUtable);
			printk(KERN_WARNING "INFTL: allocation of VUtable "
				"failed (%d bytes)\n",
				inftl->nb_blocks * sizeof(u16));
			return -ENOMEM;
		}
		
		/* Mark the blocks before INFTL MediaHeader as reserved */
		for (i = 0; i < inftl->nb_boot_blocks; i++)
			inftl->PUtable[i] = BLOCK_RESERVED;
		/* Mark all remaining blocks as potentially containing data */
		for (; i < inftl->nb_blocks; i++)
			inftl->PUtable[i] = BLOCK_NOTEXPLORED;

		/* Mark this boot record (NFTL MediaHeader) block as reserved */
		inftl->PUtable[block] = BLOCK_RESERVED;

#if 0
		/* Read Bad Erase Unit Table and modify PUtable[] accordingly */
		for (i = 0; i < inftl->nb_blocks; i++) {
			if ((i & (SECTORSIZE - 1)) == 0) {
				/* read one sector for every SECTORSIZE of blocks */
				if ((ret = MTD_READECC(inftl->mtd,
				    block * inftl->EraseSize + i + SECTORSIZE,
				    SECTORSIZE, &retlen, buf,
				    (char *)&oob, NULL)) < 0) {
					printk(KERN_WARNING "INFTL: read of "
						"bad sector table failed "
						"(err %d)\n", ret);
					kfree(inftl->VUtable);
					kfree(inftl->PUtable);
					return -1;
				}
			}
			/* Mark the Bad Erase Unit as RESERVED in PUtable */
			if (buf[i & (SECTORSIZE - 1)] != 0xff)
				inftl->PUtable[i] = BLOCK_RESERVED;
		}
#endif

		inftl->MediaUnit = block;
		boot_record_count++;
	}
		
	return boot_record_count ? 0 : -1;
}
Пример #4
0
static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
			   char *buffer)
{
	struct INFTLrecord *inftl = (void *)mbd;
	unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
        unsigned int status;
	int silly = MAX_LOOPS;
        struct inftl_bci bci;
	size_t retlen;

	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_readblock(inftl=0x%x,block=%d,"
		"buffer=0x%x)\n", (int)inftl, block, (int)buffer);

	while (thisEUN < inftl->nb_blocks) {
		if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +
		     blockofs, 8, &retlen, (char *)&bci) < 0)
			status = SECTOR_IGNORE;
		else
			status = bci.Status | bci.Status1;

		switch (status) {
		case SECTOR_DELETED:
			thisEUN = BLOCK_NIL;
			goto foundit;
		case SECTOR_USED:
			goto foundit;
		case SECTOR_FREE:
		case SECTOR_IGNORE:
			break;
		default:
			printk(KERN_WARNING "INFTL: unknown status for "
				"block %ld in EUN %d: 0x%04x\n",
				block, thisEUN, status);
			break;
		}

		if (!silly--) {
			printk(KERN_WARNING "INFTL: infinite loop in "
				"Virtual Unit Chain 0x%lx\n",
				block / (inftl->EraseSize / SECTORSIZE));
			return 1;
		}

		thisEUN = inftl->PUtable[thisEUN];
	}

foundit:
	if (thisEUN == BLOCK_NIL) {
		/* The requested block is not on the media, return all 0x00 */
		memset(buffer, 0, SECTORSIZE);
	} else {
        	size_t retlen;
		loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
		u_char eccbuf[6];
		if (MTD_READECC(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen,
		    buffer, eccbuf, NULL))
			return -EIO;
	}
	return 0;
}
Пример #5
0
static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned pendingblock)
{
	u16 BlockMap[MAX_SECTORS_PER_UNIT];
	unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
	unsigned int thisEUN, prevEUN, status;
	int block, silly;
	unsigned int targetEUN;
	struct inftl_oob oob;
        size_t retlen;

	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_foldchain(inftl=0x%x,thisVUC=%d,"
		"pending=%d)\n", (int)inftl, thisVUC, pendingblock);

	memset(BlockMap, 0xff, sizeof(BlockMap));
	memset(BlockDeleted, 0, sizeof(BlockDeleted));

	thisEUN = targetEUN = inftl->VUtable[thisVUC];

	if (thisEUN == BLOCK_NIL) {
		printk(KERN_WARNING "INFTL: trying to fold non-existent "
		       "Virtual Unit Chain %d!\n", thisVUC);
		return BLOCK_NIL;
	}
	
	/*
	 * Scan to find the Erase Unit which holds the actual data for each
	 * 512-byte block within the Chain.
	 */
        silly = MAX_LOOPS;
	while (thisEUN < inftl->nb_blocks) {
		for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) {
			if ((BlockMap[block] != 0xffff) || BlockDeleted[block])
				continue;

			if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize)
			     + (block * SECTORSIZE), 16 , &retlen,
			     (char *)&oob) < 0)
				status = SECTOR_IGNORE;
			else
                        	status = oob.b.Status | oob.b.Status1;

			switch(status) {
			case SECTOR_FREE:
			case SECTOR_IGNORE:
				break;
			case SECTOR_USED:
				BlockMap[block] = thisEUN;
				continue;
			case SECTOR_DELETED:
				BlockDeleted[block] = 1;
				continue;
			default:
				printk(KERN_WARNING "INFTL: unknown status "
					"for block %d in EUN %d: %x\n",
					block, thisEUN, status);
				break;
			}
		}

		if (!silly--) {
			printk(KERN_WARNING "INFTL: infinite loop in Virtual "
				"Unit Chain 0x%x\n", thisVUC);
			return BLOCK_NIL;
		}
		
		thisEUN = inftl->PUtable[thisEUN];
	}

	/*
	 * OK. We now know the location of every block in the Virtual Unit
	 * Chain, and the Erase Unit into which we are supposed to be copying.
	 * Go for it.
	 */
	DEBUG(MTD_DEBUG_LEVEL1, "INFTL: folding chain %d into unit %d\n",
		thisVUC, targetEUN);

	for (block = 0; block < inftl->EraseSize/SECTORSIZE ; block++) {
		unsigned char movebuf[SECTORSIZE];
		int ret;

		/*
		 * If it's in the target EUN already, or if it's pending write,
		 * do nothing.
		 */
		if (BlockMap[block] == targetEUN || (pendingblock ==
		    (thisVUC * (inftl->EraseSize / SECTORSIZE) + block))) {
			continue;
		}

                /*
		 * Copy only in non free block (free blocks can only
                 * happen in case of media errors or deleted blocks).
		 */
                if (BlockMap[block] == BLOCK_NIL)
                        continue;
                
                ret = MTD_READECC(inftl->mbd.mtd, (inftl->EraseSize *
			BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE,
			&retlen, movebuf, (char *)&oob, NULL); 
                if (ret < 0) {
			ret = MTD_READECC(inftl->mbd.mtd, (inftl->EraseSize *
				BlockMap[block]) + (block * SECTORSIZE),
				SECTORSIZE, &retlen, movebuf, (char *)&oob,
				NULL); 
			if (ret != -EIO) 
                        	DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went "
					"away on retry?\n");
                }
                MTD_WRITEECC(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +
			(block * SECTORSIZE), SECTORSIZE, &retlen,
			movebuf, (char *)&oob, NULL);
	}

	/*
	 * Newest unit in chain now contains data from _all_ older units.
	 * So go through and erase each unit in chain, oldest first. (This
	 * is important, by doing oldest first if we crash/reboot then it
	 * it is relatively simple to clean up the mess).
	 */
	DEBUG(MTD_DEBUG_LEVEL1, "INFTL: want to erase virtual chain %d\n",
		thisVUC);

	for (;;) {
		/* Find oldest unit in chain. */
		thisEUN = inftl->VUtable[thisVUC];
		prevEUN = BLOCK_NIL;
		while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
			prevEUN = thisEUN;
			thisEUN = inftl->PUtable[thisEUN];
		}

		/* Check if we are all done */
		if (thisEUN == targetEUN)
			break;

                if (INFTL_formatblock(inftl, thisEUN) < 0) {
			/*
			 * Could not erase : mark block as reserved.
			 * FixMe: Update Bad Unit Table on disk.
			 */
			inftl->PUtable[thisEUN] = BLOCK_RESERVED;
                } else {
			/* Correctly erased : mark it as free */
			inftl->PUtable[thisEUN] = BLOCK_FREE;
			inftl->PUtable[prevEUN] = BLOCK_NIL;
			inftl->numfreeEUNs++;
                }
	}

	return targetEUN;
}
Пример #6
0
/*
 * Main program
 */
void lab_cmd_nanddump(int argc, const char **argv)
{
	unsigned char readbuf[512];
	unsigned char oobbuf[16];
	unsigned long ofs;
	struct mtd_info* mtd = NULL;
	int i, ofd, bs, start_addr, end_addr, pretty_print;
	struct mtd_oob_buf oob = {0, 16, oobbuf};
	unsigned char pretty_buf[120];
	int retlen;
	
	/* Make sure enough arguments were passed */ 
	if (argc < 3 || argv[1][1] != '\0') {
		lab_printf("usage: %s <mtd number> <dumpname> [start addr] [length]\r\n", argv[0]);
		return;
	}

	/* Open MTD device */
	if ((mtd = get_mtd_device(NULL, argv[1][0]-'0')) == NULL) {
		lab_puts("Couldn't open flash\r\n");
		return;
	}

	if (mtd->type != MTD_NANDFLASH) {
		lab_puts("This MTD is not NAND flash. I can't dump this - sorry.\r\n");
		put_mtd_device(mtd);
		return;
	}

	/* Make sure device page sizes are valid */
	if (!(mtd->oobsize == 16 && mtd->oobblock == 512) &&
	    !(mtd->oobsize == 8  && mtd->oobblock == 256)) {
		lab_puts("Unknown flash (not normal NAND)\r\n");
		put_mtd_device(mtd);
		return;
	}

	/* Open output file for writing */
	if ((ofd = sys_open(argv[2], O_WRONLY | O_CREAT, 0644)) == -1) {
		lab_puts("Couldn't open outfile\r\n");
		put_mtd_device(mtd);
		return;
	}

	/* Initialize start/end addresses and block size */
	start_addr = 0;
	end_addr = mtd->size;
	bs = mtd->oobblock;

	/* See if start address and length were specified */
	if (argc == 4) {
		start_addr = simple_strtoul(argv[3], NULL, 0) & ~(bs - 1);
		end_addr = mtd->size;
	} else if (argc == 5) {
		start_addr = simple_strtoul(argv[3], NULL, 0) & ~(bs - 1);
		end_addr = (simple_strtoul(argv[3], NULL, 0) + simple_strtoul(argv[4], NULL, 0)) & ~(bs - 1);
	}

	/* Ask user if they would like pretty output */
	lab_puts("Would you like formatted output? ");
	if (tolower(pretty_buf[0] = lab_getc_seconds(NULL, 0)) != 'y')
		pretty_print = 0;
	else
		pretty_print = 1;
	lab_putc(pretty_buf[0]);

	/* Print informative message */
	lab_printf("\r\nDumping data starting at 0x%08x and ending at 0x%08x...\r\n",
	           start_addr, end_addr);
	lab_printf("OOB size: %d. OOB block: %d\r\n",
	           mtd->oobsize, mtd->oobblock);

	/* Dump the flash contents */
	for (ofs = start_addr; ofs < end_addr ; ofs+=bs) {
		struct nand_oobinfo oobsel;

		oobsel.useecc = 0;
		/* Read page data and exit on failure */
//		if (MTD_READECC(mtd, ofs, bs, &retlen, readbuf, NULL, &oobsel)) {
		if (MTD_READECC(mtd, ofs, bs, &retlen, readbuf, &oobbuf, NULL)) {
			lab_puts("Error in mtdread\r\n");
//			put_mtd_device(mtd);
//			sys_close(ofd);
//			return;
		}

		/* Write out page data */
		if (pretty_print) {
			for (i = 0; i < bs; i += 16) {
				sprintf(pretty_buf,
					"0x%08x: %02x %02x %02x %02x %02x %02x %02x "
					"%02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
					(unsigned int) (ofs + i),  readbuf[i],
					readbuf[i+1], readbuf[i+2],
					readbuf[i+3], readbuf[i+4],
					readbuf[i+5], readbuf[i+6],
					readbuf[i+7], readbuf[i+8],
					readbuf[i+9], readbuf[i+10],
					readbuf[i+11], readbuf[i+12],
					readbuf[i+13], readbuf[i+14],
					readbuf[i+15]);
				sys_write(ofd, pretty_buf, 60);
			}
		} else
			sys_write(ofd, readbuf, bs);

		/* Read OOB data and exit on failure */
		oob.start = ofs;
		if ((ofs & 0xFFFF) == 0x0)
			lab_printf("Dumping %lx\r", ofs);
#if 0
		if (MTD_READOOB(mtd, ofs, mtd->oobsize, &retlen, oobbuf)) {
			lab_puts("ioctl(MEMREADOOB) failed\r\n");
			put_mtd_device(mtd);
			sys_close(ofd);
			return;
		}
#endif
		/* Write out OOB data */
		if (pretty_print) {
			if (mtd->oobsize == 16) {
				sprintf(pretty_buf, "  OOB Data: %02x %02x %02x %02x %02x %02x "
					"%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
					oobbuf[0], oobbuf[1], oobbuf[2],
					oobbuf[3], oobbuf[4], oobbuf[5],
					oobbuf[6], oobbuf[7], oobbuf[8],
					oobbuf[9], oobbuf[10], oobbuf[11],
					oobbuf[12], oobbuf[13], oobbuf[14],
					oobbuf[15]);
				sys_write(ofd, pretty_buf, 60);
			} else {
				sprintf(pretty_buf, "  OOB Data: %02x %02x %02x %02x %02x %02x "
					"%02x %02x\n",
					oobbuf[0], oobbuf[1], oobbuf[2],
					oobbuf[3], oobbuf[4], oobbuf[5],
					oobbuf[6], oobbuf[7]);
				sys_write(ofd, pretty_buf, 48);
			}
		} else
			sys_write(ofd, oobbuf, mtd->oobsize);
	}

	/* Close the output file and MTD device */
	put_mtd_device(mtd);
	sys_close(ofd);
	lab_puts("\n");

	/* Exit happy */
	return;
}