Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * NFTL mount code with extensive checks
0004  *
0005  * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
0006  * Copyright © 2000 Netgem S.A.
0007  * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org>
0008  */
0009 
0010 #include <linux/kernel.h>
0011 #include <asm/errno.h>
0012 #include <linux/delay.h>
0013 #include <linux/slab.h>
0014 #include <linux/mtd/mtd.h>
0015 #include <linux/mtd/rawnand.h>
0016 #include <linux/mtd/nftl.h>
0017 
0018 #define SECTORSIZE 512
0019 
0020 /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the
0021  *  various device information of the NFTL partition and Bad Unit Table. Update
0022  *  the ReplUnitTable[] table according to the Bad Unit Table. ReplUnitTable[]
0023  *  is used for management of Erase Unit in other routines in nftl.c and nftlmount.c
0024  */
0025 static int find_boot_record(struct NFTLrecord *nftl)
0026 {
0027     struct nftl_uci1 h1;
0028     unsigned int block, boot_record_count = 0;
0029     size_t retlen;
0030     u8 buf[SECTORSIZE];
0031     struct NFTLMediaHeader *mh = &nftl->MediaHdr;
0032     struct mtd_info *mtd = nftl->mbd.mtd;
0033     unsigned int i;
0034 
0035         /* Assume logical EraseSize == physical erasesize for starting the scan.
0036        We'll sort it out later if we find a MediaHeader which says otherwise */
0037     /* Actually, we won't.  The new DiskOnChip driver has already scanned
0038        the MediaHeader and adjusted the virtual erasesize it presents in
0039        the mtd device accordingly.  We could even get rid of
0040        nftl->EraseSize if there were any point in doing so. */
0041     nftl->EraseSize = nftl->mbd.mtd->erasesize;
0042         nftl->nb_blocks = (u32)nftl->mbd.mtd->size / nftl->EraseSize;
0043 
0044     nftl->MediaUnit = BLOCK_NIL;
0045     nftl->SpareMediaUnit = BLOCK_NIL;
0046 
0047     /* search for a valid boot record */
0048     for (block = 0; block < nftl->nb_blocks; block++) {
0049         int ret;
0050 
0051         /* Check for ANAND header first. Then can whinge if it's found but later
0052            checks fail */
0053         ret = mtd_read(mtd, block * nftl->EraseSize, SECTORSIZE,
0054                    &retlen, buf);
0055         /* We ignore ret in case the ECC of the MediaHeader is invalid
0056            (which is apparently acceptable) */
0057         if (retlen != SECTORSIZE) {
0058             static int warncount = 5;
0059 
0060             if (warncount) {
0061                 printk(KERN_WARNING "Block read at 0x%x of mtd%d failed: %d\n",
0062                        block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
0063                 if (!--warncount)
0064                     printk(KERN_WARNING "Further failures for this block will not be printed\n");
0065             }
0066             continue;
0067         }
0068 
0069         if (retlen < 6 || memcmp(buf, "ANAND", 6)) {
0070             /* ANAND\0 not found. Continue */
0071 #if 0
0072             printk(KERN_DEBUG "ANAND header not found at 0x%x in mtd%d\n",
0073                    block * nftl->EraseSize, nftl->mbd.mtd->index);
0074 #endif
0075             continue;
0076         }
0077 
0078         /* To be safer with BIOS, also use erase mark as discriminant */
0079         ret = nftl_read_oob(mtd, block * nftl->EraseSize +
0080                      SECTORSIZE + 8, 8, &retlen,
0081                      (char *)&h1);
0082         if (ret < 0) {
0083             printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n",
0084                    block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
0085             continue;
0086         }
0087 
0088 #if 0 /* Some people seem to have devices without ECC or erase marks
0089      on the Media Header blocks. There are enough other sanity
0090      checks in here that we can probably do without it.
0091       */
0092         if (le16_to_cpu(h1.EraseMark | h1.EraseMark1) != ERASE_MARK) {
0093             printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but erase mark not present (0x%04x,0x%04x instead)\n",
0094                    block * nftl->EraseSize, nftl->mbd.mtd->index,
0095                    le16_to_cpu(h1.EraseMark), le16_to_cpu(h1.EraseMark1));
0096             continue;
0097         }
0098 
0099         /* Finally reread to check ECC */
0100         ret = mtd->read(mtd, block * nftl->EraseSize, SECTORSIZE,
0101                 &retlen, buf);
0102         if (ret < 0) {
0103             printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n",
0104                    block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
0105             continue;
0106         }
0107 
0108         /* Paranoia. Check the ANAND header is still there after the ECC read */
0109         if (memcmp(buf, "ANAND", 6)) {
0110             printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but went away on reread!\n",
0111                    block * nftl->EraseSize, nftl->mbd.mtd->index);
0112             printk(KERN_NOTICE "New data are: %6ph\n", buf);
0113             continue;
0114         }
0115 #endif
0116         /* OK, we like it. */
0117 
0118         if (boot_record_count) {
0119             /* We've already processed one. So we just check if
0120                this one is the same as the first one we found */
0121             if (memcmp(mh, buf, sizeof(struct NFTLMediaHeader))) {
0122                 printk(KERN_NOTICE "NFTL Media Headers at 0x%x and 0x%x disagree.\n",
0123                        nftl->MediaUnit * nftl->EraseSize, block * nftl->EraseSize);
0124                 /* if (debug) Print both side by side */
0125                 if (boot_record_count < 2) {
0126                     /* We haven't yet seen two real ones */
0127                     return -1;
0128                 }
0129                 continue;
0130             }
0131             if (boot_record_count == 1)
0132                 nftl->SpareMediaUnit = block;
0133 
0134             /* Mark this boot record (NFTL MediaHeader) block as reserved */
0135             nftl->ReplUnitTable[block] = BLOCK_RESERVED;
0136 
0137 
0138             boot_record_count++;
0139             continue;
0140         }
0141 
0142         /* This is the first we've seen. Copy the media header structure into place */
0143         memcpy(mh, buf, sizeof(struct NFTLMediaHeader));
0144 
0145         /* Do some sanity checks on it */
0146 #if 0
0147 The new DiskOnChip driver scans the MediaHeader itself, and presents a virtual
0148 erasesize based on UnitSizeFactor.  So the erasesize we read from the mtd
0149 device is already correct.
0150         if (mh->UnitSizeFactor == 0) {
0151             printk(KERN_NOTICE "NFTL: UnitSizeFactor 0x00 detected. This violates the spec but we think we know what it means...\n");
0152         } else if (mh->UnitSizeFactor < 0xfc) {
0153             printk(KERN_NOTICE "Sorry, we don't support UnitSizeFactor 0x%02x\n",
0154                    mh->UnitSizeFactor);
0155             return -1;
0156         } else if (mh->UnitSizeFactor != 0xff) {
0157             printk(KERN_NOTICE "WARNING: Support for NFTL with UnitSizeFactor 0x%02x is experimental\n",
0158                    mh->UnitSizeFactor);
0159             nftl->EraseSize = nftl->mbd.mtd->erasesize << (0xff - mh->UnitSizeFactor);
0160             nftl->nb_blocks = (u32)nftl->mbd.mtd->size / nftl->EraseSize;
0161         }
0162 #endif
0163         nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN);
0164         if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) {
0165             printk(KERN_NOTICE "NFTL Media Header sanity check failed:\n");
0166             printk(KERN_NOTICE "nb_boot_blocks (%d) + 2 > nb_blocks (%d)\n",
0167                    nftl->nb_boot_blocks, nftl->nb_blocks);
0168             return -1;
0169         }
0170 
0171         nftl->numvunits = le32_to_cpu(mh->FormattedSize) / nftl->EraseSize;
0172         if (nftl->numvunits > (nftl->nb_blocks - nftl->nb_boot_blocks - 2)) {
0173             printk(KERN_NOTICE "NFTL Media Header sanity check failed:\n");
0174             printk(KERN_NOTICE "numvunits (%d) > nb_blocks (%d) - nb_boot_blocks(%d) - 2\n",
0175                    nftl->numvunits, nftl->nb_blocks, nftl->nb_boot_blocks);
0176             return -1;
0177         }
0178 
0179         nftl->mbd.size  = nftl->numvunits * (nftl->EraseSize / SECTORSIZE);
0180 
0181         /* If we're not using the last sectors in the device for some reason,
0182            reduce nb_blocks accordingly so we forget they're there */
0183         nftl->nb_blocks = le16_to_cpu(mh->NumEraseUnits) + le16_to_cpu(mh->FirstPhysicalEUN);
0184 
0185         /* XXX: will be suppressed */
0186         nftl->lastEUN = nftl->nb_blocks - 1;
0187 
0188         /* memory alloc */
0189         nftl->EUNtable = kmalloc_array(nftl->nb_blocks, sizeof(u16),
0190                            GFP_KERNEL);
0191         if (!nftl->EUNtable)
0192             return -ENOMEM;
0193 
0194         nftl->ReplUnitTable = kmalloc_array(nftl->nb_blocks,
0195                             sizeof(u16),
0196                             GFP_KERNEL);
0197         if (!nftl->ReplUnitTable) {
0198             kfree(nftl->EUNtable);
0199             return -ENOMEM;
0200         }
0201 
0202         /* mark the bios blocks (blocks before NFTL MediaHeader) as reserved */
0203         for (i = 0; i < nftl->nb_boot_blocks; i++)
0204             nftl->ReplUnitTable[i] = BLOCK_RESERVED;
0205         /* mark all remaining blocks as potentially containing data */
0206         for (; i < nftl->nb_blocks; i++) {
0207             nftl->ReplUnitTable[i] = BLOCK_NOTEXPLORED;
0208         }
0209 
0210         /* Mark this boot record (NFTL MediaHeader) block as reserved */
0211         nftl->ReplUnitTable[block] = BLOCK_RESERVED;
0212 
0213         /* read the Bad Erase Unit Table and modify ReplUnitTable[] accordingly */
0214         for (i = 0; i < nftl->nb_blocks; i++) {
0215 #if 0
0216 The new DiskOnChip driver already scanned the bad block table.  Just query it.
0217             if ((i & (SECTORSIZE - 1)) == 0) {
0218                 /* read one sector for every SECTORSIZE of blocks */
0219                 ret = mtd->read(nftl->mbd.mtd,
0220                         block * nftl->EraseSize + i +
0221                         SECTORSIZE, SECTORSIZE,
0222                         &retlen, buf);
0223                 if (ret < 0) {
0224                     printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n",
0225                            ret);
0226                     kfree(nftl->ReplUnitTable);
0227                     kfree(nftl->EUNtable);
0228                     return -1;
0229                 }
0230             }
0231             /* mark the Bad Erase Unit as RESERVED in ReplUnitTable */
0232             if (buf[i & (SECTORSIZE - 1)] != 0xff)
0233                 nftl->ReplUnitTable[i] = BLOCK_RESERVED;
0234 #endif
0235             if (mtd_block_isbad(nftl->mbd.mtd,
0236                         i * nftl->EraseSize))
0237                 nftl->ReplUnitTable[i] = BLOCK_RESERVED;
0238         }
0239 
0240         nftl->MediaUnit = block;
0241         boot_record_count++;
0242 
0243     } /* foreach (block) */
0244 
0245     return boot_record_count?0:-1;
0246 }
0247 
0248 static int memcmpb(void *a, int c, int n)
0249 {
0250     int i;
0251     for (i = 0; i < n; i++) {
0252         if (c != ((unsigned char *)a)[i])
0253             return 1;
0254     }
0255     return 0;
0256 }
0257 
0258 /* check_free_sector: check if a free sector is actually FREE, i.e. All 0xff in data and oob area */
0259 static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int len,
0260                   int check_oob)
0261 {
0262     struct mtd_info *mtd = nftl->mbd.mtd;
0263     size_t retlen;
0264     int i, ret;
0265     u8 *buf;
0266 
0267     buf = kmalloc(SECTORSIZE + mtd->oobsize, GFP_KERNEL);
0268     if (!buf)
0269         return -ENOMEM;
0270 
0271     ret = -1;
0272     for (i = 0; i < len; i += SECTORSIZE) {
0273         if (mtd_read(mtd, address, SECTORSIZE, &retlen, buf))
0274             goto out;
0275         if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
0276             goto out;
0277 
0278         if (check_oob) {
0279             if(nftl_read_oob(mtd, address, mtd->oobsize,
0280                      &retlen, &buf[SECTORSIZE]) < 0)
0281                 goto out;
0282             if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0)
0283                 goto out;
0284         }
0285         address += SECTORSIZE;
0286     }
0287 
0288     ret = 0;
0289 
0290 out:
0291     kfree(buf);
0292     return ret;
0293 }
0294 
0295 /* NFTL_format: format a Erase Unit by erasing ALL Erase Zones in the Erase Unit and
0296  *              Update NFTL metadata. Each erase operation is checked with check_free_sectors
0297  *
0298  * Return: 0 when succeed, -1 on error.
0299  *
0300  *  ToDo: 1. Is it necessary to check_free_sector after erasing ??
0301  */
0302 int NFTL_formatblock(struct NFTLrecord *nftl, int block)
0303 {
0304     size_t retlen;
0305     unsigned int nb_erases, erase_mark;
0306     struct nftl_uci1 uci;
0307     struct erase_info *instr = &nftl->instr;
0308     struct mtd_info *mtd = nftl->mbd.mtd;
0309 
0310     /* Read the Unit Control Information #1 for Wear-Leveling */
0311     if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8,
0312               8, &retlen, (char *)&uci) < 0)
0313         goto default_uci1;
0314 
0315     erase_mark = le16_to_cpu ((uci.EraseMark | uci.EraseMark1));
0316     if (erase_mark != ERASE_MARK) {
0317     default_uci1:
0318         uci.EraseMark = cpu_to_le16(ERASE_MARK);
0319         uci.EraseMark1 = cpu_to_le16(ERASE_MARK);
0320         uci.WearInfo = cpu_to_le32(0);
0321     }
0322 
0323     memset(instr, 0, sizeof(struct erase_info));
0324 
0325     /* XXX: use async erase interface, XXX: test return code */
0326     instr->addr = block * nftl->EraseSize;
0327     instr->len = nftl->EraseSize;
0328     if (mtd_erase(mtd, instr)) {
0329         printk("Error while formatting block %d\n", block);
0330         goto fail;
0331     }
0332 
0333     /* increase and write Wear-Leveling info */
0334     nb_erases = le32_to_cpu(uci.WearInfo);
0335     nb_erases++;
0336 
0337     /* wrap (almost impossible with current flash) or free block */
0338     if (nb_erases == 0)
0339         nb_erases = 1;
0340 
0341     /* check the "freeness" of Erase Unit before updating metadata
0342      * FixMe:  is this check really necessary ? since we have check the
0343      *         return code after the erase operation.
0344      */
0345     if (check_free_sectors(nftl, instr->addr, nftl->EraseSize, 1) != 0)
0346         goto fail;
0347 
0348     uci.WearInfo = le32_to_cpu(nb_erases);
0349     if (nftl_write_oob(mtd, block * nftl->EraseSize + SECTORSIZE +
0350                8, 8, &retlen, (char *)&uci) < 0)
0351         goto fail;
0352     return 0;
0353 fail:
0354     /* could not format, update the bad block table (caller is responsible
0355        for setting the ReplUnitTable to BLOCK_RESERVED on failure) */
0356     mtd_block_markbad(nftl->mbd.mtd, instr->addr);
0357     return -1;
0358 }
0359 
0360 /* check_sectors_in_chain: Check that each sector of a Virtual Unit Chain is correct.
0361  *  Mark as 'IGNORE' each incorrect sector. This check is only done if the chain
0362  *  was being folded when NFTL was interrupted.
0363  *
0364  *  The check_free_sectors in this function is necessary. There is a possible
0365  *  situation that after writing the Data area, the Block Control Information is
0366  *  not updated according (due to power failure or something) which leaves the block
0367  *  in an inconsistent state. So we have to check if a block is really FREE in this
0368  *  case. */
0369 static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_block)
0370 {
0371     struct mtd_info *mtd = nftl->mbd.mtd;
0372     unsigned int block, i, status;
0373     struct nftl_bci bci;
0374     int sectors_per_block;
0375     size_t retlen;
0376 
0377     sectors_per_block = nftl->EraseSize / SECTORSIZE;
0378     block = first_block;
0379     for (;;) {
0380         for (i = 0; i < sectors_per_block; i++) {
0381             if (nftl_read_oob(mtd,
0382                       block * nftl->EraseSize + i * SECTORSIZE,
0383                       8, &retlen, (char *)&bci) < 0)
0384                 status = SECTOR_IGNORE;
0385             else
0386                 status = bci.Status | bci.Status1;
0387 
0388             switch(status) {
0389             case SECTOR_FREE:
0390                 /* verify that the sector is really free. If not, mark
0391                    as ignore */
0392                 if (memcmpb(&bci, 0xff, 8) != 0 ||
0393                     check_free_sectors(nftl, block * nftl->EraseSize + i * SECTORSIZE,
0394                                SECTORSIZE, 0) != 0) {
0395                     printk("Incorrect free sector %d in block %d: "
0396                            "marking it as ignored\n",
0397                            i, block);
0398 
0399                     /* sector not free actually : mark it as SECTOR_IGNORE  */
0400                     bci.Status = SECTOR_IGNORE;
0401                     bci.Status1 = SECTOR_IGNORE;
0402                     nftl_write_oob(mtd, block *
0403                                nftl->EraseSize +
0404                                i * SECTORSIZE, 8,
0405                                &retlen, (char *)&bci);
0406                 }
0407                 break;
0408             default:
0409                 break;
0410             }
0411         }
0412 
0413         /* proceed to next Erase Unit on the chain */
0414         block = nftl->ReplUnitTable[block];
0415         if (!(block == BLOCK_NIL || block < nftl->nb_blocks))
0416             printk("incorrect ReplUnitTable[] : %d\n", block);
0417         if (block == BLOCK_NIL || block >= nftl->nb_blocks)
0418             break;
0419     }
0420 }
0421 
0422 /* calc_chain_length: Walk through a Virtual Unit Chain and estimate chain length */
0423 static int calc_chain_length(struct NFTLrecord *nftl, unsigned int first_block)
0424 {
0425     unsigned int length = 0, block = first_block;
0426 
0427     for (;;) {
0428         length++;
0429         /* avoid infinite loops, although this is guaranteed not to
0430            happen because of the previous checks */
0431         if (length >= nftl->nb_blocks) {
0432             printk("nftl: length too long %d !\n", length);
0433             break;
0434         }
0435 
0436         block = nftl->ReplUnitTable[block];
0437         if (!(block == BLOCK_NIL || block < nftl->nb_blocks))
0438             printk("incorrect ReplUnitTable[] : %d\n", block);
0439         if (block == BLOCK_NIL || block >= nftl->nb_blocks)
0440             break;
0441     }
0442     return length;
0443 }
0444 
0445 /* format_chain: Format an invalid Virtual Unit chain. It frees all the Erase Units in a
0446  *  Virtual Unit Chain, i.e. all the units are disconnected.
0447  *
0448  *  It is not strictly correct to begin from the first block of the chain because
0449  *  if we stop the code, we may see again a valid chain if there was a first_block
0450  *  flag in a block inside it. But is it really a problem ?
0451  *
0452  * FixMe: Figure out what the last statement means. What if power failure when we are
0453  *  in the for (;;) loop formatting blocks ??
0454  */
0455 static void format_chain(struct NFTLrecord *nftl, unsigned int first_block)
0456 {
0457     unsigned int block = first_block, block1;
0458 
0459     printk("Formatting chain at block %d\n", first_block);
0460 
0461     for (;;) {
0462         block1 = nftl->ReplUnitTable[block];
0463 
0464         printk("Formatting block %d\n", block);
0465         if (NFTL_formatblock(nftl, block) < 0) {
0466             /* cannot format !!!! Mark it as Bad Unit */
0467             nftl->ReplUnitTable[block] = BLOCK_RESERVED;
0468         } else {
0469             nftl->ReplUnitTable[block] = BLOCK_FREE;
0470         }
0471 
0472         /* goto next block on the chain */
0473         block = block1;
0474 
0475         if (!(block == BLOCK_NIL || block < nftl->nb_blocks))
0476             printk("incorrect ReplUnitTable[] : %d\n", block);
0477         if (block == BLOCK_NIL || block >= nftl->nb_blocks)
0478             break;
0479     }
0480 }
0481 
0482 /* check_and_mark_free_block: Verify that a block is free in the NFTL sense (valid erase mark) or
0483  *  totally free (only 0xff).
0484  *
0485  * Definition: Free Erase Unit -- A properly erased/formatted Free Erase Unit should have meet the
0486  *  following criteria:
0487  *  1. */
0488 static int check_and_mark_free_block(struct NFTLrecord *nftl, int block)
0489 {
0490     struct mtd_info *mtd = nftl->mbd.mtd;
0491     struct nftl_uci1 h1;
0492     unsigned int erase_mark;
0493     size_t retlen;
0494 
0495     /* check erase mark. */
0496     if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
0497               &retlen, (char *)&h1) < 0)
0498         return -1;
0499 
0500     erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1));
0501     if (erase_mark != ERASE_MARK) {
0502         /* if no erase mark, the block must be totally free. This is
0503            possible in two cases : empty filesystem or interrupted erase (very unlikely) */
0504         if (check_free_sectors (nftl, block * nftl->EraseSize, nftl->EraseSize, 1) != 0)
0505             return -1;
0506 
0507         /* free block : write erase mark */
0508         h1.EraseMark = cpu_to_le16(ERASE_MARK);
0509         h1.EraseMark1 = cpu_to_le16(ERASE_MARK);
0510         h1.WearInfo = cpu_to_le32(0);
0511         if (nftl_write_oob(mtd,
0512                    block * nftl->EraseSize + SECTORSIZE + 8, 8,
0513                    &retlen, (char *)&h1) < 0)
0514             return -1;
0515     } else {
0516 #if 0
0517         /* if erase mark present, need to skip it when doing check */
0518         for (i = 0; i < nftl->EraseSize; i += SECTORSIZE) {
0519             /* check free sector */
0520             if (check_free_sectors (nftl, block * nftl->EraseSize + i,
0521                         SECTORSIZE, 0) != 0)
0522                 return -1;
0523 
0524             if (nftl_read_oob(mtd, block * nftl->EraseSize + i,
0525                       16, &retlen, buf) < 0)
0526                 return -1;
0527             if (i == SECTORSIZE) {
0528                 /* skip erase mark */
0529                 if (memcmpb(buf, 0xff, 8))
0530                     return -1;
0531             } else {
0532                 if (memcmpb(buf, 0xff, 16))
0533                     return -1;
0534             }
0535         }
0536 #endif
0537     }
0538 
0539     return 0;
0540 }
0541 
0542 /* get_fold_mark: Read fold mark from Unit Control Information #2, we use FOLD_MARK_IN_PROGRESS
0543  *  to indicate that we are in the progression of a Virtual Unit Chain folding. If the UCI #2
0544  *  is FOLD_MARK_IN_PROGRESS when mounting the NFTL, the (previous) folding process is interrupted
0545  *  for some reason. A clean up/check of the VUC is necessary in this case.
0546  *
0547  * WARNING: return 0 if read error
0548  */
0549 static int get_fold_mark(struct NFTLrecord *nftl, unsigned int block)
0550 {
0551     struct mtd_info *mtd = nftl->mbd.mtd;
0552     struct nftl_uci2 uci;
0553     size_t retlen;
0554 
0555     if (nftl_read_oob(mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8,
0556               8, &retlen, (char *)&uci) < 0)
0557         return 0;
0558 
0559     return le16_to_cpu((uci.FoldMark | uci.FoldMark1));
0560 }
0561 
0562 int NFTL_mount(struct NFTLrecord *s)
0563 {
0564     int i;
0565     unsigned int first_logical_block, logical_block, rep_block, erase_mark;
0566     unsigned int block, first_block, is_first_block;
0567     int chain_length, do_format_chain;
0568     struct nftl_uci0 h0;
0569     struct nftl_uci1 h1;
0570     struct mtd_info *mtd = s->mbd.mtd;
0571     size_t retlen;
0572 
0573     /* search for NFTL MediaHeader and Spare NFTL Media Header */
0574     if (find_boot_record(s) < 0) {
0575         printk("Could not find valid boot record\n");
0576         return -1;
0577     }
0578 
0579     /* init the logical to physical table */
0580     for (i = 0; i < s->nb_blocks; i++) {
0581         s->EUNtable[i] = BLOCK_NIL;
0582     }
0583 
0584     /* first pass : explore each block chain */
0585     first_logical_block = 0;
0586     for (first_block = 0; first_block < s->nb_blocks; first_block++) {
0587         /* if the block was not already explored, we can look at it */
0588         if (s->ReplUnitTable[first_block] == BLOCK_NOTEXPLORED) {
0589             block = first_block;
0590             chain_length = 0;
0591             do_format_chain = 0;
0592 
0593             for (;;) {
0594                 /* read the block header. If error, we format the chain */
0595                 if (nftl_read_oob(mtd,
0596                           block * s->EraseSize + 8, 8,
0597                           &retlen, (char *)&h0) < 0 ||
0598                     nftl_read_oob(mtd,
0599                           block * s->EraseSize +
0600                           SECTORSIZE + 8, 8,
0601                           &retlen, (char *)&h1) < 0) {
0602                     s->ReplUnitTable[block] = BLOCK_NIL;
0603                     do_format_chain = 1;
0604                     break;
0605                 }
0606 
0607                 logical_block = le16_to_cpu ((h0.VirtUnitNum | h0.SpareVirtUnitNum));
0608                 rep_block = le16_to_cpu ((h0.ReplUnitNum | h0.SpareReplUnitNum));
0609                 erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1));
0610 
0611                 is_first_block = !(logical_block >> 15);
0612                 logical_block = logical_block & 0x7fff;
0613 
0614                 /* invalid/free block test */
0615                 if (erase_mark != ERASE_MARK || logical_block >= s->nb_blocks) {
0616                     if (chain_length == 0) {
0617                         /* if not currently in a chain, we can handle it safely */
0618                         if (check_and_mark_free_block(s, block) < 0) {
0619                             /* not really free: format it */
0620                             printk("Formatting block %d\n", block);
0621                             if (NFTL_formatblock(s, block) < 0) {
0622                                 /* could not format: reserve the block */
0623                                 s->ReplUnitTable[block] = BLOCK_RESERVED;
0624                             } else {
0625                                 s->ReplUnitTable[block] = BLOCK_FREE;
0626                             }
0627                         } else {
0628                             /* free block: mark it */
0629                             s->ReplUnitTable[block] = BLOCK_FREE;
0630                         }
0631                         /* directly examine the next block. */
0632                         goto examine_ReplUnitTable;
0633                     } else {
0634                         /* the block was in a chain : this is bad. We
0635                            must format all the chain */
0636                         printk("Block %d: free but referenced in chain %d\n",
0637                                block, first_block);
0638                         s->ReplUnitTable[block] = BLOCK_NIL;
0639                         do_format_chain = 1;
0640                         break;
0641                     }
0642                 }
0643 
0644                 /* we accept only first blocks here */
0645                 if (chain_length == 0) {
0646                     /* this block is not the first block in chain :
0647                        ignore it, it will be included in a chain
0648                        later, or marked as not explored */
0649                     if (!is_first_block)
0650                         goto examine_ReplUnitTable;
0651                     first_logical_block = logical_block;
0652                 } else {
0653                     if (logical_block != first_logical_block) {
0654                         printk("Block %d: incorrect logical block: %d expected: %d\n",
0655                                block, logical_block, first_logical_block);
0656                         /* the chain is incorrect : we must format it,
0657                            but we need to read it completely */
0658                         do_format_chain = 1;
0659                     }
0660                     if (is_first_block) {
0661                         /* we accept that a block is marked as first
0662                            block while being last block in a chain
0663                            only if the chain is being folded */
0664                         if (get_fold_mark(s, block) != FOLD_MARK_IN_PROGRESS ||
0665                             rep_block != 0xffff) {
0666                             printk("Block %d: incorrectly marked as first block in chain\n",
0667                                    block);
0668                             /* the chain is incorrect : we must format it,
0669                                but we need to read it completely */
0670                             do_format_chain = 1;
0671                         } else {
0672                             printk("Block %d: folding in progress - ignoring first block flag\n",
0673                                    block);
0674                         }
0675                     }
0676                 }
0677                 chain_length++;
0678                 if (rep_block == 0xffff) {
0679                     /* no more blocks after */
0680                     s->ReplUnitTable[block] = BLOCK_NIL;
0681                     break;
0682                 } else if (rep_block >= s->nb_blocks) {
0683                     printk("Block %d: referencing invalid block %d\n",
0684                            block, rep_block);
0685                     do_format_chain = 1;
0686                     s->ReplUnitTable[block] = BLOCK_NIL;
0687                     break;
0688                 } else if (s->ReplUnitTable[rep_block] != BLOCK_NOTEXPLORED) {
0689                     /* same problem as previous 'is_first_block' test:
0690                        we accept that the last block of a chain has
0691                        the first_block flag set if folding is in
0692                        progress. We handle here the case where the
0693                        last block appeared first */
0694                     if (s->ReplUnitTable[rep_block] == BLOCK_NIL &&
0695                         s->EUNtable[first_logical_block] == rep_block &&
0696                         get_fold_mark(s, first_block) == FOLD_MARK_IN_PROGRESS) {
0697                         /* EUNtable[] will be set after */
0698                         printk("Block %d: folding in progress - ignoring first block flag\n",
0699                                rep_block);
0700                         s->ReplUnitTable[block] = rep_block;
0701                         s->EUNtable[first_logical_block] = BLOCK_NIL;
0702                     } else {
0703                         printk("Block %d: referencing block %d already in another chain\n",
0704                                block, rep_block);
0705                         /* XXX: should handle correctly fold in progress chains */
0706                         do_format_chain = 1;
0707                         s->ReplUnitTable[block] = BLOCK_NIL;
0708                     }
0709                     break;
0710                 } else {
0711                     /* this is OK */
0712                     s->ReplUnitTable[block] = rep_block;
0713                     block = rep_block;
0714                 }
0715             }
0716 
0717             /* the chain was completely explored. Now we can decide
0718                what to do with it */
0719             if (do_format_chain) {
0720                 /* invalid chain : format it */
0721                 format_chain(s, first_block);
0722             } else {
0723                 unsigned int first_block1, chain_to_format, chain_length1;
0724                 int fold_mark;
0725 
0726                 /* valid chain : get foldmark */
0727                 fold_mark = get_fold_mark(s, first_block);
0728                 if (fold_mark == 0) {
0729                     /* cannot get foldmark : format the chain */
0730                     printk("Could read foldmark at block %d\n", first_block);
0731                     format_chain(s, first_block);
0732                 } else {
0733                     if (fold_mark == FOLD_MARK_IN_PROGRESS)
0734                         check_sectors_in_chain(s, first_block);
0735 
0736                     /* now handle the case where we find two chains at the
0737                        same virtual address : we select the longer one,
0738                        because the shorter one is the one which was being
0739                        folded if the folding was not done in place */
0740                     first_block1 = s->EUNtable[first_logical_block];
0741                     if (first_block1 != BLOCK_NIL) {
0742                         /* XXX: what to do if same length ? */
0743                         chain_length1 = calc_chain_length(s, first_block1);
0744                         printk("Two chains at blocks %d (len=%d) and %d (len=%d)\n",
0745                                first_block1, chain_length1, first_block, chain_length);
0746 
0747                         if (chain_length >= chain_length1) {
0748                             chain_to_format = first_block1;
0749                             s->EUNtable[first_logical_block] = first_block;
0750                         } else {
0751                             chain_to_format = first_block;
0752                         }
0753                         format_chain(s, chain_to_format);
0754                     } else {
0755                         s->EUNtable[first_logical_block] = first_block;
0756                     }
0757                 }
0758             }
0759         }
0760     examine_ReplUnitTable:;
0761     }
0762 
0763     /* second pass to format unreferenced blocks  and init free block count */
0764     s->numfreeEUNs = 0;
0765     s->LastFreeEUN = le16_to_cpu(s->MediaHdr.FirstPhysicalEUN);
0766 
0767     for (block = 0; block < s->nb_blocks; block++) {
0768         if (s->ReplUnitTable[block] == BLOCK_NOTEXPLORED) {
0769             printk("Unreferenced block %d, formatting it\n", block);
0770             if (NFTL_formatblock(s, block) < 0)
0771                 s->ReplUnitTable[block] = BLOCK_RESERVED;
0772             else
0773                 s->ReplUnitTable[block] = BLOCK_FREE;
0774         }
0775         if (s->ReplUnitTable[block] == BLOCK_FREE) {
0776             s->numfreeEUNs++;
0777             s->LastFreeEUN = block;
0778         }
0779     }
0780 
0781     return 0;
0782 }