diff --git a/update-client-hub/mbed_lib.json b/update-client-hub/mbed_lib.json index 4211a3445..d443cef61 100644 --- a/update-client-hub/mbed_lib.json +++ b/update-client-hub/mbed_lib.json @@ -66,6 +66,10 @@ "delta-storage-size": { "help": "Total storage allocated for delta image. This config item is only for multicast update.", "value": null + }, + "in-transit-hash-validation": { + "help": "Calculate payload hash in transit and not by reading back from storage. Useful on platforms without read permission.", + "value": 0 } } } diff --git a/update-client-hub/modules/common/update-client-common/arm_uc_config.h b/update-client-hub/modules/common/update-client-common/arm_uc_config.h index 5e8a414f8..51dd31099 100644 --- a/update-client-hub/modules/common/update-client-common/arm_uc_config.h +++ b/update-client-hub/modules/common/update-client-common/arm_uc_config.h @@ -357,6 +357,18 @@ ARM_UC_FEATURE_SIMPLE_COAP_SOURCE #define ARM_UC_FEATURE_PAL_LINUX 0 #define ARM_UC_FEATURE_PAL_RTL8195AM 1 +#elif defined(UPDATE_CLIENT_STORAGE_PSA) +#define ARM_UC_FEATURE_PAL_FLASHIAP 0 +#define ARM_UC_FEATURE_PAL_FLASHIAP_MCUBOOT 0 +#define ARM_UC_FEATURE_PAL_BLOCKDEVICE 0 +#define ARM_UC_FEATURE_PAL_FILESYSTEM 0 +#define ARM_UC_FEATURE_PAL_LINUX 0 +#define ARM_UC_FEATURE_PAL_RTL8195AM 0 +#define ARM_UC_FEATURE_PAL_PSA 1 +/* Disable ARM_UC_FEATURE_DELTA_PAAL which relies on ARM_UC_FEATURE_PAL_FLASHIAP */ +#undef ARM_UC_FEATURE_DELTA_PAAL +#define ARM_UC_FEATURE_DELTA_PAAL 0 + #else /* support legacy configuration method for storage */ #if defined(ARM_UC_USE_PAL_BLOCKDEVICE) && (ARM_UC_USE_PAL_BLOCKDEVICE == 1) diff --git a/update-client-hub/modules/firmware-manager/source/arm_uc_firmware_manager.c b/update-client-hub/modules/firmware-manager/source/arm_uc_firmware_manager.c index 6da836bd2..d1a4aa363 100644 --- a/update-client-hub/modules/firmware-manager/source/arm_uc_firmware_manager.c +++ b/update-client-hub/modules/firmware-manager/source/arm_uc_firmware_manager.c @@ -45,6 +45,9 @@ static arm_uc_buffer_t *back_buffer = NULL; #define UCFM_DEBUG_OUTPUT 0 +#ifndef MBED_CONF_UPDATE_CLIENT_IN_TRANSIT_HASH_VALIDATION +#define MBED_CONF_UPDATE_CLIENT_IN_TRANSIT_HASH_VALIDATION 0 +#endif static void arm_uc_signal_ucfm_handler(uintptr_t event); @@ -124,6 +127,48 @@ static void debug_output_validation(arm_uc_hash_t *hash, /******************************************************************************/ +/* Hash comparison. +*/ +static void arm_uc_internal_compare_hash(void) +{ + UC_FIRM_TRACE("arm_uc_internal_compare_hash"); + + /* use specific event for "invalid hash" */ + uint32_t error_event = UCFM_EVENT_FINALIZE_INVALID_HASH_ERROR; + + /* finalize hash calculation */ + uint8_t hash_output_ptr[2 * UCFM_MAX_BLOCK_SIZE]; + arm_uc_buffer_t hash_buffer = { + .size_max = sizeof(hash_output_ptr), + .size = 0, + .ptr = hash_output_ptr + }; + + ARM_UC_cryptoHashFinish(&mdHandle, &hash_buffer); + + /* size check before memcmp call */ + if (hash_buffer.size == ARM_UC_SHA256_SIZE) { + int diff = memcmp(hash_buffer.ptr, package_hash, ARM_UC_SHA256_SIZE); + +#if UCFM_DEBUG_OUTPUT + debug_output_validation(package_hash, &hash_buffer); +#endif + + if (diff == 0) { + /* hash matches */ + UC_FIRM_TRACE("image hash valid"); + error_event = UCFM_EVENT_FINALIZE_DONE; + } else { + UC_FIRM_ERR_MSG("image hash invalid"); + } + + /* clear local hash */ + memset(package_hash, 0, ARM_UC_SHA256_SIZE); + } + + arm_uc_signal_ucfm_handler(error_event); +} + /* Hash calculation is performed using the output buffer. This function fills the output buffer with data from the PAL. */ @@ -132,7 +177,6 @@ static void arm_uc_internal_process_hash(void) bool double_buffering = (front_buffer != back_buffer); bool needs_more_data = (package_offset < package_configuration->package_size); arm_uc_error_t status = { .code = ERR_NONE }; - uint32_t error_event = UCFM_EVENT_FINALIZE_ERROR; if (double_buffering && needs_more_data) { #if UCFM_DEBUG_OUTPUT @@ -175,45 +219,11 @@ static void arm_uc_internal_process_hash(void) back_buffer); } } else { - /* invert status code so that it has to be set explicitly for success */ - status.code = FIRM_ERR_INVALID_PARAMETER; - - /* finalize hash calculation */ - uint8_t hash_output_ptr[2 * UCFM_MAX_BLOCK_SIZE]; - arm_uc_buffer_t hash_buffer = { - .size_max = sizeof(hash_output_ptr), - .size = 0, - .ptr = hash_output_ptr - }; - - ARM_UC_cryptoHashFinish(&mdHandle, &hash_buffer); - - /* size check before memcmp call */ - if (hash_buffer.size == ARM_UC_SHA256_SIZE) { - int diff = memcmp(hash_buffer.ptr, - package_hash, - ARM_UC_SHA256_SIZE); - -#if UCFM_DEBUG_OUTPUT - debug_output_validation(package_hash, - &hash_buffer); -#endif - - /* hash matches */ - if (diff == 0) { - UC_FIRM_TRACE("UCFM_EVENT_FINALIZE_DONE"); - - arm_uc_signal_ucfm_handler(UCFM_EVENT_FINALIZE_DONE); - status.code = ERR_NONE; - } else { - /* use specific event for "invalid hash" */ - UC_FIRM_ERR_MSG("Invalid image hash"); - - error_event = UCFM_EVENT_FINALIZE_INVALID_HASH_ERROR; - } - // clear local hash - memset(package_hash, 0, ARM_UC_SHA256_SIZE); - } + /* All data has been passed through hash calculation. + * Compare hash with manifest. This function will signal + * success/failure by itself. + */ + arm_uc_internal_compare_hash(); } /* Front buffer is processed, back buffer might be reading more data. @@ -227,7 +237,7 @@ static void arm_uc_internal_process_hash(void) /* signal error if status is not clean */ if (status.error != ERR_NONE) { UC_FIRM_TRACE("UCFM_EVENT_FINALIZE_ERROR"); - arm_uc_signal_ucfm_handler(error_event); + arm_uc_signal_ucfm_handler(UCFM_EVENT_FINALIZE_ERROR); } } @@ -239,28 +249,37 @@ static void event_handler_finalize(void) { UC_FIRM_TRACE("event_handler_finalize"); - /* setup mandatory hash */ - arm_uc_mdType_t mdtype = ARM_UC_CU_SHA256; - arm_uc_error_t result = ARM_UC_cryptoHashSetup(&mdHandle, mdtype); +/* Hash is calculated while in transit */ +#if MBED_CONF_UPDATE_CLIENT_IN_TRANSIT_HASH_VALIDATION + UC_FIRM_TRACE("calculate hash in transit"); - if (result.error == ERR_NONE) { - /* initiate hash calculation */ - package_offset = 0; + /* All data has been passed through hash calculation. + * Compare hash with manifest. This function will signal + * success/failure by itself. + */ + arm_uc_internal_compare_hash(); - /* indicate number of bytes needed */ - front_buffer->size = (package_configuration->package_size < front_buffer->size_max) ? - package_configuration->package_size : front_buffer->size_max; +/* Hash is calculated by reading back payload from storage*/ +#else + UC_FIRM_TRACE("calculate hash from storage"); - /* initiate read from PAL */ - result = ARM_UCP_Read(package_configuration->package_id, - package_offset, - front_buffer); - } + /* initiate hash calculation */ + package_offset = 0; + + /* indicate number of bytes needed */ + front_buffer->size = (package_configuration->package_size < front_buffer->size_max) ? + package_configuration->package_size : front_buffer->size_max; + + /* initiate read from PAL */ + arm_uc_error_t result = ARM_UCP_Read(package_configuration->package_id, + package_offset, + front_buffer); if (result.error != ERR_NONE) { - UC_FIRM_ERR_MSG("ARM_UC_cryptoHashSetup failed"); + UC_FIRM_ERR_MSG("ARM_UCP_Read during hash check failed"); arm_uc_signal_ucfm_handler(UCFM_EVENT_FINALIZE_ERROR); } +#endif } /* Function for decoupling PAL callbacks using the internal task queue. */ @@ -416,13 +435,12 @@ static arm_uc_error_t ARM_UCFM_Prepare(ARM_UCFM_Setup_t *configuration, arm_uc_signal_ucfm_handler(UCFM_EVENT_PREPARE_ERROR); } } -#if UCFM_DEBUG_OUTPUT + /* Initialize hash to calculate downloaded fragments hash */ if (result.error == ERR_NONE) { arm_uc_mdType_t mdtype = ARM_UC_CU_SHA256; result = ARM_UC_cryptoHashSetup(&mdHandle, mdtype); } -#endif return result; } @@ -478,8 +496,8 @@ static arm_uc_error_t ARM_UCFM_Write(const arm_uc_buffer_t *fragment) fragment_offset += length_update; } } -#if UCFM_DEBUG_OUTPUT - /* calculate hash on the fly for debugging purpose */ +#if MBED_CONF_UPDATE_CLIENT_IN_TRANSIT_HASH_VALIDATION + /* calculate hash on the fly */ ARM_UC_cryptoHashUpdate(&mdHandle, fragment); #endif /* store fragment using PAL */ @@ -514,7 +532,7 @@ static arm_uc_error_t ARM_UCFM_ReadFromSlot(const arm_uc_buffer_t* output, uint3 return result; } -static arm_uc_error_t ARM_UCFM_Read(arm_uc_buffer_t *buf, uint32_t offset) +static arm_uc_error_t ARM_UCFM_Read(const arm_uc_buffer_t *buf, uint32_t offset) { arm_uc_error_t result = (arm_uc_error_t) { FIRM_ERR_UNINITIALIZED }; result = ARM_UCFM_ReadFromSlot(buf, package_configuration->package_id, offset); @@ -523,7 +541,7 @@ static arm_uc_error_t ARM_UCFM_Read(arm_uc_buffer_t *buf, uint32_t offset) static arm_uc_error_t ARM_UCFM_Finalize(arm_uc_buffer_t *front, arm_uc_buffer_t *back) { - UC_FIRM_TRACE("ARM_UCFM_Finish"); + UC_FIRM_TRACE("ARM_UCFM_Finalize"); arm_uc_error_t result = (arm_uc_error_t) { ERR_NONE }; @@ -550,20 +568,6 @@ static arm_uc_error_t ARM_UCFM_Finalize(arm_uc_buffer_t *front, arm_uc_buffer_t /* disable module until next setup call is received */ ready_to_receive = false; - -#if UCFM_DEBUG_OUTPUT - /* finalize hash calculation */ - uint8_t hash_output_ptr[2 * UCFM_MAX_BLOCK_SIZE]; - arm_uc_buffer_t hash_buffer = { - .size_max = sizeof(hash_output_ptr), - .size = 0, - .ptr = hash_output_ptr - }; - - ARM_UC_cryptoHashFinish(&mdHandle, &hash_buffer); - - debug_print_hash("downloaded hash:", hash_buffer.ptr, hash_buffer.size); -#endif } return result; diff --git a/update-client-hub/modules/pal-psa/LICENSE b/update-client-hub/modules/pal-psa/LICENSE new file mode 100644 index 000000000..c2bccaf00 --- /dev/null +++ b/update-client-hub/modules/pal-psa/LICENSE @@ -0,0 +1,166 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + diff --git a/update-client-hub/modules/pal-psa/source/arm_uc_pal_psa.c b/update-client-hub/modules/pal-psa/source/arm_uc_pal_psa.c new file mode 100644 index 000000000..7db5e8935 --- /dev/null +++ b/update-client-hub/modules/pal-psa/source/arm_uc_pal_psa.c @@ -0,0 +1,56 @@ +// ---------------------------------------------------------------------------- +// Copyright 2016-2020 ARM Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------- + +#include "update-client-common/arm_uc_config.h" +#if defined(ARM_UC_FEATURE_PAL_PSA) && (ARM_UC_FEATURE_PAL_PSA == 1) + +#include "update-client-paal/arm_uc_paal_update_api.h" + +#include "update-client-pal-psa/arm_uc_pal_psa_implementation.h" + +ARM_UC_PAAL_UPDATE_CAPABILITIES ARM_UC_PAL_PSA_Mcuboot_GetCapabilities(void) +{ + ARM_UC_PAAL_UPDATE_CAPABILITIES result = { + .installer_arm_hash = 0, + .installer_oem_hash = 0, + .installer_layout = 0, + .firmware_hash = 1, + .firmware_hmac = 0, + .firmware_campaign = 1, + .firmware_version = 1, + .firmware_size = 1 + }; + + return result; +} + +const ARM_UC_PAAL_UPDATE ARM_UCP_PSA = { + .Initialize = ARM_UC_PAL_PSA_Mcuboot_Initialize, + .GetCapabilities = ARM_UC_PAL_PSA_Mcuboot_GetCapabilities, + .GetMaxID = ARM_UC_PAL_PSA_Mcuboot_GetMaxID, + .Prepare = ARM_UC_PAL_PSA_Mcuboot_Prepare, + .Write = ARM_UC_PAL_PSA_Mcuboot_Write, + .Finalize = ARM_UC_PAL_PSA_Mcuboot_Finalize, + .Read = ARM_UC_PAL_PSA_Mcuboot_Read, + .Activate = ARM_UC_PAL_PSA_Mcuboot_Activate, + .GetActiveFirmwareDetails = ARM_UC_PAL_PSA_Mcuboot_GetActiveDetails, + .GetFirmwareDetails = ARM_UC_PAL_PSA_Mcuboot_GetFirmwareDetails, + .GetInstallerDetails = ARM_UC_PAL_PSA_Mcuboot_GetInstallerDetails +}; + +#endif /* ARM_UC_FEATURE_PAL_PSA */ diff --git a/update-client-hub/modules/pal-psa/source/arm_uc_pal_psa_flash.cpp b/update-client-hub/modules/pal-psa/source/arm_uc_pal_psa_flash.cpp new file mode 100644 index 000000000..eb318da2b --- /dev/null +++ b/update-client-hub/modules/pal-psa/source/arm_uc_pal_psa_flash.cpp @@ -0,0 +1,56 @@ +// ---------------------------------------------------------------------------- +// Copyright 2016-2020 ARM Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------- + +#include "update-client-common/arm_uc_config.h" +#if defined(ARM_UC_FEATURE_PAL_PSA) && (ARM_UC_FEATURE_PAL_PSA == 1) + +#include "update-client-pal-psa/arm_uc_pal_psa_platform.h" +#include + +/*****************************************************************************/ +/* Flash functions for active firmware partition. */ +/*****************************************************************************/ + +/** Initialize a flash IAP device + * + * Should be called once per lifetime of the object. + * @return 0 on success or a negative error code on failure + */ +int32_t arm_uc_flashiap_active_init(void) +{ + return ARM_UC_FLASHIAP_SUCCESS; +} + +/** Read data from a flash device. + * + * This method invokes memcpy - reads number of bytes from the address + * + * @param buffer Buffer to write to + * @param address Flash address to begin reading from + * @param size Size to read in bytes + * @return 0 on success, negative error code on failure + */ +int32_t arm_uc_flashiap_active_read(uint8_t *buffer, + uint32_t address, + uint32_t size) +{ + memcpy(buffer, (const void *) address, size); + return ARM_UC_FLASHIAP_SUCCESS; +} + +#endif /* ARM_UC_FEATURE_PAL_PSA */ diff --git a/update-client-hub/modules/pal-psa/source/arm_uc_pal_psa_helper.c b/update-client-hub/modules/pal-psa/source/arm_uc_pal_psa_helper.c new file mode 100644 index 000000000..27da8475b --- /dev/null +++ b/update-client-hub/modules/pal-psa/source/arm_uc_pal_psa_helper.c @@ -0,0 +1,602 @@ +// ---------------------------------------------------------------------------- +// Copyright 2016-2020 ARM Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------- + +#include "update-client-common/arm_uc_config.h" +#if defined(ARM_UC_FEATURE_PAL_PSA) && (ARM_UC_FEATURE_PAL_PSA == 1) + +#include "update-client-pal-psa/arm_uc_pal_psa_helper.h" + +#include "update-client-pal-psa/arm_uc_pal_psa_platform.h" +#include "update-client-paal/arm_uc_paal_update_api.h" + +#include "key_config_manager.h" + +#include "mbedtls/md.h" + +#include +#include +#include + +#define TRACE_GROUP "UCPI" + +/** + * Default size is a trade-off between estimated available stack size + * and reducing the number of reads from flash. + */ +#ifdef MBED_CONF_UPDATE_CLIENT_MCUBOOT_BUFFER_SIZE +#define MCUBOOT_BUFFER_SIZE MBED_CONF_UPDATE_CLIENT_MCUBOOT_BUFFER_SIZE +#else +#define MCUBOOT_BUFFER_SIZE 256 +#endif + +/*****************************************************************************/ +/* MCUBOOT header and TLV functions */ +/*****************************************************************************/ + +/** + * @brief Get total image size. + * + * @param[in] flash_reader Function pointer to internal or external flash reader. + * @param address Address in flash where TLV struct begins. + * @param tlv_size Pointer to size_t for storing total image size. + * + * @return ERR_NONE Success, the hash-struct has been populated. + * ERR_INVALID_PARAMETER Failure, unable to find hash at address. + */ +static arm_uc_error_t arm_uc_pal_psa_get_tlv_size(arm_uc_reader_p flash_reader, + uint32_t address, + uint32_t* tlv_size) +{ + UC_PAAL_TRACE("arm_uc_pal_psa_get_tlv_size"); + + arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER }; + + if (tlv_size) { + + /* get main TLV struct */ + image_tlv_info_t tlv_info = { 0 }; + + int status = flash_reader((uint8_t*) &tlv_info, address, sizeof(image_tlv_info_t)); + + /* check for header magic */ + if ((status == ARM_UC_FLASHIAP_SUCCESS) && + ((tlv_info.it_magic == IMAGE_TLV_INFO_MAGIC) || + (tlv_info.it_magic == IMAGE_TLV_PROT_INFO_MAGIC))) { + + UC_PAAL_TRACE("magic: %" PRIX16, tlv_info.it_magic); + UC_PAAL_TRACE("size: %" PRIX16, tlv_info.it_tlv_tot); + + /* return total TLV size */ + *tlv_size = tlv_info.it_tlv_tot; + UC_PAAL_TRACE("tlv_size: %" PRIX32, *tlv_size); + + result.code = ERR_NONE; + } + } + + return result; +} + +/** + * @brief Get hash from MCUBOOT TLV struct. + * + * This function reads the hash directly from the TLV struct. + * + * @param[in] flash_reader Function pointer to internal or external flash reader. + * @param address Address in flash where TLV struct begins. + * @param header_hash Pointer to hash-struct to be filed. + * + * @return ERR_NONE Success, the hash-struct has been populated. + * ERR_INVALID_PARAMETER Failure, unable to find hash at address. + */ +arm_uc_error_t arm_uc_pal_psa_get_hash_from_tlv(arm_uc_reader_p flash_reader, + uint32_t address, + arm_uc_hash_t* header_hash) +{ + UC_PAAL_TRACE("arm_uc_pal_psa_get_hash_from_tlv"); + + arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER }; + + if (header_hash) { + + /* get main TLV struct */ + image_tlv_info_t tlv_info = { 0 }; + + int status = flash_reader((uint8_t*) &tlv_info, address, sizeof(image_tlv_info_t)); + + /* check for header magic */ + if ((status == ARM_UC_FLASHIAP_SUCCESS) && + ((tlv_info.it_magic == IMAGE_TLV_INFO_MAGIC) || + (tlv_info.it_magic == IMAGE_TLV_PROT_INFO_MAGIC))) { + + UC_PAAL_TRACE("magic: %" PRIX16, tlv_info.it_magic); + UC_PAAL_TRACE("size: %" PRIX16, tlv_info.it_tlv_tot); + + /* step through TLV records, adjust address and size for main TLV above */ + address += sizeof(image_tlv_info_t); + uint32_t address_end = address + tlv_info.it_tlv_tot - sizeof(image_tlv_info_t); + + while ((address < address_end) && (status == ARM_UC_FLASHIAP_SUCCESS)) { + + /* read TLV record */ + image_tlv_t record; + + status = flash_reader((uint8_t*) &record, + address, + sizeof(image_tlv_t)); + + if (status == ARM_UC_FLASHIAP_SUCCESS) { + + UC_PAAL_TRACE("type: %" PRIX16, record.it_type); + + /* search for the mandatory SHA256 record */ + if (record.it_type == IMAGE_TLV_SHA256) { + + /* read hash into struct */ + status = flash_reader((uint8_t*) header_hash, + address + sizeof(image_tlv_t), + sizeof(arm_uc_hash_t)); + + if (status == ARM_UC_FLASHIAP_SUCCESS) { + +#if ARM_UC_PAAL_TRACE_ENABLE + printf("[TRACE][SRCE] MCUBOOT hash: "); + for (size_t index = 0; index < sizeof(arm_uc_hash_t); index++) { + printf("%02X", (*header_hash)[index]); + } + printf("\r\n"); +#endif + + result.code = ERR_NONE; + } + + /* breakout whether read was succesful or not */ + break; + } + + /* skip to next record */ + address += sizeof(image_tlv_t) + record.it_len; + } + } + } + } + + if (result.error != ERR_NONE) { + UC_PAAL_TRACE("hash not found in TLV area"); + } + + return result; +} + +/** + * @brief Get hash from MCUBOOT TLV struct. + * + * This function uses the MCUBOOT header to find the address + * for the TLV struct where the hash is stored. + * + * @param[in] address Address in flash where MCUBOOT header begins. + * @param header_hash Pointer to hash-struct to be filled. + * @param total_size Pointer to size_t for storing total, signed image size. + * + * @return ERR_NONE Success, the hash-struct has been populated. + * ERR_INVALID_PARAMETER Failure, unable to find hash at address. + */ +arm_uc_error_t arm_uc_pal_psa_get_hash_from_header(arm_uc_reader_p flash_reader, + uint32_t address, + arm_uc_hash_t* header_hash, + uint32_t* total_size) +{ + UC_PAAL_TRACE("arm_uc_pal_psa_get_hash_from_header"); + + arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER }; + + if (header_hash && total_size) { + + /* get MCUBOOT header */ + image_header_t header = { 0 }; + + int status = flash_reader((uint8_t*) &header, address, sizeof(image_header_t)); + + /* check for header magic */ + if ((status == ARM_UC_FLASHIAP_SUCCESS) && (header.ih_magic == IMAGE_MAGIC)) { + + UC_PAAL_TRACE("magic: %" PRIX32, header.ih_magic); + UC_PAAL_TRACE("load: %" PRIX32, header.ih_load_addr); + UC_PAAL_TRACE("hdr: %" PRIX16, header.ih_hdr_size); + UC_PAAL_TRACE("img: %" PRIX32, header.ih_img_size); + UC_PAAL_TRACE("prot: %" PRIX16, header.ih_protect_tlv_size); + + /* find address for TLV */ + uint32_t offset = header.ih_hdr_size + header.ih_img_size; + + /* search protected TLV first */ + result = arm_uc_pal_psa_get_hash_from_tlv(flash_reader, + address + offset, + header_hash); + + /** + * If hash wasn't found, assume we just searched the optional protected TLV. + * Proceed to the main TLV and search for hash. + */ + if ((result.error != ERR_NONE) && header.ih_protect_tlv_size) { + + offset += header.ih_protect_tlv_size; + result = arm_uc_pal_psa_get_hash_from_tlv(flash_reader, + address + offset, + header_hash); + } + + /** + * Find remaining TLV and return full, signed image size. + */ + if (result.error == ERR_NONE) { + + uint32_t tlv_size = 0; + result = arm_uc_pal_psa_get_tlv_size(flash_reader, + address + offset, + &tlv_size); + + /* total size is offset + latest TLV size */ + if (result.error == ERR_NONE) { + *total_size = offset + tlv_size; + } + } + } else { + UC_PAAL_TRACE("no header at address: %" PRIX32, address); + } + } + + return result; +} + +/** + * @brief Calculate hash directly from stored image. + * + * @param[in] flash_reader Function pointer to internal or external flash reader. + * @param address Address in flash where MCUBOOT header begins. + * @param total_size uint32_t with total, signed image size. + * @param header_hash Pointer to hash-struct to be filled. + * + * @return ERR_NONE Success, the hash-struct has been populated. + * ERR_INVALID_PARAMETER Failure, unable to find hash at address. + */ +arm_uc_error_t arm_uc_pal_psa_calculate_hash(arm_uc_reader_p flash_reader, + uint32_t address, + uint32_t total_size, + arm_uc_hash_t* header_hash) +{ + UC_PAAL_TRACE("arm_uc_pal_psa_calculate_hash"); + + arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER }; + + if (header_hash) { + + /* use Mbed TLS to calculate firmware's SHA256 hash */ + mbedtls_md_context_t context = { 0 }; + const mbedtls_md_info_t* md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); + + mbedtls_md_init(&context); + mbedtls_md_setup(&context, md_info, 0); + mbedtls_md_starts(&context); + + /* use flash read to calculate hash one fragment at a time + * to support off-chip storage. + */ + uint8_t buffer[MCUBOOT_BUFFER_SIZE] = { 0 }; + uint32_t offset = 0; + int status = ARM_UC_FLASHIAP_SUCCESS; + + while ((offset < total_size) && (status == ARM_UC_FLASHIAP_SUCCESS)) { + + uint32_t actual_size = MCUBOOT_BUFFER_SIZE; + uint32_t remaining = total_size - offset; + + /* limit the last fragment's read size */ + if (MCUBOOT_BUFFER_SIZE > remaining) { + actual_size = remaining; + } + + /* read fragment into buffer */ + status = flash_reader((uint8_t*) buffer, + address + offset, + actual_size); + + /* update hash calculation with fragment */ + if (status == ARM_UC_FLASHIAP_SUCCESS) { + mbedtls_md_update(&context, buffer, actual_size); + } + + /* move to next fragment */ + offset += actual_size; + } + + mbedtls_md_finish(&context, (uint8_t*) header_hash); + + if (status == ARM_UC_FLASHIAP_SUCCESS) { + result.code = ERR_NONE; + } + } + + return result; +} + +/*****************************************************************************/ +/* KCM getting and setting firmware details */ +/*****************************************************************************/ + +/** + * @brief Helper function for reading firmware details from KCM + * when given slot name and hash. + * + * @param[in] slot_name String with name to match. + * @param[in] slot_size Size of name, doesn't have to include '\0'. + * @param header_hash Pointer to hash-struct to match. + * @param details Pointer to details-struct to be filled. + * + * @return TRUE/FALSE On success/failure. + */ +static bool internal_get_details_from_slot(const uint8_t* slot_name, + size_t slot_size, + arm_uc_hash_t* header_hash, + arm_uc_firmware_details_t* details) +{ + bool result = false; + + /** + * Read entry into temporary buffer. + * Hash and details are stored one after another in memory. + */ + uint8_t buffer[sizeof(arm_uc_hash_t) + sizeof(arm_uc_firmware_details_t)]; + size_t buffer_size = sizeof(arm_uc_hash_t) + sizeof(arm_uc_firmware_details_t); + size_t item_size; + + kcm_status_e status = kcm_item_get_data(slot_name, slot_size, + KCM_CONFIG_ITEM, + buffer, buffer_size, + &item_size); + + if ((status == KCM_STATUS_SUCCESS) && (item_size == buffer_size)) { + + /* compare provided hash with the one stored in KCM entry */ + int diff = memcmp(header_hash, buffer, sizeof(arm_uc_hash_t)); + + if (diff == 0) { + + /* copy firmware details if hash matches */ + memcpy(details, buffer + sizeof(arm_uc_hash_t), sizeof(arm_uc_firmware_details_t)); + result = true; + } + } + + return result; +} + +/** + * @brief Helper function for erasing slot in KCM. + * + * @param[in] slot_name String with slot name to erase. + * @param[in] slot_size Size of name, doesn't have to include '\0'. + * + * @return TRUE/FALSE On success/failure. + */ +static bool internal_erase_slot(const uint8_t* slot_name, + size_t slot_size) +{ + kcm_status_e status = kcm_item_delete(slot_name, slot_size, KCM_CONFIG_ITEM); + + return (status == KCM_STATUS_SUCCESS); +} + +/** + * @brief Get firmware details stored in KCM. + * + * The function uses a SHA256 hash to search two preassigned + * locations in the KCM for the firmware details associated + * with the hash. + * + * @param header_hash Hash from MCUBOOT header. + * @param details Pointer to firmware details struct to be filled. + * + * @return ERR_NONE Success, the details-struct has been populated. + * ERR_INVALID_PARAMETER Failure, unable to find entry with hash. + */ +arm_uc_error_t arm_uc_pal_psa_get_kcm_details(arm_uc_hash_t* header_hash, + arm_uc_firmware_details_t* details) +{ + UC_PAAL_TRACE("arm_uc_pal_psa_get_kcm_details"); + + arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER }; + + if (header_hash && details) { + + /* search for active firmware details in slot A */ + bool status = internal_get_details_from_slot(SLOT_A_NAME, SLOT_A_SIZE, header_hash, details); + + if (status) { + + UC_PAAL_TRACE("found active details in slot A; search slot B next"); + + arm_uc_firmware_details_t slot_b_details = { 0 }; + + status = internal_get_details_from_slot(SLOT_B_NAME, SLOT_B_SIZE, + header_hash, + &slot_b_details); + + if (status) { + + UC_PAAL_TRACE("found active details in slot B; compare version next"); + + if (slot_b_details.version > details->version) { + + UC_PAAL_TRACE("slot B details are newer; clean up slot A"); + + /* copy details to return struct */ + memcpy(details, &slot_b_details, sizeof(arm_uc_firmware_details_t)); + + /* clean up slot A */ + internal_erase_slot(SLOT_A_NAME, SLOT_A_SIZE); + + } else { + + UC_PAAL_TRACE("slot A details are newer; clean up slot B"); + + /* clean up slot B */ + internal_erase_slot(SLOT_B_NAME, SLOT_B_SIZE); + } + + } else { + + UC_PAAL_TRACE("no active details in slot B; clean up slot B"); + + /* clean up slot B */ + internal_erase_slot(SLOT_B_NAME, SLOT_B_SIZE); + } + + /* set return value */ + result.code = ERR_NONE; + + } else { + + /* active firmware details were not found in slot A, search slot B */ + status = internal_get_details_from_slot(SLOT_B_NAME, SLOT_B_SIZE, header_hash, details); + + if (status) { + + UC_PAAL_TRACE("found active details in slot B; clean up slot A"); + + /* active firmware details found in slot B, clean up slot A*/ + internal_erase_slot(SLOT_A_NAME, SLOT_A_SIZE); + + /* set return value */ + result.code = ERR_NONE; + } else { + + UC_PAAL_TRACE("no details in KCM"); + } + } + } + + return result; +} + +/** + * @brief Helper function for writing firmware details to KCM + * under given slot name and hash. + * + * @param[in] slot_name String with name for slot. + * @param[in] slot_size Size of name, doesn't have to include '\0'. + * @param header_hash Pointer to hash-struct to store. + * @param details Pointer to details-struct to store. + * + * @return TRUE/FALSE On success/failure. + */ +static bool internal_set_details_in_slot(const uint8_t* slot_name, + size_t slot_size, + arm_uc_hash_t* header_hash, + arm_uc_firmware_details_t* details) +{ + /* copy hash and details to same buffer */ + uint8_t buffer[sizeof(arm_uc_hash_t) + sizeof(arm_uc_firmware_details_t)]; + size_t buffer_size = sizeof(arm_uc_hash_t) + sizeof(arm_uc_firmware_details_t); + + memcpy(buffer, header_hash, sizeof(arm_uc_hash_t)); + memcpy(buffer + sizeof(arm_uc_hash_t), details, sizeof(arm_uc_firmware_details_t)); + + /* store buffer in KCM*/ + kcm_status_e status = kcm_item_store(slot_name, slot_size, + KCM_CONFIG_ITEM, false, + buffer, buffer_size, + NULL); + + UC_PAAL_TRACE("storing: %.*s", slot_size, slot_name); +#if ARM_UC_SOURCE_MANAGER_TRACE_ENABLE + for (size_t index = 0; index < buffer_size; index++) { + printf("%02X", buffer[index]); + } + printf("\r\n"); +#endif + UC_PAAL_TRACE("result: %d", status); + + return (status == KCM_STATUS_SUCCESS); +} + +/** + * @brief Set firmware details in KCM. + * + * The function stores firmware details in the KCM using a + * SHA256 hash as the key for two preallocated entries. + * The hash for the active firmware is passed as argument + * as well to resolve any ambiguity on which entry should + * be used. + * + * @param header_hash_active Hash for active firmware. + * @param header_hash_candidate Hash for candidate firmware. + * @param details Pointer to the candidate firmware's details. + * + * @return ERR_NONE Success, firmware details has been stored in KCM. + * ERR_INVALID_PARAMETER Failure, unable to store details. + */ +arm_uc_error_t arm_uc_pal_psa_set_kcm_details(arm_uc_hash_t* header_hash_active, + arm_uc_hash_t* header_hash_candidate, + arm_uc_firmware_details_t* details) +{ + UC_PAAL_TRACE("arm_uc_pal_psa_set_kcm_details"); + + arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER }; + + if (header_hash_active && header_hash_candidate && details) { + + /** + * Find the KCM slot that holds the active firmware's details, + * then use the other slot to store the candidate details. + */ + arm_uc_firmware_details_t throw_away; + + /* search slot A for active firmware details */ + bool status = internal_get_details_from_slot(SLOT_A_NAME, SLOT_A_SIZE, + header_hash_active, + &throw_away); + + if (status) { + + UC_PAAL_TRACE("found active details in slot A; erase and store candidate details in slot B"); + + internal_erase_slot(SLOT_B_NAME, SLOT_B_SIZE); + status = internal_set_details_in_slot(SLOT_B_NAME, SLOT_B_SIZE, + header_hash_candidate, + details); + } else { + + UC_PAAL_TRACE("active details not in slot A; erase and store candidate details in slot A"); + + internal_erase_slot(SLOT_A_NAME, SLOT_A_SIZE); + status = internal_set_details_in_slot(SLOT_A_NAME, SLOT_A_SIZE, + header_hash_candidate, + details); + } + + /* set return value based on storage status */ + if (status) { + result.code = ERR_NONE; + } + } + + return result; +} + +#endif // ARM_UC_FEATURE_PAL_PSA diff --git a/update-client-hub/modules/pal-psa/source/arm_uc_pal_psa_implementation.c b/update-client-hub/modules/pal-psa/source/arm_uc_pal_psa_implementation.c new file mode 100644 index 000000000..b8a6f5693 --- /dev/null +++ b/update-client-hub/modules/pal-psa/source/arm_uc_pal_psa_implementation.c @@ -0,0 +1,548 @@ +// ---------------------------------------------------------------------------- +// Copyright 2016-2020 ARM Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------- + +#include "update-client-common/arm_uc_config.h" +#if defined(ARM_UC_FEATURE_PAL_PSA) && (ARM_UC_FEATURE_PAL_PSA == 1) + +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif + +#include + +#include "update-client-pal-psa/arm_uc_pal_psa.h" +#include "update-client-pal-psa/arm_uc_pal_psa_platform.h" +#include "update-client-pal-psa/arm_uc_pal_psa_helper.h" + +#include +#include + +#include "mbedtls/md.h" + +/* PSA Firmware Update API */ +#include "psa/update.h" + +#define TRACE_GROUP "UCPI" + +/* consistency check */ +#if !(MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS == 1) +#error Update client storage locations must be 1. +#endif + +#if SN_COAP_MAX_BLOCKWISE_PAYLOAD_SIZE < IMAGE_HEADER_SIZE +#error SN_COAP_MAX_BLOCKWISE_PAYLOAD_SIZE must be larger than \ + or equal to MCUBOOTs header size, IMAGE_HEADER_SIZE. +#endif + +/** + * Transfer firmare details across function calls. + * Details are provided in Prepare and used in Activate + */ +static arm_uc_firmware_details_t arm_uc_pal_flashiap_details = { 0 }; + +/** + * Callback functions + */ +static void (*arm_uc_pal_psa_callback)(uint32_t) = NULL; + +static void arm_uc_pal_psa_signal_internal(uint32_t event) +{ + if (arm_uc_pal_psa_callback) { + arm_uc_pal_psa_callback(event); + } +} + +/* firmware update context struct */ +typedef struct firmware_update_ctx_s { + /* fields for PSA Firmware Update API */ + struct psa_fwu_s { + psa_image_id_t image_id; + psa_image_info_t info; + psa_image_id_t dependency_uuid; + psa_image_version_t dependency_version; + } psa_fwu; + + image_header_t image_header; // cached image header + mbedtls_md_context_t hash_ctx; // Because PSA Firmware Update API + arm_uc_hash_t header_hash; // doesn't support psa_fwu_write(), + uint32_t header_hash_total; // calculate on-the-fly over padded header, + // body, and protected TLV + arm_uc_firmware_details_t details; // cached firmware details +} firmware_update_ctx_t; + +/* non-secure firmware update context */ +static firmware_update_ctx_t firmware_update_ctx_nonsecure; + +/** + * @brief Initialise the flash IAP API + * + * @param callback function pointer to the PAAL event handler + * @return Returns ERR_NONE on success. + * Returns ERR_INVALID_PARAMETER on error. + */ +arm_uc_error_t ARM_UC_PAL_PSA_Mcuboot_Initialize(void (*callback)(uint32_t)) +{ + arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER }; + + int32_t active = arm_uc_flashiap_active_init(); + if (active != ARM_UC_FLASHIAP_SUCCESS) { + UC_PAAL_ERR_MSG("arm_uc_flashiap_active_init() failed: %d\r\n", active); + result.code = ERR_UNSPECIFIED; + return result; + } + + arm_uc_pal_psa_callback = callback; + + /* signal done */ + arm_uc_pal_psa_signal_internal(ARM_UC_PAAL_EVENT_INITIALIZE_DONE); + + result.code = ERR_NONE; + + return result; +} + +/** + * @brief Get maximum number of supported storage locations. + * + * @return Number of storage locations. + */ +uint32_t ARM_UC_PAL_PSA_Mcuboot_GetMaxID(void) +{ + return MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS; +} + +/** + * @brief Prepare the storage layer for a new firmware image. + * @details The storage location is set up to receive an image with + * the details passed in the details struct. + * + * @param slot_id Storage location ID. + * @param details Pointer to a struct with firmware details. + * @param buffer Temporary buffer for formatting and storing metadata. + * @return Returns ERR_NONE on accept, and signals the event handler with + * either DONE or ERROR when complete. + * Returns ERR_INVALID_PARAMETER on reject, and no signal is sent. + */ +arm_uc_error_t ARM_UC_PAL_PSA_Mcuboot_Prepare(uint32_t slot_id, + const arm_uc_firmware_details_t* details, + arm_uc_buffer_t* buffer) +{ + arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER }; + + /* validate input */ + if (details && + buffer && buffer->ptr && + (slot_id < MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS)) { + UC_PAAL_TRACE("ARM_UC_PAL_PSA_Mcuboot_Prepare slot_id %" PRIu32 " details %p buffer %p", + slot_id, details, buffer); + UC_PAAL_TRACE("FW size %" PRIu64, details->size); + + /* TODO: Currently, only non-secure firmware update is supported. To support + * secure/combined firmware update, it is necessary to read + * header (and TLV) of active located in SPE, which is prohibited by + * TF-M. */ + + /* clear for clean */ + memset(&firmware_update_ctx_nonsecure, 0x00, sizeof(firmware_update_ctx_t)); + + /* PSA image ID: non-secure + staging */ + firmware_update_ctx_nonsecure.psa_fwu.image_id = \ + (psa_image_id_t) FWU_CALCULATE_IMAGE_ID(FWU_IMAGE_ID_SLOT_STAGE, + FWU_IMAGE_TYPE_NONSECURE, + 0); + + /* initialize hash context */ + mbedtls_md_init(&firmware_update_ctx_nonsecure.hash_ctx); + mbedtls_md_setup(&firmware_update_ctx_nonsecure.hash_ctx, + mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), + 0); + mbedtls_md_starts(&firmware_update_ctx_nonsecure.hash_ctx); + + /* store firmware details */ + memcpy(&firmware_update_ctx_nonsecure.details, details, sizeof(arm_uc_firmware_details_t)); + + /* support rewrite sequence aborting previous one */ + psa_fwu_abort(firmware_update_ctx_nonsecure.psa_fwu.image_id); + + /* signal done */ + arm_uc_pal_psa_signal_internal(ARM_UC_PAAL_EVENT_PREPARE_DONE); + + result.code = ERR_NONE; + } + + return result; +} + +/** + * @brief Write a fragment to the indicated storage location. + * @details The storage location must have been allocated using the Prepare + * call. The call is expected to write the entire fragment before + * signaling completion. + * + * @param slot_id Storage location ID. + * @param offset Offset in bytes to where the fragment should be written. + * @param buffer Pointer to buffer struct with fragment. + * @return Returns ERR_NONE on accept, and signals the event handler with + * either DONE or ERROR when complete. + * Returns ERR_INVALID_PARAMETER on reject, and no signal is sent. + */ +arm_uc_error_t ARM_UC_PAL_PSA_Mcuboot_Write(uint32_t slot_id, + uint32_t offset, + const arm_uc_buffer_t* buffer) +{ + arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER }; + + if ((slot_id < MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS) && + buffer && buffer->ptr) { + UC_PAAL_TRACE("ARM_UC_PAL_PSA_Mcuboot_Write: buffer=%p buffer size=%" PRIX32 " offset=%" PRIX32, + buffer->ptr, buffer->size, offset); + + /* catch MCUBOOT header at offset 0 and store it in buffer for later activation. */ + if (offset == 0) { + UC_PAAL_TRACE("cache MCUBOOT header for later activation"); + + if (buffer->size < sizeof(image_header_t)) { + UC_PAAL_ERR_MSG("failed to cache MCUBOOT header for later activation at first write: write size=%d", + buffer->size); + result.code = ERR_UNSPECIFIED; + return result; + } + + memcpy(&firmware_update_ctx_nonsecure.image_header, buffer->ptr, sizeof(image_header_t)); + + if (firmware_update_ctx_nonsecure.image_header.ih_magic != IMAGE_MAGIC) { + UC_PAAL_ERR_MSG("Invalid MCUBOOT header magic"); + result.code = ERR_UNSPECIFIED; + return result; + } + + UC_PAAL_TRACE("Image header: padded header size=%d, image size=%d, protected TLV size=%d", + firmware_update_ctx_nonsecure.image_header.ih_hdr_size, + firmware_update_ctx_nonsecure.image_header.ih_img_size, + firmware_update_ctx_nonsecure.image_header.ih_protect_tlv_size); + + /* padded header + body + protected TLV */ + firmware_update_ctx_nonsecure.header_hash_total = + firmware_update_ctx_nonsecure.image_header.ih_hdr_size + + firmware_update_ctx_nonsecure.image_header.ih_img_size + + firmware_update_ctx_nonsecure.image_header.ih_protect_tlv_size; + } + + /* write through psa_fwu_write(), with max block size PSA_FWU_MAX_BLOCK_SIZE */ + const uint8_t *fwu_src_pos = buffer->ptr; + const uint8_t *fwu_src_end = buffer->ptr + buffer->size; + size_t fwu_dst_pos = offset; + size_t fwu_todo; + size_t hash_end = firmware_update_ctx_nonsecure.header_hash_total; + size_t hash_todo; + psa_status_t status; + + while (fwu_src_pos < fwu_src_end) { + fwu_todo = fwu_src_end - fwu_src_pos; + if (fwu_todo > PSA_FWU_MAX_BLOCK_SIZE) { + fwu_todo = PSA_FWU_MAX_BLOCK_SIZE; + } + + status = psa_fwu_write(firmware_update_ctx_nonsecure.psa_fwu.image_id, + fwu_dst_pos, + fwu_src_pos, + fwu_todo); + if (status != PSA_SUCCESS) { + UC_PAAL_ERR_MSG("psa_fwu_write(offset=%d, size=%d) failed: %d\r\n", + fwu_dst_pos, fwu_todo, status); + result.code = FIRM_ERR_WRITE; + return result; + } + + /* calculate on-the-fly */ + hash_todo = (hash_end > fwu_dst_pos) ? (hash_end - fwu_dst_pos) : 0; + if (hash_todo > fwu_todo) { + hash_todo = fwu_todo; + } + if (hash_todo) { + mbedtls_md_update(&firmware_update_ctx_nonsecure.hash_ctx, fwu_src_pos, hash_todo); + } + + /* Next block */ + fwu_dst_pos += fwu_todo; + fwu_src_pos += fwu_todo; + } + + /* signal done */ + arm_uc_pal_psa_signal_internal(ARM_UC_PAAL_EVENT_WRITE_DONE); + + result.code = ERR_NONE; + } + + return result; +} + +/** + * @brief Close storage location for writing and flush pending data. + * + * @param slot_id Storage location ID. + * @return Returns ERR_NONE on accept, and signals the event handler with + * either DONE or ERROR when complete. + * Returns ERR_INVALID_PARAMETER on reject, and no signal is sent. + */ +arm_uc_error_t ARM_UC_PAL_PSA_Mcuboot_Finalize(uint32_t slot_id) +{ + arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER }; + + /* validate input */ + if ((slot_id < MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS)) { + UC_PAAL_TRACE("ARM_UC_PAL_PSA_Mcuboot_Finalize"); + + mbedtls_md_finish(&firmware_update_ctx_nonsecure.hash_ctx, + (uint8_t*) firmware_update_ctx_nonsecure.header_hash); + + /* signal done */ + arm_uc_pal_psa_signal_internal(ARM_UC_PAAL_EVENT_FINALIZE_DONE); + + result.code = ERR_NONE; + } + + return result; +} + +/** + * @brief Read a fragment from the indicated storage location. + * @details The function will read until the buffer is full or the end of + * the storage location has been reached. The actual amount of + * bytes read is set in the buffer struct. + * + * @param slot_id Storage location ID. + * @param offset Offset in bytes to read from. + * @param buffer Pointer to buffer struct to store fragment. buffer->size + * contains the intended read size. + * @return Returns ERR_NONE on accept, and signals the event handler with + * either DONE or ERROR when complete. + * Returns ERR_INVALID_PARAMETER on reject, and no signal is sent. + * buffer->size contains actual bytes read on return. + */ +arm_uc_error_t ARM_UC_PAL_PSA_Mcuboot_Read(uint32_t slot_id, + uint32_t offset, + arm_uc_buffer_t* buffer) +{ + UC_PAAL_TRACE("ARM_UC_PAL_PSA_Mcuboot_Read"); + + (void) slot_id; + (void) offset; + (void) buffer; + + arm_uc_error_t result = { .code = ERR_NOT_READY }; + + return result; +} + +/** + * @brief Set the firmware image in the slot to be the new active image. + * @details This call is responsible for initiating the process for + * applying a new/different image. Depending on the platform this + * could be: + * * An empty call, if the installer can deduce which slot to + * choose from based on the firmware details. + * * Setting a flag to indicate which slot to use next. + * * Decompressing/decrypting/installing the firmware image on + * top of another. + * + * @param slot_id Storage location ID. + * @return Returns ERR_NONE on accept, and signals the event handler with + * either DONE or ERROR when complete. + * Returns ERR_INVALID_PARAMETER on reject, and no signal is sent. + */ +arm_uc_error_t ARM_UC_PAL_PSA_Mcuboot_Activate(uint32_t slot_id) +{ + arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER }; + + /* validate input */ + if ((slot_id < MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS)) { + UC_PAAL_TRACE("ARM_UC_PAL_PSA_Mcuboot_Activate"); + + /* get active image's MCUBOOT header hash and total size. */ + arm_uc_hash_t header_hash_active = { 0 }; + uint32_t total_size = 0; + + result = + arm_uc_pal_psa_get_hash_from_header(arm_uc_flashiap_active_read, + MBED_CONF_UPDATE_CLIENT_APPLICATION_DETAILS, + &header_hash_active, + &total_size); + if (result.error != ERR_NONE) { + return result; + } + + image_header_t* header_candidate = &firmware_update_ctx_nonsecure.image_header; + arm_uc_hash_t *header_hash_candidate = &firmware_update_ctx_nonsecure.header_hash; + arm_uc_firmware_details_t *details_candidate = &firmware_update_ctx_nonsecure.details; + + UC_PAAL_TRACE("magic: %" PRIX32, header_candidate->ih_magic); + UC_PAAL_TRACE("load: %" PRIX32, header_candidate->ih_load_addr); + UC_PAAL_TRACE("hdr: %" PRIX16, header_candidate->ih_hdr_size); + UC_PAAL_TRACE("img: %" PRIX32, header_candidate->ih_img_size); + UC_PAAL_TRACE("prot: %" PRIX16, header_candidate->ih_protect_tlv_size); + + /* Write details to KCM. The active header hash is used to identify + * which key-value pair to replace. */ + result = arm_uc_pal_psa_set_kcm_details(&header_hash_active, + header_hash_candidate, + details_candidate); + if (result.error != ERR_NONE) { + return result; + } + + psa_status_t status = psa_fwu_install(firmware_update_ctx_nonsecure.psa_fwu.image_id, + &firmware_update_ctx_nonsecure.psa_fwu.dependency_uuid, + &firmware_update_ctx_nonsecure.psa_fwu.dependency_version); + if (status != PSA_SUCCESS && status != PSA_SUCCESS_REBOOT) { + UC_PAAL_ERR_MSG("psa_fwu_install() failed: %d\r\n", status); + result.code = FIRM_ERR_ACTIVATE; + return result; + } + + /* signal done */ + arm_uc_pal_psa_signal_internal(ARM_UC_PAAL_EVENT_ACTIVATE_DONE); + + result.code = ERR_NONE; + } + + return result; +} + +/** + * @brief Get firmware details for the firmware image in the slot passed. + * @details This call populates the passed details struct with information + * about the firmware image in the slot passed. Only the fields + * marked as supported in the capabilities bitmap will have valid + * values. + * + * @param details Pointer to firmware details struct to be populated. + * @return Returns ERR_NONE on accept, and signals the event handler with + * either DONE or ERROR when complete. + * Returns ERR_INVALID_PARAMETER on reject, and no signal is sent. + */ +arm_uc_error_t ARM_UC_PAL_PSA_Mcuboot_GetFirmwareDetails( + uint32_t slot_id, + arm_uc_firmware_details_t* details) +{ + UC_PAAL_TRACE("ARM_UC_PAL_PSA_Mcuboot_GetFirmwareDetails"); + + (void) slot_id; + (void) details; + + /* This function is not used for MCUBOOT. */ + + arm_uc_error_t result = { .code = ERR_NOT_READY }; + + return result; +} + +/*****************************************************************************/ + +arm_uc_error_t ARM_UC_PAL_PSA_Mcuboot_GetActiveDetails(arm_uc_firmware_details_t* details) +{ + UC_PAAL_TRACE("ARM_UC_PAL_PSA_Mcuboot_GetActiveDetails"); + + arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER }; + + if (details) { + + /* Clear for clean */ + memset(details, 0x00, sizeof(arm_uc_firmware_details_t)); + + /* parse MCUBOOT header and get hash from TLV struct and total size */ + arm_uc_hash_t header_hash = { 0 }; + uint32_t total_size = 0; + + result = + arm_uc_pal_psa_get_hash_from_header(arm_uc_flashiap_active_read, + MBED_CONF_UPDATE_CLIENT_APPLICATION_DETAILS, + &header_hash, + &total_size); + if (result.code != ERR_NONE) { + return result; + } + + /* use MCUBOOT hash to lookup firmware details in KCM */ + result = arm_uc_pal_psa_get_kcm_details(&header_hash, details); + + /* if no details were found in KCM */ + if (result.error != ERR_NONE) { + + /* calculate hash from active image */ + result = arm_uc_pal_psa_calculate_hash(arm_uc_flashiap_active_read, + MBED_CONF_UPDATE_CLIENT_APPLICATION_DETAILS, + total_size, + &(details->hash)); + if (result.error != ERR_NONE) { + return result; + } + + details->size = total_size; + + /* Write details to KCM. The first parameter is supposed to be + * the active image hash so that the details can be written in + * the opposite slot, using the second parameter as key. Since + * both slots are empty, passing in the same hash as both + * parameters lets the function operate as intended. */ + result = arm_uc_pal_psa_set_kcm_details(&header_hash, + &header_hash, + details); + if (result.error != ERR_NONE) { + return result; + } + } + + /* signal event if operation was successful */ + arm_uc_pal_psa_signal_internal(ARM_UC_PAAL_EVENT_GET_ACTIVE_FIRMWARE_DETAILS_DONE); + + result.code = ERR_NONE; + } + + return result; +} + +/** + * @brief Get details for the firmware installer. + * @details This call populates the passed details struct with information + * about the firmware installer. + * + * @param details Pointer to firmware details struct to be populated. + * @return Returns ERR_NONE on accept, and signals the event handler with + * either DONE or ERROR when complete. + * Returns ERR_INVALID_PARAMETER on reject, and no signal is sent. + */ +arm_uc_error_t ARM_UC_PAL_PSA_Mcuboot_GetInstallerDetails(arm_uc_installer_details_t* details) +{ + arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER }; + + if (details) { + + /* installer details not supported with MCUBOOT, zero details struct */ + memset(details, 0, sizeof(arm_uc_installer_details_t)); + + /* signal done */ + arm_uc_pal_psa_signal_internal(ARM_UC_PAAL_EVENT_GET_INSTALLER_DETAILS_DONE); + + result.code = ERR_NONE; + } + + return result; +} + +#endif /* ARM_UC_FEATURE_PAL_FLASHIAP */ diff --git a/update-client-hub/modules/pal-psa/update-client-pal-psa/arm_uc_pal_psa.h b/update-client-hub/modules/pal-psa/update-client-pal-psa/arm_uc_pal_psa.h new file mode 100644 index 000000000..5fdd9b7ee --- /dev/null +++ b/update-client-hub/modules/pal-psa/update-client-pal-psa/arm_uc_pal_psa.h @@ -0,0 +1,26 @@ +// ---------------------------------------------------------------------------- +// Copyright 2016-2020 ARM Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------- + +#ifndef ARM_UC_PAL_PSA_H +#define ARM_UC_PAL_PSA_H + +#include "update-client-paal/arm_uc_paal_update_api.h" + +extern ARM_UC_PAAL_UPDATE ARM_UCP_PSA; + +#endif /* ARM_UC_PAL_PSA_H */ diff --git a/update-client-hub/modules/pal-psa/update-client-pal-psa/arm_uc_pal_psa_helper.h b/update-client-hub/modules/pal-psa/update-client-pal-psa/arm_uc_pal_psa_helper.h new file mode 100644 index 000000000..2d881a752 --- /dev/null +++ b/update-client-hub/modules/pal-psa/update-client-pal-psa/arm_uc_pal_psa_helper.h @@ -0,0 +1,175 @@ +// ---------------------------------------------------------------------------- +// Copyright 2016-2020 ARM Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------- + +#ifndef ARM_UC_PAL_PSA_HELPER_H +#define ARM_UC_PAL_PSA_HELPER_H + +#include "update-client-metadata-header/arm_uc_metadata_header_v2.h" + +/*****************************************************************************/ +/* MCUBOOT */ +/*****************************************************************************/ + +#define IMAGE_MAGIC 0x96f3b83d +#define IMAGE_HEADER_SIZE 32 + +/** + * MCUBOOT Image version. + */ +typedef struct image_version { + uint8_t iv_major; + uint8_t iv_minor; + uint16_t iv_revision; + uint32_t iv_build_num; +} image_version_t; + +/** + * MCUBOOT Image header. All fields are in little endian byte order. + */ +typedef struct image_header { + uint32_t ih_magic; + uint32_t ih_load_addr; + uint16_t ih_hdr_size; /* Size of image header (bytes). */ + uint16_t ih_protect_tlv_size; /* Size of protected TLV area (bytes). */ + uint32_t ih_img_size; /* Does not include header. */ + uint32_t ih_flags; /* IMAGE_F_[...]. */ + image_version_t ih_ver; + uint32_t _pad1; +} image_header_t; + +/** + * MCUBOOT Image TLV header. All fields in little endian. + */ +typedef struct image_tlv_info { + uint16_t it_magic; + uint16_t it_tlv_tot; /* size of TLV area (including tlv_info header) */ +} image_tlv_info_t; + +/** + * MCUBOOT Image trailer TLV format. All fields in little endian. + */ +typedef struct image_tlv { + uint8_t it_type; /* IMAGE_TLV_[...]. */ + uint8_t _pad; + uint16_t it_len; /* Data length (not including TLV header). */ +} image_tlv_t; + +#define IMAGE_TLV_INFO_MAGIC 0x6907 +#define IMAGE_TLV_PROT_INFO_MAGIC 0x6908 +#define IMAGE_TLV_SHA256 0x10 /* SHA256 of image hdr and body */ + +/*****************************************************************************/ +/* KCM */ +/*****************************************************************************/ + +/* Shorthand for key-names used for KCM access. */ +#define SLOT_A_NAME ((const uint8_t*) "UC_A") +#define SLOT_A_SIZE (4) +#define SLOT_B_NAME ((const uint8_t*) "UC_B") +#define SLOT_B_SIZE (4) + +typedef int32_t (*arm_uc_reader_p)(uint8_t *, uint32_t, uint32_t); + +/** + * @brief Get hash from MCUBOOT TLV struct. + * + * This function reads the hash directly from the TLV struct. + * + * @param[in] flash_reader Function pointer to internal or external flash reader. + * @param address Address in flash where TLV struct begins. + * @param header_hash Pointer to hash-struct to be filed. + * + * @return ERR_NONE Success, the hash-struct has been populated. + * ERR_INVALID_PARAMETER Failure, unable to find hash at address. + */ +arm_uc_error_t arm_uc_pal_psa_get_hash_from_tlv(arm_uc_reader_p flash_reader, + uint32_t address, + arm_uc_hash_t* header_hash); + +/** + * @brief Get hash from MCUBOOT TLV struct. + * + * This function uses the MCUBOOT header to find the address + * for the TLV struct where the hash is stored. + * + * @param[in] flash_reader Function pointer to internal or external flash reader. + * @param address Address in flash where MCUBOOT header begins. + * @param header_hash Pointer to hash-struct to be filled. + * @param total_size Pointer to uint32_t for storing total, signed image size. + * + * @return ERR_NONE Success, the hash-struct has been populated. + * ERR_INVALID_PARAMETER Failure, unable to find hash at address. + */ +arm_uc_error_t arm_uc_pal_psa_get_hash_from_header(arm_uc_reader_p flash_reader, + uint32_t address, + arm_uc_hash_t* header_hash, + uint32_t* total_size); + +/** + * @brief Calculate hash directly from stored image. + * + * @param[in] flash_reader Function pointer to internal or external flash reader. + * @param address Address in flash where MCUBOOT header begins. + * @param total_size uint32_t with total, signed image size. + * @param header_hash Pointer to hash-struct to be filled. + * + * @return ERR_NONE Success, the hash-struct has been populated. + * ERR_INVALID_PARAMETER Failure, unable to find hash at address. + */ +arm_uc_error_t arm_uc_pal_psa_calculate_hash(arm_uc_reader_p flash_reader, + uint32_t address, + uint32_t total_size, + arm_uc_hash_t* header_hash); + +/** + * @brief Get firmware details stored in KCM. + * + * The function uses a SHA256 hash to search two preassigned + * locations in the KCM for the firmware details associated + * with the hash. + * + * @param header_hash Hash from MCUBOOT header. + * @param details Pointer to firmware details struct to be filled. + * + * @return ERR_NONE Success, the details-struct has been populated. + * ERR_INVALID_PARAMETER Failure, unable to find entry with hash. + */ +arm_uc_error_t arm_uc_pal_psa_get_kcm_details(arm_uc_hash_t* header_hash, + arm_uc_firmware_details_t* details); + +/** + * @brief Set firmware details in KCM. + * + * The function stores firmware details in the KCM using a + * SHA256 hash as the key for two preallocated entries. + * The hash for the active firmware is passed as argument + * as well to resolve any ambiguity on which entry should + * be used. + * + * @param header_hash_active Hash for active firmware. + * @param header_hash_candidate Hash for candidate firmware. + * @param details Pointer to the candidate firmware's details. + * + * @return ERR_NONE Success, firmware details has been stored in KCM. + * ERR_INVALID_PARAMETER Failure, unable to store details. + */ +arm_uc_error_t arm_uc_pal_psa_set_kcm_details(arm_uc_hash_t* header_hash_active, + arm_uc_hash_t* header_hash_candidate, + arm_uc_firmware_details_t* details); + +#endif /* ARM_UC_PAL_PSA_HELPER_H */ diff --git a/update-client-hub/modules/pal-psa/update-client-pal-psa/arm_uc_pal_psa_implementation.h b/update-client-hub/modules/pal-psa/update-client-pal-psa/arm_uc_pal_psa_implementation.h new file mode 100644 index 000000000..c7819bdef --- /dev/null +++ b/update-client-hub/modules/pal-psa/update-client-pal-psa/arm_uc_pal_psa_implementation.h @@ -0,0 +1,143 @@ +// ---------------------------------------------------------------------------- +// Copyright 2016-2020 ARM Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------- + +#ifndef ARM_UC_PAL_PSA_IMPLEMENTATION_H +#define ARM_UC_PAL_PSA_IMPLEMENTATION_H + +#include "update-client-paal/arm_uc_paal_update_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +arm_uc_error_t ARM_UC_PAL_PSA_Mcuboot_Initialize(ARM_UC_PAAL_UPDATE_SignalEvent_t callback); + +/** + * @brief Get maximum number of supported storage locations. + * + * @return Number of storage locations. + */ +uint32_t ARM_UC_PAL_PSA_Mcuboot_GetMaxID(void); + +/** + * @brief Prepare the storage layer for a new firmware image. + * @details The storage location is set up to receive an image with + * the details passed in the details struct. + * + * @param location Storage location ID. + * @param details Pointer to a struct with firmware details. + * @param buffer Temporary buffer for formatting and storing metadata. + * @return Returns ERR_NONE on accept, and signals the event handler with + * either DONE or ERROR when complete. + * Returns ERR_INVALID_PARAMETER on reject, and no signal is sent. + */ +arm_uc_error_t ARM_UC_PAL_PSA_Mcuboot_Prepare(uint32_t location, + const arm_uc_firmware_details_t *details, + arm_uc_buffer_t *buffer); + +/** + * @brief Write a fragment to the indicated storage location. + * @details The storage location must have been allocated using the Prepare + * call. The call is expected to write the entire fragment before + * signaling completion. + * + * @param location Storage location ID. + * @param offset Offset in bytes to where the fragment should be written. + * @param buffer Pointer to buffer struct with fragment. + * @return Returns ERR_NONE on accept, and signals the event handler with + * either DONE or ERROR when complete. + * Returns ERR_INVALID_PARAMETER on reject, and no signal is sent. + */ +arm_uc_error_t ARM_UC_PAL_PSA_Mcuboot_Write(uint32_t location, + uint32_t offset, + const arm_uc_buffer_t *buffer); + +/** + * @brief Close storage location for writing and flush pending data. + * + * @param location Storage location ID. + * @return Returns ERR_NONE on accept, and signals the event handler with + * either DONE or ERROR when complete. + * Returns ERR_INVALID_PARAMETER on reject, and no signal is sent. + */ +arm_uc_error_t ARM_UC_PAL_PSA_Mcuboot_Finalize(uint32_t location); + +/** + * @brief Read a fragment from the indicated storage location. + * @details The function will read until the buffer is full or the end of + * the storage location has been reached. The actual amount of + * bytes read is set in the buffer struct. + * + * @param location Storage location ID. + * @param offset Offset in bytes to read from. + * @param buffer Pointer to buffer struct to store fragment. buffer->size + * contains the intended read size. + * @return Returns ERR_NONE on accept, and signals the event handler with + * either DONE or ERROR when complete. + * Returns ERR_INVALID_PARAMETER on reject, and no signal is sent. + * buffer->size contains actual bytes read on return. + */ +arm_uc_error_t ARM_UC_PAL_PSA_Mcuboot_Read(uint32_t location, + uint32_t offset, + arm_uc_buffer_t *buffer); + +/** + * @brief Set the firmware image in the slot to be the new active image. + * @details This call is responsible for initiating the process for + * applying a new/different image. Depending on the platform this + * could be: + * * An empty call, if the installer can deduce which slot to + * choose from based on the firmware details. + * * Setting a flag to indicate which slot to use next. + * * Decompressing/decrypting/installing the firmware image on + * top of another. + * + * @param location Storage location ID. + * @return Returns ERR_NONE on accept, and signals the event handler with + * either DONE or ERROR when complete. + * Returns ERR_INVALID_PARAMETER on reject, and no signal is sent. + */ +arm_uc_error_t ARM_UC_PAL_PSA_Mcuboot_Activate(uint32_t location); + +/** + * @brief Get firmware details for the firmware image in the slot passed. + * @details This call populates the passed details struct with information + * about the firmware image in the slot passed. Only the fields + * marked as supported in the capabilities bitmap will have valid + * values. + * + * @param details Pointer to firmware details struct to be populated. + * @return Returns ERR_NONE on accept, and signals the event handler with + * either DONE or ERROR when complete. + * Returns ERR_INVALID_PARAMETER on reject, and no signal is sent. + */ +arm_uc_error_t ARM_UC_PAL_PSA_Mcuboot_GetFirmwareDetails( + uint32_t location, + arm_uc_firmware_details_t *details); + +/*****************************************************************************/ + +arm_uc_error_t ARM_UC_PAL_PSA_Mcuboot_GetActiveDetails(arm_uc_firmware_details_t *details); + +arm_uc_error_t ARM_UC_PAL_PSA_Mcuboot_GetInstallerDetails(arm_uc_installer_details_t *details); + +#ifdef __cplusplus +} +#endif + +#endif // ARM_UC_PAL_PSA_IMPLEMENTATION_H diff --git a/update-client-hub/modules/pal-psa/update-client-pal-psa/arm_uc_pal_psa_platform.h b/update-client-hub/modules/pal-psa/update-client-pal-psa/arm_uc_pal_psa_platform.h new file mode 100644 index 000000000..e9d38f34c --- /dev/null +++ b/update-client-hub/modules/pal-psa/update-client-pal-psa/arm_uc_pal_psa_platform.h @@ -0,0 +1,63 @@ +// ---------------------------------------------------------------------------- +// Copyright 2016-2020 ARM Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------- + +#ifndef ARM_UC_PAL_PSA_PLATFORM_H +#define ARM_UC_PAL_PSA_PLATFORM_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + ARM_UC_FLASHIAP_SUCCESS = 0, + ARM_UC_FLASHIAP_FAIL = -1 +}; + +#define ARM_UC_FLASH_INVALID_SIZE 0xFFFFFFFF + +/*****************************************************************************/ +/* Flash functions for active firmware partition. */ +/*****************************************************************************/ + +/** Initialize a flash IAP device + * + * Should be called once per lifetime of the object. + * @return 0 on success or a negative error code on failure + */ +int32_t arm_uc_flashiap_active_init(void); + +/** Read data from a flash device. + * + * This method invokes memcpy - reads number of bytes from the address + * + * @param buffer Buffer to write to + * @param address Flash address to begin reading from + * @param size Size to read in bytes + * @return 0 on success, negative error code on failure + */ +int32_t arm_uc_flashiap_active_read(uint8_t *buffer, + uint32_t address, + uint32_t size); + +#ifdef __cplusplus +} +#endif + +#endif