From 434cc613fc995446c27f76329d57eb6e047c713c Mon Sep 17 00:00:00 2001 From: Karthick Shanmugham Date: Wed, 9 Sep 2020 20:54:00 +0530 Subject: [PATCH] ipq: Added nand bad block skipping to dump_to_flash feature This change will skip the bad blocks offsets, if any while writing into the nand flash duirng crash when dump_to_flash enabled. This change also verifies whether the dump data is not overwritten into the next partition. Signed-off-by: Karthick Shanmugham Change-Id: I0eec4c772a5f5efb3c17bfd1fd3d3d9a5ff85d1d --- arch/arm/cpu/armv7/qca/common/smem.c | 46 +++++++ arch/arm/include/asm/arch-qca-common/smem.h | 1 + board/qca/arm/common/crashdump.c | 129 ++++++++++++++++---- 3 files changed, 153 insertions(+), 23 deletions(-) diff --git a/arch/arm/cpu/armv7/qca/common/smem.c b/arch/arm/cpu/armv7/qca/common/smem.c index 91a943641a..bb731ba8c0 100644 --- a/arch/arm/cpu/armv7/qca/common/smem.c +++ b/arch/arm/cpu/armv7/qca/common/smem.c @@ -585,6 +585,52 @@ unsigned int get_partition_table_offset(void) return primary_mibib; } +/* + * smem_getpart_from_offset - retreive partition start and size for given offset + * belongs to. + * @part_name: offset for which part start and size needed + * @start: location where the start offset is to be stored + * @size: location where the size is to be stored + * + * Returns 0 at success or -ENOENT otherwise. + */ +int smem_getpart_from_offset(uint32_t offset, uint32_t *start, uint32_t *size) +{ + unsigned i; + qca_smem_flash_info_t *sfi = &qca_smem_flash_info; + struct smem_ptn *p; + uint32_t bsize; + + for (i = 0; i < smem_ptable.len; i++) { + p = &smem_ptable.parts[i]; + *start = p->start; + bsize = get_part_block_size(p, sfi); + + if (p->size == (~0u)) { + /* + * Partition size is 'till end of device', calculate + * appropriately + */ +#ifdef CONFIG_CMD_NAND + *size = (nand_info[get_device_id_by_part(p)].size / + bsize) - p->start; +#else + *size = 0; + bsize = bsize; +#endif + } else { + *size = p->size; + } + *start = *start * bsize; + *size = *size * bsize; + if (*start <= offset && *start + *size > offset) { + return 0; + } + } + + return -ENOENT; + +} /* * smem_getpart - retreive partition start and size * @part_name: partition name diff --git a/arch/arm/include/asm/arch-qca-common/smem.h b/arch/arm/include/asm/arch-qca-common/smem.h index fdf48e3815..0438ed4f30 100644 --- a/arch/arm/include/asm/arch-qca-common/smem.h +++ b/arch/arm/include/asm/arch-qca-common/smem.h @@ -61,6 +61,7 @@ int smem_get_boot_flash(uint32_t *flash_type, uint32_t *flash_block_size, uint32_t *flash_density); int smem_getpart(char *name, uint32_t *start, uint32_t *size); +int smem_getpart_from_offset(uint32_t offset, uint32_t *start, uint32_t *size); int getpart_offset_size(char *part_name, uint32_t *offset, uint32_t *size); unsigned int smem_get_board_machtype(void); uint32_t get_nand_block_size(uint8_t dev_id); diff --git a/board/qca/arm/common/crashdump.c b/board/qca/arm/common/crashdump.c index d1cc5f0704..d0f6af7618 100644 --- a/board/qca/arm/common/crashdump.c +++ b/board/qca/arm/common/crashdump.c @@ -97,6 +97,8 @@ struct crashdump_flash_nand_cxt { int cur_page_data_len; int write_size; unsigned char temp_data[MAX_NAND_PAGE_SIZE]; + uint32_t part_start; + uint32_t part_size; }; #ifdef CONFIG_QCA_SPI @@ -748,6 +750,93 @@ reset: reset_board(); } #ifdef CONFIG_MTD_DEVICE + +/* +* NAND flash check and write. Before writing into the nand flash +* this function checks if the block is non-bad, and skips if bad. While +* skipping, there is also possiblity of crossing the partition and corrupting +* next partition with crashdump data. So this function also checks whether +* offset is within the partition, where the configured offset belongs. +* +* Returns 0 on succes and 1 otherwise +*/ +static int check_and_write_crashdump_nand_flash( + struct crashdump_flash_nand_cxt *nand_cnxt, + nand_info_t *nand, unsigned char *data, + unsigned int req_size) +{ + nand_erase_options_t nand_erase_options; + uint32_t part_start = nand_cnxt->part_start; + uint32_t part_end = nand_cnxt->part_start + nand_cnxt->part_size; + unsigned int remaining_len = req_size; + unsigned int data_offset = 0; + loff_t skipoff, skipoff_cmp, *offset; + int ret = 0; + static int first_erase = 1; + + offset = &nand_cnxt->cur_crashdump_offset; + + memset(&nand_erase_options, 0, sizeof(nand_erase_options)); + nand_erase_options.length = nand->erasesize; + + while (remaining_len) + { + + skipoff = *offset - (*offset & (nand->erasesize - 1)); + skipoff_cmp = skipoff; + + for (; skipoff < part_end; skipoff += nand->erasesize) { + if (nand_block_isbad(nand, skipoff)) { + printf("Skipping bad block at 0x%llx\n", skipoff); + continue; + } + else + break; + } + if (skipoff_cmp != skipoff) + *offset = skipoff; + + if(part_start > *offset || ((*offset + remaining_len) >= part_end)) { + printf("Failure: Attempt to write in next partition\n"); + return 1; + } + + if((*offset & (nand->erasesize - 1)) == 0 || first_erase){ + nand_erase_options.offset = *offset; + + ret = nand_erase_opts(&nand_info[0], + &nand_erase_options); + if (ret) + return ret; + first_erase = 0; + } + + if( remaining_len > nand->erasesize) { + + ret = nand_write(nand, *offset, &nand->erasesize, + data + data_offset); + + if (ret) + return ret; + + remaining_len -= nand->erasesize; + *offset += nand->erasesize; + data_offset += nand->erasesize; + } + else { + + ret = nand_write(nand, *offset, &remaining_len, + data + data_offset); + + *offset += remaining_len; + remaining_len = 0; + } + } + + return ret; + +} + /* * Init function for NAND flash writing. It intializes its own context * and erases the required sectors @@ -755,10 +844,14 @@ reset: int init_crashdump_nand_flash_write(void *cnxt, loff_t offset, unsigned int total_size) { - nand_erase_options_t nand_erase_options; struct crashdump_flash_nand_cxt *nand_cnxt = cnxt; int ret; + ret = smem_getpart_from_offset(offset, &nand_cnxt->part_start, + &nand_cnxt->part_size); + if (ret) + return ret; + nand_cnxt->start_crashdump_offset = offset; nand_cnxt->cur_crashdump_offset = offset; nand_cnxt->cur_page_data_len = 0; @@ -769,16 +862,6 @@ int init_crashdump_nand_flash_write(void *cnxt, loff_t offset, return -ENOMEM; } - memset(&nand_erase_options, 0, sizeof(nand_erase_options)); - - nand_erase_options.length = total_size; - nand_erase_options.offset = offset; - - ret = nand_erase_opts(&nand_info[0], - &nand_erase_options); - if (ret) - return ret; - return 0; } @@ -803,9 +886,11 @@ int deinit_crashdump_nand_flash_write(void *cnxt) 0xFF, remaining_bytes); cur_nand_write_len = nand_cnxt->write_size; - ret_val = nand_write(&nand_info[0], - nand_cnxt->cur_crashdump_offset, - &cur_nand_write_len, nand_cnxt->temp_data); + + ret_val = check_and_write_crashdump_nand_flash(nand_cnxt, + &nand_info[0], nand_cnxt->temp_data, + cur_nand_write_len); + } return ret_val; @@ -853,15 +938,14 @@ int crashdump_nand_flash_write_data(void *cnxt, memcpy(nand_cnxt->temp_data + nand_cnxt->cur_page_data_len, data, remaining_len_cur_page); - ret_val = nand_write(&nand_info[0], nand_cnxt->cur_crashdump_offset, - &cur_nand_write_len, - nand_cnxt->temp_data); + ret_val = check_and_write_crashdump_nand_flash(nand_cnxt, + &nand_info[0], nand_cnxt->temp_data, + cur_nand_write_len); if (ret_val) return ret_val; cur_data_pos += remaining_len_cur_page; - nand_cnxt->cur_crashdump_offset += cur_nand_write_len; /* * Calculate the write length in multiple of page length and do the nand @@ -871,17 +955,16 @@ int crashdump_nand_flash_write_data(void *cnxt, nand_cnxt->write_size) * nand_cnxt->write_size; if (cur_nand_write_len > 0) { - ret_val = nand_write(&nand_info[0], - nand_cnxt->cur_crashdump_offset, - &cur_nand_write_len, - cur_data_pos); + ret_val = check_and_write_crashdump_nand_flash(nand_cnxt, + &nand_info[0], cur_data_pos, + cur_nand_write_len); if (ret_val) return ret_val; + } cur_data_pos += cur_nand_write_len; - nand_cnxt->cur_crashdump_offset += cur_nand_write_len; /* Store the remaining data in temp data */ remaining_bytes = data + size - cur_data_pos;