📄 scan.c
字号:
/* * Copyright (c) International Business Machines Corp., 2006 * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Artem Bityutskiy (Битюцкий Артём) *//* * UBI scanning sub-system. * * This sub-system is responsible for scanning the flash media, checking UBI * headers and providing complete information about the UBI flash image. * * The scanning information is represented by a &struct ubi_scan_info' object. * Information about found volumes is represented by &struct ubi_scan_volume * objects which are kept in volume RB-tree with root at the @volumes field. * The RB-tree is indexed by the volume ID. * * Found logical eraseblocks are represented by &struct ubi_scan_leb objects. * These objects are kept in per-volume RB-trees with the root at the * corresponding &struct ubi_scan_volume object. To put it differently, we keep * an RB-tree of per-volume objects and each of these objects is the root of * RB-tree of per-eraseblock objects. * * Corrupted physical eraseblocks are put to the @corr list, free physical * eraseblocks are put to the @free list and the physical eraseblock to be * erased are put to the @erase list. */#include <linux/err.h>#include <linux/crc32.h>#include <asm/div64.h>#include "ubi.h"#ifdef CONFIG_MTD_UBI_DEBUG_PARANOIDstatic int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si);#else#define paranoid_check_si(ubi, si) 0#endif/* Temporary variables used during scanning */static struct ubi_ec_hdr *ech;static struct ubi_vid_hdr *vidh;/** * add_to_list - add physical eraseblock to a list. * @si: scanning information * @pnum: physical eraseblock number to add * @ec: erase counter of the physical eraseblock * @list: the list to add to * * This function adds physical eraseblock @pnum to free, erase, corrupted or * alien lists. Returns zero in case of success and a negative error code in * case of failure. */static int add_to_list(struct ubi_scan_info *si, int pnum, int ec, struct list_head *list){ struct ubi_scan_leb *seb; if (list == &si->free) dbg_bld("add to free: PEB %d, EC %d", pnum, ec); else if (list == &si->erase) dbg_bld("add to erase: PEB %d, EC %d", pnum, ec); else if (list == &si->corr) dbg_bld("add to corrupted: PEB %d, EC %d", pnum, ec); else if (list == &si->alien) dbg_bld("add to alien: PEB %d, EC %d", pnum, ec); else BUG(); seb = kmalloc(sizeof(struct ubi_scan_leb), GFP_KERNEL); if (!seb) return -ENOMEM; seb->pnum = pnum; seb->ec = ec; list_add_tail(&seb->u.list, list); return 0;}/** * validate_vid_hdr - check volume identifier header. * @vid_hdr: the volume identifier header to check * @sv: information about the volume this logical eraseblock belongs to * @pnum: physical eraseblock number the VID header came from * * This function checks that data stored in @vid_hdr is consistent. Returns * non-zero if an inconsistency was found and zero if not. * * Note, UBI does sanity check of everything it reads from the flash media. * Most of the checks are done in the I/O sub-system. Here we check that the * information in the VID header is consistent to the information in other VID * headers of the same volume. */static int validate_vid_hdr(const struct ubi_vid_hdr *vid_hdr, const struct ubi_scan_volume *sv, int pnum){ int vol_type = vid_hdr->vol_type; int vol_id = be32_to_cpu(vid_hdr->vol_id); int used_ebs = be32_to_cpu(vid_hdr->used_ebs); int data_pad = be32_to_cpu(vid_hdr->data_pad); if (sv->leb_count != 0) { int sv_vol_type; /* * This is not the first logical eraseblock belonging to this * volume. Ensure that the data in its VID header is consistent * to the data in previous logical eraseblock headers. */ if (vol_id != sv->vol_id) { dbg_err("inconsistent vol_id"); goto bad; } if (sv->vol_type == UBI_STATIC_VOLUME) sv_vol_type = UBI_VID_STATIC; else sv_vol_type = UBI_VID_DYNAMIC; if (vol_type != sv_vol_type) { dbg_err("inconsistent vol_type"); goto bad; } if (used_ebs != sv->used_ebs) { dbg_err("inconsistent used_ebs"); goto bad; } if (data_pad != sv->data_pad) { dbg_err("inconsistent data_pad"); goto bad; } } return 0;bad: ubi_err("inconsistent VID header at PEB %d", pnum); ubi_dbg_dump_vid_hdr(vid_hdr); ubi_dbg_dump_sv(sv); return -EINVAL;}/** * add_volume - add volume to the scanning information. * @si: scanning information * @vol_id: ID of the volume to add * @pnum: physical eraseblock number * @vid_hdr: volume identifier header * * If the volume corresponding to the @vid_hdr logical eraseblock is already * present in the scanning information, this function does nothing. Otherwise * it adds corresponding volume to the scanning information. Returns a pointer * to the scanning volume object in case of success and a negative error code * in case of failure. */static struct ubi_scan_volume *add_volume(struct ubi_scan_info *si, int vol_id, int pnum, const struct ubi_vid_hdr *vid_hdr){ struct ubi_scan_volume *sv; struct rb_node **p = &si->volumes.rb_node, *parent = NULL; ubi_assert(vol_id == be32_to_cpu(vid_hdr->vol_id)); /* Walk the volume RB-tree to look if this volume is already present */ while (*p) { parent = *p; sv = rb_entry(parent, struct ubi_scan_volume, rb); if (vol_id == sv->vol_id) return sv; if (vol_id > sv->vol_id) p = &(*p)->rb_left; else p = &(*p)->rb_right; } /* The volume is absent - add it */ sv = kmalloc(sizeof(struct ubi_scan_volume), GFP_KERNEL); if (!sv) return ERR_PTR(-ENOMEM); sv->highest_lnum = sv->leb_count = 0; sv->vol_id = vol_id; sv->root = RB_ROOT; sv->used_ebs = be32_to_cpu(vid_hdr->used_ebs); sv->data_pad = be32_to_cpu(vid_hdr->data_pad); sv->compat = vid_hdr->compat; sv->vol_type = vid_hdr->vol_type == UBI_VID_DYNAMIC ? UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME; if (vol_id > si->highest_vol_id) si->highest_vol_id = vol_id; rb_link_node(&sv->rb, parent, p); rb_insert_color(&sv->rb, &si->volumes); si->vols_found += 1; dbg_bld("added volume %d", vol_id); return sv;}/** * compare_lebs - find out which logical eraseblock is newer. * @ubi: UBI device description object * @seb: first logical eraseblock to compare * @pnum: physical eraseblock number of the second logical eraseblock to * compare * @vid_hdr: volume identifier header of the second logical eraseblock * * This function compares 2 copies of a LEB and informs which one is newer. In * case of success this function returns a positive value, in case of failure, a * negative error code is returned. The success return codes use the following * bits: * o bit 0 is cleared: the first PEB (described by @seb) is newer then the * second PEB (described by @pnum and @vid_hdr); * o bit 0 is set: the second PEB is newer; * o bit 1 is cleared: no bit-flips were detected in the newer LEB; * o bit 1 is set: bit-flips were detected in the newer LEB; * o bit 2 is cleared: the older LEB is not corrupted; * o bit 2 is set: the older LEB is corrupted. */static int compare_lebs(struct ubi_device *ubi, const struct ubi_scan_leb *seb, int pnum, const struct ubi_vid_hdr *vid_hdr){ void *buf; int len, err, second_is_newer, bitflips = 0, corrupted = 0; uint32_t data_crc, crc; struct ubi_vid_hdr *vh = NULL; unsigned long long sqnum2 = be64_to_cpu(vid_hdr->sqnum); if (sqnum2 == seb->sqnum) { /* * This must be a really ancient UBI image which has been * created before sequence numbers support has been added. At * that times we used 32-bit LEB versions stored in logical * eraseblocks. That was before UBI got into mainline. We do not * support these images anymore. Well, those images will work * still work, but only if no unclean reboots happened. */ ubi_err("unsupported on-flash UBI format\n"); return -EINVAL; } /* Obviously the LEB with lower sequence counter is older */ second_is_newer = !!(sqnum2 > seb->sqnum); /* * Now we know which copy is newer. If the copy flag of the PEB with * newer version is not set, then we just return, otherwise we have to * check data CRC. For the second PEB we already have the VID header, * for the first one - we'll need to re-read it from flash. * * Note: this may be optimized so that we wouldn't read twice. */ if (second_is_newer) { if (!vid_hdr->copy_flag) { /* It is not a copy, so it is newer */ dbg_bld("second PEB %d is newer, copy_flag is unset", pnum); return 1; } } else { pnum = seb->pnum; vh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL); if (!vh) return -ENOMEM; err = ubi_io_read_vid_hdr(ubi, pnum, vh, 0); if (err) { if (err == UBI_IO_BITFLIPS) bitflips = 1; else { dbg_err("VID of PEB %d header is bad, but it " "was OK earlier", pnum); if (err > 0) err = -EIO; goto out_free_vidh; } } if (!vh->copy_flag) { /* It is not a copy, so it is newer */ dbg_bld("first PEB %d is newer, copy_flag is unset", pnum); err = bitflips << 1; goto out_free_vidh; } vid_hdr = vh; } /* Read the data of the copy and check the CRC */ len = be32_to_cpu(vid_hdr->data_size); buf = vmalloc(len); if (!buf) { err = -ENOMEM; goto out_free_vidh; } err = ubi_io_read_data(ubi, buf, pnum, 0, len); if (err && err != UBI_IO_BITFLIPS && err != -EBADMSG) goto out_free_buf; data_crc = be32_to_cpu(vid_hdr->data_crc); crc = crc32(UBI_CRC32_INIT, buf, len); if (crc != data_crc) { dbg_bld("PEB %d CRC error: calculated %#08x, must be %#08x", pnum, crc, data_crc); corrupted = 1; bitflips = 0; second_is_newer = !second_is_newer; } else { dbg_bld("PEB %d CRC is OK", pnum); bitflips = !!err; } vfree(buf); ubi_free_vid_hdr(ubi, vh); if (second_is_newer) dbg_bld("second PEB %d is newer, copy_flag is set", pnum); else dbg_bld("first PEB %d is newer, copy_flag is set", pnum); return second_is_newer | (bitflips << 1) | (corrupted << 2);out_free_buf: vfree(buf);out_free_vidh: ubi_free_vid_hdr(ubi, vh); return err;}/** * ubi_scan_add_used - add physical eraseblock to the scanning information. * @ubi: UBI device description object * @si: scanning information * @pnum: the physical eraseblock number * @ec: erase counter * @vid_hdr: the volume identifier header * @bitflips: if bit-flips were detected when this physical eraseblock was read * * This function adds information about a used physical eraseblock to the * 'used' tree of the corresponding volume. The function is rather complex * because it has to handle cases when this is not the first physical * eraseblock belonging to the same logical eraseblock, and the newer one has * to be picked, while the older one has to be dropped. This function returns * zero in case of success and a negative error code in case of failure. */int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum, int ec, const struct ubi_vid_hdr *vid_hdr, int bitflips){ int err, vol_id, lnum; unsigned long long sqnum; struct ubi_scan_volume *sv; struct ubi_scan_leb *seb; struct rb_node **p, *parent = NULL; vol_id = be32_to_cpu(vid_hdr->vol_id); lnum = be32_to_cpu(vid_hdr->lnum); sqnum = be64_to_cpu(vid_hdr->sqnum); dbg_bld("PEB %d, LEB %d:%d, EC %d, sqnum %llu, bitflips %d", pnum, vol_id, lnum, ec, sqnum, bitflips); sv = add_volume(si, vol_id, pnum, vid_hdr); if (IS_ERR(sv)) return PTR_ERR(sv); if (si->max_sqnum < sqnum) si->max_sqnum = sqnum; /* * Walk the RB-tree of logical eraseblocks of volume @vol_id to look * if this is the first instance of this logical eraseblock or not. */ p = &sv->root.rb_node; while (*p) { int cmp_res; parent = *p; seb = rb_entry(parent, struct ubi_scan_leb, u.rb); if (lnum != seb->lnum) { if (lnum < seb->lnum) p = &(*p)->rb_left; else p = &(*p)->rb_right; continue; } /* * There is already a physical eraseblock describing the same * logical eraseblock present. */ dbg_bld("this LEB already exists: PEB %d, sqnum %llu, " "EC %d", seb->pnum, seb->sqnum, seb->ec); /* * Make sure that the logical eraseblocks have different * sequence numbers. Otherwise the image is bad. * * However, if the sequence number is zero, we assume it must * be an ancient UBI image from the era when UBI did not have * sequence numbers. We still can attach these images, unless * there is a need to distinguish between old and new * eraseblocks, in which case we'll refuse the image in * 'compare_lebs()'. In other words, we attach old clean * images, but refuse attaching old images with duplicated * logical eraseblocks because there was an unclean reboot. */ if (seb->sqnum == sqnum && sqnum != 0) { ubi_err("two LEBs with same sequence number %llu", sqnum); ubi_dbg_dump_seb(seb, 0); ubi_dbg_dump_vid_hdr(vid_hdr);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -