FWU: Add Firmware Update partition
Firmware Update(FWU) partition provides the functionality
of updating firmware images. This patch implemented the
partition in Library mode.
Change-Id: I736477549b055c64cd8106ad57c3ad7b1b2007ee
Signed-off-by: Sherry Zhang <sherry.zhang2@arm.com>
diff --git a/secure_fw/CMakeLists.txt b/secure_fw/CMakeLists.txt
index 364e245..26dbfa2 100644
--- a/secure_fw/CMakeLists.txt
+++ b/secure_fw/CMakeLists.txt
@@ -26,6 +26,7 @@
add_subdirectory(partitions/internal_trusted_storage)
add_subdirectory(partitions/platform)
add_subdirectory(partitions/psa_proxy)
+add_subdirectory(partitions/firmware_update)
add_subdirectory(spm)
target_include_directories(secure_fw
diff --git a/secure_fw/partitions/firmware_update/CMakeLists.txt b/secure_fw/partitions/firmware_update/CMakeLists.txt
new file mode 100644
index 0000000..b3bfe2d
--- /dev/null
+++ b/secure_fw/partitions/firmware_update/CMakeLists.txt
@@ -0,0 +1,73 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+if (NOT TFM_PARTITION_FIRMWARE_UPDATE)
+ return()
+endif()
+
+cmake_minimum_required(VERSION 3.15)
+cmake_policy(SET CMP0079 NEW)
+
+add_library(tfm_psa_rot_partition_fwu STATIC)
+
+target_include_directories(tfm_psa_rot_partition_fwu
+ PRIVATE
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+ ${CMAKE_BINARY_DIR}/generated/secure_fw/partitions/firmware_update
+)
+
+target_sources(tfm_psa_rot_partition_fwu
+ PRIVATE
+ tfm_fwu_req_mngr.c
+ tfm_fwu.c
+)
+
+# Include the bootloader specific configuration.
+if ((NOT TFM_FWU_BOOTLOADER_LIB) OR (NOT EXISTS ${TFM_FWU_BOOTLOADER_LIB}))
+ message(FATAL_ERROR "TFM_FWU_BOOTLOADER_LIB invalid")
+endif()
+
+include(${TFM_FWU_BOOTLOADER_LIB})
+
+target_link_libraries(tfm_psa_rot_partition_fwu
+ PRIVATE
+ tfm_secure_api
+ psa_interface
+ platform_s
+ tfm_sprt
+)
+
+target_compile_definitions(tfm_psa_rot_partition_fwu
+ PRIVATE
+ $<$<BOOL:${TFM_PSA_API}>:TFM_PSA_API>
+)
+
+############################ Secure API ########################################
+
+target_sources(tfm_secure_api
+ INTERFACE
+ ${CMAKE_CURRENT_SOURCE_DIR}/tfm_fwu_secure_api.c
+)
+
+# The veneers give warnings about not being properly declared so they get hidden
+# to not overshadow _real_ warnings.
+set_source_files_properties(tfm_fwu_secure_api.c
+ PROPERTIES
+ COMPILE_FLAGS -Wno-implicit-function-declaration
+)
+
+############################ Partition Defs ####################################
+
+target_link_libraries(tfm_partitions
+ INTERFACE
+ tfm_psa_rot_partition_fwu
+)
+
+target_compile_definitions(tfm_partition_defs
+ INTERFACE
+ TFM_PARTITION_FIRMWARE_UPDATE
+)
diff --git a/secure_fw/partitions/firmware_update/bootloader/mcuboot/mcuboot_utilities.cmake b/secure_fw/partitions/firmware_update/bootloader/mcuboot/mcuboot_utilities.cmake
new file mode 100644
index 0000000..9aa315c
--- /dev/null
+++ b/secure_fw/partitions/firmware_update/bootloader/mcuboot/mcuboot_utilities.cmake
@@ -0,0 +1,40 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+add_library(tfm_fwu_mcuboot_util INTERFACE)
+
+target_sources(tfm_fwu_mcuboot_util
+ INTERFACE
+ ${MCUBOOT_PATH}/boot/bootutil/src/bootutil_misc.c
+ ${CMAKE_SOURCE_DIR}/bl2/src/flash_map.c
+ ${CMAKE_SOURCE_DIR}/bl2/ext/mcuboot/flash_map_extended.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/bootloader/mcuboot/tfm_mcuboot_fwu.c
+)
+
+target_include_directories(tfm_fwu_mcuboot_util
+ INTERFACE
+ ${CMAKE_BINARY_DIR}/bl2/ext/mcuboot
+ ${CMAKE_SOURCE_DIR}/bl2/ext/mcuboot/include
+ ${MCUBOOT_PATH}/boot/bootutil/include
+ ${MCUBOOT_PATH}/boot/bootutil/src
+ ${CMAKE_CURRENT_SOURCE_DIR}/bootloader
+)
+
+target_link_libraries(tfm_fwu_mcuboot_util
+ INTERFACE
+ platform_region_defs
+)
+
+target_link_libraries(tfm_psa_rot_partition_fwu
+ PRIVATE
+ tfm_fwu_mcuboot_util
+)
+
+target_compile_definitions(tfm_psa_rot_partition_fwu
+ PRIVATE
+ MCUBOOT_${MCUBOOT_UPGRADE_STRATEGY}
+)
diff --git a/secure_fw/partitions/firmware_update/bootloader/mcuboot/tfm_mcuboot_fwu.c b/secure_fw/partitions/firmware_update/bootloader/mcuboot/tfm_mcuboot_fwu.c
new file mode 100644
index 0000000..6ad347c
--- /dev/null
+++ b/secure_fw/partitions/firmware_update/bootloader/mcuboot/tfm_mcuboot_fwu.c
@@ -0,0 +1,540 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "psa/crypto.h"
+#include "log/tfm_log.h"
+#include "bootutil_priv.h"
+#include "bootutil/bootutil.h"
+#include "bootutil/image.h"
+#include "flash_map_backend/flash_map_backend.h"
+#include "sysflash/sysflash.h"
+#include "tfm_bootloader_fwu_abstraction.h"
+#include "tfm_fwu_req_mngr.h"
+#include "tfm_boot_status.h"
+#include "service_api.h"
+#include "tfm_memory_utils.h"
+#include "tfm_secure_api.h"
+
+#if (MCUBOOT_IMAGE_NUMBER == 1)
+#define MAX_IMAGE_INFO_LENGTH (sizeof(struct image_version) + \
+ SHARED_DATA_ENTRY_HEADER_SIZE)
+#else
+#define MAX_IMAGE_INFO_LENGTH 2 * (sizeof(struct image_version) + \
+ SHARED_DATA_ENTRY_HEADER_SIZE)
+#endif
+#define TFM_MCUBOOT_FWU_INVALID_IMAGE_ID 0xFF
+/*
+ * \struct fwu_image_info_data
+ *
+ * \brief Contains the received boot status information from bootloader
+ *
+ * \details This is a redefinition of \ref tfm_boot_data to allocate the
+ * appropriate, service dependent size of \ref boot_data.
+ */
+typedef struct fwu_image_info_data_s {
+ struct shared_data_tlv_header header;
+ uint8_t data[MAX_IMAGE_INFO_LENGTH];
+} fwu_image_info_data_t;
+
+typedef struct tfm_fwu_mcuboot_ctx_s {
+ /* The flash area corresponding to mcuboot_image_id. */
+ const struct flash_area *fap;
+ uint8_t mcuboot_image_id;
+
+ /* The size of the downloaded data in the FWU process. */
+ size_t loaded_size;
+} tfm_fwu_mcuboot_ctx_t;
+
+static tfm_fwu_mcuboot_ctx_t mcuboot_ctx[TFM_FWU_MAX_IMAGES];
+static fwu_image_info_data_t boot_shared_data;
+
+static int convert_id_from_bl_to_mcuboot(bl_image_id_t bl_image_id,
+ uint8_t *mcuboot_image_id)
+{
+#if (MCUBOOT_IMAGE_NUMBER == 1)
+ /* Only full image upgrade is supported in this case. */
+ if (bl_image_id != FWU_IMAGE_TYPE_FULL) {
+ LOG_MSG("TFM FWU: multi-image is not supported in current mcuboot configuration.");
+ return -1;
+ }
+
+ /* The image id in mcuboot. 0: the full image. */
+ *mcuboot_image_id = 0;
+#else
+ if (bl_image_id == FWU_IMAGE_TYPE_SECURE) {
+ /* The image id in mcuboot. 0: the secure image. */
+ *mcuboot_image_id = 0;
+ } else if (bl_image_id == FWU_IMAGE_TYPE_NONSECURE) {
+ /* The image id in mcuboot. 1: the non-secure image. */
+ *mcuboot_image_id = 1;
+ } else {
+ LOG_MSG("TFM FWU: invalid image_type: %d", bl_image_id);
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+static int convert_id_from_mcuboot_to_bl(uint8_t mcuboot_image_id,
+ bl_image_id_t *bl_image_id)
+{
+ uint8_t image_type;
+
+ if (bl_image_id == NULL) {
+ return -1;
+ }
+
+#if (MCUBOOT_IMAGE_NUMBER == 1)
+ /* Only full image upgrade is supported in this case. */
+ if (mcuboot_image_id != 0) {
+ LOG_MSG("TFM FWU: multi-image is not supported in current mcuboot configuration.\n\r");
+ return -1;
+ }
+
+ /* The image id in mcuboot. 0: the full image. */
+ image_type = FWU_IMAGE_TYPE_FULL;
+#else
+ if (mcuboot_image_id == 0) {
+ /* The image id in mcuboot. 0: the secure image. */
+ image_type = FWU_IMAGE_TYPE_SECURE;
+ } else if (mcuboot_image_id == 1) {
+ /* The image id in mcuboot. 1: the non-secure image. */
+ image_type = FWU_IMAGE_TYPE_NONSECURE;
+ } else {
+ LOG_MSG("TFM FWU: invalid mcuboot image id\n\r: %d",
+ mcuboot_image_id);
+ return -1;
+ }
+#endif
+ *bl_image_id = image_type;
+ return 0;
+}
+
+
+/*
+ * Get the flash area of the image mcuboot_image_id.
+ */
+static bool get_flash_image_index(uint8_t mcuboot_image_id, uint8_t *index)
+{
+ for (uint8_t i = 0; i < TFM_FWU_MAX_IMAGES; i++) {
+ if (mcuboot_ctx[i].mcuboot_image_id == mcuboot_image_id) {
+ *index = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool get_next_free_index(uint8_t *index)
+{
+ for (uint8_t i = 0; i < TFM_FWU_MAX_IMAGES; i++) {
+ if (mcuboot_ctx[i].fap == NULL) {
+ *index = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+static int fwu_bootloader_get_shared_data(void)
+{
+ return tfm_core_get_boot_data(TLV_MAJOR_FWU,
+ (struct tfm_boot_data *)&boot_shared_data,
+ sizeof(boot_shared_data));
+}
+
+psa_status_t fwu_bootloader_init(void)
+{
+ if (fwu_bootloader_get_shared_data() != TFM_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t fwu_bootloader_staging_area_init(bl_image_id_t bootloader_image_id)
+{
+ uint8_t mcuboot_image_id = 0;
+ uint8_t index;
+ const struct flash_area *fap;
+
+ if (convert_id_from_bl_to_mcuboot(bootloader_image_id,
+ &mcuboot_image_id) != 0) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (flash_area_open(FLASH_AREA_IMAGE_SECONDARY(mcuboot_image_id),
+ &fap) != 0) {
+ LOG_MSG("TFM FWU: opening flash failed.\r\n");
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ if (get_flash_image_index(mcuboot_image_id, &index) ||
+ get_next_free_index(&index)) {
+ mcuboot_ctx[index].mcuboot_image_id = mcuboot_image_id;
+ mcuboot_ctx[index].fap = fap;
+
+ /* Reset the loaded_size. */
+ mcuboot_ctx[index].loaded_size = 0;
+ } else {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+
+ if (flash_area_erase(fap, 0, fap->fa_size) != 0) {
+ LOG_MSG("TFM FWU: erasing flash failed.\r\n");
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t fwu_bootloader_load_image(bl_image_id_t bootloader_image_id,
+ size_t block_offset,
+ const void *block,
+ size_t block_size)
+{
+ uint8_t mcuboot_image_id = 0;
+ uint8_t index;
+ const struct flash_area *fap;
+
+ if (block == NULL) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (convert_id_from_bl_to_mcuboot(bootloader_image_id,
+ &mcuboot_image_id) != 0) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ /* The image should already be added into the mcuboot_ctx. */
+ if (get_flash_image_index(mcuboot_image_id, &index)) {
+ fap = mcuboot_ctx[index].fap;
+ } else {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (flash_area_write(fap, block_offset, block, block_size) != 0) {
+ LOG_MSG("TFM FWU: write flash failed.\r\n");
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* The overflow check has been done in flash_area_write. */
+ mcuboot_ctx[index].loaded_size += block_size;
+ return PSA_SUCCESS;
+}
+
+static bool check_image_dependency(uint8_t mcuboot_image_id,
+ uint8_t *dependency,
+ psa_image_version_t *version)
+{
+ bool found = false;
+
+ if ((dependency == NULL || version == NULL)) {
+ return found;
+ }
+
+ /* Currently only single image update is supported. So no dependency is
+ * required.
+ */
+ /* TODO: Add the dependency check to support multiple image update.*/
+ *dependency = TFM_MCUBOOT_FWU_INVALID_IMAGE_ID;
+ *version = (psa_image_version_t){.iv_major = 0, .iv_minor = 0,
+ .iv_revision = 0, .iv_build_num = 0};
+
+ return found;
+}
+
+psa_status_t fwu_bootloader_install_image(bl_image_id_t bootloader_image_id,
+ bl_image_id_t *dependency,
+ psa_image_version_t *dependency_version)
+{
+ uint8_t mcuboot_image_id = 0;
+ uint8_t dependency_mcuboot;
+ bl_image_id_t dependency_bl;
+ psa_image_version_t version;
+ const struct flash_area *fap;
+ uint8_t index;
+
+ if ((dependency == NULL || dependency_version == NULL)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (convert_id_from_bl_to_mcuboot(bootloader_image_id,
+ &mcuboot_image_id) != 0) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ /* The image should already be added into the mcuboot_ctx. */
+ if (get_flash_image_index(mcuboot_image_id, &index)) {
+ fap = mcuboot_ctx[index].fap;
+ } else {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (check_image_dependency(mcuboot_image_id,
+ &dependency_mcuboot,
+ &version)) {
+ if (convert_id_from_mcuboot_to_bl(dependency_mcuboot,
+ &dependency_bl) != 0) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ *dependency = dependency_bl;
+ *dependency_version = version;
+
+ /* PSA_ERROR_DEPENDENCY_NEEDED indicates that a dependency is
+ * required. See the function description in
+ * tfm_bootloader_fwu_abstraction.h.
+ */
+ return PSA_ERROR_DEPENDENCY_NEEDED;
+ } else {
+ /* Write the magic in the image trailer so that this image will be set
+ * taken as a candidate.
+ */
+ if (boot_write_magic(fap) != 0) {
+ return PSA_ERROR_GENERIC_ERROR;
+ } else {
+ /* System reboot is always required. */
+ return PSA_SUCCESS_REBOOT;
+ }
+ }
+}
+
+psa_status_t fwu_bootloader_mark_image_accepted(void)
+{
+ /* As DIRECT_XIP, RAM_LOAD and OVERWRITE_ONLY do not support image revert.
+ * So there is nothing to do in these three upgrade strategies. Image
+ * revert is only supported in SWAP upgrade strategy. In this case, the
+ * image need to be set as a permanent image, so that the next time reboot
+ * up, the image will still be the running image, otherwise, the image will
+ * be reverted and the original image will be chosen as the running image.
+ */
+#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) && \
+ !defined(MCUBOOT_OVERWRITE_ONLY)
+ if (boot_set_confirmed() != 0) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+#endif
+ return PSA_SUCCESS;
+}
+
+psa_status_t fwu_bootloader_abort(bl_image_id_t bootloader_image_id)
+{
+ uint8_t mcuboot_image_id = 0;
+ const struct flash_area *fap;
+ uint8_t index;
+
+ if (convert_id_from_bl_to_mcuboot(bootloader_image_id,
+ &mcuboot_image_id) != 0) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ /* The image should already be added into the mcuboot_ctx. */
+ if (get_flash_image_index(mcuboot_image_id, &index)) {
+ fap = mcuboot_ctx[index].fap;
+ } else {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ flash_area_erase(fap, 0, fap->fa_size);
+ flash_area_close(fap);
+ mcuboot_ctx[index].fap = NULL;
+ mcuboot_ctx[index].loaded_size = 0;
+ return PSA_SUCCESS;
+}
+
+
+static psa_status_t util_img_hash(const struct flash_area *fap,
+ size_t data_size,
+ uint8_t *hash_result,
+ size_t buf_size,
+ size_t *hash_size)
+{
+ psa_hash_operation_t handle = psa_hash_operation_init();
+ psa_status_t status;
+ uint8_t tmpbuf[BOOT_TMPBUF_SZ];
+ uint32_t tmp_buf_sz = BOOT_TMPBUF_SZ;
+ uint32_t blk_sz;
+ uint32_t off;
+
+ /* Setup the hash object for the desired hash. */
+ status = psa_hash_setup(&handle, PSA_ALG_SHA_256);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ for (off = 0; off < data_size; off += blk_sz) {
+ blk_sz = data_size - off;
+ if (blk_sz > tmp_buf_sz) {
+ blk_sz = tmp_buf_sz;
+ }
+
+ if (flash_area_read(fap, off, tmpbuf, blk_sz)) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+ status = psa_hash_update(&handle, tmpbuf, blk_sz);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ }
+
+ status = psa_hash_finish(&handle, hash_result, buf_size, hash_size);
+
+ return status;
+}
+
+static psa_status_t get_secondary_image_info(uint8_t image_id,
+ psa_image_info_t *info)
+{
+ const struct flash_area *fap = NULL;
+ struct image_header hdr = {0};
+ uint8_t hash[PSA_FWU_MAX_DIGEST_SIZE] = {0};
+ size_t hash_size = 0;
+ psa_status_t ret = PSA_SUCCESS;
+ uint8_t index;
+ size_t data_size;
+
+ if (info == NULL) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ if ((flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_id),
+ &fap)) != 0) {
+ LOG_MSG("TFM FWU: opening flash failed.\r\n");
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ if (flash_area_read(fap, 0, &hdr, sizeof(hdr)) != 0) {
+ flash_area_close(fap);
+ LOG_MSG("TFM FWU: reading flash failed.\r\n");
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+
+ /* Check if the image is in a FWU process. */
+ if (get_flash_image_index(image_id, &index)) {
+
+ /* Calculate hash on the downloaded data. */
+ data_size = mcuboot_ctx[index].loaded_size;
+ } else {
+ /* Check if a valid image exists on the staging area.
+ * If a valid image exists, the FWU partition has no information on the
+ * image size downloaded as the image is copied from the running slot
+ * during the reboot. So in this case when calculating the image hash,
+ * the range starts from the image header till the end of the protected
+ * TLV area.
+ */
+ if (hdr.ih_magic == IMAGE_MAGIC) {
+ info->version.iv_major = hdr.ih_ver.iv_major;
+ info->version.iv_minor = hdr.ih_ver.iv_minor;
+ info->version.iv_revision = hdr.ih_ver.iv_revision;
+ info->version.iv_build_num = hdr.ih_ver.iv_build_num;
+ LOG_MSG("version= %d., %d., %d.,+ %d\n\r",
+ info->version.iv_major,
+ info->version.iv_minor,
+ info->version.iv_revision,
+ info->version.iv_build_num);
+
+ /* Calculate hash on from the image header to the protected TLV. */
+ data_size = hdr.ih_hdr_size + hdr.ih_img_size +
+ hdr.ih_protect_tlv_size;
+ } else {
+ /* No image in the staging area. */
+ return PSA_ERROR_DOES_NOT_EXIST;
+ }
+ }
+
+ if (util_img_hash(fap, data_size, hash, (size_t)PSA_FWU_MAX_DIGEST_SIZE,
+ &hash_size) == PSA_SUCCESS) {
+ tfm_memcpy(info->digest, hash, hash_size);
+ ret = PSA_SUCCESS;
+ } else {
+ ret = PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* The actual image state will be filled in the tfm_fwu_req_mngr.c where
+ * the image state is maintained.
+ */
+ info->state = PSA_IMAGE_UNDEFINED;
+ flash_area_close(fap);
+ return ret;
+}
+
+psa_status_t fwu_bootloader_get_image_info(bl_image_id_t bootloader_image_id,
+ bool active_image,
+ psa_image_info_t *info)
+{
+ struct image_version image_ver = { 0 };
+ struct shared_data_tlv_entry tlv_entry;
+ uint8_t *tlv_end;
+ uint8_t *tlv_curr;
+ bool found = false;
+ uint8_t mcuboot_image_id = 0;
+
+ if (info == NULL) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (convert_id_from_bl_to_mcuboot(bootloader_image_id,
+ &mcuboot_image_id) != 0) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ memset(info, 0, sizeof(psa_image_info_t));
+ memset(info->digest, TFM_IMAGE_INFO_INVALID_DIGEST, sizeof(info->digest));
+
+ if (active_image) {
+ /* Set the image state as accepted. */
+ /* TODO: check the image state by reading the image_ok flag in SWAP
+ * case.
+ */
+ info->state = PSA_IMAGE_INSTALLED;
+
+ /* When getting the primary image information, read it from the
+ * shared memory.
+ */
+ if (boot_shared_data.header.tlv_magic != SHARED_DATA_TLV_INFO_MAGIC) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ tlv_end = (uint8_t *)&boot_shared_data +
+ boot_shared_data.header.tlv_tot_len;
+ tlv_curr = boot_shared_data.data;
+
+ while (tlv_curr < tlv_end) {
+ (void)memcpy(&tlv_entry, tlv_curr, SHARED_DATA_ENTRY_HEADER_SIZE);
+ if ((GET_FWU_CLAIM(tlv_entry.tlv_type) == SW_VERSION) &&
+ (GET_FWU_MODULE(tlv_entry.tlv_type) == mcuboot_image_id)) {
+ if (tlv_entry.tlv_len != sizeof(struct image_version)) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+ memcpy(&image_ver,
+ tlv_curr + SHARED_DATA_ENTRY_HEADER_SIZE,
+ tlv_entry.tlv_len);
+ found = true;
+ break;
+ }
+ tlv_curr += SHARED_DATA_ENTRY_HEADER_SIZE + tlv_entry.tlv_len;
+ }
+ if (found) {
+ info->version.iv_major = image_ver.iv_major;
+ info->version.iv_minor = image_ver.iv_minor;
+ info->version.iv_revision = image_ver.iv_revision;
+ info->version.iv_build_num = image_ver.iv_build_num;
+
+ /* The image in the primary slot is verified by the bootloader.
+ * The image digest in the primary slot should not be exposed to
+ * nonsecure.
+ */
+ return PSA_SUCCESS;
+ } else {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+ } else {
+ return get_secondary_image_info(mcuboot_image_id, info);
+ }
+}
diff --git a/secure_fw/partitions/firmware_update/bootloader/tfm_bootloader_fwu_abstraction.h b/secure_fw/partitions/firmware_update/bootloader/tfm_bootloader_fwu_abstraction.h
new file mode 100644
index 0000000..06c3eff
--- /dev/null
+++ b/secure_fw/partitions/firmware_update/bootloader/tfm_bootloader_fwu_abstraction.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TFM_BOOTLOADER_FWU_ABSTRACTION_H__
+#define __TFM_BOOTLOADER_FWU_ABSTRACTION_H__
+
+#include "stdbool.h"
+#include "psa/update.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef uint8_t bl_image_id_t;
+
+/**
+ * Bootloader related initialization for firmware update, such as reading
+ * some necessary shared data from the memory if needed.
+ *
+ * \return PSA_SUCCESS On success
+ * PSA_ERROR_GENERIC_ERROR On failure
+ */
+psa_status_t fwu_bootloader_init(void);
+
+/**
+ * \brief Initialize the staging area of the image with given ID.
+ *
+ * Prepare the staging area of the image with given ID for image download.
+ * For example, initialize the staging area, open the flash area, and so on.
+ * The image will be written into the staging area later.
+ *
+ * \param[in] bootloader_image_id The identifier of the target image in
+ * bootloader
+ *
+ * \return PSA_SUCCESS On success
+ * PSA_ERROR_INVALID_ARGUMENT Invalid input parameter
+ * PSA_ERROR_GENERIC_ERROR A fatal error occurred
+ *
+ */
+psa_status_t fwu_bootloader_staging_area_init(bl_image_id_t
+ bootloader_image_id);
+
+/**
+ * \brief Load the image into the target device.
+ *
+ * Prepare the staging area of the image with given ID for image download.
+ * For example, initialize the staging area, open the flash area, and so on.
+ * The image will be written into the staging area later.
+ *
+ * \param[in] bootloader_image_id The identifier of the target image in
+ * bootloader
+ * \param[in] block_offset The offset of the image being passed into
+ * block, in bytes
+ * \param[in] block A buffer containing a block of image data.
+ * This might be a complete image or a subset.
+ * \param[in] block_size Size of block.
+ *
+ * \return PSA_SUCCESS On success
+ * PSA_ERROR_INVALID_ARGUMENT Invalid input parameter
+ * PSA_ERROR_INSUFFICIENT_MEMORY There is no enough memory to
+ * process the update
+ * PSA_ERROR_INSUFFICIENT_STORAGE There is no enough storage to
+ * process the update
+ * PSA_ERROR_GENERIC_ERROR A fatal error occurred
+ *
+ */
+psa_status_t fwu_bootloader_load_image(bl_image_id_t bootloader_image_id,
+ size_t block_offset,
+ const void *block,
+ size_t block_size);
+
+/**
+ * \brief Starts the installation of an image.
+ *
+ * Check the authenticity and integrity of the image.
+ *
+ * If a reboot is required to complete the check, then mark this image as a
+ * candidate so that the next time bootloader runs it will take this image
+ * as a candidate one to bootup. Return the error code PSA_SUCCESS_REBOOT.
+ *
+ * \param[in] bootloader_image_id The identifier of the target image in
+ * bootloader
+ * \param[out] dependency Bootloader image ID of dependency if needed
+ * \param[out] dependency_version Bootloader image version of dependency if
+ * needed
+ *
+ * \return PSA_SUCCESS On success
+ * PSA_SUCCESS_REBOOT A system reboot is needed to finish installation
+ * TFM_SUCCESS_RESTART A restart of the updated component is required
+ * to complete the update
+ * PSA_ERROR_DEPENDENCY_NEEDED Another image needs to be installed to
+ * finish installation
+ * PSA_ERROR_INVALID_ARGUMENT Invalid input parameter
+ * PSA_ERROR_INVALID_SIGNATURE The signature is incorrect
+ * PSA_ERROR_GENERIC_ERROR A fatal error occurred
+ * TFM_ERROR_ROLLBACK_DETECTED The image is too old to be installed.
+ * If the image metadata contains a
+ * timestamp and it has expired, then
+ * this error is also returned.
+ */
+psa_status_t fwu_bootloader_install_image(bl_image_id_t bootloader_image_id,
+ bl_image_id_t *dependency,
+ psa_image_version_t *dependency_version);
+
+/**
+ * \brief Marks the image in the primary slot as confirmed.
+ *
+ * Call this API to mark the running images as permanent/accepted to avoid
+ * revert when next time bootup. Usually, this API is called after the running
+ * images have been verified as valid.
+ *
+ * \return PSA_SUCCESS On success
+ * PSA_ERROR_GENERIC_ERROR A fatal error occurred
+ */
+psa_status_t fwu_bootloader_mark_image_accepted(void);
+
+/**
+ * \brief Abort the current image download process.
+ *
+ * \return PSA_SUCCESS On success
+ * PSA_ERROR_INVALID_ARGUMENT Invalid input parameter
+ * PSA_ERROR_GENERIC_ERROR A fatal error occurred
+ */
+psa_status_t fwu_bootloader_abort(bl_image_id_t bootloader_image_id);
+
+/**
+ * \brief Get the image information.
+ *
+ * Get the image information of the given bootloader_image_id in staging area
+ * or the running area.
+ *
+ * \param[in] bootloader_image_id The identifier of the target image in
+ * bootloader
+ * \param[in] active_image Indicates image location
+ * True: the running image.
+ * False: the image in the passive(or staging)
+ * slot.
+ * \param[out] info Buffer containing return the image
+ * information
+ *
+ * \return PSA_SUCCESS On success
+ * PSA_ERROR_INVALID_ARGUMENT Invalid input parameter
+ * PSA_ERROR_GENERIC_ERROR A fatal error occurred
+ */
+psa_status_t fwu_bootloader_get_image_info(bl_image_id_t bootloader_image_id,
+ bool active_image,
+ psa_image_info_t *info);
+#ifdef __cplusplus
+}
+#endif
+#endif /* __TFM_BOOTLOADER_FWU_ABSTRACTION_H__ */
diff --git a/secure_fw/partitions/firmware_update/tfm_firmware_update.yaml b/secure_fw/partitions/firmware_update/tfm_firmware_update.yaml
new file mode 100644
index 0000000..e2db153
--- /dev/null
+++ b/secure_fw/partitions/firmware_update/tfm_firmware_update.yaml
@@ -0,0 +1,107 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+{
+ "psa_framework_version": 1.0,
+ "name": "TFM_SP_FWU",
+ "type": "PSA-ROT",
+ "priority": "NORMAL",
+ "entry_point": "tfm_fwu_init",
+ "stack_size": "0x2000",
+ "secure_functions": [
+ {
+ "name": "TFM_FWU_WRITE",
+ "signal": "TFM_FWU_WRITE_REQ",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "TFM_FWU_INSTALL",
+ "signal": "TFM_FWU_INSTALL_REQ",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "TFM_FWU_ABORT",
+ "signal": "TFM_FWU_ABORT_REQ",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "TFM_FWU_QUERY",
+ "signal": "TFM_FWU_QUERY_REQ",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "TFM_FWU_REQUEST_REBOOT",
+ "signal": "TFM_FWU_REQUEST_REBOOT_REQ",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "TFM_FWU_ACCEPT",
+ "signal": "TFM_FWU_ACCEPT_REQ",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ ],
+ "services" : [
+ {
+ "name": "TFM_FWU_WRITE",
+ "sid": "0x000000A0",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "TFM_FWU_INSTALL",
+ "sid": "0x000000A1",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "TFM_FWU_ABORT",
+ "sid": "0x000000A2",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "TFM_FWU_QUERY",
+ "sid": "0x000000A3",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "TFM_FWU_REQUEST_REBOOT",
+ "sid": "0x000000A4",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ },
+ {
+ "name": "TFM_FWU_ACCEPT",
+ "sid": "0x000000A5",
+ "non_secure_clients": true,
+ "version": 1,
+ "version_policy": "STRICT"
+ }
+ ],
+ "dependencies": [
+ "TFM_CRYPTO",
+ "TFM_SP_PLATFORM_SYSTEM_RESET"
+ ]
+}
\ No newline at end of file
diff --git a/secure_fw/partitions/firmware_update/tfm_fwu.c b/secure_fw/partitions/firmware_update/tfm_fwu.c
new file mode 100644
index 0000000..3768f65
--- /dev/null
+++ b/secure_fw/partitions/firmware_update/tfm_fwu.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdint.h>
+#include "log/tfm_log.h"
+#include "tfm_platform_api.h"
+#include "tfm_fwu.h"
+
+psa_status_t tfm_internal_fwu_initialize(psa_image_id_t image_id)
+{
+ uint8_t image_type = (uint8_t)FWU_IMAGE_ID_GET_TYPE(image_id);
+ uint8_t slot_id = (uint8_t)FWU_IMAGE_ID_GET_SLOT(image_id);
+
+ /* Check the image slot, the target should be the staging slot. */
+ if (slot_id != FWU_IMAGE_ID_SLOT_STAGE) {
+ LOG_MSG("TFM FWU: invalid slot_id: %d", slot_id);
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ return fwu_bootloader_staging_area_init(image_type);
+}
+
+psa_status_t tfm_internal_fwu_write(psa_image_id_t image_id,
+ size_t block_offset,
+ const void *block,
+ size_t block_size)
+{
+ uint8_t image_type = (uint8_t)FWU_IMAGE_ID_GET_TYPE(image_id);
+ uint8_t slot_id = (uint8_t)FWU_IMAGE_ID_GET_SLOT(image_id);
+
+ if ((block == NULL) || (slot_id != FWU_IMAGE_ID_SLOT_STAGE)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ return fwu_bootloader_load_image(image_type,
+ block_offset,
+ block,
+ block_size);
+}
+
+psa_status_t tfm_internal_fwu_install(psa_image_id_t image_id,
+ psa_image_id_t *dependency,
+ psa_image_version_t *dependency_version)
+{
+ uint8_t image_type = (uint8_t)FWU_IMAGE_ID_GET_TYPE(image_id);
+ uint8_t slot_id = (uint8_t)FWU_IMAGE_ID_GET_SLOT(image_id);
+ bl_image_id_t dependency_bl;
+ psa_image_version_t version;
+ psa_status_t result;
+
+ /* Check the image slot, the target should be the staging slot. */
+ if (slot_id != FWU_IMAGE_ID_SLOT_STAGE) {
+ LOG_MSG("TFM FWU: invalid slot_id: %d", slot_id);
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ result = fwu_bootloader_install_image(image_type,
+ &dependency_bl,
+ &version);
+ if (result == PSA_ERROR_DEPENDENCY_NEEDED) {
+ if (dependency == NULL || dependency_version == NULL) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ *dependency = (psa_image_id_t)FWU_CALCULATE_IMAGE_ID(FWU_IMAGE_ID_SLOT_STAGE,
+ dependency_bl,
+ 0);
+ *dependency_version = version;
+ }
+
+ return result;
+}
+
+psa_status_t tfm_internal_fwu_abort(psa_image_id_t image_id)
+{
+ uint8_t image_type = (uint8_t)FWU_IMAGE_ID_GET_TYPE(image_id);
+ uint8_t slot_id = (uint8_t)FWU_IMAGE_ID_GET_SLOT(image_id);
+
+ if (slot_id != FWU_IMAGE_ID_SLOT_STAGE) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ return fwu_bootloader_abort(image_type);
+}
+
+/* The image version of the given image. */
+psa_status_t tfm_internal_fwu_query(psa_image_id_t image_id,
+ psa_image_info_t *info)
+{
+ uint8_t image_type = (uint8_t)FWU_IMAGE_ID_GET_TYPE(image_id);
+ uint8_t slot_id = (uint8_t)FWU_IMAGE_ID_GET_SLOT(image_id);
+ bool active_image = 0;
+
+ if (slot_id == FWU_IMAGE_ID_SLOT_STAGE) {
+ active_image = false;
+ } else if (slot_id == FWU_IMAGE_ID_SLOT_ACTIVE) {
+ active_image = true;
+ } else {
+ LOG_MSG("TFM FWU: invalid slot_id: %d", slot_id);
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ return fwu_bootloader_get_image_info(image_type, active_image, info);
+}
+
+void tfm_internal_fwu_request_reboot(void)
+{
+ tfm_platform_system_reset();
+}
+
+psa_status_t tfm_internal_fwu_accept(void)
+{
+ return fwu_bootloader_mark_image_accepted();
+}
diff --git a/secure_fw/partitions/firmware_update/tfm_fwu.h b/secure_fw/partitions/firmware_update/tfm_fwu.h
new file mode 100644
index 0000000..a4cc83f
--- /dev/null
+++ b/secure_fw/partitions/firmware_update/tfm_fwu.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TFM_FWU_H__
+#define __TFM_FWU_H__
+
+#include <stddef.h>
+
+#include "psa/update.h"
+#include "tfm_bootloader_fwu_abstraction.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Initialization for starting a firmware update with image_id.
+ */
+psa_status_t tfm_internal_fwu_initialize(psa_image_id_t image_id);
+
+psa_status_t tfm_internal_fwu_write(psa_image_id_t image_id,
+ size_t block_offset,
+ const void *block,
+ size_t block_size);
+
+/*
+ * Starts the installation of an image with image_id. Check the authenticity
+ * and integrity of the image. Error code PSA_SUCCESS_REBOOT is returned if a
+ * reboot is needed to complete the check.
+ */
+psa_status_t tfm_internal_fwu_install(psa_image_id_t image_id,
+ psa_image_id_t *dependency_uuid,
+ psa_image_version_t *dependency_version);
+
+/*
+ * Abort the firmware update process.
+ */
+psa_status_t tfm_internal_fwu_abort(psa_image_id_t image_id);
+
+/*
+ * Get the image information of the given image_id in staging area
+ * or the running area.
+ */
+psa_status_t tfm_internal_fwu_query(psa_image_id_t image_id,
+ psa_image_info_t *info);
+
+/*
+ * Request a reboot.
+ */
+void tfm_internal_fwu_request_reboot(void);
+
+/*
+ * Marks the image in the primary slot as confirmed.
+ */
+psa_status_t tfm_internal_fwu_accept(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TFM_FWU_H__ */
diff --git a/secure_fw/partitions/firmware_update/tfm_fwu_req_mngr.c b/secure_fw/partitions/firmware_update/tfm_fwu_req_mngr.c
new file mode 100644
index 0000000..19256fa
--- /dev/null
+++ b/secure_fw/partitions/firmware_update/tfm_fwu_req_mngr.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "tfm_fwu_req_mngr.h"
+#include "tfm_fwu.h"
+#include "psa/update.h"
+#include "service_api.h"
+#include "tfm_api.h"
+
+#ifdef TFM_PSA_API
+#include "psa/service.h"
+#include "psa_manifest/tfm_firmware_update.h"
+#endif
+
+typedef struct tfm_fwu_ctx_s {
+ psa_image_id_t image_id;
+ uint8_t image_state;
+ bool in_use;
+} tfm_fwu_ctx_t;
+
+/**
+ * \brief The context of FWU service.
+ */
+static tfm_fwu_ctx_t fwu_ctx[TFM_FWU_MAX_IMAGES];
+
+/**
+ * \brief Check if the image is in FWU process, return the index if it is.
+ */
+static bool get_image_index(psa_image_id_t image_id, uint8_t *index)
+{
+ if (!index) {
+ return false;
+ }
+
+ for (uint8_t i = 0; i < TFM_FWU_MAX_IMAGES; i++) {
+ if (fwu_ctx[i].in_use && (fwu_ctx[i].image_id == image_id)) {
+ *index = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * \brief The the next free index in fwu_ctx.
+ */
+static bool get_next_free_index(uint8_t *index)
+{
+ for (uint8_t i = 0; i < TFM_FWU_MAX_IMAGES; i++) {
+ if (!fwu_ctx[i].in_use) {
+ *index = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+#ifndef TFM_PSA_API
+psa_status_t tfm_fwu_write_req(psa_invec *in_vec, size_t in_len,
+ psa_outvec *out_vec, size_t out_len)
+{
+ psa_image_id_t image_id;
+ size_t block_offset;
+ uint8_t *p_data;
+ size_t data_length;
+ psa_status_t status = PSA_SUCCESS;
+ uint8_t image_index;
+
+ (void)out_vec;
+
+ /* Check input parameters. */
+ if (in_vec[0].len != sizeof(image_id) ||
+ in_vec[1].len != sizeof(block_offset)) {
+ /* The size of one of the arguments is incorrect */
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ p_data = (uint8_t * const)in_vec[2].base;
+
+ if (p_data == NULL) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ block_offset = *((size_t *)in_vec[1].base);
+ data_length = in_vec[2].len;
+ image_id = *((psa_image_id_t *)in_vec[0].base);
+
+ if (get_image_index(image_id, &image_index)) {
+ /* The image is in FWU process. */
+ if ((fwu_ctx[image_index].image_state != PSA_IMAGE_CANDIDATE) &&
+ (fwu_ctx[image_index].image_state != PSA_IMAGE_REJECTED)) {
+ return PSA_ERROR_CURRENTLY_INSTALLING;
+ }
+ } else {
+ /* The image is not in FWU process. */
+ if (get_next_free_index(&image_index)) {
+ /* Get a free index. Start the FWU process of this image. */
+ status = tfm_internal_fwu_initialize(image_id);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ fwu_ctx[image_index].in_use = true;
+ fwu_ctx[image_index].image_id = image_id;
+ fwu_ctx[image_index].image_state = PSA_IMAGE_CANDIDATE;
+ } else {
+ /* No more resource can be used. */
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+ }
+
+ return tfm_internal_fwu_write(image_id,
+ block_offset,
+ p_data,
+ data_length);
+}
+
+psa_status_t tfm_fwu_install_req(psa_invec *in_vec, size_t in_len,
+ psa_outvec *out_vec, size_t out_len)
+{
+ psa_image_id_t image_id;
+ psa_image_id_t *dependency_id;
+ psa_image_version_t *dependency_version;
+ psa_status_t status;
+ uint8_t image_index;
+
+ /* Check input/output parameters. */
+ if (in_vec[0].len != sizeof(image_id) ||
+ out_vec[0].len != sizeof(*dependency_id) ||
+ out_vec[1].len != sizeof(*dependency_version)) {
+ /* The size of one of the arguments is incorrect. */
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ image_id = *((psa_image_id_t *)in_vec[0].base);
+ dependency_id = out_vec[0].base;
+ dependency_version = out_vec[1].base;
+
+ if ((!get_image_index(image_id, &image_index)) ||
+ (fwu_ctx[image_index].image_state != PSA_IMAGE_CANDIDATE)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ } else {
+ status = tfm_internal_fwu_install(image_id,
+ dependency_id,
+ dependency_version);
+ if (status == PSA_SUCCESS) {
+ fwu_ctx[image_index].image_state = PSA_IMAGE_INSTALLED;
+
+ /* The image has been successfully installed, free the index. */
+ fwu_ctx[image_index].in_use = false;
+ } else if (status == PSA_SUCCESS_REBOOT) {
+ fwu_ctx[image_index].image_state = PSA_IMAGE_REBOOT_NEEDED;
+ } else {
+ fwu_ctx[image_index].image_state = PSA_IMAGE_REJECTED;
+ }
+
+ return status;
+ }
+}
+
+psa_status_t tfm_fwu_query_req(psa_invec *in_vec, size_t in_len,
+ psa_outvec *out_vec, size_t out_len)
+{
+ psa_image_id_t image_id;
+ psa_image_info_t *info_p;
+ psa_status_t result = 0;
+ uint8_t image_index;
+
+ if ((in_vec[0].len != sizeof(image_id)) ||
+ (out_vec[0].len != sizeof(*info_p))) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ image_id = *((psa_image_id_t *)in_vec[0].base);
+ info_p = (psa_image_info_t *)out_vec[0].base;
+ result = tfm_internal_fwu_query(image_id, info_p);
+ if (result == PSA_SUCCESS) {
+ /* Fill the image state if the query image is not the running image. */
+ if (info_p->state == PSA_IMAGE_UNDEFINED) {
+ if (get_image_index(image_id, &image_index)) {
+ /* The queried image is the currently updating image. */
+ info_p->state = fwu_ctx[image_index].image_state;
+ }
+ }
+ }
+
+ return result;
+}
+
+psa_status_t tfm_fwu_request_reboot_req(psa_invec *in_vec, size_t in_len,
+ psa_outvec *out_vec, size_t out_len)
+{
+ (void)in_vec;
+ (void)out_vec;
+ (void)in_len;
+ (void)out_len;
+
+ tfm_internal_fwu_request_reboot();
+
+ /* If it goes here, then the reboot fails. */
+ return PSA_ERROR_SERVICE_FAILURE;
+}
+
+psa_status_t tfm_fwu_accept_req(psa_invec *in_vec, size_t in_len,
+ psa_outvec *out_vec, size_t out_len)
+{
+ (void)in_vec;
+ (void)out_vec;
+ (void)in_len;
+ (void)out_len;
+
+ /* This operation set the running image to INSTALLED state, the images
+ * in the staging area does not impact this operation.
+ */
+ return tfm_internal_fwu_accept();
+}
+
+/* Abort the currently running FWU. */
+psa_status_t tfm_fwu_abort_req(psa_invec *in_vec, size_t in_len,
+ psa_outvec *out_vec, size_t out_len)
+{
+ psa_image_id_t image_id;
+ psa_status_t status;
+ uint8_t image_index;
+
+ (void)out_vec;
+
+ if (in_vec[0].len != sizeof(image_id)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ image_id = *((psa_image_id_t *)in_vec[0].base);
+ if (get_image_index(image_id, &image_index)) {
+ /* The image is in FWU process. */
+ if ((fwu_ctx[image_index].image_state == PSA_IMAGE_CANDIDATE) ||
+ (fwu_ctx[image_index].image_state == PSA_IMAGE_REBOOT_NEEDED) ||
+ (fwu_ctx[image_index].image_state == PSA_IMAGE_REJECTED)) {
+ status = tfm_internal_fwu_abort(image_id);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ fwu_ctx[image_index].image_state = PSA_IMAGE_UNDEFINED;
+ fwu_ctx[image_index].in_use = false;
+ fwu_ctx[image_index].image_id = TFM_FWU_INVALID_IMAGE_ID;
+ return PSA_SUCCESS;
+ } else {
+ /* If the image is in INSTALLED state or UNDEFINED, it should not in
+ * a FWU process.
+ */
+ return PSA_ERROR_PROGRAMMER_ERROR;
+ }
+ } else {
+ /* No image with the provided image_id is not in FWU process. */
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+}
+#endif
+
+psa_status_t tfm_fwu_init(void)
+{
+ return fwu_bootloader_init();
+}
diff --git a/secure_fw/partitions/firmware_update/tfm_fwu_req_mngr.h b/secure_fw/partitions/firmware_update/tfm_fwu_req_mngr.h
new file mode 100644
index 0000000..d7cd92e
--- /dev/null
+++ b/secure_fw/partitions/firmware_update/tfm_fwu_req_mngr.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TFM_FWU_REQ_MNGR_H__
+#define __TFM_FWU_REQ_MNGR_H__
+
+#include <stddef.h>
+
+#include "psa/client.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The maxinum of images that TF-M support to update concurrently. */
+#define TFM_FWU_MAX_IMAGES 2
+
+/**
+ * \brief Handles the write request.
+ *
+ * \param[in] in_vec Pointer to the input vector which contains the input
+ * parameters.
+ * \param[in] in_len Number of input parameters in the input vector.
+ * \param[out] out_vec Pointer to the output vector which contains the output
+ * parameters.
+ * \param[in] out_len Number of output parameters in the output vector.
+ *
+ * \return A status indicating the success/failure of the operation as
+ * specified in \ref psa_status_t
+ */
+psa_status_t tfm_fwu_write_req(psa_invec *in_vec, size_t in_len,
+ psa_outvec *out_vec, size_t out_len);
+
+/**
+ * \brief Handles the install request.
+ *
+ * \param[in] in_vec Pointer to the input vector which contains the input
+ * parameters.
+ * \param[in] in_len Number of input parameters in the input vector.
+ * \param[out] out_vec Pointer to the output vector which contains the output
+ * parameters.
+ * \param[in] out_len Number of output parameters in the output vector.
+ *
+ * \return A status indicating the success/failure of the operation as
+ * specified in \ref psa_status_t
+ */
+psa_status_t tfm_fwu_install_req(psa_invec *in_vec, size_t in_len,
+ psa_outvec *out_vec, size_t out_len);
+
+/**
+ * \brief Handles the abort request.
+ *
+ * \param[in] in_vec Pointer to the input vector which contains the input
+ * parameters.
+ * \param[in] in_len Number of input parameters in the input vector.
+ * \param[out] out_vec Pointer to the output vector which contains the output
+ * parameters.
+ * \param[in] out_len Number of output parameters in the output vector.
+ *
+ * \return A status indicating the success/failure of the operation as
+ * specified in \ref psa_status_t
+ */
+psa_status_t tfm_fwu_abort_req(psa_invec *in_vec, size_t in_len,
+ psa_outvec *out_vec, size_t out_len);
+
+/**
+ * \brief Query the infor of the primary slot.
+ *
+ * \param[in] in_vec Pointer to the input vector which contains the input
+ * parameters.
+ * \param[in] in_len Number of input parameters in the input vector.
+ * \param[out] out_vec Pointer to the output vector which contains the output
+ * parameters.
+ * \param[in] out_len Number of output parameters in the output vector.
+ *
+ * \return A status indicating the success/failure of the operation as
+ * specified in \ref psa_status_t
+ */
+
+psa_status_t tfm_fwu_query_req(psa_invec *in_vec, size_t in_len,
+ psa_outvec *out_vec, size_t out_len);
+
+psa_status_t tfm_fwu_request_reboot_req(psa_invec *in_vec, size_t in_len,
+ psa_outvec *out_vec, size_t out_len);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __TFM_FWU_REQ_MNGR_H__ */
diff --git a/secure_fw/partitions/firmware_update/tfm_fwu_secure_api.c b/secure_fw/partitions/firmware_update/tfm_fwu_secure_api.c
new file mode 100644
index 0000000..815927a
--- /dev/null
+++ b/secure_fw/partitions/firmware_update/tfm_fwu_secure_api.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "psa/update.h"
+#include "tfm_api.h"
+
+#ifdef TFM_PSA_API
+#include "psa/client.h"
+#include "psa_manifest/sid.h"
+#else
+#include "tfm_veneers.h"
+#endif
+
+#define IOVEC_LEN(x) (uint32_t)(sizeof(x)/sizeof(x[0]))
+
+psa_status_t psa_fwu_write(uint32_t image_id,
+ size_t block_offset,
+ const void *block,
+ size_t block_size)
+{
+ psa_status_t status;
+
+ psa_invec in_vec[] = {
+ { .base = &image_id, .len = sizeof(image_id) },
+ { .base = &block_offset, .len = sizeof(block_offset) },
+ { .base = block, .len = block_size }
+ };
+
+ status = tfm_tfm_fwu_write_req_veneer(in_vec,
+ IOVEC_LEN(in_vec),
+ NULL,
+ 0);
+
+ /* A parameter with a buffer pointer where its data length is longer than
+ * maximum permitted, it is treated as a secure violation.
+ * TF-M framework rejects the request with TFM_ERROR_INVALID_PARAMETER.
+ * The firmware update secure PSA implementation returns
+ * PSA_ERROR_INVALID_ARGUMENT in that case.
+ */
+ if (status == (psa_status_t)TFM_ERROR_INVALID_PARAMETER) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ return status;
+}
+
+psa_status_t psa_fwu_install(psa_image_id_t image_id,
+ psa_image_id_t *dependency_uuid,
+ psa_image_version_t *dependency_version)
+{
+ psa_status_t status;
+
+ psa_invec in_vec[] = {
+ { .base = &image_id, .len = sizeof(image_id) }
+ };
+
+ psa_outvec out_vec[] = {
+ { .base = dependency_uuid, .len = sizeof(*dependency_uuid) },
+ { .base = dependency_version, .len = sizeof(*dependency_version)}
+ };
+
+ if ((dependency_uuid == NULL) || (dependency_version == NULL)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ status = tfm_tfm_fwu_install_req_veneer(in_vec, IOVEC_LEN(in_vec),
+ out_vec, IOVEC_LEN(out_vec));
+
+ /* A parameter with a buffer pointer where its data length is longer than
+ * maximum permitted, it is treated as a secure violation.
+ * TF-M framework rejects the request with TFM_ERROR_INVALID_PARAMETER.
+ * The firmware update secure PSA implementation returns
+ * PSA_ERROR_INVALID_ARGUMENT in that case.
+ */
+ if (status == (psa_status_t)TFM_ERROR_INVALID_PARAMETER) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ return status;
+}
+
+psa_status_t psa_fwu_abort(psa_image_id_t image_id)
+{
+ psa_status_t status;
+
+ psa_invec in_vec[] = {
+ { .base = &image_id, .len = sizeof(image_id) }
+ };
+
+ status = tfm_tfm_fwu_abort_req_veneer(in_vec, IOVEC_LEN(in_vec),
+ NULL, 0);
+
+ /* A parameter with a buffer pointer where its data length is longer than
+ * maximum permitted, it is treated as a secure violation.
+ * TF-M framework rejects the request with TFM_ERROR_INVALID_PARAMETER.
+ * The firmware update secure PSA implementation returns
+ * PSA_ERROR_INVALID_ARGUMENT in that case.
+ */
+ if (status == (psa_status_t)TFM_ERROR_INVALID_PARAMETER) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ return status;
+}
+
+psa_status_t psa_fwu_query(psa_image_id_t image_id, psa_image_info_t *info)
+{
+ psa_status_t status;
+
+ psa_invec in_vec[] = {
+ { .base = &image_id, .len = sizeof(image_id) }
+ };
+ psa_outvec out_vec[] = {
+ { .base = info, .len = sizeof(*info)}
+ };
+
+ status = tfm_tfm_fwu_query_req_veneer(in_vec, IOVEC_LEN(in_vec),
+ out_vec, IOVEC_LEN(out_vec));
+
+ if (status == (psa_status_t)TFM_ERROR_INVALID_PARAMETER) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ return status;
+}
+
+psa_status_t psa_fwu_request_reboot(void)
+{
+ psa_status_t status;
+
+ status = tfm_tfm_fwu_request_reboot_req_veneer(NULL,
+ 0,
+ NULL,
+ 0);
+
+ if (status == (psa_status_t)TFM_ERROR_INVALID_PARAMETER) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ return status;
+}
+
+psa_status_t psa_fwu_accept(void)
+{
+ psa_status_t status;
+
+ status = tfm_tfm_fwu_accept_req_veneer(NULL,
+ 0,
+ NULL,
+ 0);
+
+ if (status == (psa_status_t)TFM_ERROR_INVALID_PARAMETER) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ return status;
+}
+
+psa_status_t psa_fwu_set_manifest(psa_image_id_t image_id,
+ const void *manifest,
+ size_t manifest_size,
+ psa_hash_t *manifest_dependency)
+{
+ psa_status_t status;
+
+ status = PSA_ERROR_NOT_SUPPORTED;
+
+ return status;
+}
diff --git a/secure_fw/spm/ffm/tfm_boot_data.c b/secure_fw/spm/ffm/tfm_boot_data.c
index f05e0e3..b2115f5 100644
--- a/secure_fw/spm/ffm/tfm_boot_data.c
+++ b/secure_fw/spm/ffm/tfm_boot_data.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2021, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
@@ -68,6 +68,7 @@
*/
static const struct boot_data_access_policy access_policy_table[] = {
{TFM_SP_INITIAL_ATTESTATION, TLV_MAJOR_IAS},
+ {TFM_SP_FWU, TLV_MAJOR_FWU},
};
/*!
diff --git a/secure_fw/spm/include/tfm_boot_status.h b/secure_fw/spm/include/tfm_boot_status.h
index 27ce6cb..597a6fe 100644
--- a/secure_fw/spm/include/tfm_boot_status.h
+++ b/secure_fw/spm/include/tfm_boot_status.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2021, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
@@ -21,6 +21,7 @@
*/
#define TLV_MAJOR_CORE 0x0
#define TLV_MAJOR_IAS 0x1
+#define TLV_MAJOR_FWU 0x2
/**
* The shared data between boot loader and runtime SW is TLV encoded. The
@@ -149,17 +150,25 @@
#define GET_MAJOR(tlv_type) ((tlv_type) >> MAJOR_POS)
#define GET_MINOR(tlv_type) ((tlv_type) & MINOR_MASK)
-/* Initial attestation specific macros */
+/* Initial attenstation and Firmware Update common macros */
#define MODULE_POS 6 /* 6 bit */
+#define MODULE_MASK 0x3F /* 6 bit */
#define CLAIM_MASK 0x3F /* 6 bit */
+
+/* Initial attestation specific macros */
#define MEASUREMENT_CLAIM_POS 3 /* 3 bit */
#define GET_IAS_MODULE(tlv_type) (GET_MINOR(tlv_type) >> MODULE_POS)
-#define GET_IAS_CLAIM(tlv_type) (GET_MINOR(tlv_type) & CLAIM_MASK)
+#define GET_IAS_CLAIM(tlv_type) (GET_MINOR(tlv_type) & CLAIM_MASK)
#define SET_IAS_MINOR(sw_module, claim) (((sw_module) << 6) | (claim))
-
#define GET_IAS_MEASUREMENT_CLAIM(ias_claim) ((ias_claim) >> \
MEASUREMENT_CLAIM_POS)
+/* Firmware Update specific macros */
+#define SET_FWU_MINOR(sw_module, claim) \
+ ((uint16_t)((sw_module & MODULE_MASK) << 6) | \
+ (uint16_t)(claim & CLAIM_MASK))
+#define GET_FWU_CLAIM(tlv_type) ((uint16_t)GET_MINOR(tlv_type) & CLAIM_MASK)
+#define GET_FWU_MODULE(tlv_type) ((uint16_t)GET_MINOR(tlv_type) >> MODULE_POS)
/* Magic value which marks the beginning of shared data area in memory */
#define SHARED_DATA_TLV_INFO_MAGIC 0x2016