static int verify_eraseblock(int ebnum) { size_t read; int err = 0; loff_t addr = ebnum * mtd->erasesize; prandom_bytes_state(&rnd_state, writebuf, subpgsize); clear_data(readbuf, subpgsize); err = mtd_read(mtd, addr, subpgsize, &read, readbuf); if (unlikely(err || read != subpgsize)) { if (mtd_is_bitflip(err) && read == subpgsize) { pr_info("ECC correction at %#llx\n", (long long)addr); err = 0; } else { pr_err("error: read failed at %#llx\n", (long long)addr); return err ? err : -1; } } if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { pr_err("error: verify failed at %#llx\n", (long long)addr); pr_info("------------- written----------------\n"); print_subpage(writebuf); pr_info("------------- read ------------------\n"); print_subpage(readbuf); pr_info("-------------------------------------\n"); errcnt += 1; } addr += subpgsize; prandom_bytes_state(&rnd_state, writebuf, subpgsize); clear_data(readbuf, subpgsize); err = mtd_read(mtd, addr, subpgsize, &read, readbuf); if (unlikely(err || read != subpgsize)) { if (mtd_is_bitflip(err) && read == subpgsize) { pr_info("ECC correction at %#llx\n", (long long)addr); err = 0; } else { pr_err("error: read failed at %#llx\n", (long long)addr); return err ? err : -1; } } if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { pr_info("error: verify failed at %#llx\n", (long long)addr); pr_info("------------- written----------------\n"); print_subpage(writebuf); pr_info("------------- read ------------------\n"); print_subpage(readbuf); pr_info("-------------------------------------\n"); errcnt += 1; } return err; }
static int verify_eraseblock2(int ebnum) { size_t read; int err = 0, k; loff_t addr = ebnum * mtd->erasesize; for (k = 1; k < 33; ++k) { if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize) break; prandom_bytes_state(&rnd_state, writebuf, subpgsize * k); clear_data(readbuf, subpgsize * k); err = mtd_read(mtd, addr, subpgsize * k, &read, readbuf); if (unlikely(err || read != subpgsize * k)) { if (mtd_is_bitflip(err) && read == subpgsize * k) { pr_info("ECC correction at %#llx\n", (long long)addr); err = 0; } else { pr_err("error: read failed at " "%#llx\n", (long long)addr); return err ? err : -1; } } if (unlikely(memcmp(readbuf, writebuf, subpgsize * k))) { pr_err("error: verify failed at %#llx\n", (long long)addr); errcnt += 1; } addr += subpgsize * k; } return err; }
static int verify_eraseblock_ff(int ebnum) { uint32_t j; size_t read; int err = 0; loff_t addr = ebnum * mtd->erasesize; memset(writebuf, 0xff, subpgsize); for (j = 0; j < mtd->erasesize / subpgsize; ++j) { clear_data(readbuf, subpgsize); err = mtd_read(mtd, addr, subpgsize, &read, readbuf); if (unlikely(err || read != subpgsize)) { if (mtd_is_bitflip(err) && read == subpgsize) { pr_info("ECC correction at %#llx\n", (long long)addr); err = 0; } else { pr_err("error: read failed at " "%#llx\n", (long long)addr); return err ? err : -1; } } if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { pr_err("error: verify 0xff failed at " "%#llx\n", (long long)addr); errcnt += 1; } addr += subpgsize; } return err; }
static int read_eraseblock_by_page(int ebnum) { size_t read; int i, err = 0; loff_t addr = ebnum * mtd->erasesize; void *buf = iobuf; for (i = 0; i < pgcnt; i++) { err = mtd_read(mtd, addr, pgsize, &read, buf); /* Ignore corrected ECC errors */ if (mtd_is_bitflip(err)) err = 0; if (err || read != pgsize) { printk(PRINT_PREF "error: read failed at %#llx\n", addr); if (!err) err = -EINVAL; break; } addr += pgsize; buf += pgsize; } return err; }
static int do_read(void) { size_t read; int eb = rand_eb(); int offs = rand_offs(); int len = rand_len(offs), err; loff_t addr; if (bbt[eb + 1]) { if (offs >= mtd->erasesize) offs -= mtd->erasesize; if (offs + len > mtd->erasesize) len = mtd->erasesize - offs; } addr = eb * mtd->erasesize + offs; err = mtd_read(mtd, addr, len, &read, readbuf); if (mtd_is_bitflip(err)) err = 0; if (unlikely(err || read != len)) { pr_err("error: read failed at 0x%llx\n", (long long)addr); if (!err) err = -EINVAL; return err; } return 0; }
static int erasetest(void) { size_t read, written; int err = 0, i, ebnum, ok = 1; loff_t addr0; printk(PRINT_PREF "erasetest\n"); ebnum = 0; addr0 = 0; for (i = 0; i < ebcnt && bbt[i]; ++i) { addr0 += mtd->erasesize; ebnum += 1; } printk(PRINT_PREF "erasing block %d\n", ebnum); err = erase_eraseblock(ebnum); if (err) return err; printk(PRINT_PREF "writing 1st page of block %d\n", ebnum); set_random_data(writebuf, pgsize); err = mtd_write(mtd, addr0, pgsize, &written, writebuf); if (err || written != pgsize) { printk(PRINT_PREF "error: write failed at %#llx\n", (long long)addr0); return err ? err : -1; } printk(PRINT_PREF "erasing block %d\n", ebnum); err = erase_eraseblock(ebnum); if (err) return err; printk(PRINT_PREF "reading 1st page of block %d\n", ebnum); err = mtd_read(mtd, addr0, pgsize, &read, twopages); if (mtd_is_bitflip(err)) err = 0; if (err || read != pgsize) { printk(PRINT_PREF "error: read failed at %#llx\n", (long long)addr0); return err ? err : -1; } printk(PRINT_PREF "verifying 1st page of block %d is all 0xff\n", ebnum); for (i = 0; i < pgsize; ++i) if (twopages[i] != 0xff) { printk(PRINT_PREF "verifying all 0xff failed at %d\n", i); errcnt += 1; ok = 0; break; } if (ok && !err) printk(PRINT_PREF "erasetest ok\n"); return err; }
static int concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { struct mtd_concat *concat = CONCAT(mtd); struct mtd_oob_ops devops = *ops; int i, err, ret = 0; ops->retlen = ops->oobretlen = 0; for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; if (from >= subdev->size) { from -= subdev->size; continue; } /* partial read ? */ if (from + devops.len > subdev->size) devops.len = subdev->size - from; err = subdev->read_oob(subdev, from, &devops); ops->retlen += devops.retlen; ops->oobretlen += devops.oobretlen; /* Save information about bitflips! */ if (unlikely(err)) { if (mtd_is_eccerr(err)) { mtd->ecc_stats.failed++; ret = err; } else if (mtd_is_bitflip(err)) { mtd->ecc_stats.corrected++; /* Do not overwrite -EBADMSG !! */ if (!ret) ret = err; } else return err; } if (devops.datbuf) { devops.len = ops->len - ops->retlen; if (!devops.len) return ret; devops.datbuf += devops.retlen; } if (devops.oobbuf) { devops.ooblen = ops->ooblen - ops->oobretlen; if (!devops.ooblen) return ret; devops.oobbuf += ops->oobretlen; } from = 0; } return -EINVAL; }
static int concat_read(struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) { struct mtd_concat *concat = CONCAT(mtd); int ret = 0, err; int i; #ifdef __UBOOT__ *retlen = 0; #endif for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; size_t size, retsize; if (from >= subdev->size) { /* Not destined for this subdev */ size = 0; from -= subdev->size; continue; } if (from + len > subdev->size) /* First part goes into this subdev */ size = subdev->size - from; else /* Entire transaction goes into this subdev */ size = len; err = mtd_read(subdev, from, size, &retsize, buf); /* Save information about bitflips! */ if (unlikely(err)) { if (mtd_is_eccerr(err)) { mtd->ecc_stats.failed++; ret = err; } else if (mtd_is_bitflip(err)) { mtd->ecc_stats.corrected++; /* Do not overwrite -EBADMSG !! */ if (!ret) ret = err; } else return err; } *retlen += retsize; len -= size; if (len == 0) return ret; buf += size; from = 0; } return -EINVAL; }
/** * self_check_write - make sure write succeeded. * @ubi: UBI device description object * @buf: buffer with data which were written * @pnum: physical eraseblock number the data were written to * @offset: offset within the physical eraseblock the data were written to * @len: how many bytes were written * * This functions reads data which were recently written and compares it with * the original data buffer - the data have to match. Returns zero if the data * match and a negative error code if not or in case of failure. */ static int self_check_write(struct ubi_device *ubi, const void *buf, int pnum, int offset, int len) { int err, i; size_t read; void *buf1; loff_t addr = (loff_t)pnum * ubi->peb_size + offset; if (!ubi_dbg_chk_io(ubi)) return 0; buf1 = __vmalloc(len, GFP_NOFS, PAGE_KERNEL); if (!buf1) { ubi_err("cannot allocate memory to check writes"); return 0; } err = mtd_read(ubi->mtd, addr, len, &read, buf1); if (err && !mtd_is_bitflip(err)) goto out_free; for (i = 0; i < len; i++) { uint8_t c = ((uint8_t *)buf)[i]; uint8_t c1 = ((uint8_t *)buf1)[i]; #if !defined(CONFIG_UBI_SILENCE_MSG) int dump_len = max_t(int, 128, len - i); #endif if (c == c1) continue; ubi_err("self-check failed for PEB %d:%d, len %d", pnum, offset, len); ubi_msg("data differ at position %d", i); ubi_msg("hex dump of the original buffer from %d to %d", i, i + dump_len); print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, buf + i, dump_len, 1); ubi_msg("hex dump of the read buffer from %d to %d", i, i + dump_len); print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, buf1 + i, dump_len, 1); dump_stack(); err = -EINVAL; goto out_free; } vfree(buf1); return 0; out_free: vfree(buf1); return err; }
static void find_next_position(struct mtdoops_context *cxt) { struct mtd_info *mtd = cxt->mtd; int ret, page, maxpos = 0; u32 count[2], maxcount = 0xffffffff; size_t retlen; for (page = 0; page < cxt->oops_pages; page++) { if (mtd_can_have_bb(mtd) && mtd_block_isbad(mtd, page * record_size)) continue; /* Assume the page is used */ mark_page_used(cxt, page); ret = mtd_read(mtd, page * record_size, MTDOOPS_HEADER_SIZE, &retlen, (u_char *)&count[0]); if (retlen != MTDOOPS_HEADER_SIZE || (ret < 0 && !mtd_is_bitflip(ret))) { printk(KERN_ERR "mtdoops: read failure at %ld (%td of %d read), err %d\n", page * record_size, retlen, MTDOOPS_HEADER_SIZE, ret); continue; } if (count[0] == 0xffffffff && count[1] == 0xffffffff) mark_page_unused(cxt, page); if (count[0] == 0xffffffff) continue; if (maxcount == 0xffffffff) { maxcount = count[0]; maxpos = page; } else if (count[0] < 0x40000000 && maxcount > 0xc0000000) { maxcount = count[0]; maxpos = page; } else if (count[0] > maxcount && count[0] < 0xc0000000) { maxcount = count[0]; maxpos = page; } else if (count[0] > maxcount && count[0] > 0xc0000000 && maxcount > 0x80000000) { maxcount = count[0]; maxpos = page; } } if (maxcount == 0xffffffff) { cxt->nextpage = 0; cxt->nextcount = 1; schedule_work(&cxt->work_erase); return; } cxt->nextpage = maxpos; cxt->nextcount = maxcount; mtdoops_inc_counter(cxt); }
/* * Check that the contents of eraseblock number @enbum is equivalent to the * @buf buffer. */ static inline int check_eraseblock(int ebnum, unsigned char *buf) { int err, retries = 0; size_t read; loff_t addr = ebnum * mtd->erasesize; size_t len = mtd->erasesize; if (pgcnt) { addr = (ebnum + 1) * mtd->erasesize - pgcnt * pgsize; len = pgcnt * pgsize; } retry: err = mtd_read(mtd, addr, len, &read, check_buf); if (mtd_is_bitflip(err)) pr_err("single bit flip occurred at EB %d " "MTD reported that it was fixed.\n", ebnum); else if (err) { pr_err("error %d while reading EB %d, " "read %zd\n", err, ebnum, read); return err; } if (read != len) { pr_err("failed to read %zd bytes from EB %d, " "read only %zd, but no error reported\n", len, ebnum, read); return -EIO; } if (memcmp(buf, check_buf, len)) { pr_err("read wrong data from EB %d\n", ebnum); report_corrupt(check_buf, buf); if (retries++ < RETRIES) { /* Try read again */ yield(); pr_info("re-try reading data from EB %d\n", ebnum); goto retry; } else { pr_info("retried %d times, still errors, " "give-up\n", RETRIES); return -EINVAL; } } if (retries != 0) pr_info("only attempt number %d was OK (!!!)\n", retries); return 0; }
static int read_eraseblock_by_page(int ebnum) { size_t read = 0; int i, ret, err = 0; loff_t addr = ebnum * mtd->erasesize; void *buf = iobuf; void *oobbuf = iobuf1; for (i = 0; i < pgcnt; i++) { memset(buf, 0 , pgcnt); ret = mtd->read(mtd, addr, pgsize, &read, buf); if (ret == -EUCLEAN) ret = 0; if (ret || read != pgsize) { printk(PRINT_PREF "error: read failed at %#llx\n", (long long)addr); if (!err) err = ret; if (!err) err = -EINVAL; } if (mtd->oobsize) { struct mtd_oob_ops ops; ops.mode = MTD_OPS_PLACE_OOB; ops.len = 0; ops.retlen = 0; ops.ooblen = mtd->oobsize; ops.oobretlen = 0; ops.ooboffs = 0; ops.datbuf = NULL; ops.oobbuf = oobbuf; ret = mtd->read_oob(mtd, addr, &ops); if ((ret && !mtd_is_bitflip(ret)) || ops.oobretlen != mtd->oobsize) { printk(PRINT_PREF "error: read oob failed at " "%#llx\n", (long long)addr); if (!err) err = ret; if (!err) err = -EINVAL; } oobbuf += mtd->oobsize; } addr += pgsize; buf += pgsize; } return err; }
static int read_eraseblock_by_2pages(int ebnum) { size_t read, sz = pgsize * 2; int i, n = pgcnt / 2, err = 0; loff_t addr = ebnum * mtd->erasesize; void *buf = iobuf; for (i = 0; i < n; i++) { err = mtd_read(mtd, addr, sz, &read, buf); /* Ignore corrected ECC errors */ if (mtd_is_bitflip(err)) err = 0; if (err || read != sz) { printk(PRINT_PREF "error: read failed at %#llx\n", addr); if (!err) err = -EINVAL; return err; } addr += sz; buf += sz; } if (pgcnt % 2) { err = mtd_read(mtd, addr, pgsize, &read, buf); /* Ignore corrected ECC errors */ if (mtd_is_bitflip(err)) err = 0; if (err || read != pgsize) { printk(PRINT_PREF "error: read failed at %#llx\n", addr); if (!err) err = -EINVAL; } } return err; }
int mtdtest_read(struct mtd_info *mtd, loff_t addr, size_t size, void *buf) { size_t read; int err; err = mtd_read(mtd, addr, size, &read, buf); /* Ignore corrected ECC errors */ if (mtd_is_bitflip(err)) err = 0; if (!err && read != size) err = -EIO; if (err) pr_err("error: read failed at %#llx\n", addr); return err; }
static int part_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { struct mtd_part *part = PART(mtd); struct mtd_ecc_stats stats; int res; stats = part->master->ecc_stats; res = mtd_read(part->master, from + part->offset, len, retlen, buf); if (unlikely(res)) { if (mtd_is_bitflip(res)) mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected; if (mtd_is_eccerr(res)) mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed; } return res; }
static int read_eraseblock(int ebnum) { size_t read; int err = 0; loff_t addr = ebnum * mtd->erasesize; err = mtd_read(mtd, addr, mtd->erasesize, &read, iobuf); /* Ignore corrected ECC errors */ if (mtd_is_bitflip(err)) err = 0; if (err || read != mtd->erasesize) { printk(PRINT_PREF "error: read failed at %#llx\n", addr); if (!err) err = -EINVAL; } return err; }
/** * ubi_self_check_all_ff - check that a region of flash is empty. * @ubi: UBI device description object * @pnum: the physical eraseblock number to check * @offset: the starting offset within the physical eraseblock to check * @len: the length of the region to check * * This function returns zero if only 0xFF bytes are present at offset * @offset of the physical eraseblock @pnum, and a negative error code if not * or if an error occurred. */ int ubi_self_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len) { size_t read; int err; void *buf; loff_t addr = (loff_t)pnum * ubi->peb_size + offset; if (!ubi_dbg_chk_io(ubi)) return 0; buf = __vmalloc(len, GFP_NOFS, PAGE_KERNEL); if (!buf) { ubi_err(ubi, "cannot allocate memory to check for 0xFFs"); return 0; } err = mtd_read(ubi->mtd, addr, len, &read, buf); if (err && !mtd_is_bitflip(err)) { ubi_err(ubi, "err %d while reading %d bytes from PEB %d:%d, read %zd bytes", err, len, pnum, offset, read); goto error; } err = ubi_check_pattern(buf, 0xFF, len); if (err == 0) { ubi_err(ubi, "flash region at PEB %d:%d, length %d does not contain all 0xFF bytes", pnum, offset, len); goto fail; } vfree(buf); return 0; fail: ubi_err(ubi, "self-check failed for PEB %d", pnum); ubi_msg(ubi, "hex dump of the %d-%d region", offset, offset + len); print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, buf, len, 1); err = -EINVAL; error: dump_stack(); vfree(buf); return err; }
static int part_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { struct mtd_part *part = PART(mtd); int res; if (from >= mtd->size) return -EINVAL; if (ops->datbuf && from + ops->len > mtd->size) return -EINVAL; res = mtd_read_oob(part->master, from + part->offset, ops); if (unlikely(res)) { if (mtd_is_bitflip(res)) mtd->ecc_stats.corrected++; if (mtd_is_eccerr(res)) mtd->ecc_stats.failed++; } return res; }
static const char *bcm47xxpart_trx_data_part_name(struct mtd_info *master, size_t offset) { uint32_t buf; size_t bytes_read; int err; err = mtd_read(master, offset, sizeof(buf), &bytes_read, (uint8_t *)&buf); if (err && !mtd_is_bitflip(err)) { pr_err("mtd_read error while parsing (offset: 0x%X): %d\n", offset, err); goto out_default; } if (buf == UBI_EC_MAGIC) return "ubi"; out_default: return "rootfs"; }
static int bitfliptest_do_read(void) { int err = 0; size_t read = 0; loff_t addr = 0; unsigned int corrected_num = 0; unsigned int eb = bitfliptest_rand_eb(); unsigned int offs = bitfliptest_rand_offs(); unsigned int len = bitfliptest_rand_len(offs); struct mtd_ecc_stats stats= {0}; if (bbt[eb + 1]) { if (offs >= mtd->erasesize) offs -= mtd->erasesize; if (offs + len > mtd->erasesize) len = mtd->erasesize - offs; } addr = eb * mtd->erasesize + offs; // pr_info("read, addr: 0x%.8x len: %d\n", (unsigned int)addr, len); stats = mtd_part->master->ecc_stats; err = mtd_read(mtd, addr, len, &read, readbuf); if (mtd_is_bitflip(err)) err = 0; if (unlikely(err || read != len)) { pr_err("error: read failed at 0x%llx, err:%d len: %dread:%d\n", (long long)addr, err, len, read); if (!err) err = -EINVAL; return err; } corrected_num = mtd_part->master->ecc_stats.corrected - stats.corrected; if(corrected_num != 0) { errcnt += corrected_num; pr_info("When we read addr:0x%.8x:0x%.8x, we found %d(%d) bit-flip!\n", (unsigned int)addr, len, corrected_num, errcnt); } rdpgcnt += len/mtd->writesize; rdcnt ++; return 0; }
static int sharpsl_nand_read_laddr(struct mtd_info *mtd, loff_t from, size_t len, void *buf, struct sharpsl_ftl *ftl) { unsigned int log_num, final_log_num; unsigned int block_num; loff_t block_adr; loff_t block_ofs; size_t retlen; int err; log_num = mtd_div_by_eb((u32)from, mtd); final_log_num = mtd_div_by_eb(((u32)from + len - 1), mtd); if (len <= 0 || log_num >= ftl->logmax || final_log_num > log_num) return -EINVAL; block_num = ftl->log2phy[log_num]; block_adr = block_num * mtd->erasesize; block_ofs = mtd_mod_by_eb((u32)from, mtd); err = mtd_read(mtd, block_adr + block_ofs, len, &retlen, buf); /* Ignore corrected ECC errors */ if (mtd_is_bitflip(err)) err = 0; if (!err && retlen != len) err = -EIO; if (err) pr_err("sharpslpart: error, read failed at %#llx\n", block_adr + block_ofs); return err; }
static int verify_eraseblock(int ebnum) { uint32_t j; size_t read; int err = 0, i; loff_t addr0, addrn; loff_t addr = ebnum * mtd->erasesize; addr0 = 0; for (i = 0; i < ebcnt && bbt[i]; ++i) addr0 += mtd->erasesize; addrn = mtd->size; for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i) addrn -= mtd->erasesize; set_random_data(writebuf, mtd->erasesize); for (j = 0; j < pgcnt - 1; ++j, addr += pgsize) { /* Do a read to set the internal dataRAMs to different data */ err = mtd_read(mtd, addr0, bufsize, &read, twopages); if (mtd_is_bitflip(err)) err = 0; if (err || read != bufsize) { printk(PRINT_PREF "error: read failed at %#llx\n", (long long)addr0); return err; } err = mtd_read(mtd, addrn - bufsize, bufsize, &read, twopages); if (mtd_is_bitflip(err)) err = 0; if (err || read != bufsize) { printk(PRINT_PREF "error: read failed at %#llx\n", (long long)(addrn - bufsize)); return err; } memset(twopages, 0, bufsize); err = mtd_read(mtd, addr, bufsize, &read, twopages); if (mtd_is_bitflip(err)) err = 0; if (err || read != bufsize) { printk(PRINT_PREF "error: read failed at %#llx\n", (long long)addr); break; } if (memcmp(twopages, writebuf + (j * pgsize), bufsize)) { printk(PRINT_PREF "error: verify failed at %#llx\n", (long long)addr); errcnt += 1; } } /* Check boundary between eraseblocks */ if (addr <= addrn - pgsize - pgsize && !bbt[ebnum + 1]) { unsigned long oldnext = next; /* Do a read to set the internal dataRAMs to different data */ err = mtd_read(mtd, addr0, bufsize, &read, twopages); if (mtd_is_bitflip(err)) err = 0; if (err || read != bufsize) { printk(PRINT_PREF "error: read failed at %#llx\n", (long long)addr0); return err; } err = mtd_read(mtd, addrn - bufsize, bufsize, &read, twopages); if (mtd_is_bitflip(err)) err = 0; if (err || read != bufsize) { printk(PRINT_PREF "error: read failed at %#llx\n", (long long)(addrn - bufsize)); return err; } memset(twopages, 0, bufsize); err = mtd_read(mtd, addr, bufsize, &read, twopages); if (mtd_is_bitflip(err)) err = 0; if (err || read != bufsize) { printk(PRINT_PREF "error: read failed at %#llx\n", (long long)addr); return err; } memcpy(boundary, writebuf + mtd->erasesize - pgsize, pgsize); set_random_data(boundary + pgsize, pgsize); if (memcmp(twopages, boundary, bufsize)) { printk(PRINT_PREF "error: verify failed at %#llx\n", (long long)addr); errcnt += 1; } next = oldnext; } return err; }
/** * ubi_io_read - read data from a physical eraseblock. * @ubi: UBI device description object * @buf: buffer where to store the read data * @pnum: physical eraseblock number to read from * @offset: offset within the physical eraseblock from where to read * @len: how many bytes to read * * This function reads data from offset @offset of physical eraseblock @pnum * and stores the read data in the @buf buffer. The following return codes are * possible: * * o %0 if all the requested data were successfully read; * o %UBI_IO_BITFLIPS if all the requested data were successfully read, but * correctable bit-flips were detected; this is harmless but may indicate * that this eraseblock may become bad soon (but do not have to); * o %-EBADMSG if the MTD subsystem reported about data integrity problems, for * example it can be an ECC error in case of NAND; this most probably means * that the data is corrupted; * o %-EIO if some I/O error occurred; * o other negative error codes in case of other errors. */ int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, int len) { int err, retries = 0; size_t read; loff_t addr; dbg_io("read %d bytes from PEB %d:%d", len, pnum, offset); ubi_assert(pnum >= 0 && pnum < ubi->peb_count); ubi_assert(offset >= 0 && offset + len <= ubi->peb_size); ubi_assert(len > 0); err = self_check_not_bad(ubi, pnum); if (err) return err; /* * Deliberately corrupt the buffer to improve robustness. Indeed, if we * do not do this, the following may happen: * 1. The buffer contains data from previous operation, e.g., read from * another PEB previously. The data looks like expected, e.g., if we * just do not read anything and return - the caller would not * notice this. E.g., if we are reading a VID header, the buffer may * contain a valid VID header from another PEB. * 2. The driver is buggy and returns us success or -EBADMSG or * -EUCLEAN, but it does not actually put any data to the buffer. * * This may confuse UBI or upper layers - they may think the buffer * contains valid data while in fact it is just old data. This is * especially possible because UBI (and UBIFS) relies on CRC, and * treats data as correct even in case of ECC errors if the CRC is * correct. * * Try to prevent this situation by changing the first byte of the * buffer. */ *((uint8_t *)buf) ^= 0xFF; addr = (loff_t)pnum * ubi->peb_size + offset; retry: err = mtd_read(ubi->mtd, addr, len, &read, buf); if (err) { const char *errstr = mtd_is_eccerr(err) ? " (ECC error)" : ""; if (mtd_is_bitflip(err)) { /* * -EUCLEAN is reported if there was a bit-flip which * was corrected, so this is harmless. * * We do not report about it here unless debugging is * enabled. A corresponding message will be printed * later, when it is has been scrubbed. */ ubi_msg(ubi, "fixable bit-flip detected at PEB %d", pnum); ubi_assert(len == read); return UBI_IO_BITFLIPS; } if (retries++ < UBI_IO_RETRIES) { ubi_warn(ubi, "error %d%s while reading %d bytes from PEB %d:%d, read only %zd bytes, retry", err, errstr, len, pnum, offset, read); yield(); goto retry; } ubi_err(ubi, "error %d%s while reading %d bytes from PEB %d:%d, read %zd bytes", err, errstr, len, pnum, offset, read); dump_stack(); /* * The driver should never return -EBADMSG if it failed to read * all the requested data. But some buggy drivers might do * this, so we change it to -EIO. */ if (read != len && mtd_is_eccerr(err)) { ubi_assert(0); err = -EIO; } } else { ubi_assert(len == read); if (ubi_dbg_is_bitflip(ubi)) { dbg_gen("bit-flip (emulated)"); err = UBI_IO_BITFLIPS; } } return err; }
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); struct mtd_info *mtd = inftl->mbd.mtd; unsigned int status; int silly = MAX_LOOPS; struct inftl_bci bci; size_t retlen; pr_debug("INFTL: inftl_readblock(inftl=%p,block=%ld," "buffer=%p)\n", inftl, block, buffer); while (thisEUN < inftl->nb_blocks) { if (inftl_read_oob(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; int ret = mtd->read(mtd, ptr, SECTORSIZE, &retlen, buffer); /* Handle corrected bit flips gracefully */ if (ret < 0 && !mtd_is_bitflip(ret)) return -EIO; } return 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; struct mtd_info *mtd = inftl->mbd.mtd; int block, silly; unsigned int targetEUN; struct inftl_oob oob; size_t retlen; pr_debug("INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d,pending=%d)\n", 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] != BLOCK_NIL) || BlockDeleted[block]) continue; if (inftl_read_oob(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. */ pr_debug("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->read(mtd, (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE, &retlen, movebuf); if (ret < 0 && !mtd_is_bitflip(ret)) { ret = mtd->read(mtd, (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE, &retlen, movebuf); if (ret != -EIO) pr_debug("INFTL: error went away on retry?\n"); } memset(&oob, 0xff, sizeof(struct inftl_oob)); oob.b.Status = oob.b.Status1 = SECTOR_USED; inftl_write(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) + (block * SECTORSIZE), SECTORSIZE, &retlen, movebuf, (char *)&oob); } /* * 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). */ pr_debug("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; /* Unlink the last block from the chain. */ inftl->PUtable[prevEUN] = BLOCK_NIL; /* Now try to erase it. */ if (INFTL_formatblock(inftl, thisEUN) < 0) { /* * Could not erase : mark block as reserved. */ inftl->PUtable[thisEUN] = BLOCK_RESERVED; } else { /* Correctly erased : mark it as free */ inftl->PUtable[thisEUN] = BLOCK_FREE; inftl->numfreeEUNs++; } } return targetEUN; }
static int bcm47xxpart_parse(struct mtd_info *master, const struct mtd_partition **pparts, struct mtd_part_parser_data *data) { struct mtd_partition *parts; uint8_t i, curr_part = 0; uint32_t *buf; size_t bytes_read; uint32_t offset; uint32_t blocksize = master->erasesize; int trx_parts[2]; /* Array with indexes of TRX partitions */ int trx_num = 0; /* Number of found TRX partitions */ int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, }; int err; /* * Some really old flashes (like AT45DB*) had smaller erasesize-s, but * partitions were aligned to at least 0x1000 anyway. */ if (blocksize < 0x1000) blocksize = 0x1000; /* Alloc */ parts = kcalloc(BCM47XXPART_MAX_PARTS, sizeof(struct mtd_partition), GFP_KERNEL); if (!parts) return -ENOMEM; buf = kzalloc(BCM47XXPART_BYTES_TO_READ, GFP_KERNEL); if (!buf) { kfree(parts); return -ENOMEM; } /* Parse block by block looking for magics */ for (offset = 0; offset <= master->size - blocksize; offset += blocksize) { /* Nothing more in higher memory on BCM47XX (MIPS) */ if (IS_ENABLED(CONFIG_BCM47XX) && offset >= 0x2000000) break; if (curr_part >= BCM47XXPART_MAX_PARTS) { pr_warn("Reached maximum number of partitions, scanning stopped!\n"); break; } /* Read beginning of the block */ err = mtd_read(master, offset, BCM47XXPART_BYTES_TO_READ, &bytes_read, (uint8_t *)buf); if (err && !mtd_is_bitflip(err)) { pr_err("mtd_read error while parsing (offset: 0x%X): %d\n", offset, err); continue; } /* Magic or small NVRAM at 0x400 */ if ((buf[0x4e0 / 4] == CFE_MAGIC && buf[0x4e4 / 4] == CFE_MAGIC) || (buf[0x400 / 4] == NVRAM_HEADER)) { bcm47xxpart_add_part(&parts[curr_part++], "boot", offset, MTD_WRITEABLE); continue; } /* * board_data starts with board_id which differs across boards, * but we can use 'MPFR' (hopefully) magic at 0x100 */ if (buf[0x100 / 4] == BOARD_DATA_MAGIC) { bcm47xxpart_add_part(&parts[curr_part++], "board_data", offset, MTD_WRITEABLE); continue; } /* Found on Huawei E970 */ if (buf[0x000 / 4] == FACTORY_MAGIC) { bcm47xxpart_add_part(&parts[curr_part++], "factory", offset, MTD_WRITEABLE); continue; } /* POT(TOP) */ if (buf[0x000 / 4] == POT_MAGIC1 && (buf[0x004 / 4] & 0xFFFF) == POT_MAGIC2) { bcm47xxpart_add_part(&parts[curr_part++], "POT", offset, MTD_WRITEABLE); continue; } /* ML */ if (buf[0x010 / 4] == ML_MAGIC1 && buf[0x014 / 4] == ML_MAGIC2) { bcm47xxpart_add_part(&parts[curr_part++], "ML", offset, MTD_WRITEABLE); continue; } /* TRX */ if (buf[0x000 / 4] == TRX_MAGIC) { struct trx_header *trx; uint32_t last_subpart; uint32_t trx_size; if (trx_num >= ARRAY_SIZE(trx_parts)) pr_warn("No enough space to store another TRX found at 0x%X\n", offset); else trx_parts[trx_num++] = curr_part; bcm47xxpart_add_part(&parts[curr_part++], "firmware", offset, 0); /* * Try to find TRX size. The "length" field isn't fully * reliable as it could be decreased to make CRC32 cover * only part of TRX data. It's commonly used as checksum * can't cover e.g. ever-changing rootfs partition. * Use offsets as helpers for assuming min TRX size. */ trx = (struct trx_header *)buf; last_subpart = max3(trx->offset[0], trx->offset[1], trx->offset[2]); trx_size = max(trx->length, last_subpart + blocksize); /* * Skip the TRX data. Decrease offset by block size as * the next loop iteration will increase it. */ offset += roundup(trx_size, blocksize) - blocksize; continue; } /* Squashfs on devices not using TRX */ if (le32_to_cpu(buf[0x000 / 4]) == SQUASHFS_MAGIC || buf[0x000 / 4] == SHSQ_MAGIC) { bcm47xxpart_add_part(&parts[curr_part++], "rootfs", offset, 0); continue; } /* * New (ARM?) devices may have NVRAM in some middle block. Last * block will be checked later, so skip it. */ if (offset != master->size - blocksize && buf[0x000 / 4] == NVRAM_HEADER) { bcm47xxpart_add_part(&parts[curr_part++], "nvram", offset, 0); continue; } /* Read middle of the block */ err = mtd_read(master, offset + 0x8000, 0x4, &bytes_read, (uint8_t *)buf); if (err && !mtd_is_bitflip(err)) { pr_err("mtd_read error while parsing (offset: 0x%X): %d\n", offset, err); continue; } /* Some devices (ex. WNDR3700v3) don't have a standard 'MPFR' */ if (buf[0x000 / 4] == BOARD_DATA_MAGIC2) { bcm47xxpart_add_part(&parts[curr_part++], "board_data", offset, MTD_WRITEABLE); continue; } } /* Look for NVRAM at the end of the last block. */ for (i = 0; i < ARRAY_SIZE(possible_nvram_sizes); i++) { if (curr_part >= BCM47XXPART_MAX_PARTS) { pr_warn("Reached maximum number of partitions, scanning stopped!\n"); break; } offset = master->size - possible_nvram_sizes[i]; err = mtd_read(master, offset, 0x4, &bytes_read, (uint8_t *)buf); if (err && !mtd_is_bitflip(err)) { pr_err("mtd_read error while reading (offset 0x%X): %d\n", offset, err); continue; } /* Standard NVRAM */ if (buf[0] == NVRAM_HEADER) { bcm47xxpart_add_part(&parts[curr_part++], "nvram", master->size - blocksize, 0); break; } } kfree(buf); /* * Assume that partitions end at the beginning of the one they are * followed by. */ for (i = 0; i < curr_part; i++) { u64 next_part_offset = (i < curr_part - 1) ? parts[i + 1].offset : master->size; parts[i].size = next_part_offset - parts[i].offset; } /* If there was TRX parse it now */ for (i = 0; i < trx_num; i++) { struct mtd_partition *trx = &parts[trx_parts[i]]; if (i == bcm47xxpart_bootpartition()) trx->types = trx_types; else trx->name = "failsafe"; } *pparts = parts; return curr_part; };
static int bcm47xxpart_parse(struct mtd_info *master, const struct mtd_partition **pparts, struct mtd_part_parser_data *data) { struct mtd_partition *parts; uint8_t i, curr_part = 0; uint32_t *buf; size_t bytes_read; uint32_t offset; uint32_t blocksize = master->erasesize; struct trx_header *trx; int trx_part = -1; int last_trx_part = -1; int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, }; int err; /* * Some really old flashes (like AT45DB*) had smaller erasesize-s, but * partitions were aligned to at least 0x1000 anyway. */ if (blocksize < 0x1000) blocksize = 0x1000; /* Alloc */ parts = kzalloc(sizeof(struct mtd_partition) * BCM47XXPART_MAX_PARTS, GFP_KERNEL); if (!parts) return -ENOMEM; buf = kzalloc(BCM47XXPART_BYTES_TO_READ, GFP_KERNEL); if (!buf) { kfree(parts); return -ENOMEM; } /* Parse block by block looking for magics */ for (offset = 0; offset <= master->size - blocksize; offset += blocksize) { /* Nothing more in higher memory on BCM47XX (MIPS) */ if (IS_ENABLED(CONFIG_BCM47XX) && offset >= 0x2000000) break; if (curr_part >= BCM47XXPART_MAX_PARTS) { pr_warn("Reached maximum number of partitions, scanning stopped!\n"); break; } /* Read beginning of the block */ err = mtd_read(master, offset, BCM47XXPART_BYTES_TO_READ, &bytes_read, (uint8_t *)buf); if (err && !mtd_is_bitflip(err)) { pr_err("mtd_read error while parsing (offset: 0x%X): %d\n", offset, err); continue; } /* Magic or small NVRAM at 0x400 */ if ((buf[0x4e0 / 4] == CFE_MAGIC && buf[0x4e4 / 4] == CFE_MAGIC) || (buf[0x400 / 4] == NVRAM_HEADER)) { bcm47xxpart_add_part(&parts[curr_part++], "boot", offset, MTD_WRITEABLE); continue; } /* * board_data starts with board_id which differs across boards, * but we can use 'MPFR' (hopefully) magic at 0x100 */ if (buf[0x100 / 4] == BOARD_DATA_MAGIC) { bcm47xxpart_add_part(&parts[curr_part++], "board_data", offset, MTD_WRITEABLE); continue; } /* Found on Huawei E970 */ if (buf[0x000 / 4] == FACTORY_MAGIC) { bcm47xxpart_add_part(&parts[curr_part++], "factory", offset, MTD_WRITEABLE); continue; } /* POT(TOP) */ if (buf[0x000 / 4] == POT_MAGIC1 && (buf[0x004 / 4] & 0xFFFF) == POT_MAGIC2) { bcm47xxpart_add_part(&parts[curr_part++], "POT", offset, MTD_WRITEABLE); continue; } /* ML */ if (buf[0x010 / 4] == ML_MAGIC1 && buf[0x014 / 4] == ML_MAGIC2) { bcm47xxpart_add_part(&parts[curr_part++], "ML", offset, MTD_WRITEABLE); continue; } /* TRX */ if (buf[0x000 / 4] == TRX_MAGIC) { if (BCM47XXPART_MAX_PARTS - curr_part < 4) { pr_warn("Not enough partitions left to register trx, scanning stopped!\n"); break; } trx = (struct trx_header *)buf; trx_part = curr_part; bcm47xxpart_add_part(&parts[curr_part++], "firmware", offset, 0); i = 0; /* We have LZMA loader if offset[2] points to sth */ if (trx->offset[2]) { bcm47xxpart_add_part(&parts[curr_part++], "loader", offset + trx->offset[i], 0); i++; } if (trx->offset[i]) { bcm47xxpart_add_part(&parts[curr_part++], "linux", offset + trx->offset[i], 0); i++; } /* * Pure rootfs size is known and can be calculated as: * trx->length - trx->offset[i]. We don't fill it as * we want to have jffs2 (overlay) in the same mtd. */ if (trx->offset[i]) { const char *name; name = bcm47xxpart_trx_data_part_name(master, offset + trx->offset[i]); bcm47xxpart_add_part(&parts[curr_part++], name, offset + trx->offset[i], 0); i++; } last_trx_part = curr_part - 1; /* Jump to the end of TRX */ offset = roundup(offset + trx->length, blocksize); /* Next loop iteration will increase the offset */ offset -= blocksize; continue; } /* Squashfs on devices not using TRX */ if (le32_to_cpu(buf[0x000 / 4]) == SQUASHFS_MAGIC || buf[0x000 / 4] == SHSQ_MAGIC) { bcm47xxpart_add_part(&parts[curr_part++], "rootfs", offset, 0); continue; } /* * New (ARM?) devices may have NVRAM in some middle block. Last * block will be checked later, so skip it. */ if (offset != master->size - blocksize && buf[0x000 / 4] == NVRAM_HEADER) { bcm47xxpart_add_part(&parts[curr_part++], "nvram", offset, 0); continue; } /* Read middle of the block */ err = mtd_read(master, offset + 0x8000, 0x4, &bytes_read, (uint8_t *)buf); if (err && !mtd_is_bitflip(err)) { pr_err("mtd_read error while parsing (offset: 0x%X): %d\n", offset, err); continue; } /* Some devices (ex. WNDR3700v3) don't have a standard 'MPFR' */ if (buf[0x000 / 4] == BOARD_DATA_MAGIC2) { bcm47xxpart_add_part(&parts[curr_part++], "board_data", offset, MTD_WRITEABLE); continue; } } /* Look for NVRAM at the end of the last block. */ for (i = 0; i < ARRAY_SIZE(possible_nvram_sizes); i++) { if (curr_part >= BCM47XXPART_MAX_PARTS) { pr_warn("Reached maximum number of partitions, scanning stopped!\n"); break; } offset = master->size - possible_nvram_sizes[i]; err = mtd_read(master, offset, 0x4, &bytes_read, (uint8_t *)buf); if (err && !mtd_is_bitflip(err)) { pr_err("mtd_read error while reading (offset 0x%X): %d\n", offset, err); continue; } /* Standard NVRAM */ if (buf[0] == NVRAM_HEADER) { bcm47xxpart_add_part(&parts[curr_part++], "nvram", master->size - blocksize, 0); break; } } kfree(buf); /* * Assume that partitions end at the beginning of the one they are * followed by. */ for (i = 0; i < curr_part; i++) { u64 next_part_offset = (i < curr_part - 1) ? parts[i + 1].offset : master->size; parts[i].size = next_part_offset - parts[i].offset; if (i == last_trx_part && trx_part >= 0) parts[trx_part].size = next_part_offset - parts[trx_part].offset; } *pparts = parts; return curr_part; };
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; struct mtd_info *mtd = inftl->mbd.mtd; int block, silly; unsigned int targetEUN; struct inftl_oob oob; size_t retlen; pr_debug("INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d,pending=%d)\n", 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; } silly = MAX_LOOPS; while (thisEUN < inftl->nb_blocks) { for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) { if ((BlockMap[block] != BLOCK_NIL) || BlockDeleted[block]) continue; if (inftl_read_oob(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]; } pr_debug("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 (BlockMap[block] == targetEUN || (pendingblock == (thisVUC * (inftl->EraseSize / SECTORSIZE) + block))) { continue; } if (BlockMap[block] == BLOCK_NIL) continue; ret = mtd_read(mtd, (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE, &retlen, movebuf); if (ret < 0 && !mtd_is_bitflip(ret)) { ret = mtd_read(mtd, (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE, &retlen, movebuf); if (ret != -EIO) pr_debug("INFTL: error went away on retry?\n"); } memset(&oob, 0xff, sizeof(struct inftl_oob)); oob.b.Status = oob.b.Status1 = SECTOR_USED; inftl_write(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) + (block * SECTORSIZE), SECTORSIZE, &retlen, movebuf, (char *)&oob); } pr_debug("INFTL: want to erase virtual chain %d\n", thisVUC); for (;;) { thisEUN = inftl->VUtable[thisVUC]; prevEUN = BLOCK_NIL; while (inftl->PUtable[thisEUN] != BLOCK_NIL) { prevEUN = thisEUN; thisEUN = inftl->PUtable[thisEUN]; } if (thisEUN == targetEUN) break; inftl->PUtable[prevEUN] = BLOCK_NIL; if (INFTL_formatblock(inftl, thisEUN) < 0) { inftl->PUtable[thisEUN] = BLOCK_RESERVED; } else { inftl->PUtable[thisEUN] = BLOCK_FREE; inftl->numfreeEUNs++; } } return targetEUN; }
static int crosstest(void) { size_t read; int err = 0, i; loff_t addr, addr0, addrn; unsigned char *pp1, *pp2, *pp3, *pp4; printk(PRINT_PREF "crosstest\n"); pp1 = kmalloc(pgsize * 4, GFP_KERNEL); if (!pp1) { printk(PRINT_PREF "error: cannot allocate memory\n"); return -ENOMEM; } pp2 = pp1 + pgsize; pp3 = pp2 + pgsize; pp4 = pp3 + pgsize; memset(pp1, 0, pgsize * 4); addr0 = 0; for (i = 0; i < ebcnt && bbt[i]; ++i) addr0 += mtd->erasesize; addrn = mtd->size; for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i) addrn -= mtd->erasesize; /* Read 2nd-to-last page to pp1 */ addr = addrn - pgsize - pgsize; err = mtd_read(mtd, addr, pgsize, &read, pp1); if (mtd_is_bitflip(err)) err = 0; if (err || read != pgsize) { printk(PRINT_PREF "error: read failed at %#llx\n", (long long)addr); kfree(pp1); return err; } /* Read 3rd-to-last page to pp1 */ addr = addrn - pgsize - pgsize - pgsize; err = mtd_read(mtd, addr, pgsize, &read, pp1); if (mtd_is_bitflip(err)) err = 0; if (err || read != pgsize) { printk(PRINT_PREF "error: read failed at %#llx\n", (long long)addr); kfree(pp1); return err; } /* Read first page to pp2 */ addr = addr0; printk(PRINT_PREF "reading page at %#llx\n", (long long)addr); err = mtd_read(mtd, addr, pgsize, &read, pp2); if (mtd_is_bitflip(err)) err = 0; if (err || read != pgsize) { printk(PRINT_PREF "error: read failed at %#llx\n", (long long)addr); kfree(pp1); return err; } /* Read last page to pp3 */ addr = addrn - pgsize; printk(PRINT_PREF "reading page at %#llx\n", (long long)addr); err = mtd_read(mtd, addr, pgsize, &read, pp3); if (mtd_is_bitflip(err)) err = 0; if (err || read != pgsize) { printk(PRINT_PREF "error: read failed at %#llx\n", (long long)addr); kfree(pp1); return err; } /* Read first page again to pp4 */ addr = addr0; printk(PRINT_PREF "reading page at %#llx\n", (long long)addr); err = mtd_read(mtd, addr, pgsize, &read, pp4); if (mtd_is_bitflip(err)) err = 0; if (err || read != pgsize) { printk(PRINT_PREF "error: read failed at %#llx\n", (long long)addr); kfree(pp1); return err; } /* pp2 and pp4 should be the same */ printk(PRINT_PREF "verifying pages read at %#llx match\n", (long long)addr0); if (memcmp(pp2, pp4, pgsize)) { printk(PRINT_PREF "verify failed!\n"); errcnt += 1; } else if (!err) printk(PRINT_PREF "crosstest ok\n"); kfree(pp1); return err; }
static int erasecrosstest(void) { size_t read, written; int err = 0, i, ebnum, ebnum2; loff_t addr0; char *readbuf = twopages; printk(PRINT_PREF "erasecrosstest\n"); ebnum = 0; addr0 = 0; for (i = 0; i < ebcnt && bbt[i]; ++i) { addr0 += mtd->erasesize; ebnum += 1; } ebnum2 = ebcnt - 1; while (ebnum2 && bbt[ebnum2]) ebnum2 -= 1; printk(PRINT_PREF "erasing block %d\n", ebnum); err = erase_eraseblock(ebnum); if (err) return err; printk(PRINT_PREF "writing 1st page of block %d\n", ebnum); set_random_data(writebuf, pgsize); strcpy(writebuf, "There is no data like this!"); err = mtd_write(mtd, addr0, pgsize, &written, writebuf); if (err || written != pgsize) { printk(PRINT_PREF "error: write failed at %#llx\n", (long long)addr0); return err ? err : -1; } printk(PRINT_PREF "reading 1st page of block %d\n", ebnum); memset(readbuf, 0, pgsize); err = mtd_read(mtd, addr0, pgsize, &read, readbuf); if (mtd_is_bitflip(err)) err = 0; if (err || read != pgsize) { printk(PRINT_PREF "error: read failed at %#llx\n", (long long)addr0); return err ? err : -1; } printk(PRINT_PREF "verifying 1st page of block %d\n", ebnum); if (memcmp(writebuf, readbuf, pgsize)) { printk(PRINT_PREF "verify failed!\n"); errcnt += 1; return -1; } printk(PRINT_PREF "erasing block %d\n", ebnum); err = erase_eraseblock(ebnum); if (err) return err; printk(PRINT_PREF "writing 1st page of block %d\n", ebnum); set_random_data(writebuf, pgsize); strcpy(writebuf, "There is no data like this!"); err = mtd_write(mtd, addr0, pgsize, &written, writebuf); if (err || written != pgsize) { printk(PRINT_PREF "error: write failed at %#llx\n", (long long)addr0); return err ? err : -1; } printk(PRINT_PREF "erasing block %d\n", ebnum2); err = erase_eraseblock(ebnum2); if (err) return err; printk(PRINT_PREF "reading 1st page of block %d\n", ebnum); memset(readbuf, 0, pgsize); err = mtd_read(mtd, addr0, pgsize, &read, readbuf); if (mtd_is_bitflip(err)) err = 0; if (err || read != pgsize) { printk(PRINT_PREF "error: read failed at %#llx\n", (long long)addr0); return err ? err : -1; } printk(PRINT_PREF "verifying 1st page of block %d\n", ebnum); if (memcmp(writebuf, readbuf, pgsize)) { printk(PRINT_PREF "verify failed!\n"); errcnt += 1; return -1; } if (!err) printk(PRINT_PREF "erasecrosstest ok\n"); return err; }