in nftlmount.c [25:246]
static int find_boot_record(struct NFTLrecord *nftl)
{
struct nftl_uci1 h1;
unsigned int block, boot_record_count = 0;
size_t retlen;
u8 buf[SECTORSIZE];
struct NFTLMediaHeader *mh = &nftl->MediaHdr;
struct mtd_info *mtd = nftl->mbd.mtd;
unsigned int i;
/* Assume logical EraseSize == physical erasesize for starting the scan.
We'll sort it out later if we find a MediaHeader which says otherwise */
/* Actually, we won't. The new DiskOnChip driver has already scanned
the MediaHeader and adjusted the virtual erasesize it presents in
the mtd device accordingly. We could even get rid of
nftl->EraseSize if there were any point in doing so. */
nftl->EraseSize = nftl->mbd.mtd->erasesize;
nftl->nb_blocks = (u32)nftl->mbd.mtd->size / nftl->EraseSize;
nftl->MediaUnit = BLOCK_NIL;
nftl->SpareMediaUnit = BLOCK_NIL;
/* search for a valid boot record */
for (block = 0; block < nftl->nb_blocks; block++) {
int ret;
/* Check for ANAND header first. Then can whinge if it's found but later
checks fail */
ret = mtd_read(mtd, block * nftl->EraseSize, SECTORSIZE,
&retlen, buf);
/* We ignore ret in case the ECC of the MediaHeader is invalid
(which is apparently acceptable) */
if (retlen != SECTORSIZE) {
static int warncount = 5;
if (warncount) {
printk(KERN_WARNING "Block read at 0x%x of mtd%d failed: %d\n",
block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
if (!--warncount)
printk(KERN_WARNING "Further failures for this block will not be printed\n");
}
continue;
}
if (retlen < 6 || memcmp(buf, "ANAND", 6)) {
/* ANAND\0 not found. Continue */
#if 0
printk(KERN_DEBUG "ANAND header not found at 0x%x in mtd%d\n",
block * nftl->EraseSize, nftl->mbd.mtd->index);
#endif
continue;
}
/* To be safer with BIOS, also use erase mark as discriminant */
ret = nftl_read_oob(mtd, block * nftl->EraseSize +
SECTORSIZE + 8, 8, &retlen,
(char *)&h1);
if (ret < 0) {
printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n",
block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
continue;
}
#if 0 /* Some people seem to have devices without ECC or erase marks
on the Media Header blocks. There are enough other sanity
checks in here that we can probably do without it.
*/
if (le16_to_cpu(h1.EraseMark | h1.EraseMark1) != ERASE_MARK) {
printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but erase mark not present (0x%04x,0x%04x instead)\n",
block * nftl->EraseSize, nftl->mbd.mtd->index,
le16_to_cpu(h1.EraseMark), le16_to_cpu(h1.EraseMark1));
continue;
}
/* Finally reread to check ECC */
ret = mtd->read(mtd, block * nftl->EraseSize, SECTORSIZE,
&retlen, buf);
if (ret < 0) {
printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n",
block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
continue;
}
/* Paranoia. Check the ANAND header is still there after the ECC read */
if (memcmp(buf, "ANAND", 6)) {
printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but went away on reread!\n",
block * nftl->EraseSize, nftl->mbd.mtd->index);
printk(KERN_NOTICE "New data are: %6ph\n", buf);
continue;
}
#endif
/* OK, we like it. */
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 NFTLMediaHeader))) {
printk(KERN_NOTICE "NFTL Media Headers at 0x%x and 0x%x disagree.\n",
nftl->MediaUnit * nftl->EraseSize, block * nftl->EraseSize);
/* if (debug) Print both side by side */
if (boot_record_count < 2) {
/* We haven't yet seen two real ones */
return -1;
}
continue;
}
if (boot_record_count == 1)
nftl->SpareMediaUnit = block;
/* Mark this boot record (NFTL MediaHeader) block as reserved */
nftl->ReplUnitTable[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 NFTLMediaHeader));
/* Do some sanity checks on it */
#if 0
The new DiskOnChip driver scans the MediaHeader itself, and presents a virtual
erasesize based on UnitSizeFactor. So the erasesize we read from the mtd
device is already correct.
if (mh->UnitSizeFactor == 0) {
printk(KERN_NOTICE "NFTL: UnitSizeFactor 0x00 detected. This violates the spec but we think we know what it means...\n");
} else if (mh->UnitSizeFactor < 0xfc) {
printk(KERN_NOTICE "Sorry, we don't support UnitSizeFactor 0x%02x\n",
mh->UnitSizeFactor);
return -1;
} else if (mh->UnitSizeFactor != 0xff) {
printk(KERN_NOTICE "WARNING: Support for NFTL with UnitSizeFactor 0x%02x is experimental\n",
mh->UnitSizeFactor);
nftl->EraseSize = nftl->mbd.mtd->erasesize << (0xff - mh->UnitSizeFactor);
nftl->nb_blocks = (u32)nftl->mbd.mtd->size / nftl->EraseSize;
}
#endif
nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN);
if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) {
printk(KERN_NOTICE "NFTL Media Header sanity check failed:\n");
printk(KERN_NOTICE "nb_boot_blocks (%d) + 2 > nb_blocks (%d)\n",
nftl->nb_boot_blocks, nftl->nb_blocks);
return -1;
}
nftl->numvunits = le32_to_cpu(mh->FormattedSize) / nftl->EraseSize;
if (nftl->numvunits > (nftl->nb_blocks - nftl->nb_boot_blocks - 2)) {
printk(KERN_NOTICE "NFTL Media Header sanity check failed:\n");
printk(KERN_NOTICE "numvunits (%d) > nb_blocks (%d) - nb_boot_blocks(%d) - 2\n",
nftl->numvunits, nftl->nb_blocks, nftl->nb_boot_blocks);
return -1;
}
nftl->mbd.size = nftl->numvunits * (nftl->EraseSize / SECTORSIZE);
/* If we're not using the last sectors in the device for some reason,
reduce nb_blocks accordingly so we forget they're there */
nftl->nb_blocks = le16_to_cpu(mh->NumEraseUnits) + le16_to_cpu(mh->FirstPhysicalEUN);
/* XXX: will be suppressed */
nftl->lastEUN = nftl->nb_blocks - 1;
/* memory alloc */
nftl->EUNtable = kmalloc_array(nftl->nb_blocks, sizeof(u16),
GFP_KERNEL);
if (!nftl->EUNtable)
return -ENOMEM;
nftl->ReplUnitTable = kmalloc_array(nftl->nb_blocks,
sizeof(u16),
GFP_KERNEL);
if (!nftl->ReplUnitTable) {
kfree(nftl->EUNtable);
return -ENOMEM;
}
/* mark the bios blocks (blocks before NFTL MediaHeader) as reserved */
for (i = 0; i < nftl->nb_boot_blocks; i++)
nftl->ReplUnitTable[i] = BLOCK_RESERVED;
/* mark all remaining blocks as potentially containing data */
for (; i < nftl->nb_blocks; i++) {
nftl->ReplUnitTable[i] = BLOCK_NOTEXPLORED;
}
/* Mark this boot record (NFTL MediaHeader) block as reserved */
nftl->ReplUnitTable[block] = BLOCK_RESERVED;
/* read the Bad Erase Unit Table and modify ReplUnitTable[] accordingly */
for (i = 0; i < nftl->nb_blocks; i++) {
#if 0
The new DiskOnChip driver already scanned the bad block table. Just query it.
if ((i & (SECTORSIZE - 1)) == 0) {
/* read one sector for every SECTORSIZE of blocks */
ret = mtd->read(nftl->mbd.mtd,
block * nftl->EraseSize + i +
SECTORSIZE, SECTORSIZE,
&retlen, buf);
if (ret < 0) {
printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n",
ret);
kfree(nftl->ReplUnitTable);
kfree(nftl->EUNtable);
return -1;
}
}
/* mark the Bad Erase Unit as RESERVED in ReplUnitTable */
if (buf[i & (SECTORSIZE - 1)] != 0xff)
nftl->ReplUnitTable[i] = BLOCK_RESERVED;
#endif
if (mtd_block_isbad(nftl->mbd.mtd,
i * nftl->EraseSize))
nftl->ReplUnitTable[i] = BLOCK_RESERVED;
}
nftl->MediaUnit = block;
boot_record_count++;
} /* foreach (block) */
return boot_record_count?0:-1;
}