diff --git a/examples/lighting-app/qpg/include/CHIPProjectConfig.h b/examples/lighting-app/qpg/include/CHIPProjectConfig.h index f19f47d8845051..3d663bc093ba83 100755 --- a/examples/lighting-app/qpg/include/CHIPProjectConfig.h +++ b/examples/lighting-app/qpg/include/CHIPProjectConfig.h @@ -68,6 +68,15 @@ */ #define CHIP_DEVICE_CONFIG_DEVICE_HARDWARE_VERSION 1 +/** + * CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION + * + * A uint32_t identifying the software version running on the device. + */ +#ifndef CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION 0x0001 +#endif + /** * CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING * diff --git a/examples/persistent-storage/qpg/main.cpp b/examples/persistent-storage/qpg/main.cpp index b53692e19e448b..cae4e0eab862c0 100644 --- a/examples/persistent-storage/qpg/main.cpp +++ b/examples/persistent-storage/qpg/main.cpp @@ -47,25 +47,29 @@ void TestTask(void * pvParameter) } } +void Application_Init(void) +{ + /* Launch application task */ + qvCHIP_Printf(LOG_MODULE_ID, "============================"); + qvCHIP_Printf(LOG_MODULE_ID, "Qorvo " APP_NAME " Launching"); + qvCHIP_Printf(LOG_MODULE_ID, "============================"); + + // Run tests + xTaskCreateStatic(TestTask, APP_NAME, 2048, NULL, 1, appStack, &appTaskStruct); +} + int main(void) { int result; /* Initialize Qorvo stack */ - result = qvCHIP_init(); + result = qvCHIP_init(Application_Init); if (result < 0) { goto exit; } - /* Launch application task */ - qvCHIP_Printf(LOG_MODULE_ID, "============================"); - qvCHIP_Printf(LOG_MODULE_ID, "Qorvo " APP_NAME " Launching"); - qvCHIP_Printf(LOG_MODULE_ID, "============================"); - - // Run tests - xTaskCreateStatic(TestTask, APP_NAME, 2048, NULL, 1, appStack, &appTaskStruct); qvCHIP_Printf(LOG_MODULE_ID, "Starting FreeRTOS scheduler"); vTaskStartScheduler(); diff --git a/examples/platform/qpg/app/main.cpp b/examples/platform/qpg/app/main.cpp index dc71281d592e5d..56eca7761cabbd 100644 --- a/examples/platform/qpg/app/main.cpp +++ b/examples/platform/qpg/app/main.cpp @@ -69,9 +69,20 @@ constexpr int extDiscTimeoutSecs = 20; /***************************************************************************** * Application Function Definitions *****************************************************************************/ +CHIP_ERROR CHIP_Init(void); -int Application_Init(void) +void Application_Init(void) { + CHIP_ERROR error; + + /* Initialize CHIP stack */ + error = CHIP_Init(); + if (error != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "CHIP_Init failed"); + return; + } + /* Launch application task */ ChipLogProgress(NotSpecified, "============================"); ChipLogProgress(NotSpecified, "Qorvo " APP_NAME " Launching"); @@ -81,10 +92,8 @@ int Application_Init(void) if (ret != CHIP_NO_ERROR) { ChipLogError(NotSpecified, "GetAppTask().Init() failed"); - return -1; + return; } - - return 0; } CHIP_ERROR CHIP_Init(void) @@ -172,32 +181,15 @@ CHIP_ERROR CHIP_Init(void) int main(void) { int result; - CHIP_ERROR error; /* Initialize Qorvo stack */ - result = qvCHIP_init(); - if (result < 0) - { - goto exit; - } - - /* Initialize CHIP stack */ - error = CHIP_Init(); - if (error != CHIP_NO_ERROR) - { - goto exit; - } - - /* Application task */ - result = Application_Init(); + result = qvCHIP_init(Application_Init); if (result < 0) { - goto exit; + return 0; } /* Start FreeRTOS */ vTaskStartScheduler(); - -exit: return 0; } diff --git a/examples/platform/qpg/ota/ota.cpp b/examples/platform/qpg/ota/ota.cpp index 43639400c1b224..2ae61482c75729 100644 --- a/examples/platform/qpg/ota/ota.cpp +++ b/examples/platform/qpg/ota/ota.cpp @@ -50,6 +50,19 @@ OTAImageProcessorImpl gImageProcessor; * Application Function Definitions *****************************************************************************/ +bool OtaHeaderValidationCb(qvCHIP_Ota_ImageHeader_t imageHeader) +{ + // Check that the image matches vendor and product ID and that the version is higher than what we currently have + if (imageHeader.vendorId != CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID || + imageHeader.productId != CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_ID || + imageHeader.softwareVersion <= CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION) + { + return false; + } + + return true; +} + void InitializeOTARequestor(void) { // Initialize and interconnect the Requestor and Image Processor objects @@ -60,6 +73,9 @@ void InitializeOTARequestor(void) gImageProcessor.SetOTADownloader(&gDownloader); gDownloader.SetImageProcessorDelegate(&gImageProcessor); gRequestorUser.Init(&gRequestorCore, &gImageProcessor); + + // Initialize OTA image validation callback + qvCHIP_OtaSetHeaderValidationCb(OtaHeaderValidationCb); } void TriggerOTAQuery(void) diff --git a/src/platform/qpg/CHIPDevicePlatformConfig.h b/src/platform/qpg/CHIPDevicePlatformConfig.h index fb08c6b3145ef5..dd2434ce95f897 100644 --- a/src/platform/qpg/CHIPDevicePlatformConfig.h +++ b/src/platform/qpg/CHIPDevicePlatformConfig.h @@ -49,7 +49,7 @@ // ========== Platform-specific Configuration Overrides ========= #ifndef CHIP_DEVICE_CONFIG_CHIP_TASK_STACK_SIZE -#define CHIP_DEVICE_CONFIG_CHIP_TASK_STACK_SIZE (5 * 1024) +#define CHIP_DEVICE_CONFIG_CHIP_TASK_STACK_SIZE (6 * 1024) #endif // CHIP_DEVICE_CONFIG_CHIP_TASK_STACK_SIZE #ifndef CHIP_DEVICE_CONFIG_THREAD_TASK_STACK_SIZE diff --git a/src/platform/qpg/OTAImageProcessorImpl.cpp b/src/platform/qpg/OTAImageProcessorImpl.cpp index 4d537069c8ee27..0cc8c533de3f51 100644 --- a/src/platform/qpg/OTAImageProcessorImpl.cpp +++ b/src/platform/qpg/OTAImageProcessorImpl.cpp @@ -17,22 +17,87 @@ */ #include +#include #include "OTAImageProcessorImpl.h" namespace chip { +bool OTAImageProcessorImpl::IsFirstImageRun() +{ + OTARequestorInterface * requestor = chip::GetRequestorInstance(); + if (requestor == nullptr) + { + return false; + } + + return requestor->GetCurrentUpdateState() == OTARequestorInterface::OTAUpdateStateEnum::kApplying; +} + +CHIP_ERROR OTAImageProcessorImpl::ConfirmCurrentImage() +{ + OTARequestorInterface * requestor = chip::GetRequestorInstance(); + if (requestor == nullptr) + { + return CHIP_ERROR_INTERNAL; + } + + uint32_t currentVersion; + ReturnErrorOnFailure(DeviceLayer::ConfigurationMgr().GetSoftwareVersion(currentVersion)); + + if (currentVersion != requestor->GetTargetVersion()) + { + return CHIP_ERROR_INCORRECT_STATE; + } + + return CHIP_NO_ERROR; +} + CHIP_ERROR OTAImageProcessorImpl::PrepareDownload() { // Get OTA status - under what circumstances does prepared break? // what happens if a prepare is pending and another one is invoked - // Should we store the state here and wait til we receive notification + // Should we store the state here and wait until we receive notification + + mHeaderParser.Init(); DeviceLayer::PlatformMgr().ScheduleWork(HandlePrepareDownload, reinterpret_cast(this)); return CHIP_NO_ERROR; } +CHIP_ERROR OTAImageProcessorImpl::ProcessHeader(ByteSpan & block) +{ + if (mHeaderParser.IsInitialized()) + { + OTAImageHeader header; + CHIP_ERROR error = mHeaderParser.AccumulateAndDecode(block, header); + + // Needs more data to decode the header + ReturnErrorCodeIf(error == CHIP_ERROR_BUFFER_TOO_SMALL, CHIP_NO_ERROR); + ReturnErrorOnFailure(error); + + mParams.totalFileBytes = header.mPayloadSize; + mHeaderParser.Clear(); + + // Load qvCHIP_Ota header structure and call application callback to validate image header + qvCHIP_Ota_ImageHeader_t qvCHIP_OtaImgHeader; + this->mSwVer = header.mSoftwareVersion; // Store software version in imageProcessor as well + qvCHIP_OtaImgHeader.vendorId = header.mVendorId; + qvCHIP_OtaImgHeader.productId = header.mProductId; + qvCHIP_OtaImgHeader.softwareVersion = header.mSoftwareVersion; + qvCHIP_OtaImgHeader.minApplicableVersion = header.mMinApplicableVersion.ValueOr(0); + qvCHIP_OtaImgHeader.maxApplicableVersion = header.mMaxApplicableVersion.ValueOr(0); + + if (true != qvCHIP_OtaValidateImage(qvCHIP_OtaImgHeader)) + { + return CHIP_ERROR_UNSUPPORTED_EXCHANGE_VERSION; + } + } + + return CHIP_NO_ERROR; +} + CHIP_ERROR OTAImageProcessorImpl::Finalize() { DeviceLayer::PlatformMgr().ScheduleWork(HandleFinalize, reinterpret_cast(this)); @@ -57,16 +122,27 @@ CHIP_ERROR OTAImageProcessorImpl::Abort() CHIP_ERROR OTAImageProcessorImpl::ProcessBlock(ByteSpan & block) { + CHIP_ERROR err; + if ((block.data() == nullptr) || block.empty()) { return CHIP_ERROR_INVALID_ARGUMENT; } + // Process block header info + err = ProcessHeader(block); + if (err != CHIP_NO_ERROR) + { + ChipLogError(SoftwareUpdate, "Cannot process block header: %" CHIP_ERROR_FORMAT, err.Format()); + return err; + } + // Store block data for HandleProcessBlock to access - CHIP_ERROR err = SetBlock(block); + err = SetBlock(block); if (err != CHIP_NO_ERROR) { ChipLogError(SoftwareUpdate, "Cannot set block data: %" CHIP_ERROR_FORMAT, err.Format()); + return err; } DeviceLayer::PlatformMgr().ScheduleWork(HandleProcessBlock, reinterpret_cast(this)); @@ -109,8 +185,7 @@ void OTAImageProcessorImpl::HandleFinalize(intptr_t context) ChipLogProgress(SoftwareUpdate, "Q: HandleFinalize"); - // FIXME - Versions need to be filled in - qvCHIP_OtaSetPendingImage(imageProcessor->mSwVer /*swVer*/, imageProcessor->mHwVer /*hwVer*/, qvCHIP_OtaGetAreaStartAddress(), + qvCHIP_OtaSetPendingImage(imageProcessor->mSwVer /*swVer*/, CHIP_DEVICE_CONFIG_DEVICE_HARDWARE_VERSION /*hwVer*/, 0, static_cast(imageProcessor->mParams.downloadedBytes) /*imgSz*/); imageProcessor->ReleaseBlock(); @@ -149,11 +224,10 @@ void OTAImageProcessorImpl::HandleProcessBlock(intptr_t context) } ChipLogProgress(SoftwareUpdate, "Q: HandleProcessBlock"); - // TODO: Process block header if any - status = qvCHIP_OtaWriteChunk(qvCHIP_OtaGetAreaStartAddress() + imageProcessor->mParams.downloadedBytes, - static_cast(imageProcessor->mBlock.size()), - reinterpret_cast(imageProcessor->mBlock.data())); + status = + qvCHIP_OtaWriteChunk(imageProcessor->mParams.downloadedBytes, static_cast(imageProcessor->mBlock.size()), + reinterpret_cast(imageProcessor->mBlock.data())); if (status != qvCHIP_OtaStatusSuccess) { diff --git a/src/platform/qpg/OTAImageProcessorImpl.h b/src/platform/qpg/OTAImageProcessorImpl.h index b76b813c9ce989..493aa23842e816 100644 --- a/src/platform/qpg/OTAImageProcessorImpl.h +++ b/src/platform/qpg/OTAImageProcessorImpl.h @@ -19,6 +19,7 @@ #pragma once #include +#include #include #include @@ -29,12 +30,13 @@ class OTAImageProcessorImpl : public OTAImageProcessorInterface public: //////////// OTAImageProcessorInterface Implementation /////////////// CHIP_ERROR PrepareDownload() override; + CHIP_ERROR ProcessHeader(ByteSpan & block); CHIP_ERROR Finalize() override; CHIP_ERROR Apply() override; CHIP_ERROR Abort() override; CHIP_ERROR ProcessBlock(ByteSpan & block) override; - bool IsFirstImageRun() override { return false; } - CHIP_ERROR ConfirmCurrentImage() override { return CHIP_NO_ERROR; } + bool IsFirstImageRun() override; + CHIP_ERROR ConfirmCurrentImage() override; void SetOTADownloader(OTADownloader * downloader) { mDownloader = downloader; } @@ -59,7 +61,8 @@ class OTAImageProcessorImpl : public OTAImageProcessorInterface std::uint32_t mHwVer; MutableByteSpan mBlock; - OTADownloader * mDownloader; + OTADownloader * mDownloader = nullptr; + OTAImageHeaderParser mHeaderParser; }; } // namespace chip diff --git a/third_party/qpg_sdk/qpg_executable.gni b/third_party/qpg_sdk/qpg_executable.gni index 6c422d9bdec454..454040e25d728f 100644 --- a/third_party/qpg_sdk/qpg_executable.gni +++ b/third_party/qpg_sdk/qpg_executable.gni @@ -14,11 +14,9 @@ import("//build_overrides/build.gni") import("//build_overrides/chip.gni") -import("//build_overrides/pigweed.gni") import("${build_root}/toolchain/flashable_executable.gni") import("${chip_root}/src/platform/device.gni") -import("${dir_pw_build}/python_action.gni") import("qpg_sdk.gni") # Run the generator script that takes a .HEX file and adds the OTA header to it. @@ -43,7 +41,7 @@ template("gen_ota_header") { "data_deps", ]) - pw_python_action(target_name) { + action(target_name) { outputs = [ ota_header_script_name ] args = ota_header_options @@ -96,10 +94,8 @@ template("qpg_executable") { # If OTA requestor is enabled, generate OTA image from HEX if (chip_enable_ota_requestor) { - ota_image_name = invoker.output_name + ".ota" - gen_ota_header("$executable_target_name.ota") { - ota_header_script_name = "${root_out_dir}/${ota_image_name}" + ota_header_script_name = "${root_out_dir}/${executable_target_name}.ota" out_dir = rebase_path(root_out_dir, root_build_dir) ota_header_generator = "${qpg_sdk_root}/Tools/ota/generate_ota_img.py" @@ -119,6 +115,14 @@ template("qpg_executable") { "--out_file", "${out_dir}/${invoker.output_name}.ota", ]), + string_join("=", + [ + "--pem_file_path", + rebase_path(qpg_sdk_root, root_build_dir) + + "/Tools/ota/example_private_key.pem.example", + ]), + "--pem_password=test1234", + "--sign", ] deps = [ ":$executable_target_name" ] } diff --git a/third_party/qpg_sdk/qpg_sdk.gni b/third_party/qpg_sdk/qpg_sdk.gni index fa7816763ece58..708a29c15b6b74 100644 --- a/third_party/qpg_sdk/qpg_sdk.gni +++ b/third_party/qpg_sdk/qpg_sdk.gni @@ -82,7 +82,7 @@ template("qpg_sdk") { "${qpg_sdk_root}/${qpg_sdk_lib_dir}/MatterQorvoGlue_${qpg_target_ic}_libbuild/libMatterQorvoGlue_${qpg_target_ic}_libbuild.a", "${qpg_sdk_root}/${qpg_sdk_lib_dir}/QorvoStack_${qpg_target_ic}/libQorvoStack_${qpg_target_ic}.a", "${qpg_sdk_root}/${qpg_sdk_lib_dir}/mbedtls_alt_${qpg_target_ic}/libmbedtls_alt_${qpg_target_ic}.a", - "${qpg_sdk_root}/${qpg_sdk_lib_dir}/Bootloader_${qpg_target_ic}/libBootloader_${qpg_target_ic}.a", + "${qpg_sdk_root}/${qpg_sdk_lib_dir}/Bootloader_${qpg_target_ic}_compr_secure/libBootloader_${qpg_target_ic}_compr_secure.a", ] } diff --git a/third_party/qpg_sdk/repo b/third_party/qpg_sdk/repo index a29b995c4e2c20..fa660d1762c709 160000 --- a/third_party/qpg_sdk/repo +++ b/third_party/qpg_sdk/repo @@ -1 +1 @@ -Subproject commit a29b995c4e2c209876996d05a042740521d9d64d +Subproject commit fa660d1762c70938430d058f5830777770356bbd