/* * Copyright (c) 2018 Nordic Semiconductor ASA * Copyright (c) 2020 Cypress Semiconductor Corporation * * SPDX-License-Identifier: Apache-2.0 */ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /*******************************************************************************/ #ifdef MCUBOOT_HAVE_ASSERT_H #include "mcuboot_config/mcuboot_assert.h" #else #include #endif #include #include #include #include "mcuboot_config/mcuboot_config.h" #include "flash_map_backend/flash_map_backend.h" #include #include "bootutil/bootutil_log.h" #include "cy_pdl.h" #ifdef CY_BOOT_USE_EXTERNAL_FLASH #include "cy_smif_psoc6.h" #endif /* * For now, we only support one flash device. * * Pick a random device ID for it that's unlikely to collide with * anything "real". */ #define FLASH_DEVICE_ID 111 #define FLASH_MAP_ENTRY_MAGIC 0xd00dbeef #define FLASH_AREA_IMAGE_SECTOR_SIZE FLASH_AREA_IMAGE_SCRATCH_SIZE #ifndef CY_BOOTLOADER_START_ADDRESS #define CY_BOOTLOADER_START_ADDRESS (0x10000000) #endif #ifndef CY_BOOT_INTERNAL_FLASH_ERASE_VALUE /* This is the value of internal flash bytes after an erase */ #define CY_BOOT_INTERNAL_FLASH_ERASE_VALUE (0x00) #endif #ifndef CY_BOOT_EXTERNAL_FLASH_ERASE_VALUE /* This is the value of external flash bytes after an erase */ #define CY_BOOT_EXTERNAL_FLASH_ERASE_VALUE (0xff) #endif #ifdef CY_FLASH_MAP_EXT_DESC /* Nothing to be there when external FlashMap Descriptors are used */ #else static struct flash_area bootloader = { .fa_id = FLASH_AREA_BOOTLOADER, .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, .fa_off = CY_BOOTLOADER_START_ADDRESS, .fa_size = CY_BOOT_BOOTLOADER_SIZE }; static struct flash_area primary_1 = { .fa_id = FLASH_AREA_IMAGE_PRIMARY(0), .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, .fa_off = CY_FLASH_BASE + CY_BOOT_BOOTLOADER_SIZE, .fa_size = CY_BOOT_PRIMARY_1_SIZE }; #ifndef CY_BOOT_USE_EXTERNAL_FLASH static struct flash_area secondary_1 = { .fa_id = FLASH_AREA_IMAGE_SECONDARY(0), .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, .fa_off = CY_FLASH_BASE +\ CY_BOOT_BOOTLOADER_SIZE +\ CY_BOOT_PRIMARY_1_SIZE, .fa_size = CY_BOOT_SECONDARY_1_SIZE }; #else static struct flash_area secondary_1 = { .fa_id = FLASH_AREA_IMAGE_SECONDARY(0), .fa_device_id = FLASH_DEVICE_EXTERNAL_FLASH(CY_BOOT_EXTERNAL_DEVICE_INDEX), .fa_off = CY_SMIF_BASE_MEM_OFFSET, .fa_size = CY_BOOT_SECONDARY_1_SIZE }; #endif #if (MCUBOOT_IMAGE_NUMBER == 2) /* if dual-image */ static struct flash_area primary_2 = { .fa_id = FLASH_AREA_IMAGE_PRIMARY(1), .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, .fa_off = CY_FLASH_BASE +\ CY_BOOT_BOOTLOADER_SIZE +\ CY_BOOT_PRIMARY_1_SIZE +\ CY_BOOT_SECONDARY_1_SIZE, .fa_size = CY_BOOT_PRIMARY_2_SIZE }; static struct flash_area secondary_2 = { .fa_id = FLASH_AREA_IMAGE_SECONDARY(1), /* it is for external flash memory .fa_device_id = FLASH_DEVICE_EXTERNAL_FLASH(CY_BOOT_EXTERNAL_DEVICE_INDEX), */ #ifndef CY_BOOT_USE_EXTERNAL_FLASH .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, .fa_off = CY_FLASH_BASE +\ CY_BOOT_BOOTLOADER_SIZE +\ CY_BOOT_PRIMARY_1_SIZE +\ CY_BOOT_SECONDARY_1_SIZE +\ CY_BOOT_PRIMARY_2_SIZE, #else .fa_device_id = FLASH_DEVICE_EXTERNAL_FLASH(CY_BOOT_EXTERNAL_DEVICE_INDEX), .fa_off = CY_SMIF_BASE_MEM_OFFSET + 0x40000, #endif .fa_size = CY_BOOT_SECONDARY_2_SIZE }; #endif #endif #ifdef MCUBOOT_SWAP_USING_SCRATCH static struct flash_area scratch = { .fa_id = FLASH_AREA_IMAGE_SCRATCH, .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, #if (MCUBOOT_IMAGE_NUMBER == 1) /* if single-image */ .fa_off = CY_FLASH_BASE +\ CY_BOOT_BOOTLOADER_SIZE +\ CY_BOOT_PRIMARY_1_SIZE +\ CY_BOOT_SECONDARY_1_SIZE, #elif (MCUBOOT_IMAGE_NUMBER == 2) /* if dual-image */ .fa_off = CY_FLASH_BASE +\ CY_BOOT_BOOTLOADER_SIZE +\ CY_BOOT_PRIMARY_1_SIZE +\ CY_BOOT_SECONDARY_1_SIZE +\ CY_BOOT_PRIMARY_2_SIZE +\ CY_BOOT_SECONDARY_2_SIZE, #endif .fa_size = CY_BOOT_SCRATCH_SIZE }; #endif #ifdef CY_FLASH_MAP_EXT_DESC /* Use external Flash Map Descriptors */ extern struct flash_area *boot_area_descs[]; #else struct flash_area *boot_area_descs[] = { &bootloader, &primary_1, &secondary_1, #if (MCUBOOT_IMAGE_NUMBER == 2) /* if dual-image */ &primary_2, &secondary_2, #endif #ifdef MCUBOOT_SWAP_USING_SCRATCH &scratch, #endif NULL }; #endif /* Returns device flash start based on supported fa_id */ int flash_device_base(uint8_t fd_id, uintptr_t *ret) { if (fd_id != FLASH_DEVICE_INTERNAL_FLASH) { BOOT_LOG_ERR("invalid flash ID %d; expected %d", fd_id, FLASH_DEVICE_INTERNAL_FLASH); return -1; } *ret = CY_FLASH_BASE; return 0; } /* Opens the area for use. id is one of the `fa_id`s */ int flash_area_open(uint8_t id, const struct flash_area **fa) { int ret = -1; uint32_t i = 0; while(NULL != boot_area_descs[i]) { if(id == boot_area_descs[i]->fa_id) { *fa = boot_area_descs[i]; ret = 0; break; } i++; } return ret; } void flash_area_close(const struct flash_area *fa) { (void)fa;/* Nothing to do there */ } /* * Reads `len` bytes of flash memory at `off` to the buffer at `dst` */ int flash_area_read(const struct flash_area *fa, uint32_t off, void *dst, uint32_t len) { int rc = 0; size_t addr; /* check if requested offset not less then flash area (fa) start */ assert(off < fa->fa_off); assert(off + len < fa->fa_off); /* convert to absolute address inside a device*/ addr = fa->fa_off + off; if (fa->fa_device_id == FLASH_DEVICE_INTERNAL_FLASH) { /* flash read by simple memory copying */ memcpy((void *)dst, (const void*)addr, (size_t)len); } #ifdef CY_BOOT_USE_EXTERNAL_FLASH else if ((fa->fa_device_id & FLASH_DEVICE_EXTERNAL_FLAG) == FLASH_DEVICE_EXTERNAL_FLAG) { rc = psoc6_smif_read(fa, addr, dst, len); } #endif else { /* incorrect/non-existing flash device id */ rc = -1; } if (rc != 0) { BOOT_LOG_ERR("Flash area read error, rc = %d", (int)rc); } return rc; } /* * Writes `len` bytes of flash memory at `off` from the buffer at `src` */ int flash_area_write(const struct flash_area *fa, uint32_t off, const void *src, uint32_t len) { cy_en_flashdrv_status_t rc = CY_FLASH_DRV_SUCCESS; size_t write_start_addr; size_t write_end_addr; const uint32_t * row_ptr = NULL; assert(off < fa->fa_off); assert(off + len < fa->fa_off); /* convert to absolute address inside a device */ write_start_addr = fa->fa_off + off; write_end_addr = fa->fa_off + off + len; if (fa->fa_device_id == FLASH_DEVICE_INTERNAL_FLASH) { uint32_t row_number = 0; uint32_t row_addr = 0; assert(!(len % CY_FLASH_SIZEOF_ROW)); assert(!(write_start_addr % CY_FLASH_SIZEOF_ROW)); row_number = (write_end_addr - write_start_addr) / CY_FLASH_SIZEOF_ROW; row_addr = write_start_addr; row_ptr = (uint32_t *) src; for (uint32_t i = 0; i < row_number; i++) { rc = Cy_Flash_WriteRow(row_addr, row_ptr); row_addr += (uint32_t) CY_FLASH_SIZEOF_ROW; row_ptr = row_ptr + CY_FLASH_SIZEOF_ROW / 4; } } #ifdef CY_BOOT_USE_EXTERNAL_FLASH else if ((fa->fa_device_id & FLASH_DEVICE_EXTERNAL_FLAG) == FLASH_DEVICE_EXTERNAL_FLAG) { rc = psoc6_smif_write(fa, write_start_addr, src, len); } #endif else { /* incorrect/non-existing flash device id */ rc = -1; } return (int) rc; } /*< Erases `len` bytes of flash memory at `off` */ int flash_area_erase(const struct flash_area *fa, uint32_t off, uint32_t len) { cy_en_flashdrv_status_t rc = CY_FLASH_DRV_SUCCESS; size_t erase_start_addr; size_t erase_end_addr; assert(off < fa->fa_off); assert(off + len < fa->fa_off); assert(!(len % CY_FLASH_SIZEOF_ROW)); /* convert to absolute address inside a device*/ erase_start_addr = fa->fa_off + off; erase_end_addr = fa->fa_off + off + len; if (fa->fa_device_id == FLASH_DEVICE_INTERNAL_FLASH) { int row_number = 0; uint32_t row_addr = 0; row_number = (erase_end_addr - erase_start_addr) / CY_FLASH_SIZEOF_ROW; while (row_number != 0) { row_number--; row_addr = erase_start_addr + row_number * (uint32_t) CY_FLASH_SIZEOF_ROW; rc = Cy_Flash_EraseRow(row_addr); } } #ifdef CY_BOOT_USE_EXTERNAL_FLASH else if ((fa->fa_device_id & FLASH_DEVICE_EXTERNAL_FLAG) == FLASH_DEVICE_EXTERNAL_FLAG) { rc = psoc6_smif_erase(erase_start_addr, len); } #endif else { /* incorrect/non-existing flash device id */ rc = -1; } return (int) rc; } /*< Returns this `flash_area`s alignment */ uint32_t flash_area_align(const struct flash_area *fa) { int ret = -1; if (fa->fa_device_id == FLASH_DEVICE_INTERNAL_FLASH) { ret = CY_FLASH_ALIGN; } #ifdef CY_BOOT_USE_EXTERNAL_FLASH else if ((fa->fa_device_id & FLASH_DEVICE_EXTERNAL_FLAG) == FLASH_DEVICE_EXTERNAL_FLAG) { return qspi_get_prog_size(); } #endif else { /* incorrect/non-existing flash device id */ ret = -1; } return ret; } #ifdef MCUBOOT_USE_FLASH_AREA_GET_SECTORS /*< Initializes an array of flash_area elements for the slot's sectors */ int flash_area_to_sectors(int idx, int *cnt, struct flash_area *fa) { int rc = 0; if (fa->fa_device_id == FLASH_DEVICE_INTERNAL_FLASH) { (void)idx; (void)cnt; rc = 0; } #ifdef CY_BOOT_USE_EXTERNAL_FLASH else if ((fa->fa_device_id & FLASH_DEVICE_EXTERNAL_FLAG) == FLASH_DEVICE_EXTERNAL_FLAG) { (void)idx; (void)cnt; rc = 0; } #endif else { /* incorrect/non-existing flash device id */ rc = -1; } return rc; } #endif /* * This depends on the mappings defined in sysflash.h. * MCUBoot uses continuous numbering for the primary slot, the secondary slot, * and the scratch while zephyr might number it differently. */ int flash_area_id_from_multi_image_slot(int image_index, int slot) { switch (slot) { case 0: return FLASH_AREA_IMAGE_PRIMARY(image_index); case 1: return FLASH_AREA_IMAGE_SECONDARY(image_index); case 2: return FLASH_AREA_IMAGE_SCRATCH; } return -1; /* flash_area_open will fail on that */ } int flash_area_id_from_image_slot(int slot) { return flash_area_id_from_multi_image_slot(0, slot); } int flash_area_id_to_multi_image_slot(int image_index, int area_id) { if (area_id == FLASH_AREA_IMAGE_PRIMARY(image_index)) { return 0; } if (area_id == FLASH_AREA_IMAGE_SECONDARY(image_index)) { return 1; } return -1; } int flash_area_id_to_image_slot(int area_id) { return flash_area_id_to_multi_image_slot(0, area_id); } uint8_t flash_area_erased_val(const struct flash_area *fap) { int ret = 0; if (fap->fa_device_id == FLASH_DEVICE_INTERNAL_FLASH) { ret = CY_BOOT_INTERNAL_FLASH_ERASE_VALUE; } #ifdef CY_BOOT_USE_EXTERNAL_FLASH else if ((fap->fa_device_id & FLASH_DEVICE_EXTERNAL_FLAG) == FLASH_DEVICE_EXTERNAL_FLAG) { ret = CY_BOOT_EXTERNAL_FLASH_ERASE_VALUE; } #endif else { assert(false) ; } return ret ; } int flash_area_read_is_empty(const struct flash_area *fa, uint32_t off, void *dst, uint32_t len) { uint8_t *mem_dest; int rc; mem_dest = (uint8_t *)dst; rc = flash_area_read(fa, off, dst, len); if (rc) { return -1; } for (uint8_t i = 0; i < len; i++) { if (mem_dest[i] != flash_area_erased_val(fa)) { return 0; } } return 1; } #ifdef MCUBOOT_USE_FLASH_AREA_GET_SECTORS int flash_area_get_sectors(int idx, uint32_t *cnt, struct flash_sector *ret) { int rc = 0; uint32_t i = 0; struct flash_area *fa = NULL; while(NULL != boot_area_descs[i]) { if(idx == boot_area_descs[i]->fa_id) { fa = boot_area_descs[i]; break; } i++; } if(NULL != boot_area_descs[i]) { size_t sector_size = 0; if(fa->fa_device_id == FLASH_DEVICE_INTERNAL_FLASH) { sector_size = CY_FLASH_SIZEOF_ROW; } #ifdef CY_BOOT_USE_EXTERNAL_FLASH else if((fa->fa_device_id & FLASH_DEVICE_EXTERNAL_FLAG) == FLASH_DEVICE_EXTERNAL_FLAG) { /* implement for SMIF */ /* lets assume they are equal */ sector_size = CY_FLASH_SIZEOF_ROW; } #endif else { rc = -1; } if(0 == rc) { uint32_t addr = 0; size_t sectors_n = 0; sectors_n = (fa->fa_size + (sector_size - 1)) / sector_size; assert(sectors_n <= *cnt); addr = fa->fa_off; for(i = 0; i < sectors_n; i++) { ret[i].fs_size = sector_size ; ret[i].fs_off = addr ; addr += sector_size ; } *cnt = sectors_n; } } else { rc = -1; } return rc; } #endif