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