From 0b23b9159d3c10db1f91f6cded26ebc63870a058 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 17 Dec 2012 16:48:30 +0100 Subject: [PATCH] lib: add libscan Imported from mtd-utils and stripped down to needed functionality. Add prefix to functions so we have a clean namespace. Signed-off-by: Wolfram Sang Signed-off-by: Sascha Hauer --- include/mtd/libscan.h | 101 +++++++++++++++++++ lib/Kconfig | 3 + lib/Makefile | 1 + lib/libscan.c | 219 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 324 insertions(+) create mode 100644 include/mtd/libscan.h create mode 100644 lib/libscan.c diff --git a/include/mtd/libscan.h b/include/mtd/libscan.h new file mode 100644 index 000000000..bb0148203 --- /dev/null +++ b/include/mtd/libscan.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * Author: Artem Bityutskiy + * + * UBI scanning library. + */ + +#ifndef __LIBSCAN_H__ +#define __LIBSCAN_H__ + +#include + +/* + * If an eraseblock does not contain an erase counter, this value is used + * instead of the erase counter. + */ +#define NO_EC 0xFFFFFFFF + +/* + * If an eraseblock contains a corrupted erase counter, this value is used + * instead of the erase counter. + */ +#define CORRUPT_EC 0xFFFFFFFE + +/* + * If an eraseblock does not contain an erase counter, one of these values is + * used. + * + * @EB_EMPTY: the eraseblock appeared to be empty + * @EB_CORRUPTED: the eraseblock contains corrupted erase counter header + * @EB_ALIEN: the eraseblock contains some non-UBI data + * @EC_MAX: maximum allowed erase counter value + */ +enum +{ + EB_EMPTY = 0xFFFFFFFF, + EB_CORRUPTED = 0xFFFFFFFE, + EB_ALIEN = 0xFFFFFFFD, + EB_BAD = 0xFFFFFFFC, + EC_MAX = UBI_MAX_ERASECOUNTER, +}; + +/** + * struct ubi_scan_info - UBI scanning information. + * @ec: erase counters or eraseblock status for all eraseblocks + * @mean_ec: mean erase counter + * @ok_cnt: count of eraseblock with correct erase counter header + * @empty_cnt: count of supposedly eraseblocks + * @corrupted_cnt: count of eraseblocks with corrupted erase counter header + * @alien_cnt: count of eraseblock containing non-ubi data + * @bad_cnt: count of bad eraseblocks + * @bad_cnt: count of non-bad eraseblocks + * @vid_hdr_offs: volume ID header offset from the found EC headers (%-1 means + * undefined) + * @data_offs: data offset from the found EC headers (%-1 means undefined) + */ +struct ubi_scan_info +{ + uint32_t *ec; + long long mean_ec; + int ok_cnt; + int empty_cnt; + int corrupted_cnt; + int alien_cnt; + int bad_cnt; + int good_cnt; + int vid_hdr_offs; + int data_offs; +}; + +struct mtd_dev_info; + +/** + * ubi_scan - scan an MTD device. + * @mtd: information about the MTD device to scan + * @fd: MTD device node file descriptor + * @info: the result of the scanning is returned here + * @verbose: verbose mode: %0 - be silent, %1 - output progress information, + * 2 - debugging output mode + */ +int libscan_ubi_scan(struct mtd_dev_info *mtd, int fd, struct ubi_scan_info **info, + int verbose); + +/** + * ubi_scan_free - free scanning information. + * @si: scanning information to free + */ +void libscan_ubi_scan_free(struct ubi_scan_info *si); + +#endif /* __LIBSCAN_H__ */ diff --git a/lib/Kconfig b/lib/Kconfig index 596638694..32e80a03d 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -41,6 +41,9 @@ config QSORT config XYMODEM bool +config LIBSCAN + bool + source lib/gui/Kconfig endmenu diff --git a/lib/Makefile b/lib/Makefile index 635d52e65..ed4197481 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_UNCOMPRESS) += uncompress.o obj-$(CONFIG_BCH) += bch.o obj-$(CONFIG_BITREV) += bitrev.o obj-$(CONFIG_QSORT) += qsort.o +obj-$(CONFIG_LIBSCAN) += libscan.o obj-y += gui/ obj-$(CONFIG_XYMODEM) += xymodem.o obj-y += unlink-recursive.o diff --git a/lib/libscan.c b/lib/libscan.c new file mode 100644 index 000000000..af55269ec --- /dev/null +++ b/lib/libscan.c @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * Author: Artem Bityutskiy + * + * UBI scanning library. + */ + +#define PROGRAM_NAME "libscan" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int all_ff(const void *buf, int len) +{ + int i; + const uint8_t *p = buf; + + for (i = 0; i < len; i++) + if (p[i] != 0xFF) + return 0; + return 1; +} + +int libscan_ubi_scan(struct mtd_dev_info *mtd, int fd, struct ubi_scan_info **info, + int verbose) +{ + int eb, v = (verbose == 2), pr = (verbose == 1); + struct ubi_scan_info *si; + unsigned long long sum = 0; + + si = calloc(1, sizeof(struct ubi_scan_info)); + if (!si) + return sys_errmsg("cannot allocate %zd bytes of memory", + sizeof(struct ubi_scan_info)); + + si->ec = calloc(mtd->eb_cnt, sizeof(uint32_t)); + if (!si->ec) { + sys_errmsg("cannot allocate %zd bytes of memory", + sizeof(struct ubi_scan_info)); + goto out_si; + } + + si->vid_hdr_offs = si->data_offs = -1; + + verbose(v, "start scanning eraseblocks 0-%d", mtd->eb_cnt); + for (eb = 0; eb < mtd->eb_cnt; eb++) { + int ret; + uint32_t crc; + struct ubi_ec_hdr ech; + unsigned long long ec; + + if (v) + normsg_cont("scanning eraseblock %d", eb); + if (pr) { + printf("\r" PROGRAM_NAME ": scanning eraseblock %d -- %2u %% complete ", + eb, (eb + 1) * 100 / mtd->eb_cnt); + } + + ret = mtd_is_bad(mtd, fd, eb); + if (ret == -1) + goto out_ec; + if (ret) { + si->bad_cnt += 1; + si->ec[eb] = EB_BAD; + if (v) + printf(": bad\n"); + continue; + } + + ret = mtd_read(mtd, fd, eb, 0, &ech, sizeof(struct ubi_ec_hdr)); + if (ret < 0) + goto out_ec; + + if (be32_to_cpu(ech.magic) != UBI_EC_HDR_MAGIC) { + if (all_ff(&ech, sizeof(struct ubi_ec_hdr))) { + si->empty_cnt += 1; + si->ec[eb] = EB_EMPTY; + if (v) + printf(": empty\n"); + } else { + si->alien_cnt += 1; + si->ec[eb] = EB_ALIEN; + if (v) + printf(": alien\n"); + } + continue; + } + + crc = crc32_no_comp(UBI_CRC32_INIT, &ech, UBI_EC_HDR_SIZE_CRC); + if (be32_to_cpu(ech.hdr_crc) != crc) { + si->corrupted_cnt += 1; + si->ec[eb] = EB_CORRUPTED; + if (v) + printf(": bad CRC %#08x, should be %#08x\n", + crc, be32_to_cpu(ech.hdr_crc)); + continue; + } + + ec = be64_to_cpu(ech.ec); + if (ec > EC_MAX) { + if (pr) + printf("\n"); + errmsg("erase counter in EB %d is %llu, while this " + "program expects them to be less than %u", + eb, ec, EC_MAX); + goto out_ec; + } + + if (si->vid_hdr_offs == -1) { + si->vid_hdr_offs = be32_to_cpu(ech.vid_hdr_offset); + si->data_offs = be32_to_cpu(ech.data_offset); + if (si->data_offs % mtd->min_io_size) { + if (pr) + printf("\n"); + if (v) + printf(": corrupted because of the below\n"); + warnmsg("bad data offset %d at eraseblock %d (n" + "of multiple of min. I/O unit size %d)", + si->data_offs, eb, mtd->min_io_size); + warnmsg("treat eraseblock %d as corrupted", eb); + si->corrupted_cnt += 1; + si->ec[eb] = EB_CORRUPTED; + continue; + + } + } else { + if ((int)be32_to_cpu(ech.vid_hdr_offset) != si->vid_hdr_offs) { + if (pr) + printf("\n"); + if (v) + printf(": corrupted because of the below\n"); + warnmsg("inconsistent VID header offset: was " + "%d, but is %d in eraseblock %d", + si->vid_hdr_offs, + be32_to_cpu(ech.vid_hdr_offset), eb); + warnmsg("treat eraseblock %d as corrupted", eb); + si->corrupted_cnt += 1; + si->ec[eb] = EB_CORRUPTED; + continue; + } + if ((int)be32_to_cpu(ech.data_offset) != si->data_offs) { + if (pr) + printf("\n"); + if (v) + printf(": corrupted because of the below\n"); + warnmsg("inconsistent data offset: was %d, but" + " is %d in eraseblock %d", + si->data_offs, + be32_to_cpu(ech.data_offset), eb); + warnmsg("treat eraseblock %d as corrupted", eb); + si->corrupted_cnt += 1; + si->ec[eb] = EB_CORRUPTED; + continue; + } + } + + si->ok_cnt += 1; + si->ec[eb] = ec; + if (v) + printf(": OK, erase counter %u\n", si->ec[eb]); + } + + if (si->ok_cnt != 0) { + /* Calculate mean erase counter */ + for (eb = 0; eb < mtd->eb_cnt; eb++) { + if (si->ec[eb] > EC_MAX) + continue; + sum += si->ec[eb]; + } + do_div(sum, si->ok_cnt); + si->mean_ec = sum; + } + + si->good_cnt = mtd->eb_cnt - si->bad_cnt; + verbose(v, "finished, mean EC %lld, %d OK, %d corrupted, %d empty, %d " + "alien, bad %d", si->mean_ec, si->ok_cnt, si->corrupted_cnt, + si->empty_cnt, si->alien_cnt, si->bad_cnt); + + *info = si; + if (pr) + printf("\n"); + return 0; + +out_ec: + free(si->ec); +out_si: + free(si); + *info = NULL; + return -1; +} + +void libscan_ubi_scan_free(struct ubi_scan_info *si) +{ + free(si->ec); + free(si); +}