| /* |
| * 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 <logging/log.h> |
| LOG_MODULE_REGISTER(LOG_MODULE_NAME); |
| |
| #include <zephyr/types.h> |
| #include <stddef.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <dfu/flash_img.h> |
| #include <inttypes.h> |
| |
| #ifdef CONFIG_IMG_ERASE_PROGRESSIVELY |
| #include <dfu/mcuboot.h> |
| #include <drivers/flash.h> |
| #endif |
| |
| #include <devicetree.h> |
| /* 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)); |
| } |