/* * Copyright (c) 2017 Nordic Semiconductor ASA * Copyright (c) 2017 Linaro Limited * * SPDX-License-Identifier: Apache-2.0 */ #define LOG_MODULE_NAME fota_flash_block #define LOG_LEVEL CONFIG_IMG_MANAGER_LOG_LEVEL #include LOG_MODULE_REGISTER(LOG_MODULE_NAME); #include #include #include #include #include #include #include #ifdef CONFIG_IMG_ERASE_PROGRESSIVELY #include #include #endif #include /* DT_FLASH_AREA_IMAGE_XX_YY values used below are auto-generated by DT */ #ifdef CONFIG_TRUSTED_EXECUTION_NONSECURE #define FLASH_AREA_IMAGE_SECONDARY DT_FLASH_AREA_IMAGE_1_NONSECURE_ID #else #define FLASH_AREA_IMAGE_SECONDARY DT_FLASH_AREA_IMAGE_1_ID #endif /* CONFIG_TRUSTED_EXECUTION_NONSECURE */ BUILD_ASSERT_MSG((CONFIG_IMG_BLOCK_BUF_SIZE % DT_FLASH_WRITE_BLOCK_SIZE == 0), "CONFIG_IMG_BLOCK_BUF_SIZE is not a multiple of " "DT_FLASH_WRITE_BLOCK_SIZE"); static bool flash_verify(const struct flash_area *fa, off_t offset, u8_t *data, size_t len) { size_t size; u32_t temp; int rc; while (len) { size = (len >= sizeof(temp)) ? sizeof(temp) : len; rc = flash_area_read(fa, offset, &temp, size); if (rc) { LOG_ERR("flash_read error %d offset=0x%08lx", rc, (long)offset); break; } if (memcmp(data, &temp, size)) { LOG_ERR("offset=0x%08lx VERIFY FAIL. " "expected: 0x%08x, actual: 0x%08x", (long)offset, temp, UNALIGNED_GET(data)); break; } len -= size; offset += size; data += size; } return (len == 0) ? true : false; } #ifdef CONFIG_IMG_ERASE_PROGRESSIVELY static int flash_sector_from_off(struct flash_area const *fap, off_t off, struct flash_sector *sector) { struct flash_pages_info page; struct device *flash_dev; int rc = -ENODEV; flash_dev = flash_area_get_device(fap); off += fap->fa_off; /* flash driver uses offset from memory beginning */ if (flash_dev) { rc = flash_get_page_info_by_offs(flash_dev, off, &page); if (rc == 0) { sector->fs_off = page.start_offset; sector->fs_size = page.size; } } return rc; } /** * Erase the image slot progressively * * This function erases a flash page to which offset belongs if only this page * wasn't erased before. * * @param[in] ctx context of the image collection process. * @param[in] off offset from the beginning of the image flash area beginning * * @return 0 on success, negative errno code on fail. */ static int flash_progressive_erase(struct flash_img_context *ctx, off_t off) { struct flash_sector sector; int rc; rc = flash_sector_from_off(ctx->flash_area, off, §or); if (rc) { LOG_ERR("Unable to determine flash sector size"); } else { if (ctx->off_last != sector.fs_off) { ctx->off_last = sector.fs_off; LOG_INF("Erasing sector at offset 0x%08lx", (long)sector.fs_off); rc = flash_area_erase(ctx->flash_area, sector.fs_off - ctx->flash_area->fa_off, sector.fs_size); if (rc) { LOG_ERR("Error %d while erasing sector", rc); } } } return rc; } #endif /* CONFIG_IMG_ERASE_PROGRESSIVELY */ static int flash_sync(struct flash_img_context *ctx) { int rc = 0; if (ctx->buf_bytes < CONFIG_IMG_BLOCK_BUF_SIZE) { (void)memset(ctx->buf + ctx->buf_bytes, 0xFF, CONFIG_IMG_BLOCK_BUF_SIZE - ctx->buf_bytes); } #ifdef CONFIG_IMG_ERASE_PROGRESSIVELY rc = flash_progressive_erase(ctx, ctx->bytes_written + CONFIG_IMG_BLOCK_BUF_SIZE); if (rc) { LOG_ERR("flash_progressive_erase error %d offset=0x%08zx", rc, ctx->bytes_written); return rc; } #endif rc = flash_area_write(ctx->flash_area, ctx->bytes_written, ctx->buf, CONFIG_IMG_BLOCK_BUF_SIZE); if (rc) { LOG_ERR("flash_write error %d offset=0x%08zx", rc, ctx->bytes_written); return rc; } if (!flash_verify(ctx->flash_area, ctx->bytes_written, ctx->buf, CONFIG_IMG_BLOCK_BUF_SIZE)) { return -EIO; } ctx->bytes_written += ctx->buf_bytes; ctx->buf_bytes = 0U; return rc; } int flash_img_buffered_write(struct flash_img_context *ctx, u8_t *data, size_t len, bool flush) { int processed = 0; int rc = 0; int buf_empty_bytes; while ((len - processed) >= (buf_empty_bytes = CONFIG_IMG_BLOCK_BUF_SIZE - ctx->buf_bytes)) { memcpy(ctx->buf + ctx->buf_bytes, data + processed, buf_empty_bytes); ctx->buf_bytes = CONFIG_IMG_BLOCK_BUF_SIZE; rc = flash_sync(ctx); if (rc) { return rc; } processed += buf_empty_bytes; } /* place rest of the data into ctx->buf */ if (processed < len) { memcpy(ctx->buf + ctx->buf_bytes, data + processed, len - processed); ctx->buf_bytes += len - processed; } if (!flush) { return rc; } if (ctx->buf_bytes > 0) { /* pad the rest of ctx->buf and write it out */ rc = flash_sync(ctx); if (rc) { return rc; } } #ifdef CONFIG_IMG_ERASE_PROGRESSIVELY /* erase the image trailer area if it was not erased */ rc = flash_progressive_erase(ctx, BOOT_TRAILER_IMG_STATUS_OFFS(ctx->flash_area)); if (rc) { return rc; } #endif flash_area_close(ctx->flash_area); ctx->flash_area = NULL; return rc; } size_t flash_img_bytes_written(struct flash_img_context *ctx) { return ctx->bytes_written; } int flash_img_init(struct flash_img_context *ctx) { ctx->bytes_written = 0; ctx->buf_bytes = 0U; #ifdef CONFIG_IMG_ERASE_PROGRESSIVELY ctx->off_last = -1; #endif return flash_area_open(FLASH_AREA_IMAGE_SECONDARY, (const struct flash_area **)&(ctx->flash_area)); }