| // SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause |
| /* |
| * Copyright (c) Thomas Gleixner <tglx@linutronix.de> |
| * |
| * The parts taken from the kernel implementation are: |
| * |
| * Copyright (c) International Business Machines Corp., 2006 |
| */ |
| |
| #include <common.h> |
| #include <errno.h> |
| #include <ubispl.h> |
| |
| #include <linux/crc32.h> |
| |
| #include "ubispl.h" |
| |
| /** |
| * ubi_calc_fm_size - calculates the fastmap size in bytes for an UBI device. |
| * @ubi: UBI device description object |
| */ |
| static size_t ubi_calc_fm_size(struct ubi_scan_info *ubi) |
| { |
| size_t size; |
| |
| size = sizeof(struct ubi_fm_sb) + |
| sizeof(struct ubi_fm_hdr) + |
| sizeof(struct ubi_fm_scan_pool) + |
| sizeof(struct ubi_fm_scan_pool) + |
| (ubi->peb_count * sizeof(struct ubi_fm_ec)) + |
| (sizeof(struct ubi_fm_eba) + |
| (ubi->peb_count * sizeof(__be32))) + |
| sizeof(struct ubi_fm_volhdr) * UBI_MAX_VOLUMES; |
| return roundup(size, ubi->leb_size); |
| } |
| |
| static int ubi_io_read(struct ubi_scan_info *ubi, void *buf, int pnum, |
| unsigned long from, unsigned long len) |
| { |
| return ubi->read(pnum + ubi->peb_offset, from, len, buf); |
| } |
| |
| static int ubi_io_is_bad(struct ubi_scan_info *ubi, int peb) |
| { |
| return peb >= ubi->peb_count || peb < 0; |
| } |
| |
| #ifdef CONFIG_SPL_UBI_LOAD_BY_VOLNAME |
| |
| /** |
| * ubi_dump_vtbl_record - dump a &struct ubi_vtbl_record object. |
| * @r: the object to dump |
| * @idx: volume table index |
| */ |
| void ubi_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx) |
| { |
| int name_len = be16_to_cpu(r->name_len); |
| |
| ubi_dbg("Volume table record %d dump: size: %d", |
| idx, sizeof(struct ubi_vtbl_record)); |
| ubi_dbg("\treserved_pebs %d", be32_to_cpu(r->reserved_pebs)); |
| ubi_dbg("\talignment %d", be32_to_cpu(r->alignment)); |
| ubi_dbg("\tdata_pad %d", be32_to_cpu(r->data_pad)); |
| ubi_dbg("\tvol_type %d", (int)r->vol_type); |
| ubi_dbg("\tupd_marker %d", (int)r->upd_marker); |
| ubi_dbg("\tname_len %d", name_len); |
| |
| if (r->name[0] == '\0') { |
| ubi_dbg("\tname NULL"); |
| return; |
| } |
| |
| if (name_len <= UBI_VOL_NAME_MAX && |
| strnlen(&r->name[0], name_len + 1) == name_len) { |
| ubi_dbg("\tname %s", &r->name[0]); |
| } else { |
| ubi_dbg("\t1st 5 characters of name: %c%c%c%c%c", |
| r->name[0], r->name[1], r->name[2], r->name[3], |
| r->name[4]); |
| } |
| ubi_dbg("\tcrc %#08x", be32_to_cpu(r->crc)); |
| } |
| |
| /* Empty volume table record */ |
| static struct ubi_vtbl_record empty_vtbl_record; |
| |
| /** |
| * vtbl_check - check if volume table is not corrupted and sensible. |
| * @ubi: UBI device description object |
| * @vtbl: volume table |
| * |
| * This function returns zero if @vtbl is all right, %1 if CRC is incorrect, |
| * and %-EINVAL if it contains inconsistent data. |
| */ |
| static int vtbl_check(struct ubi_scan_info *ubi, |
| struct ubi_vtbl_record *vtbl) |
| { |
| int i, n, reserved_pebs, alignment, data_pad, vol_type, name_len; |
| int upd_marker, err; |
| uint32_t crc; |
| const char *name; |
| |
| for (i = 0; i < UBI_SPL_VOL_IDS; i++) { |
| reserved_pebs = be32_to_cpu(vtbl[i].reserved_pebs); |
| alignment = be32_to_cpu(vtbl[i].alignment); |
| data_pad = be32_to_cpu(vtbl[i].data_pad); |
| upd_marker = vtbl[i].upd_marker; |
| vol_type = vtbl[i].vol_type; |
| name_len = be16_to_cpu(vtbl[i].name_len); |
| name = &vtbl[i].name[0]; |
| |
| crc = crc32(UBI_CRC32_INIT, &vtbl[i], UBI_VTBL_RECORD_SIZE_CRC); |
| if (be32_to_cpu(vtbl[i].crc) != crc) { |
| ubi_err("bad CRC at record %u: %#08x, not %#08x", |
| i, crc, be32_to_cpu(vtbl[i].crc)); |
| ubi_dump_vtbl_record(&vtbl[i], i); |
| return 1; |
| } |
| |
| if (reserved_pebs == 0) { |
| if (memcmp(&vtbl[i], &empty_vtbl_record, |
| UBI_VTBL_RECORD_SIZE)) { |
| err = 2; |
| goto bad; |
| } |
| continue; |
| } |
| |
| if (reserved_pebs < 0 || alignment < 0 || data_pad < 0 || |
| name_len < 0) { |
| err = 3; |
| goto bad; |
| } |
| |
| if (alignment > ubi->leb_size || alignment == 0) { |
| err = 4; |
| goto bad; |
| } |
| |
| n = alignment & (CONFIG_SPL_UBI_VID_OFFSET - 1); |
| if (alignment != 1 && n) { |
| err = 5; |
| goto bad; |
| } |
| |
| n = ubi->leb_size % alignment; |
| if (data_pad != n) { |
| ubi_err("bad data_pad, has to be %d", n); |
| err = 6; |
| goto bad; |
| } |
| |
| if (vol_type != UBI_VID_DYNAMIC && vol_type != UBI_VID_STATIC) { |
| err = 7; |
| goto bad; |
| } |
| |
| if (upd_marker != 0 && upd_marker != 1) { |
| err = 8; |
| goto bad; |
| } |
| |
| if (name_len > UBI_VOL_NAME_MAX) { |
| err = 10; |
| goto bad; |
| } |
| |
| if (name[0] == '\0') { |
| err = 11; |
| goto bad; |
| } |
| |
| if (name_len != strnlen(name, name_len + 1)) { |
| err = 12; |
| goto bad; |
| } |
| |
| ubi_dump_vtbl_record(&vtbl[i], i); |
| } |
| |
| /* Checks that all names are unique */ |
| for (i = 0; i < UBI_SPL_VOL_IDS - 1; i++) { |
| for (n = i + 1; n < UBI_SPL_VOL_IDS; n++) { |
| int len1 = be16_to_cpu(vtbl[i].name_len); |
| int len2 = be16_to_cpu(vtbl[n].name_len); |
| |
| if (len1 > 0 && len1 == len2 && |
| !strncmp(vtbl[i].name, vtbl[n].name, len1)) { |
| ubi_err("volumes %d and %d have the same name \"%s\"", |
| i, n, vtbl[i].name); |
| ubi_dump_vtbl_record(&vtbl[i], i); |
| ubi_dump_vtbl_record(&vtbl[n], n); |
| return -EINVAL; |
| } |
| } |
| } |
| |
| return 0; |
| |
| bad: |
| ubi_err("volume table check failed: record %d, error %d", i, err); |
| ubi_dump_vtbl_record(&vtbl[i], i); |
| return -EINVAL; |
| } |
| |
| static int ubi_read_volume_table(struct ubi_scan_info *ubi, u32 pnum) |
| { |
| int err = -EINVAL; |
| |
| empty_vtbl_record.crc = cpu_to_be32(0xf116c36b); |
| |
| err = ubi_io_read(ubi, &ubi->vtbl, pnum, ubi->leb_start, |
| sizeof(struct ubi_vtbl_record) * UBI_SPL_VOL_IDS); |
| if (err && err != UBI_IO_BITFLIPS) { |
| ubi_err("unable to read volume table"); |
| goto out; |
| } |
| |
| if (!vtbl_check(ubi, ubi->vtbl)) { |
| ubi->vtbl_valid = 1; |
| err = 0; |
| } |
| out: |
| return err; |
| } |
| |
| #endif /* CONFIG_SPL_UBI_LOAD_BY_VOLNAME */ |
| |
| static int ubi_io_read_vid_hdr(struct ubi_scan_info *ubi, int pnum, |
| struct ubi_vid_hdr *vh, int unused) |
| { |
| u32 magic; |
| int res; |
| |
| /* No point in rescanning a corrupt block */ |
| if (test_bit(pnum, ubi->corrupt)) |
| return UBI_IO_BAD_HDR; |
| /* |
| * If the block has been scanned already, no need to rescan |
| */ |
| if (test_and_set_bit(pnum, ubi->scanned)) |
| return 0; |
| |
| res = ubi_io_read(ubi, vh, pnum, ubi->vid_offset, sizeof(*vh)); |
| |
| /* |
| * Bad block, unrecoverable ECC error, skip the block |
| */ |
| if (res) { |
| ubi_dbg("Skipping bad or unreadable block %d", pnum); |
| vh->magic = 0; |
| generic_set_bit(pnum, ubi->corrupt); |
| return res; |
| } |
| |
| /* Magic number available ? */ |
| magic = be32_to_cpu(vh->magic); |
| if (magic != UBI_VID_HDR_MAGIC) { |
| generic_set_bit(pnum, ubi->corrupt); |
| if (magic == 0xffffffff) |
| return UBI_IO_FF; |
| ubi_msg("Bad magic in block 0%d %08x", pnum, magic); |
| return UBI_IO_BAD_HDR; |
| } |
| |
| /* Header CRC correct ? */ |
| if (crc32(UBI_CRC32_INIT, vh, UBI_VID_HDR_SIZE_CRC) != |
| be32_to_cpu(vh->hdr_crc)) { |
| ubi_msg("Bad CRC in block 0%d", pnum); |
| generic_set_bit(pnum, ubi->corrupt); |
| return UBI_IO_BAD_HDR; |
| } |
| |
| ubi_dbg("RV: pnum: %i sqnum %llu", pnum, be64_to_cpu(vh->sqnum)); |
| |
| return 0; |
| } |
| |
| static int ubi_rescan_fm_vid_hdr(struct ubi_scan_info *ubi, |
| struct ubi_vid_hdr *vh, |
| u32 fm_pnum, u32 fm_vol_id, u32 fm_lnum) |
| { |
| int res; |
| |
| if (ubi_io_is_bad(ubi, fm_pnum)) |
| return -EINVAL; |
| |
| res = ubi_io_read_vid_hdr(ubi, fm_pnum, vh, 0); |
| if (!res) { |
| /* Check volume id, volume type and lnum */ |
| if (be32_to_cpu(vh->vol_id) == fm_vol_id && |
| vh->vol_type == UBI_VID_STATIC && |
| be32_to_cpu(vh->lnum) == fm_lnum) |
| return 0; |
| ubi_dbg("RS: PEB %u vol: %u : %u typ %u lnum %u %u", |
| fm_pnum, fm_vol_id, vh->vol_type, |
| be32_to_cpu(vh->vol_id), |
| fm_lnum, be32_to_cpu(vh->lnum)); |
| } |
| return res; |
| } |
| |
| /* Insert the logic block into the volume info */ |
| static int ubi_add_peb_to_vol(struct ubi_scan_info *ubi, |
| struct ubi_vid_hdr *vh, u32 vol_id, |
| u32 pnum, u32 lnum) |
| { |
| struct ubi_vol_info *vi = ubi->volinfo + vol_id; |
| u32 *ltp; |
| |
| /* |
| * If the volume is larger than expected, yell and give up :( |
| */ |
| if (lnum >= UBI_MAX_VOL_LEBS) { |
| ubi_warn("Vol: %u LEB %d > %d", vol_id, lnum, UBI_MAX_VOL_LEBS); |
| return -EINVAL; |
| } |
| |
| ubi_dbg("SC: Add PEB %u to Vol %u as LEB %u fnd %d sc %d", |
| pnum, vol_id, lnum, !!test_bit(lnum, vi->found), |
| !!test_bit(pnum, ubi->scanned)); |
| |
| /* Points to the translation entry */ |
| ltp = vi->lebs_to_pebs + lnum; |
| |
| /* If the block is already assigned, check sqnum */ |
| if (__test_and_set_bit(lnum, vi->found)) { |
| u32 cur_pnum = *ltp; |
| struct ubi_vid_hdr *cur = ubi->blockinfo + cur_pnum; |
| |
| /* |
| * If the current block hase not yet been scanned, we |
| * need to do that. The other block might be stale or |
| * the current block corrupted and the FM not yet |
| * updated. |
| */ |
| if (!test_bit(cur_pnum, ubi->scanned)) { |
| /* |
| * If the scan fails, we use the valid block |
| */ |
| if (ubi_rescan_fm_vid_hdr(ubi, cur, cur_pnum, vol_id, |
| lnum)) { |
| *ltp = pnum; |
| return 0; |
| } |
| } |
| |
| /* |
| * Should not happen .... |
| */ |
| if (test_bit(cur_pnum, ubi->corrupt)) { |
| *ltp = pnum; |
| return 0; |
| } |
| |
| ubi_dbg("Vol %u LEB %u PEB %u->sqnum %llu NPEB %u->sqnum %llu", |
| vol_id, lnum, cur_pnum, be64_to_cpu(cur->sqnum), pnum, |
| be64_to_cpu(vh->sqnum)); |
| |
| /* |
| * Compare sqnum and take the newer one |
| */ |
| if (be64_to_cpu(cur->sqnum) < be64_to_cpu(vh->sqnum)) |
| *ltp = pnum; |
| } else { |
| *ltp = pnum; |
| if (lnum > vi->last_block) |
| vi->last_block = lnum; |
| } |
| |
| return 0; |
| } |
| |
| static int ubi_scan_vid_hdr(struct ubi_scan_info *ubi, struct ubi_vid_hdr *vh, |
| u32 pnum) |
| { |
| u32 vol_id, lnum; |
| int res; |
| |
| if (ubi_io_is_bad(ubi, pnum)) |
| return -EINVAL; |
| |
| res = ubi_io_read_vid_hdr(ubi, pnum, vh, 0); |
| if (res) |
| return res; |
| |
| /* Get volume id */ |
| vol_id = be32_to_cpu(vh->vol_id); |
| |
| /* If this is the fastmap anchor, return right away */ |
| if (vol_id == UBI_FM_SB_VOLUME_ID) |
| return ubi->fm_enabled ? UBI_FASTMAP_ANCHOR : 0; |
| |
| #ifdef CONFIG_SPL_UBI_LOAD_BY_VOLNAME |
| /* If this is a UBI volume table, read it and return */ |
| if (vol_id == UBI_LAYOUT_VOLUME_ID && !ubi->vtbl_valid) { |
| res = ubi_read_volume_table(ubi, pnum); |
| return res; |
| } |
| #endif |
| |
| /* We only care about static volumes with an id < UBI_SPL_VOL_IDS */ |
| if (vol_id >= UBI_SPL_VOL_IDS || vh->vol_type != UBI_VID_STATIC) |
| return 0; |
| |
| #ifndef CONFIG_SPL_UBI_LOAD_BY_VOLNAME |
| /* We are only interested in the volumes to load */ |
| if (!test_bit(vol_id, ubi->toload)) |
| return 0; |
| #endif |
| lnum = be32_to_cpu(vh->lnum); |
| return ubi_add_peb_to_vol(ubi, vh, vol_id, pnum, lnum); |
| } |
| |
| static int assign_aeb_to_av(struct ubi_scan_info *ubi, u32 pnum, u32 lnum, |
| u32 vol_id, u32 vol_type, u32 used) |
| { |
| struct ubi_vid_hdr *vh; |
| |
| if (ubi_io_is_bad(ubi, pnum)) |
| return -EINVAL; |
| |
| ubi->fastmap_pebs++; |
| |
| #ifndef CONFIG_SPL_UBI_LOAD_BY_VOLNAME |
| if (vol_id >= UBI_SPL_VOL_IDS || vol_type != UBI_STATIC_VOLUME) |
| return 0; |
| |
| /* We are only interested in the volumes to load */ |
| if (!test_bit(vol_id, ubi->toload)) |
| return 0; |
| #endif |
| vh = ubi->blockinfo + pnum; |
| |
| return ubi_scan_vid_hdr(ubi, vh, pnum); |
| } |
| |
| static int scan_pool(struct ubi_scan_info *ubi, __be32 *pebs, int pool_size) |
| { |
| struct ubi_vid_hdr *vh; |
| u32 pnum; |
| int i; |
| |
| ubi_dbg("Scanning pool size: %d", pool_size); |
| |
| for (i = 0; i < pool_size; i++) { |
| pnum = be32_to_cpu(pebs[i]); |
| |
| if (ubi_io_is_bad(ubi, pnum)) { |
| ubi_err("FM: Bad PEB in fastmap pool! %u", pnum); |
| return UBI_BAD_FASTMAP; |
| } |
| |
| vh = ubi->blockinfo + pnum; |
| /* |
| * We allow the scan to fail here. The loader will notice |
| * and look for a replacement. |
| */ |
| ubi_scan_vid_hdr(ubi, vh, pnum); |
| } |
| return 0; |
| } |
| |
| /* |
| * Fastmap code is stolen from Linux kernel and this stub structure is used |
| * to make it happy. |
| */ |
| struct ubi_attach_info { |
| int i; |
| }; |
| |
| static int ubi_attach_fastmap(struct ubi_scan_info *ubi, |
| struct ubi_attach_info *ai, |
| struct ubi_fastmap_layout *fm) |
| { |
| struct ubi_fm_hdr *fmhdr; |
| struct ubi_fm_scan_pool *fmpl1, *fmpl2; |
| struct ubi_fm_ec *fmec; |
| struct ubi_fm_volhdr *fmvhdr; |
| struct ubi_fm_eba *fm_eba; |
| int ret, i, j, pool_size, wl_pool_size; |
| size_t fm_pos = 0, fm_size = ubi->fm_size; |
| void *fm_raw = ubi->fm_buf; |
| |
| memset(ubi->fm_used, 0, sizeof(ubi->fm_used)); |
| |
| fm_pos += sizeof(struct ubi_fm_sb); |
| if (fm_pos >= fm_size) |
| goto fail_bad; |
| |
| fmhdr = (struct ubi_fm_hdr *)(fm_raw + fm_pos); |
| fm_pos += sizeof(*fmhdr); |
| if (fm_pos >= fm_size) |
| goto fail_bad; |
| |
| if (be32_to_cpu(fmhdr->magic) != UBI_FM_HDR_MAGIC) { |
| ubi_err("bad fastmap header magic: 0x%x, expected: 0x%x", |
| be32_to_cpu(fmhdr->magic), UBI_FM_HDR_MAGIC); |
| goto fail_bad; |
| } |
| |
| fmpl1 = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos); |
| fm_pos += sizeof(*fmpl1); |
| if (fm_pos >= fm_size) |
| goto fail_bad; |
| if (be32_to_cpu(fmpl1->magic) != UBI_FM_POOL_MAGIC) { |
| ubi_err("bad fastmap pool magic: 0x%x, expected: 0x%x", |
| be32_to_cpu(fmpl1->magic), UBI_FM_POOL_MAGIC); |
| goto fail_bad; |
| } |
| |
| fmpl2 = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos); |
| fm_pos += sizeof(*fmpl2); |
| if (fm_pos >= fm_size) |
| goto fail_bad; |
| if (be32_to_cpu(fmpl2->magic) != UBI_FM_POOL_MAGIC) { |
| ubi_err("bad fastmap pool magic: 0x%x, expected: 0x%x", |
| be32_to_cpu(fmpl2->magic), UBI_FM_POOL_MAGIC); |
| goto fail_bad; |
| } |
| |
| pool_size = be16_to_cpu(fmpl1->size); |
| wl_pool_size = be16_to_cpu(fmpl2->size); |
| fm->max_pool_size = be16_to_cpu(fmpl1->max_size); |
| fm->max_wl_pool_size = be16_to_cpu(fmpl2->max_size); |
| |
| if (pool_size > UBI_FM_MAX_POOL_SIZE || pool_size < 0) { |
| ubi_err("bad pool size: %i", pool_size); |
| goto fail_bad; |
| } |
| |
| if (wl_pool_size > UBI_FM_MAX_POOL_SIZE || wl_pool_size < 0) { |
| ubi_err("bad WL pool size: %i", wl_pool_size); |
| goto fail_bad; |
| } |
| |
| if (fm->max_pool_size > UBI_FM_MAX_POOL_SIZE || |
| fm->max_pool_size < 0) { |
| ubi_err("bad maximal pool size: %i", fm->max_pool_size); |
| goto fail_bad; |
| } |
| |
| if (fm->max_wl_pool_size > UBI_FM_MAX_POOL_SIZE || |
| fm->max_wl_pool_size < 0) { |
| ubi_err("bad maximal WL pool size: %i", fm->max_wl_pool_size); |
| goto fail_bad; |
| } |
| |
| /* read EC values from free list */ |
| for (i = 0; i < be32_to_cpu(fmhdr->free_peb_count); i++) { |
| fmec = (struct ubi_fm_ec *)(fm_raw + fm_pos); |
| fm_pos += sizeof(*fmec); |
| if (fm_pos >= fm_size) |
| goto fail_bad; |
| } |
| |
| /* read EC values from used list */ |
| for (i = 0; i < be32_to_cpu(fmhdr->used_peb_count); i++) { |
| fmec = (struct ubi_fm_ec *)(fm_raw + fm_pos); |
| fm_pos += sizeof(*fmec); |
| if (fm_pos >= fm_size) |
| goto fail_bad; |
| |
| generic_set_bit(be32_to_cpu(fmec->pnum), ubi->fm_used); |
| } |
| |
| /* read EC values from scrub list */ |
| for (i = 0; i < be32_to_cpu(fmhdr->scrub_peb_count); i++) { |
| fmec = (struct ubi_fm_ec *)(fm_raw + fm_pos); |
| fm_pos += sizeof(*fmec); |
| if (fm_pos >= fm_size) |
| goto fail_bad; |
| } |
| |
| /* read EC values from erase list */ |
| for (i = 0; i < be32_to_cpu(fmhdr->erase_peb_count); i++) { |
| fmec = (struct ubi_fm_ec *)(fm_raw + fm_pos); |
| fm_pos += sizeof(*fmec); |
| if (fm_pos >= fm_size) |
| goto fail_bad; |
| } |
| |
| /* Iterate over all volumes and read their EBA table */ |
| for (i = 0; i < be32_to_cpu(fmhdr->vol_count); i++) { |
| u32 vol_id, vol_type, used, reserved; |
| |
| fmvhdr = (struct ubi_fm_volhdr *)(fm_raw + fm_pos); |
| fm_pos += sizeof(*fmvhdr); |
| if (fm_pos >= fm_size) |
| goto fail_bad; |
| |
| if (be32_to_cpu(fmvhdr->magic) != UBI_FM_VHDR_MAGIC) { |
| ubi_err("bad fastmap vol header magic: 0x%x, " \ |
| "expected: 0x%x", |
| be32_to_cpu(fmvhdr->magic), UBI_FM_VHDR_MAGIC); |
| goto fail_bad; |
| } |
| |
| vol_id = be32_to_cpu(fmvhdr->vol_id); |
| vol_type = fmvhdr->vol_type; |
| used = be32_to_cpu(fmvhdr->used_ebs); |
| |
| fm_eba = (struct ubi_fm_eba *)(fm_raw + fm_pos); |
| fm_pos += sizeof(*fm_eba); |
| fm_pos += (sizeof(__be32) * be32_to_cpu(fm_eba->reserved_pebs)); |
| if (fm_pos >= fm_size) |
| goto fail_bad; |
| |
| if (be32_to_cpu(fm_eba->magic) != UBI_FM_EBA_MAGIC) { |
| ubi_err("bad fastmap EBA header magic: 0x%x, " \ |
| "expected: 0x%x", |
| be32_to_cpu(fm_eba->magic), UBI_FM_EBA_MAGIC); |
| goto fail_bad; |
| } |
| |
| reserved = be32_to_cpu(fm_eba->reserved_pebs); |
| ubi_dbg("FA: vol %u used %u res: %u", vol_id, used, reserved); |
| for (j = 0; j < reserved; j++) { |
| int pnum = be32_to_cpu(fm_eba->pnum[j]); |
| |
| if ((int)be32_to_cpu(fm_eba->pnum[j]) < 0) |
| continue; |
| |
| if (!__test_and_clear_bit(pnum, ubi->fm_used)) |
| continue; |
| |
| /* |
| * We only handle static volumes so used_ebs |
| * needs to be handed in. And we do not assign |
| * the reserved blocks |
| */ |
| if (j >= used) |
| continue; |
| |
| ret = assign_aeb_to_av(ubi, pnum, j, vol_id, |
| vol_type, used); |
| if (!ret) |
| continue; |
| |
| /* |
| * Nasty: The fastmap claims that the volume |
| * has one block more than it, but that block |
| * is always empty and the other blocks have |
| * the correct number of total LEBs in the |
| * headers. Deal with it. |
| */ |
| if (ret != UBI_IO_FF && j != used - 1) |
| goto fail_bad; |
| ubi_dbg("FA: Vol: %u Ignoring empty LEB %d of %d", |
| vol_id, j, used); |
| } |
| } |
| |
| ret = scan_pool(ubi, fmpl1->pebs, pool_size); |
| if (ret) |
| goto fail; |
| |
| ret = scan_pool(ubi, fmpl2->pebs, wl_pool_size); |
| if (ret) |
| goto fail; |
| |
| #ifdef CHECKME |
| /* |
| * If fastmap is leaking PEBs (must not happen), raise a |
| * fat warning and fall back to scanning mode. |
| * We do this here because in ubi_wl_init() it's too late |
| * and we cannot fall back to scanning. |
| */ |
| if (WARN_ON(count_fastmap_pebs(ai) != ubi->peb_count - |
| ai->bad_peb_count - fm->used_blocks)) |
| goto fail_bad; |
| #endif |
| |
| return 0; |
| |
| fail_bad: |
| ret = UBI_BAD_FASTMAP; |
| fail: |
| return ret; |
| } |
| |
| static int ubi_scan_fastmap(struct ubi_scan_info *ubi, |
| struct ubi_attach_info *ai, |
| int fm_anchor) |
| { |
| struct ubi_fm_sb *fmsb, *fmsb2; |
| struct ubi_vid_hdr *vh; |
| struct ubi_fastmap_layout *fm; |
| int i, used_blocks, pnum, ret = 0; |
| size_t fm_size; |
| __be32 crc, tmp_crc; |
| unsigned long long sqnum = 0; |
| |
| fmsb = &ubi->fm_sb; |
| fm = &ubi->fm_layout; |
| |
| ret = ubi_io_read(ubi, fmsb, fm_anchor, ubi->leb_start, sizeof(*fmsb)); |
| if (ret && ret != UBI_IO_BITFLIPS) |
| goto free_fm_sb; |
| else if (ret == UBI_IO_BITFLIPS) |
| fm->to_be_tortured[0] = 1; |
| |
| if (be32_to_cpu(fmsb->magic) != UBI_FM_SB_MAGIC) { |
| ubi_err("bad super block magic: 0x%x, expected: 0x%x", |
| be32_to_cpu(fmsb->magic), UBI_FM_SB_MAGIC); |
| ret = UBI_BAD_FASTMAP; |
| goto free_fm_sb; |
| } |
| |
| if (fmsb->version != UBI_FM_FMT_VERSION) { |
| ubi_err("bad fastmap version: %i, expected: %i", |
| fmsb->version, UBI_FM_FMT_VERSION); |
| ret = UBI_BAD_FASTMAP; |
| goto free_fm_sb; |
| } |
| |
| used_blocks = be32_to_cpu(fmsb->used_blocks); |
| if (used_blocks > UBI_FM_MAX_BLOCKS || used_blocks < 1) { |
| ubi_err("number of fastmap blocks is invalid: %i", used_blocks); |
| ret = UBI_BAD_FASTMAP; |
| goto free_fm_sb; |
| } |
| |
| fm_size = ubi->leb_size * used_blocks; |
| if (fm_size != ubi->fm_size) { |
| ubi_err("bad fastmap size: %zi, expected: %zi", fm_size, |
| ubi->fm_size); |
| ret = UBI_BAD_FASTMAP; |
| goto free_fm_sb; |
| } |
| |
| vh = &ubi->fm_vh; |
| |
| for (i = 0; i < used_blocks; i++) { |
| pnum = be32_to_cpu(fmsb->block_loc[i]); |
| |
| if (ubi_io_is_bad(ubi, pnum)) { |
| ret = UBI_BAD_FASTMAP; |
| goto free_hdr; |
| } |
| |
| #ifdef LATER |
| int image_seq; |
| ret = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); |
| if (ret && ret != UBI_IO_BITFLIPS) { |
| ubi_err("unable to read fastmap block# %i EC (PEB: %i)", |
| i, pnum); |
| if (ret > 0) |
| ret = UBI_BAD_FASTMAP; |
| goto free_hdr; |
| } else if (ret == UBI_IO_BITFLIPS) |
| fm->to_be_tortured[i] = 1; |
| |
| image_seq = be32_to_cpu(ech->image_seq); |
| if (!ubi->image_seq) |
| ubi->image_seq = image_seq; |
| /* |
| * Older UBI implementations have image_seq set to zero, so |
| * we shouldn't fail if image_seq == 0. |
| */ |
| if (image_seq && (image_seq != ubi->image_seq)) { |
| ubi_err("wrong image seq:%d instead of %d", |
| be32_to_cpu(ech->image_seq), ubi->image_seq); |
| ret = UBI_BAD_FASTMAP; |
| goto free_hdr; |
| } |
| #endif |
| ret = ubi_io_read_vid_hdr(ubi, pnum, vh, 0); |
| if (ret && ret != UBI_IO_BITFLIPS) { |
| ubi_err("unable to read fastmap block# %i (PEB: %i)", |
| i, pnum); |
| goto free_hdr; |
| } |
| |
| /* |
| * Mainline code rescans the anchor header. We've done |
| * that already so we merily copy it over. |
| */ |
| if (pnum == fm_anchor) |
| memcpy(vh, ubi->blockinfo + pnum, sizeof(*fm)); |
| |
| if (i == 0) { |
| if (be32_to_cpu(vh->vol_id) != UBI_FM_SB_VOLUME_ID) { |
| ubi_err("bad fastmap anchor vol_id: 0x%x," \ |
| " expected: 0x%x", |
| be32_to_cpu(vh->vol_id), |
| UBI_FM_SB_VOLUME_ID); |
| ret = UBI_BAD_FASTMAP; |
| goto free_hdr; |
| } |
| } else { |
| if (be32_to_cpu(vh->vol_id) != UBI_FM_DATA_VOLUME_ID) { |
| ubi_err("bad fastmap data vol_id: 0x%x," \ |
| " expected: 0x%x", |
| be32_to_cpu(vh->vol_id), |
| UBI_FM_DATA_VOLUME_ID); |
| ret = UBI_BAD_FASTMAP; |
| goto free_hdr; |
| } |
| } |
| |
| if (sqnum < be64_to_cpu(vh->sqnum)) |
| sqnum = be64_to_cpu(vh->sqnum); |
| |
| ret = ubi_io_read(ubi, ubi->fm_buf + (ubi->leb_size * i), pnum, |
| ubi->leb_start, ubi->leb_size); |
| if (ret && ret != UBI_IO_BITFLIPS) { |
| ubi_err("unable to read fastmap block# %i (PEB: %i, " \ |
| "err: %i)", i, pnum, ret); |
| goto free_hdr; |
| } |
| } |
| |
| fmsb2 = (struct ubi_fm_sb *)(ubi->fm_buf); |
| tmp_crc = be32_to_cpu(fmsb2->data_crc); |
| fmsb2->data_crc = 0; |
| crc = crc32(UBI_CRC32_INIT, ubi->fm_buf, fm_size); |
| if (crc != tmp_crc) { |
| ubi_err("fastmap data CRC is invalid"); |
| ubi_err("CRC should be: 0x%x, calc: 0x%x", tmp_crc, crc); |
| ret = UBI_BAD_FASTMAP; |
| goto free_hdr; |
| } |
| |
| fmsb2->sqnum = sqnum; |
| |
| fm->used_blocks = used_blocks; |
| |
| ret = ubi_attach_fastmap(ubi, ai, fm); |
| if (ret) { |
| if (ret > 0) |
| ret = UBI_BAD_FASTMAP; |
| goto free_hdr; |
| } |
| |
| ubi->fm = fm; |
| ubi->fm_pool.max_size = ubi->fm->max_pool_size; |
| ubi->fm_wl_pool.max_size = ubi->fm->max_wl_pool_size; |
| ubi_msg("attached by fastmap %uMB %u blocks", |
| ubi->fsize_mb, ubi->peb_count); |
| ubi_dbg("fastmap pool size: %d", ubi->fm_pool.max_size); |
| ubi_dbg("fastmap WL pool size: %d", ubi->fm_wl_pool.max_size); |
| |
| out: |
| if (ret) |
| ubi_err("Attach by fastmap failed, doing a full scan!"); |
| return ret; |
| |
| free_hdr: |
| free_fm_sb: |
| goto out; |
| } |
| |
| /* |
| * Scan the flash and attempt to attach via fastmap |
| */ |
| static void ipl_scan(struct ubi_scan_info *ubi) |
| { |
| unsigned int pnum; |
| int res; |
| |
| /* |
| * Scan first for the fastmap super block |
| */ |
| for (pnum = 0; pnum < UBI_FM_MAX_START; pnum++) { |
| res = ubi_scan_vid_hdr(ubi, ubi->blockinfo + pnum, pnum); |
| /* |
| * We ignore errors here as we are meriliy scanning |
| * the headers. |
| */ |
| if (res != UBI_FASTMAP_ANCHOR) |
| continue; |
| |
| /* |
| * If fastmap is disabled, continue scanning. This |
| * might happen because the previous attempt failed or |
| * the caller disabled it right away. |
| */ |
| if (!ubi->fm_enabled) |
| continue; |
| |
| /* |
| * Try to attach the fastmap, if that fails continue |
| * scanning. |
| */ |
| if (!ubi_scan_fastmap(ubi, NULL, pnum)) |
| return; |
| /* |
| * Fastmap failed. Clear everything we have and start |
| * over. We are paranoid and do not trust anything. |
| */ |
| memset(ubi->volinfo, 0, sizeof(ubi->volinfo)); |
| pnum = 0; |
| break; |
| } |
| |
| /* |
| * Continue scanning, ignore errors, we might find what we are |
| * looking for, |
| */ |
| for (; pnum < ubi->peb_count; pnum++) |
| ubi_scan_vid_hdr(ubi, ubi->blockinfo + pnum, pnum); |
| } |
| |
| /* |
| * Load a logical block of a volume into memory |
| */ |
| static int ubi_load_block(struct ubi_scan_info *ubi, uint8_t *laddr, |
| struct ubi_vol_info *vi, u32 vol_id, u32 lnum, |
| u32 last) |
| { |
| struct ubi_vid_hdr *vh, *vrepl; |
| u32 pnum, crc, dlen; |
| |
| retry: |
| /* |
| * If this is a fastmap run, we try to rescan full, otherwise |
| * we simply give up. |
| */ |
| if (!test_bit(lnum, vi->found)) { |
| ubi_warn("LEB %d of %d is missing", lnum, last); |
| return -EINVAL; |
| } |
| |
| pnum = vi->lebs_to_pebs[lnum]; |
| |
| ubi_dbg("Load vol %u LEB %u PEB %u", vol_id, lnum, pnum); |
| |
| if (ubi_io_is_bad(ubi, pnum)) { |
| ubi_warn("Corrupted mapping block %d PB %d\n", lnum, pnum); |
| return -EINVAL; |
| } |
| |
| if (test_bit(pnum, ubi->corrupt)) |
| goto find_other; |
| |
| /* |
| * Lets try to read that block |
| */ |
| vh = ubi->blockinfo + pnum; |
| |
| if (!test_bit(pnum, ubi->scanned)) { |
| ubi_warn("Vol: %u LEB %u PEB %u not yet scanned", vol_id, |
| lnum, pnum); |
| if (ubi_rescan_fm_vid_hdr(ubi, vh, pnum, vol_id, lnum)) |
| goto find_other; |
| } |
| |
| /* |
| * Check, if the total number of blocks is correct |
| */ |
| if (be32_to_cpu(vh->used_ebs) != last) { |
| ubi_dbg("Block count missmatch."); |
| ubi_dbg("vh->used_ebs: %d nrblocks: %d", |
| be32_to_cpu(vh->used_ebs), last); |
| generic_set_bit(pnum, ubi->corrupt); |
| goto find_other; |
| } |
| |
| /* |
| * Get the data length of this block. |
| */ |
| dlen = be32_to_cpu(vh->data_size); |
| |
| /* |
| * Read the data into RAM. We ignore the return value |
| * here as the only thing which might go wrong are |
| * bitflips. Try nevertheless. |
| */ |
| ubi_io_read(ubi, laddr, pnum, ubi->leb_start, dlen); |
| |
| /* Calculate CRC over the data */ |
| crc = crc32(UBI_CRC32_INIT, laddr, dlen); |
| |
| if (crc != be32_to_cpu(vh->data_crc)) { |
| ubi_warn("Vol: %u LEB %u PEB %u data CRC failure", vol_id, |
| lnum, pnum); |
| generic_set_bit(pnum, ubi->corrupt); |
| goto find_other; |
| } |
| |
| /* We are good. Return the data length we read */ |
| return dlen; |
| |
| find_other: |
| ubi_dbg("Find replacement for LEB %u PEB %u", lnum, pnum); |
| generic_clear_bit(lnum, vi->found); |
| vrepl = NULL; |
| |
| for (pnum = 0; pnum < ubi->peb_count; pnum++) { |
| struct ubi_vid_hdr *tmp = ubi->blockinfo + pnum; |
| u32 t_vol_id = be32_to_cpu(tmp->vol_id); |
| u32 t_lnum = be32_to_cpu(tmp->lnum); |
| |
| if (test_bit(pnum, ubi->corrupt)) |
| continue; |
| |
| if (t_vol_id != vol_id || t_lnum != lnum) |
| continue; |
| |
| if (!test_bit(pnum, ubi->scanned)) { |
| ubi_warn("Vol: %u LEB %u PEB %u not yet scanned", |
| vol_id, lnum, pnum); |
| if (ubi_rescan_fm_vid_hdr(ubi, tmp, pnum, vol_id, lnum)) |
| continue; |
| } |
| |
| /* |
| * We found one. If its the first, assign it otherwise |
| * compare the sqnum |
| */ |
| generic_set_bit(lnum, vi->found); |
| |
| if (!vrepl) { |
| vrepl = tmp; |
| continue; |
| } |
| |
| if (be64_to_cpu(vrepl->sqnum) < be64_to_cpu(tmp->sqnum)) |
| vrepl = tmp; |
| } |
| |
| if (vrepl) { |
| /* Update the vi table */ |
| pnum = vrepl - ubi->blockinfo; |
| vi->lebs_to_pebs[lnum] = pnum; |
| ubi_dbg("Trying PEB %u for LEB %u", pnum, lnum); |
| vh = vrepl; |
| } |
| goto retry; |
| } |
| |
| /* |
| * Load a volume into RAM |
| */ |
| static int ipl_load(struct ubi_scan_info *ubi, const u32 vol_id, uint8_t *laddr) |
| { |
| struct ubi_vol_info *vi; |
| u32 lnum, last, len; |
| |
| if (vol_id >= UBI_SPL_VOL_IDS) |
| return -EINVAL; |
| |
| len = 0; |
| vi = ubi->volinfo + vol_id; |
| last = vi->last_block + 1; |
| |
| /* Read the blocks to RAM, check CRC */ |
| for (lnum = 0 ; lnum < last; lnum++) { |
| int res = ubi_load_block(ubi, laddr, vi, vol_id, lnum, last); |
| |
| if (res < 0) { |
| ubi_warn("Failed to load volume %u", vol_id); |
| return res; |
| } |
| /* res is the data length of the read block */ |
| laddr += res; |
| len += res; |
| } |
| return len; |
| } |
| |
| int ubispl_load_volumes(struct ubispl_info *info, struct ubispl_load *lvols, |
| int nrvols) |
| { |
| struct ubi_scan_info *ubi = info->ubi; |
| int res, i, fastmap = info->fastmap; |
| u32 fsize; |
| |
| retry: |
| /* |
| * We do a partial initializiation of @ubi. Cleaning fm_buf is |
| * not necessary. |
| */ |
| memset(ubi, 0, offsetof(struct ubi_scan_info, fm_buf)); |
| |
| ubi->read = info->read; |
| |
| /* Precalculate the offsets */ |
| ubi->vid_offset = info->vid_offset; |
| ubi->leb_start = info->leb_start; |
| ubi->leb_size = info->peb_size - ubi->leb_start; |
| ubi->peb_count = info->peb_count; |
| ubi->peb_offset = info->peb_offset; |
| |
| #ifdef CONFIG_SPL_UBI_LOAD_BY_VOLNAME |
| ubi->vtbl_valid = 0; |
| #endif |
| |
| fsize = info->peb_size * info->peb_count; |
| ubi->fsize_mb = fsize >> 20; |
| |
| /* Fastmap init */ |
| ubi->fm_size = ubi_calc_fm_size(ubi); |
| ubi->fm_enabled = fastmap; |
| |
| for (i = 0; i < nrvols; i++) { |
| struct ubispl_load *lv = lvols + i; |
| |
| generic_set_bit(lv->vol_id, ubi->toload); |
| } |
| |
| ipl_scan(ubi); |
| |
| for (i = 0; i < nrvols; i++) { |
| struct ubispl_load *lv = lvols + i; |
| |
| #ifdef CONFIG_SPL_UBI_LOAD_BY_VOLNAME |
| if (lv->vol_id == -1) { |
| for (int j = 0; j < UBI_SPL_VOL_IDS; j++) { |
| int len = be16_to_cpu(ubi->vtbl[j].name_len); |
| |
| if (strncmp(lv->name, |
| ubi->vtbl[j].name, |
| len) == 0) { |
| lv->vol_id = j; |
| break; |
| } |
| } |
| } |
| ubi_msg("Loading VolName %s (VolId #%d)", lv->name, lv->vol_id); |
| #else |
| ubi_msg("Loading VolId #%d", lv->vol_id); |
| #endif |
| res = ipl_load(ubi, lv->vol_id, lv->load_addr); |
| if (res < 0) { |
| if (fastmap) { |
| fastmap = 0; |
| goto retry; |
| } |
| ubi_warn("Failed"); |
| return res; |
| } |
| } |
| return 0; |
| } |