platform: msm-shared: Add fastmap awareness to UBI-flasher
If the flashed UBI image contains Fastmap data but bad blocks are found during
flashing of the image, the fastmap becomes invalid since the free/used lists aren't
true any more. In such a case Fastmap needs to be invalidated by erasing Fastmap
superblock.
Change-Id: I085dd34e0ec461091c6274dea2a720b00236053a
diff --git a/include/dev/flash-ubi.h b/include/dev/flash-ubi.h
index 36e8c8a..b758352 100644
--- a/include/dev/flash-ubi.h
+++ b/include/dev/flash-ubi.h
@@ -58,9 +58,32 @@
uint32_t hdr_crc;
};
-#define UBI_EC_HDR_SIZE sizeof(struct ubi_ec_hdr)
-#define UBI_EC_HDR_SIZE_CRC (UBI_EC_HDR_SIZE - sizeof(uint32_t))
+/* Volume identifier header fields */
+struct __attribute__ ((packed)) ubi_vid_hdr {
+ uint32_t magic;
+ uint8_t version;
+ uint8_t vol_type;
+ uint8_t copy_flag;
+ uint8_t compat;
+ uint32_t vol_id;
+ uint32_t lnum;
+ uint8_t padding1[4];
+ uint32_t data_size;
+ uint32_t used_ebs;
+ uint32_t data_pad;
+ uint32_t data_crc;
+ uint8_t padding2[4];
+ uint64_t sqnum;
+ uint8_t padding3[12];
+ uint32_t hdr_crc;
+};
+#define UBI_EC_HDR_SIZE sizeof(struct ubi_ec_hdr)
+#define UBI_VID_HDR_SIZE sizeof(struct ubi_vid_hdr)
+#define UBI_EC_HDR_SIZE_CRC (UBI_EC_HDR_SIZE - sizeof(uint32_t))
+#define UBI_VID_HDR_SIZE_CRC (UBI_VID_HDR_SIZE - sizeof(uint32_t))
+
+#define UBI_FM_SB_VOLUME_ID (0x7FFFFFFF - 4096 + 1)
/**
* struct ubi_scan_info - UBI scanning information.
* @ec: erase counters or eraseblock status for all eraseblocks
diff --git a/platform/msm_shared/flash-ubi.c b/platform/msm_shared/flash-ubi.c
index b748bf6..13f24f0 100644
--- a/platform/msm_shared/flash-ubi.c
+++ b/platform/msm_shared/flash-ubi.c
@@ -411,6 +411,61 @@
}
/**
+ * fastmap_present - returns true if Fastmap superblock is found
+ * @data: raw data to test
+ *
+ * This function returns 1 if the provided PEB data contains
+ * Fastmap superblock, 0 otherwise
+ */
+static int fastmap_present(const void *data){
+ struct ubi_ec_hdr *ec_hdr = (struct ubi_ec_hdr *)(data);
+ struct ubi_vid_hdr *vid_hdr;
+
+ vid_hdr = (struct ubi_vid_hdr *)(data + BE32(ec_hdr->vid_hdr_offset));
+ if (BE32(vid_hdr->vol_id) == UBI_FM_SB_VOLUME_ID)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * ubi_erase_peb - Erase PEB and update EC header
+ * @peb_num: number of the PEB to erase
+ * @need_erase: if true PEB will be erased
+ * @si: UBI scan information
+ * @ptn_start: first PEB of the flashed partition
+ *
+ * This function erases the given PEB (if required) and writes a new EC
+ * header for it.
+ *
+ * Returns: -1 on error
+ * 0 on success
+ */
+static int ubi_erase_peb(int peb_num, int need_erase,
+ struct ubi_scan_info *si, int ptn_start)
+{
+ struct ubi_ec_hdr new_ech;
+ int page_size = flash_page_size();
+ int num_pages_per_blk = flash_block_size() / page_size;
+ int ret;
+
+ if (need_erase && qpic_nand_blk_erase(peb_num * num_pages_per_blk)) {
+ dprintf(INFO, "flash_ubi_img: erase of %d failed\n", peb_num);
+ return -1;
+ }
+ memset(&new_ech, 0xff, sizeof(new_ech));
+ update_ec_header(&new_ech, si, peb_num - ptn_start, true);
+
+ /* Write new ec_header */
+ ret = write_ec_header(peb_num, &new_ech);
+ if (ret) {
+ dprintf(CRITICAL, "flash_ubi_img: write ec_header to %d failed\n",
+ peb_num);
+ return -1;
+ }
+ return 0;
+}
+/**
* flash_ubi_img() - Write the provided (UBI) image to given partition
* @ptn: partition to write the image to
* @data: the image to write
@@ -424,7 +479,6 @@
{
struct ubi_scan_info *si;
struct ubi_ec_hdr *old_ech;
- struct ubi_ec_hdr new_ech;
uint32_t curr_peb = ptn->start;
void *img_peb;
unsigned page_size = flash_page_size();
@@ -432,6 +486,8 @@
int num_pages_per_blk = block_size / page_size;
int num_pages;
int ret, need_erase;
+ int bad_blocks_cnt = 0;
+ int fmsb_peb = 0;
si = scan_partition(ptn);
if (!si) {
@@ -456,6 +512,7 @@
if (need_erase && qpic_nand_blk_erase(curr_peb * num_pages_per_blk)) {
dprintf(CRITICAL, "flash_ubi_img: erase of %d failed\n",
curr_peb);
+ bad_blocks_cnt++;
curr_peb++;
continue;
}
@@ -473,6 +530,7 @@
if (ret) {
dprintf(CRITICAL, "flash_ubi_img: writing to peb-%d failed\n",
curr_peb);
+ bad_blocks_cnt++;
curr_peb++;
continue;
}
@@ -480,6 +538,9 @@
size = 0;
else
size -= block_size;
+
+ if (fastmap_present(img_peb))
+ fmsb_peb = curr_peb;
img_peb += flash_block_size();
curr_peb++;
}
@@ -492,25 +553,21 @@
}
/* Erase and write ec_header for the rest of the blocks */
- for (; curr_peb < ptn->start + ptn->length; curr_peb++) {
- if (need_erase && qpic_nand_blk_erase(curr_peb * num_pages_per_blk)) {
- dprintf(INFO, "flash_ubi_img: erase of %d failed\n", curr_peb);
- curr_peb++;
- continue;
- }
- memset(&new_ech, 0xff, sizeof(new_ech));
- update_ec_header(&new_ech, si, curr_peb - ptn->start, true);
+ for (; curr_peb < ptn->start + ptn->length; curr_peb++)
+ if (ubi_erase_peb(curr_peb, need_erase, si, ptn->start))
+ bad_blocks_cnt++;
- /* Write new ec_header */
- ret = write_ec_header(curr_peb, &new_ech);
- if (ret) {
- dprintf(CRITICAL, "flash_ubi_img: write ec_header to %d failed\n",
- curr_peb);
- curr_peb++;
- continue;
- }
- }
ret = 0;
+ /*
+ * If flashed image contains fastmap data and bad blocks were found
+ * we need to invalidate the flashed fastmap since it isn't accurate
+ * anymore.
+ */
+ if (bad_blocks_cnt && fmsb_peb) {
+ dprintf(CRITICAL, "flash_ubi_img: invalidate fmsb\n");
+ ret = ubi_erase_peb(ptn->start + 2, 1, si, ptn->start);
+ }
+
out:
free(si->ec);
free(si);