Instead of writing the FCBs/DBBTs on every update write them
only if they have changed or if a block needs cleanup (returns
-EUCLEAN)

Signed-off-by: Sascha Hauer <[email protected]>
---
 common/imx-bbu-nand-fcb.c | 230 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 230 insertions(+)

diff --git a/common/imx-bbu-nand-fcb.c b/common/imx-bbu-nand-fcb.c
index 3dc9274..0c7f75c 100644
--- a/common/imx-bbu-nand-fcb.c
+++ b/common/imx-bbu-nand-fcb.c
@@ -137,6 +137,22 @@ static void encode_hamming_13_8(void *_src, void *_ecc, 
size_t size)
                ecc[i] = calculate_parity_13_8(src[i]);
 }
 
+static int lookup_single_error_13_8(unsigned char syndrome)
+{
+       int i;
+       unsigned char syndrome_table[] = {
+               0x1c, 0x16, 0x13, 0x19,
+               0x1a, 0x07, 0x15, 0x0e,
+               0x01, 0x02, 0x04, 0x08,
+               0x10,
+       };
+
+       for (i = 0; i < 13; i ++)
+               if (syndrome_table[i] == syndrome)
+                       return i;
+       return -1;
+}
+
 static uint32_t calc_chksum(void *buf, size_t size)
 {
        u32 chksum = 0;
@@ -238,6 +254,66 @@ static ssize_t raw_write_page(struct mtd_info *mtd, void 
*buf, loff_t offset)
         return ret;
 }
 
