]> www.infradead.org Git - mtd-utils.git/commitdiff
ubi-utils: add libscan
authorArtem Bityutskiy <Artem.Bityutskiy@nokia.com>
Sun, 20 Apr 2008 09:34:32 +0000 (12:34 +0300)
committerArtem Bityutskiy <Artem.Bityutskiy@nokia.com>
Sun, 20 Apr 2008 15:15:50 +0000 (18:15 +0300)
A library to scan MTD devices. For now it only reads EC header.
Later it may be improved if needed.

Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
ubi-utils/new-utils/include/libscan.h [new file with mode: 0644]
ubi-utils/new-utils/src/libscan.c [new file with mode: 0644]

diff --git a/ubi-utils/new-utils/include/libscan.h b/ubi-utils/new-utils/include/libscan.h
new file mode 100644 (file)
index 0000000..5afc93e
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem Bityutskiy
+ *
+ * UBI scanning library.
+ */
+
+#ifndef __LIBSCAN_H__
+#define __LIBSCAN_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * 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_info;
+
+/**
+ * ubi_scan - scan an MTD device.
+ * @mtd: information about the MTD device to scan
+ * @info: the result of the scanning is returned here
+ * @verbose: verbose mode: %0 - be silent, %1 - output progress information,
+ *           2 - debugging output mode
+ */
+int ubi_scan(struct mtd_info *mtd, struct ubi_scan_info **info, int verbose);
+
+/**
+ * ubi_scan_free - free scanning information.
+ * @si: scanning information to free
+ */
+void ubi_scan_free(struct ubi_scan_info *si);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LIBSCAN_H__ */
+
diff --git a/ubi-utils/new-utils/src/libscan.c b/ubi-utils/new-utils/src/libscan.c
new file mode 100644 (file)
index 0000000..3427498
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Artem Bityutskiy
+ *
+ * UBI scanning library.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <mtd_swab.h>
+#include <mtd/ubi-header.h>
+#include <mtd/mtd-user.h>
+#include <libmtd.h>
+#include <libscan.h>
+#include "common.h"
+#include "crc32.h"
+
+#define PROGRAM_NAME "libscan"
+
+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 ubi_scan(struct mtd_info *mtd, 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 hdr;
+               unsigned long long ec;
+
+               if (v) {
+                       normsg_cont("scanning eraseblock %d", eb);
+                       fflush(stdout);
+               }
+               if (pr) {
+                       printf("\r" PROGRAM_NAME ": scanning eraseblock %d -- %2lld %% complete  ",
+                              eb, (long long)(eb + 1) * 100 / mtd->eb_cnt);
+                       fflush(stdout);
+               }
+
+               ret = mtd_is_bad(mtd, 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, eb, 0, &hdr, sizeof(struct ubi_ec_hdr));;
+               if (ret < 0)
+                       goto out_ec;
+
+               /* Check the EC header */
+               if (be32_to_cpu(hdr.magic) != UBI_EC_HDR_MAGIC) {
+                       if (all_ff(&hdr, 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(UBI_CRC32_INIT, &hdr, UBI_EC_HDR_SIZE_CRC);
+               if (be32_to_cpu(hdr.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(hdr.hdr_crc));
+                       continue;
+               }
+
+               ec = be64_to_cpu(hdr.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(hdr.vid_hdr_offset);
+                       si->data_offs = be32_to_cpu(hdr.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 (be32_to_cpu(hdr.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(hdr.vid_hdr_offset), eb);
+                               warnmsg("treat eraseblock %d as corrupted", eb);
+                               si->corrupted_cnt += 1;
+                               si->ec[eb] = EB_CORRUPTED;
+                               continue;
+                       }
+                       if (be32_to_cpu(hdr.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(hdr.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];
+               }
+               si->mean_ec = sum / si->ok_cnt;
+       }
+
+       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 ubi_scan_free(struct ubi_scan_info *si)
+{
+       free(si->ec);
+       free(si);
+}