/* * Copyright (c) 2018 Nordic Semiconductor ASA * Copyright (c) 2020 Cypress Semiconductor Corporation * Copyright (c) 2021 Texas Instruments * * 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. */ /*******************************************************************************/ #include #include #include #include #include "flash_map_backend.h" #include "mcuboot_config/mcuboot_config.h" #include #include "bootutil/bootutil_log.h" // clang-format off #include #include DeviceFamily_constructPath(driverlib/flash.h) #ifndef DeviceFamily_CC23X0R5 #include DeviceFamily_constructPath(driverlib/vims.h) #endif // clang-format on #ifdef TI_BOOT_USE_EXTERNAL_FLASH #include #endif /* TI_BOOT_USE_EXTERNAL_FLASH */ #define FLASH_BASE_ADDRESS 0 #ifdef DeviceFamily_CC23X0R5 /* Remap driverlib API names that changed only for cc23x0 */ #define FlashSectorSizeGet FlashGetSectorSize #define FlashSectorErase FlashEraseSector #endif #define FLASH_ERASE_VALUE 0xFF /* XXX: Someone defined assert backwards in this file. We've changed it back to * not conflict with the GCC assert. #define assert(x) if(x) {BOOT_LOG_ERR("assert: %s line %d", __FILE__, __LINE__);} */ #include #ifdef TI_FLASH_MAP_EXT_DESC /* Nothing to be there when external FlashMap Descriptors are used */ #else static const struct flash_area bootloader = { .fa_id = FLASH_AREA_BOOTLOADER, .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, .fa_off = BOOTLOADER_BASE_ADDRESS, .fa_size = BOOT_BOOTLOADER_SIZE }; static const struct flash_area primary_1 = { .fa_id = FLASH_AREA_IMAGE_PRIMARY(0), .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, .fa_off = BOOT_PRIMARY_1_BASE_ADDRESS, .fa_size = BOOT_PRIMARY_1_SIZE }; #if (MCUBOOT_IMAGE_NUMBER == 2) static const struct flash_area primary_2 = { .fa_id = FLASH_AREA_IMAGE_PRIMARY(1), .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, .fa_off = BOOT_PRIMARY_2_BASE_ADDRESS, .fa_size = BOOT_PRIMARY_2_SIZE }; #endif static const struct flash_area secondary_1 = { .fa_id = FLASH_AREA_IMAGE_SECONDARY(0), #ifndef TI_BOOT_USE_EXTERNAL_FLASH .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, #else .fa_device_id = FLASH_DEVICE_EXTERNAL_FLASH(0), #endif .fa_off = BOOT_SECONDARY_1_BASE_ADDRESS, .fa_size = BOOT_SECONDARY_1_SIZE }; #if (MCUBOOT_IMAGE_NUMBER == 2) static const struct flash_area secondary_2 = { .fa_id = FLASH_AREA_IMAGE_SECONDARY(1), #ifndef TI_BOOT_USE_EXTERNAL_FLASH .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, #else .fa_device_id = FLASH_DEVICE_EXTERNAL_FLASH(0), #endif .fa_off = BOOT_SECONDARY_2_BASE_ADDRESS, .fa_size = BOOT_SECONDARY_2_SIZE }; #endif #endif #if (MCUBOOT_IMAGE_NUMBER == 2) && (DeviceFamily_PARENT != DeviceFamily_PARENT_CC13X4_CC26X3_CC26X4) #error "MCUBOOT_IMAGE_NUMBER == 2 for current device not supported" #endif #if (MCUBOOT_IMAGE_NUMBER > 2) #error "More than 2 images not supported" #endif #ifdef MCUBOOT_SWAP_USING_SCRATCH static struct flash_area scratch = { #error "MCUBOOT_SWAP_USING_SCRATCH not supported" }; #endif #ifdef TI_FLASH_MAP_EXT_DESC /* Use external Flash Map Descriptors */ extern struct flash_area * boot_area_descs[]; #else static const 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 #ifdef TI_BOOT_USE_EXTERNAL_FLASH static bool extFlashStatus = false; #endif #ifndef DeviceFamily_CC23X0R5 /* Prepares system for a write to flash */ static uint8_t disableCache(void) { uint8_t mode = VIMSModeGet(VIMS_BASE); VIMSLineBufDisable(VIMS_BASE); if (mode != VIMS_MODE_DISABLED) { VIMSModeSet(VIMS_BASE, VIMS_MODE_DISABLED); while (VIMSModeGet(VIMS_BASE) != VIMS_MODE_DISABLED) ; } return (mode); } /* * ======== restoreFlashCache ======== */ static void enableCache(uint8_t mode) { if (mode != VIMS_MODE_DISABLED) { VIMSModeSet(VIMS_BASE, VIMS_MODE_ENABLED); } VIMSLineBufEnable(VIMS_BASE); } #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) || (fd_id != FLASH_DEVICE_EXTERNAL_FLASH(0))) { BOOT_LOG_ERR("invalid flash ID %d", fd_id); return -1; } *ret = FLASH_BASE_ADDRESS; 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]; #ifdef TI_BOOT_USE_EXTERNAL_FLASH if (boot_area_descs[i]->fa_device_id == FLASH_DEVICE_EXTERNAL_FLASH(0)) { if (!extFlashStatus) { ret = !extFlashOpen(); // if the external flash has been opened, update the status if (ret == 0) { extFlashStatus = true; } } else { ret = 0; } } else { ret = 0; } #else ret = 0; #endif /* TI_BOOT_USE_EXTERNAL_FLASH */ break; } i++; } return ret; } void flash_area_close(const struct flash_area * fa) { #ifdef TI_BOOT_USE_EXTERNAL_FLASH if (fa->fa_device_id == FLASH_DEVICE_EXTERNAL_FLASH(0)) { if (extFlashStatus) { extFlashClose(); extFlashStatus = false; } } #endif /* TI_BOOT_USE_EXTERNAL_FLASH */ (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; /* convert to absolute address inside a device*/ addr = fa->fa_off + off; /* check if read is within bounds */ assert((addr + len) <= (fa->fa_off + fa->fa_size)); 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 TI_BOOT_USE_EXTERNAL_FLASH else if (fa->fa_device_id == FLASH_DEVICE_EXTERNAL_FLASH(0)) { /* verify that flash is open */ if (!extFlashStatus) { rc = flash_area_open(fa->fa_id, &fa); if (rc != 0) { return rc; } } else { extFlashClose(); rc = !extFlashOpen(); // if the external flash has been opened, update the status if (rc == 0) { extFlashStatus = true; } } /* Read whole metadata header */ rc = !extFlashRead(addr, (size_t) len, (void *) dst); } #endif /* TI_BOOT_USE_EXTERNAL_FLASH */ 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) { int rc = -1; size_t write_start_addr; /* convert to absolute address inside a device*/ write_start_addr = fa->fa_off + off; /* check if read is within bounds */ assert((write_start_addr + len) <= (fa->fa_off + fa->fa_size)); if (fa->fa_device_id == FLASH_DEVICE_INTERNAL_FLASH) { uint32_t flashStat; #ifndef DeviceFamily_CC23X0R5 uint8_t cacheState = disableCache(); #endif flashStat = FlashProgram((uint8_t *) src, write_start_addr, len); if (flashStat == FAPI_STATUS_SUCCESS) { rc = 0; } #ifndef DeviceFamily_CC23X0R5 enableCache(cacheState); #endif } #ifdef TI_BOOT_USE_EXTERNAL_FLASH else if (fa->fa_device_id == FLASH_DEVICE_EXTERNAL_FLASH(0)) { /* verify that flash is open */ if (!extFlashStatus) { rc = flash_area_open(fa->fa_id, &fa); if (rc != 0) { return rc; } } rc = !extFlashWrite(write_start_addr, (size_t) len, (uint8_t *) src); } #endif /* TI_BOOT_USE_EXTERNAL_FLASH */ else { /* incorrect/non-existing flash device id */ rc = -1; } return rc; } /*< Erases `len` bytes of flash memory at `off` */ int flash_area_erase(const struct flash_area * fa, uint32_t off, uint32_t len) { int rc = -1; size_t erase_start_addr; /* convert to absolute address inside a device*/ erase_start_addr = fa->fa_off + off; /* check if read is within bounds */ assert((erase_start_addr + len) <= (fa->fa_off + fa->fa_size)); if (fa->fa_device_id == FLASH_DEVICE_INTERNAL_FLASH) { uint8_t flashStat; uint32_t pageAddr; uint32_t sectorSize = FlashSectorSizeGet(); assert(0 == erase_start_addr % sectorSize); #ifndef DeviceFamily_CC23X0R5 uint8_t cacheState = disableCache(); #endif for (pageAddr = erase_start_addr; pageAddr < erase_start_addr + len; pageAddr += sectorSize) { flashStat = FlashSectorErase(pageAddr); if (flashStat != FAPI_STATUS_SUCCESS) { #ifndef DeviceFamily_CC23X0R5 enableCache(cacheState); #endif return rc; } } #ifndef DeviceFamily_CC23X0R5 enableCache(cacheState); #endif rc = 0; } #ifdef TI_BOOT_USE_EXTERNAL_FLASH else if (fa->fa_device_id == FLASH_DEVICE_EXTERNAL_FLASH(0)) { assert(0 == erase_start_addr % EXT_FLASH_PAGE_SIZE); /* verify that flash is open */ if (!extFlashStatus) { rc = flash_area_open(fa->fa_id, &fa); if (rc != 0) { return rc; } } rc = !extFlashErase(erase_start_addr, (size_t) len); } #endif /* TI_BOOT_USE_EXTERNAL_FLASH */ else { /* incorrect/non-existing flash device id */ rc = -1; } return (int) rc; } /*< Returns this `flash_area`s alignment */ size_t flash_area_align(const struct flash_area * fa) { int ret = -1; if (fa->fa_device_id == FLASH_DEVICE_INTERNAL_FLASH) { ret = FlashSectorSizeGet(); } #ifdef TI_BOOT_USE_EXTERNAL_FLASH if (fa->fa_device_id == FLASH_DEVICE_EXTERNAL_FLASH(0)) { ret = EXT_FLASH_PAGE_SIZE; } #endif /* TI_BOOT_USE_EXTERNAL_FLASH */ else { /* incorrect/non-existing flash device id */ ret = -1; } return ret; } #ifndef 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; } 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) || (fap->fa_device_id == FLASH_DEVICE_EXTERNAL_FLASH(0))) { ret = FLASH_ERASE_VALUE; } 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 = (struct flash_area *) 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 = FlashSectorSizeGet(); } #ifdef TI_BOOT_USE_EXTERNAL_FLASH else if (fa->fa_device_id == FLASH_DEVICE_EXTERNAL_FLASH(0)) { sector_size = EXT_FLASH_PAGE_SIZE; } #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 #ifdef MCUBOOT_HW_ROLLBACK_PROT #ifdef DeviceFamily_CC23X0R5 #define FLASH_WRITE_PROTECT 0 /* * There is no implementation of FlashProtectionSet() in cc23x0 driverlib. * For now it was decided to implement this feature directly in this module. * */ static void FlashProtectionSet(uint32_t address, uint32_t mode) { uint32_t sector_number; uint32_t sector_size = FlashSectorSizeGet(); uint32_t mask; /* * Check that address is within expected range, as in cc23x0r5 * only the first 32 sectors can be individually locked. */ if ((address >= (FLASH_MAIN_BASE + (32 * sector_size))) || (0 != (address & (sector_size - 1)))) { return; } sector_number = (address - FLASH_MAIN_BASE) / sector_size; mask = 1 << sector_number; HWREG(VIMS_BASE + VIMS_O_WEPRA) &= ~mask; } #endif void flash_area_lock(const struct flash_area * fa) { size_t addr; /* convert to absolute address inside a device*/ addr = fa->fa_off; if (fa->fa_device_id == FLASH_DEVICE_INTERNAL_FLASH) { FlashProtectionSet(addr, FLASH_WRITE_PROTECT); } } #endif