+static int read_fcb(struct mtd_info *mtd, int num, struct fcb_block **retfcb)
+{
+       int i;
+       int bitflips = 0;
+       u8 parity, np, syndrome, bit_to_flip;
+       u8 *fcb, *ecc;
+       int ret;
+       void *rawpage;
+
+       *retfcb = NULL;
+
+       rawpage = xmalloc(mtd->writesize + mtd->oobsize);
+
+       ret = raw_read_page(mtd, rawpage, mtd->erasesize * num);
+       if (ret) {
+               pr_err("Cannot read block %d\n", num);
+               goto err;
+       }
+
+       fcb = rawpage + 12;
+       ecc = rawpage + 512 + 12;
+
+       for (i = 0; i < 512; i++) {
+               parity = ecc[i];
+               np = calculate_parity_13_8(fcb[i]);
+
+               syndrome = np ^ parity;
+               if (syndrome == 0)
+                       continue;
+
+               if (!(hweight8(syndrome) & 1)) {
+                       pr_err("Uncorrectable error at offset %d\n", i);
+                       ret = -EIO;
+                       goto err;
+               }
+
+               bit_to_flip = lookup_single_error_13_8(syndrome);
+               if (bit_to_flip < 0) {
+                       pr_err("Uncorrectable error at offset %d\n", i);
+                       ret = -EIO;
+                       goto err;
+               }
+
+               bitflips++;
+
+               if (bit_to_flip > 7)
+                       ecc[i] ^= 1 << (bit_to_flip - 8);
+               else
+                       fcb[i] ^= 1 << bit_to_flip;
+       }
+
+       *retfcb = xmemdup(rawpage + 12, 512);
+
+       ret = 0;
+err:
+       free(rawpage);
+
+       return ret;
+}
+
 static int fcb_create(struct imx_nand_fcb_bbu_handler *imx_handler,
                struct fcb_block *fcb, struct mtd_info *mtd)
 {
@@ -495,6 +571,154 @@ out:
 }
 
 /**
+ * dbbt_block_is_bad - Check if according to the given DBBT a block is bad
+ * @dbbt: The DBBT data page
+ * @block: The block to test
+ *
+ * This function checks if a block is marked as bad in the given DBBT.
+ *
+ * return: true if the block is bad, false otherwise.
+ */
+static int dbbt_block_is_bad(void *_dbbt, int block)
+{
+       int i;
+       u32 *dbbt = _dbbt;
+       int num_bad_blocks;
+
+       if (!_dbbt)
+               return false;
+
+       dbbt++; /* reserved */
+
+       num_bad_blocks = *dbbt++;
+
+       for (i = 0; i < num_bad_blocks; i++) {
+               if (*dbbt == block)
+                       return true;
+               dbbt++;
+       }
+
+       return false;
+}
+
+/**
+ * dbbt_check - Check if DBBT is readable and consistent to the mtd BBT
+ * @mtd: The mtd Nand device
+ * @dbbt: The page where the DBBT is found
+ *
+ * This function checks if the DBBT is readable and consistent to the mtd
+ * layers idea of bad blocks.
+ *
+ * return: 0 if the DBBT is readable and consistent to the mtd BBT, a
+ * negative error code otherwise.
+ */
+static int dbbt_check(struct mtd_info *mtd, int page)
+{
+       int ret, needs_cleanup = 0;
+       size_t r;
+       void *dbbt_header;
+       void *dbbt_entries = NULL;
+       struct dbbt_block *dbbt;
+       int num_blocks = mtd_div_by_eb(mtd->size, mtd);
+       int n;
+
+       dbbt_header = xmalloc(mtd->writesize);
+
+       ret = mtd_read(mtd, page * mtd->writesize, mtd->writesize, &r, 
dbbt_header);
+       if (ret == -EUCLEAN) {
+               pr_warn("page %d needs cleaning\n", page);
+               needs_cleanup = 1;
+       } else if (ret < 0) {
+               pr_err("Cannot read page %d: %s\n", page, strerror(-ret));
+               goto out;
+       }
+
+       dbbt = dbbt_header;
+
+       if (dbbt->FingerPrint != 0x54424244) {
+               pr_err("dbbt at page %d is readable but does not contain a 
valid DBBT\n",
+                      page);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       if (dbbt->DBBTNumOfPages) {
+               dbbt_entries = xmalloc(mtd->writesize);
+
+               ret = mtd_read(mtd, (page + 4) * mtd->writesize, 
mtd->writesize, &r, dbbt_entries);
+               if (ret == -EUCLEAN) {
+                       pr_warn("page %d needs cleaning\n", page);
+                       needs_cleanup = 1;
+               } else if (ret < 0) {
+                       pr_err("Cannot read page %d: %s\n", page, 
strerror(-ret));
+                       free(dbbt_entries);
+                       goto out;
+               }
+       } else {
+               dbbt_entries = NULL;
+       }
+
+       for (n = 0; n < num_blocks; n++) {
+               if (mtd_peb_is_bad(mtd, n) != dbbt_block_is_bad(dbbt_entries, 
n)) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+       }
+
+       ret = 0;
+out:
+       free(dbbt_header);
+       free(dbbt_entries);
+
+       if (ret < 0)
+               return ret;
+       if (needs_cleanup)
+               return -EUCLEAN;
+       return 0;
+}
+
+/**
+ * fcb_dbbt_check - Check if a FCB/DBBT is valid
+ * @mtd: The mtd Nand device
+ * @num: The number of the FCB, corresponds to the eraseblock number
+ * @fcb: The FCB to check against
+ *
+ * This function checks if FCB/DBBT found on a device are valid. This
+ * means:
+ * - the FCB is readable on the device
+ * - the FCB is the same as the reference passed in @fcb
+ * - the DBBT is consistent to the mtd BBT
+ *
+ * return: 0 if the FCB/DBBT are valid, a negative error code otherwise
+ */
+static int fcb_dbbt_check(struct mtd_info *mtd, int num, struct fcb_block *fcb)
+{
+       int ret;
+       struct fcb_block *f;
+       int pages_per_block = mtd->erasesize / mtd->writesize;
+
+       ret = read_fcb(mtd, num, &f);
+       if (ret)
+               return ret;
+
+       if (memcmp(fcb, f, sizeof(*fcb))) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ret = dbbt_check(mtd, num * pages_per_block + 1);
+       if (ret)
+               goto out;
+
+       ret = 0;
+
+out:
+       free(f);
+
+       return ret;
+}
+
+/**
  * imx_bbu_write_fcbs_dbbts - Write FCBs/DBBTs to first four blocks
  * @mtd: The mtd device to write the FCBs/DBBTs to
  * @fcb: The FCB block to write
@@ -543,6 +767,12 @@ static int imx_bbu_write_fcbs_dbbts(struct mtd_info *mtd, 
struct fcb_block *fcb)
                if (mtd_peb_is_bad(mtd, i))
                        continue;
 
+               if (!fcb_dbbt_check(mtd, i, fcb)) {
+                       valid++;
+                       pr_info("FCB/DBBT on block %d still valid\n", i);
+                       continue;
+               }
+
                pr_info("Writing FCB/DBBT on block %d\n", i);
 
                ret = imx_bbu_write_fcb(mtd, i, fcb_raw_page, dbbt);
-- 
2.7.0


_______________________________________________
barebox mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/barebox

Reply via email to