diff --git a/examples/contact-sensor-app/nxp/k32w/k32w0/README.md b/examples/contact-sensor-app/nxp/k32w/k32w0/README.md index 61fae7101ca5a3..af446f80a6bd99 100644 --- a/examples/contact-sensor-app/nxp/k32w/k32w0/README.md +++ b/examples/contact-sensor-app/nxp/k32w/k32w0/README.md @@ -177,14 +177,14 @@ In order to build the Project CHIP example, we recommend using a Linux distribution (the demo-application was compiled on Ubuntu 20.04). - Download - [K32W061DK6 SDK 2.6.9](https://cache.nxp.com/lgfiles/bsps/SDK_2_6_9_K32W061DK6.zip). + [K32W061DK6 SDK 2.6.10](https://cache.nxp.com/lgfiles/bsps/SDK_2_6_10_K32W061DK6.zip). - Start building the application either with Secure Element or without - without Secure Element ``` - user@ubuntu:~/Desktop/git/connectedhomeip$ export NXP_K32W0_SDK_ROOT=/home/user/Desktop/SDK_2_6_9_K32W061DK6/ + user@ubuntu:~/Desktop/git/connectedhomeip$ export NXP_K32W0_SDK_ROOT=/home/user/Desktop/SDK_2_6_10_K32W061DK6/ user@ubuntu:~/Desktop/git/connectedhomeip$ ./third_party/nxp/k32w0_sdk/sdk_fixes/patch_k32w_sdk.sh user@ubuntu:~/Desktop/git/connectedhomeip$ source ./scripts/activate.sh user@ubuntu:~/Desktop/git/connectedhomeip$ cd examples/contact-sensor-app/nxp/k32w/k32w0 @@ -204,7 +204,7 @@ Secure Element. These can be changed if building without Secure Element Exactly the same steps as above but set argument build_for_k32w041am=1 in the gn command and use - [K32W041AMDK6 SDK 2.6.9](https://cache.nxp.com/lgfiles/bsps/SDK_2_6_9_K32W041AMDK6.zip). + [K32W041AMDK6 SDK 2.6.10](https://cache.nxp.com/lgfiles/bsps/SDK_2_6_10_K32W041AMDK6.zip). Also, in case the OM15082 Expansion Board is not attached to the DK6 board, the build argument (chip_with_OM15082) inside the gn build instruction should be set @@ -273,8 +273,8 @@ Program the firmware using the official [OpenThread Flash Instructions](https://github.com/openthread/ot-nxp/tree/main/src/k32w0/k32w061#flash-binaries). All you have to do is to replace the Openthread binaries from the above -documentation with _out/debug/chip-k32w0x-light-example.bin_ if DK6Programmer is -used or with _out/debug/chip-k32w0x-light-example_ if MCUXpresso is used. +documentation with _out/debug/chip-k32w0x-contact-example.bin_ if DK6Programmer +is used or with _out/debug/chip-k32w0x-contact-example_ if MCUXpresso is used. ## Pigweed tokenizer @@ -489,12 +489,35 @@ Build the Linux OTA provider application: user@computer1:~/connectedhomeip$ : ./scripts/examples/gn_build_example.sh examples/ota-provider-app/linux out/ota-provider-app chip_config_network_layer_ble=false ``` -Build OTA image and start the OTA Provider Application: +Build Linux chip-tool: + +``` +user@computer1:~/connectedhomeip$ : ./scripts/examples/gn_build_example.sh examples/chip-tool out/chip-tool-app +``` + +Build OTA image: + +In order to build an OTA image, use NXP wrapper over the standard tool +`src/app/ota_image_tool.py`: + +- `scripts/tools/nxp/factory_data_generator/ota_image_tool.py`. The tool can + be used to generate an OTA image with the following format: + `| OTA image header | TLV1 | TLV2 | ... | TLVn |` where each TLV is in the + form `|tag|length|value|` + +Note that "standard" TLV format is used. Matter TLV format is only used for +factory data TLV value. A user can enable the default processors by specifying +`chip_enable_ota_default_processors=1` in the build command. Please see more in +the [OTA image tool guide](../../../../../scripts/tools/nxp/ota/README.md). + +Here is an example that generate an OTA image with factory data and app TLV: +`user@computer1:~/connectedhomeip$ : ./scripts/tools/nxp/ota/ota_image_tool.py create -v 0xDEAD -p 0xBEEF -vn 1 -vs "1.0" -da sha256 -fd --cert_declaration ~/manufacturing/Chip-Test-CD-1037-a220.der -app chip-k32w0x-contact-example.bin chip-k32w0x-contact-example.bin chip-k32w0x-contact-example.ota` + +Start the OTA Provider Application: ``` -user@computer1:~/connectedhomeip$ : ./src/app/ota_image_tool.py create -v 0xDEAD -p 0xBEEF -vn 1 -vs "1.0" -da sha256 chip-k32w0x-light-example.bin chip-k32w0x-light-example.ota user@computer1:~/connectedhomeip$ : rm -rf /tmp/chip_* -user@computer1:~/connectedhomeip$ : ./out/ota-provider-app/chip-ota-provider-app -f chip-k32w0x-light-example.ota +user@computer1:~/connectedhomeip$ : ./out/ota-provider-app/chip-ota-provider-app -f chip-k32w0x-contact-example.ota ``` A note regarding OTA image header version (`-vn` option). An application binary @@ -522,7 +545,7 @@ user@computer1:~/connectedhomeip$ : ./out/chip-tool-app/chip-tool accesscontrol Provision the device and assign node id _2_: ``` -user@computer1:~/connectedhomeip$ : ./out/chip-tool-app/chip-tool pairing ble-thread 2 hex: 20202021 3840 +user@computer1:~/connectedhomeip$ : ./out/chip-tool-app/chip-tool pairing ble-thread 2 hex: 20202021 3840 ``` Start the OTA process: diff --git a/examples/contact-sensor-app/nxp/k32w/k32w0/main/AppTask.cpp b/examples/contact-sensor-app/nxp/k32w/k32w0/main/AppTask.cpp index 7c3f30d33e6644..c4332dfe0cbdee 100644 --- a/examples/contact-sensor-app/nxp/k32w/k32w0/main/AppTask.cpp +++ b/examples/contact-sensor-app/nxp/k32w/k32w0/main/AppTask.cpp @@ -37,12 +37,12 @@ /* OTA related includes */ #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR -#include "OTAImageProcessorImpl.h" #include "OtaSupport.h" #include #include #include #include +#include #endif #include "Keyboard.h" @@ -94,7 +94,6 @@ static DefaultOTARequestor gRequestorCore; static DefaultOTARequestorStorage gRequestorStorage; static DeviceLayer::DefaultOTARequestorDriver gRequestorUser; static BDXDownloader gDownloader; -static OTAImageProcessorImpl gImageProcessor; constexpr uint16_t requestedOtaBlockSize = 1024; #endif @@ -142,10 +141,10 @@ CHIP_ERROR AppTask::Init() #else SetDeviceAttestationCredentialsProvider(Examples::GetExampleDACProvider()); #endif +#endif // CONFIG_CHIP_K32W0_REAL_FACTORY_DATA // QR code will be used with CHIP Tool AppTask::PrintOnboardingInfo(); -#endif /* HW init leds */ #if !cPWR_UsePowerDownMode @@ -245,11 +244,17 @@ void AppTask::InitOTA(intptr_t arg) gRequestorStorage.Init(chip::Server::GetInstance().GetPersistentStorage()); gRequestorCore.Init(chip::Server::GetInstance(), gRequestorStorage, gRequestorUser, gDownloader); gRequestorUser.SetMaxDownloadBlockSize(requestedOtaBlockSize); - gRequestorUser.Init(&gRequestorCore, &gImageProcessor); - gImageProcessor.SetOTADownloader(&gDownloader); + auto & imageProcessor = OTAImageProcessorImpl::GetDefaultInstance(); + gRequestorUser.Init(&gRequestorCore, &imageProcessor); + CHIP_ERROR err = imageProcessor.Init(&gDownloader); + if (err != CHIP_NO_ERROR) + { + K32W_LOG("Image processor init failed"); + assert(err == CHIP_NO_ERROR); + } // Connect the gDownloader and Image Processor objects - gDownloader.SetImageProcessorDelegate(&gImageProcessor); + gDownloader.SetImageProcessorDelegate(&imageProcessor); // Initialize and interconnect the Requestor and Image Processor objects -- END } #endif diff --git a/examples/lighting-app/nxp/k32w/k32w0/README.md b/examples/lighting-app/nxp/k32w/k32w0/README.md index a4e59c92352b15..7de06266209374 100644 --- a/examples/lighting-app/nxp/k32w/k32w0/README.md +++ b/examples/lighting-app/nxp/k32w/k32w0/README.md @@ -192,13 +192,13 @@ In order to build the Project CHIP example, we recommend using a Linux distribution (the demo-application was compiled on Ubuntu 20.04). - Download - [K32W061DK6 SDK 2.6.9](https://cache.nxp.com/lgfiles/bsps/SDK_2_6_9_K32W061DK6.zip). + [K32W061DK6 SDK 2.6.10](https://cache.nxp.com/lgfiles/bsps/SDK_2_6_10_K32W061DK6.zip). - Start building the application either with Secure Element or without - without Secure Element ``` -user@ubuntu:~/Desktop/git/connectedhomeip$ export NXP_K32W0_SDK_ROOT=/home/user/Desktop/SDK_2_6_9_K32W061DK6/ +user@ubuntu:~/Desktop/git/connectedhomeip$ export NXP_K32W0_SDK_ROOT=/home/user/Desktop/SDK_2_6_10_K32W061DK6/ user@ubuntu:~/Desktop/git/connectedhomeip$ source ./scripts/activate.sh user@ubuntu:~/Desktop/git/connectedhomeip$ cd examples/lighting-app/nxp/k32w/k32w0 user@ubuntu:~/Desktop/git/connectedhomeip/examples/lighting-app/nxp/k32w/k32w0$ gn gen out/debug --args="k32w0_sdk_root=\"${NXP_K32W0_SDK_ROOT}\" chip_with_OM15082=1 chip_with_ot_cli=0 is_debug=false chip_crypto=\"platform\" chip_with_se05x=0 chip_pw_tokenizer_logging=true" @@ -217,7 +217,7 @@ Secure Element. These can be changed if building without Secure Element Exactly the same steps as above but set argument build_for_k32w041am=1 in the gn command and use - [K32W041AMDK6 SDK 2.6.9](https://cache.nxp.com/lgfiles/bsps/SDK_2_6_9_K32W041AMDK6.zip). + [K32W041AMDK6 SDK 2.6.10](https://cache.nxp.com/lgfiles/bsps/SDK_2_6_10_K32W041AMDK6.zip). Also, in case the OM15082 Expansion Board is not attached to the DK6 board, the build argument (chip_with_OM15082) inside the gn build instruction should be set @@ -502,10 +502,33 @@ Build the Linux OTA provider application: user@computer1:~/connectedhomeip$ : ./scripts/examples/gn_build_example.sh examples/ota-provider-app/linux out/ota-provider-app chip_config_network_layer_ble=false ``` -Build OTA image and start the OTA Provider Application: +Build Linux chip-tool: + +``` +user@computer1:~/connectedhomeip$ : ./scripts/examples/gn_build_example.sh examples/chip-tool out/chip-tool-app +``` + +Build OTA image: + +In order to build an OTA image, use NXP wrapper over the standard tool +`src/app/ota_image_tool.py`: + +- `scripts/tools/nxp/factory_data_generator/ota_image_tool.py` The tool can be + used to generate an OTA image with the following format: + `| OTA image header | TLV1 | TLV2 | ... | TLVn |` where each TLV is in the + form `|tag|length|value|` + +Note that "standard" TLV format is used. Matter TLV format is only used for +factory data TLV value. A user can enable the default processors by specifying +`chip_enable_ota_default_processors=1` in the build command. Please see more in +the [OTA image tool guide](../../../../../scripts/tools/nxp/ota/README.md). + +Here is an example that generate an OTA image with factory data and app TLV: +`user@computer1:~/connectedhomeip$ : ./scripts/tools/nxp/ota/ota_image_tool.py create -v 0xDEAD -p 0xBEEF -vn 1 -vs "1.0" -da sha256 -fd --cert_declaration ~/manufacturing/Chip-Test-CD-1037-a220.der -app chip-k32w0x-contact-example.bin chip-k32w0x-contact-example.bin chip-k32w0x-contact-example.ota` + +Start the OTA Provider Application: ``` -user@computer1:~/connectedhomeip$ : ./src/app/ota_image_tool.py create -v 0xDEAD -p 0xBEEF -vn 1 -vs "1.0" -da sha256 chip-k32w0x-light-example.bin chip-k32w0x-light-example.ota user@computer1:~/connectedhomeip$ : rm -rf /tmp/chip_* user@computer1:~/connectedhomeip$ : ./out/ota-provider-app/chip-ota-provider-app -f chip-k32w0x-light-example.ota ``` @@ -517,12 +540,6 @@ having a correct OTA process, the OTA header version should be the same as the binary embedded software version. A user can set a custom software version in the gn build args by setting `chip_software_version` to the wanted version. -Build Linux chip-tool: - -``` -user@computer1:~/connectedhomeip$ : ./scripts/examples/gn_build_example.sh examples/chip-tool out/chip-tool-app -``` - Provision the OTA provider application and assign node id _1_. Also, grant ACL entries to allow OTA requestors: @@ -535,7 +552,7 @@ user@computer1:~/connectedhomeip$ : ./out/chip-tool-app/chip-tool accesscontrol Provision the device and assign node id _2_: ``` -user@computer1:~/connectedhomeip$ : ./out/chip-tool-app/chip-tool pairing ble-thread 2 hex: 20202021 3840 +user@computer1:~/connectedhomeip$ : ./out/chip-tool-app/chip-tool pairing ble-thread 2 hex: 20202021 3840 ``` Start the OTA process: diff --git a/examples/lighting-app/nxp/k32w/k32w0/main/AppTask.cpp b/examples/lighting-app/nxp/k32w/k32w0/main/AppTask.cpp index dca537f2d97881..c609786f64fe23 100644 --- a/examples/lighting-app/nxp/k32w/k32w0/main/AppTask.cpp +++ b/examples/lighting-app/nxp/k32w/k32w0/main/AppTask.cpp @@ -39,12 +39,12 @@ /* OTA related includes */ #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR -#include "OTAImageProcessorImpl.h" #include "OtaSupport.h" #include #include #include #include +#include #endif #include "Keyboard.h" @@ -105,7 +105,6 @@ static DefaultOTARequestor gRequestorCore; static DefaultOTARequestorStorage gRequestorStorage; static DeviceLayer::DefaultOTARequestorDriver gRequestorUser; static BDXDownloader gDownloader; -static OTAImageProcessorImpl gImageProcessor; constexpr uint16_t requestedOtaBlockSize = 1024; #endif @@ -147,10 +146,10 @@ CHIP_ERROR AppTask::Init() #else SetDeviceAttestationCredentialsProvider(Examples::GetExampleDACProvider()); #endif +#endif // CONFIG_CHIP_K32W0_REAL_FACTORY_DATA // QR code will be used with CHIP Tool AppTask::PrintOnboardingInfo(); -#endif /* HW init leds */ LED_Init(); @@ -254,11 +253,17 @@ void AppTask::InitOTA(intptr_t arg) gRequestorStorage.Init(chip::Server::GetInstance().GetPersistentStorage()); gRequestorCore.Init(chip::Server::GetInstance(), gRequestorStorage, gRequestorUser, gDownloader); gRequestorUser.SetMaxDownloadBlockSize(requestedOtaBlockSize); - gRequestorUser.Init(&gRequestorCore, &gImageProcessor); - gImageProcessor.SetOTADownloader(&gDownloader); + auto & imageProcessor = OTAImageProcessorImpl::GetDefaultInstance(); + gRequestorUser.Init(&gRequestorCore, &imageProcessor); + CHIP_ERROR err = imageProcessor.Init(&gDownloader); + if (err != CHIP_NO_ERROR) + { + K32W_LOG("Image processor init failed"); + assert(err == CHIP_NO_ERROR); + } // Connect the gDownloader and Image Processor objects - gDownloader.SetImageProcessorDelegate(&gImageProcessor); + gDownloader.SetImageProcessorDelegate(&imageProcessor); // Initialize and interconnect the Requestor and Image Processor objects -- END } #endif diff --git a/examples/lock-app/nxp/k32w/k32w0/README.md b/examples/lock-app/nxp/k32w/k32w0/README.md index e08b43baf5e48f..d788a785016c1e 100644 --- a/examples/lock-app/nxp/k32w/k32w0/README.md +++ b/examples/lock-app/nxp/k32w/k32w0/README.md @@ -173,14 +173,14 @@ In order to build the Project CHIP example, we recommend using a Linux distribution (the demo-application was compiled on Ubuntu 20.04). - Download - [K32W061DK6 SDK 2.6.9](https://cache.nxp.com/lgfiles/bsps/SDK_2_6_9_K32W061DK6.zip). + [K32W061DK6 SDK 2.6.10](https://cache.nxp.com/lgfiles/bsps/SDK_2_6_10_K32W061DK6.zip). - Start building the application either with Secure Element or without - without Secure Element ``` -user@ubuntu:~/Desktop/git/connectedhomeip$ export NXP_K32W0_SDK_ROOT=/home/user/Desktop/SDK_2_6_9_K32W061DK6/ +user@ubuntu:~/Desktop/git/connectedhomeip$ export NXP_K32W0_SDK_ROOT=/home/user/Desktop/SDK_2_6_10_K32W061DK6/ user@ubuntu:~/Desktop/git/connectedhomeip$ ./third_party/nxp/k32w0_sdk/sdk_fixes/patch_k32w_sdk.sh user@ubuntu:~/Desktop/git/connectedhomeip$ source ./scripts/activate.sh user@ubuntu:~/Desktop/git/connectedhomeip$ cd examples/lock-app/nxp/k32w/k32w0 diff --git a/examples/lock-app/nxp/k32w/k32w0/main/AppTask.cpp b/examples/lock-app/nxp/k32w/k32w0/main/AppTask.cpp index 617f49684725f4..b4e793a525d8d4 100644 --- a/examples/lock-app/nxp/k32w/k32w0/main/AppTask.cpp +++ b/examples/lock-app/nxp/k32w/k32w0/main/AppTask.cpp @@ -108,10 +108,10 @@ CHIP_ERROR AppTask::Init() #else SetDeviceAttestationCredentialsProvider(Examples::GetExampleDACProvider()); #endif +#endif // CONFIG_CHIP_K32W0_REAL_FACTORY_DATA // QR code will be used with CHIP Tool AppTask::PrintOnboardingInfo(); -#endif /* HW init leds */ #if !cPWR_UsePowerDownMode diff --git a/examples/platform/nxp/k32w/k32w0/app/ldscripts/chip-k32w0x-linker.ld b/examples/platform/nxp/k32w/k32w0/app/ldscripts/chip-k32w0x-linker.ld index 6b9dad04822d1b..82580235b3bdda 100644 --- a/examples/platform/nxp/k32w/k32w0/app/ldscripts/chip-k32w0x-linker.ld +++ b/examples/platform/nxp/k32w/k32w0/app/ldscripts/chip-k32w0x-linker.ld @@ -328,6 +328,14 @@ SECTIONS . = ALIGN (. != 0 ? 4 : 1) ; /* avoid empty segment */ } > RAM0 + /* BSS section for unretained contents */ + .bss_discard (NOLOAD) : ALIGN(4) + { + *(.discard.bss.pdm) + + . = ALIGN (. != 0 ? 4 : 1) ; /* avoid empty segment */ + } > RAM0 + /* DEFAULT NOINIT SECTION */ .noinit (NOLOAD): ALIGN(4) { diff --git a/scripts/tools/nxp/ota/README.md b/scripts/tools/nxp/ota/README.md new file mode 100644 index 00000000000000..923e9a5dfe1797 --- /dev/null +++ b/scripts/tools/nxp/ota/README.md @@ -0,0 +1,127 @@ +--- +orphan: true +--- + +# NXP OTA image tool + +## Overview + +This tool can generate an OTA image in the `|OTA standard header|TLV1|...|TLVn|` +format. The payload contains data in standard TLV format (not Matter TLV format. +During OTA transfer, these TLV can span across multiple BDX blocks, thus the +`OTAImageProcessorImpl` instance should take this into account. + +Each TLV will be processed by its associated processor, pre-registered in +`OTAImageProcessorImpl` and identified by the TLV tag. If a processor cannot be +found for current decoded tag, the OTA transfer will be canceled. + +An application is able to define its own processors, thus enabling extending the +default OTA functionality. The application can also opt to disable the default +processors (application, bootloader and factory data) by setting +`chip_enable_ota_default_processors=0`. + +## Usage + +TODO: add more options + +Example: + +``` +python3 ./scripts/tools/nxp/ota/ota_image_tool.py create -v 0xDEAD -p 0xBEEF -vn 50000 -vs "1.0" -da sha256 -fd --cert_declaration $FACTORY_DATA_DEST/Chip-Test-CD-$VID-$PID.der --dac_cert $FACTORY_DATA_DEST/Chip-DAC-NXP-$VID-$PID-Cert.der --dac_key $FACTORY_DATA_DEST/Chip-DAC-NXP-$VID-$PID-Key.der --pai_cert $FACTORY_DATA_DEST/Chip-PAI-NXP-$VID-$PID-Cert.der -app ~/binaries/ota_update/chip-k32w0x-light-example-50000.bin --app-version 50000 --app-version-str "50000_test" --app-build-date "$DATE" ~/binaries/ota_update/chip-k32w0x-light-example-50000.bin $FACTORY_DATA_DEST/chip-k32w0x-light-example-50000.ota +``` + +Example (only factory data update): + +``` +python3 ./scripts/tools/nxp/ota/ota_image_tool.py create -v 0xDEAD -p 0xBEEF -vn 50000 -vs "1.0" -da sha256 -fd --cert_declaration $FACTORY_DATA_DEST/Chip-Test-CD-$VID-$PID.der --dac_cert $FACTORY_DATA_DEST/Chip-DAC-NXP-$VID-$PID-Cert.der --dac_key $FACTORY_DATA_DEST/Chip-DAC-NXP-$VID-$PID-Key.der --pai_cert $FACTORY_DATA_DEST/Chip-PAI-NXP-$VID-$PID-Cert.der $FACTORY_DATA_DEST/chip-k32w0x-light-example-50000.ota +``` + +## Default processors + +The default processors for K32W0 are already implemented in: + +- `OTAApplicationProcessor` for application update. +- TODO: `OTABootloaderProcessor` for SSBL update. +- `OTAFactoryDataProcessor` for factory data update. + +## Implementing custom processors + +A custom processor should implement the interface defined by the +`OTATlvProcessor` abstract interface (simplified version; see `OTATlvHeader.h` +for full version): + +``` +class OTATlvProcessor +{ +public: + virtual CHIP_ERROR Init() = 0; + virtual CHIP_ERROR Clear() = 0; + virtual CHIP_ERROR ApplyAction() = 0; + virtual CHIP_ERROR AbortAction() = 0; + + CHIP_ERROR Process(ByteSpan & block); +protected: + virtual CHIP_ERROR ProcessInternal(ByteSpan & block) = 0; +}; + +``` + +Note that `ProcessInternal` should return: + +- `CHIP_NO_ERROR` if block was processed successfully. +- `CHIP_ERROR_BUFFER_TOO_SMALL` if current block doesn't contain all necessary + data. This can happen when a TLV value field has a header, but it is split + across two blocks. +- `CHIP_OTA_FETCH_ALREADY_SCHEDULED` if block was processed successfully and + the fetching is already scheduled by the processor. This happens in the + default application processor, because the next data fetching is scheduled + through a callback (called when enough external flash was erased). + +`Process` is the public API that is used inside `OTAImageProcessorImpl` for data +processing. This is a wrapper over `ProcessInternal`, which can return +`CHIP_OTA_CHANGE_PROCESSOR` to notify a new processor should be selected for the +remaining data. + +Furthermore, a processor can use an instance of `OTADataAccumulator` to to +accumulate data until a given threshold. This is useful when a custom payload +contains metadata that need parsing: accumulate data until the threshold is +reached or return `CHIP_ERROR_BUFFER_TOO_SMALL` to signal +`OTAImageProcessorImpl` more data is needed. + +``` +/** + * This class can be used to accumulate data until a given threshold. + * Should be used by OTATlvProcessor derived classes if they need + * metadata accumulation (e.g. for custom header decoding). + */ +class OTADataAccumulator +{ +public: + void Init(uint32_t threshold); + void Clear(); + CHIP_ERROR Accumulate(ByteSpan & block); + + inline uint8_t* data() { return mBuffer.Get(); } + +private: + uint32_t mThreshold; + uint32_t mBufferOffset; + Platform::ScopedMemoryBuffer mBuffer; +}; +``` + +## Factory data update + +`DAC`, `PAI` and `CD` can be updated at a later time by creating a factory data +update OTA image. If the `PAA` changes, make sure to generate the new +certificates using the new `PAA` (which is only used by the controller, e.g. +`chip-tool`). Please see the +[manufacturing flow guide](../../../../examples/platform/nxp/doc/manufacturing_flow.md) +for generating new certificates. + +Example of OTA image generation with factory data and application update (using +env variables set in the prerequisites of manufacturing flow): + +``` +python3 ./scripts/tools/nxp/ota/ota_image_tool.py create -v 0xDEAD -p 0xBEEF -vn 50000 -vs "1.0" -da sha256 -fd --cert_declaration $FACTORY_DATA_DEST/Chip-Test-CD-$VID-$PID.der --dac_cert $FACTORY_DATA_DEST/Chip-DAC-NXP-$VID-$PID-Cert.der --dac_key $FACTORY_DATA_DEST/Chip-DAC-NXP-$VID-$PID-Key.der --pai_cert $FACTORY_DATA_DEST/Chip-PAI-NXP-$VID-$PID-Cert.der -app $FACTORY_DATA_DEST/chip-k32w0x-light-example-50000.bin --app-version 50000 --app-version-str "50000_test" --app-build-date "$DATE" $FACTORY_DATA_DEST/chip-k32w0x-light-example-50000.bin $FACTORY_DATA_DEST/chip-k32w0x-light-example-50000.ota +``` diff --git a/scripts/tools/nxp/ota/ota_image_tool.py b/scripts/tools/nxp/ota/ota_image_tool.py new file mode 100755 index 00000000000000..13b1895fac6acf --- /dev/null +++ b/scripts/tools/nxp/ota/ota_image_tool.py @@ -0,0 +1,254 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2023 Project CHIP Authors +# All rights reserved. +# +# 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. +# + +'''This file should contain a way to generate custom OTA payloads. + +The format of the custom payload is the following: +| Total size of TLVs | TLV1 | ... | TLVn | + +The OTA payload can then be used to generate an OTA image file, which +will be parsed by the OTA image processor. The total size of TLVs is +needed as input for a TLVReader. + +Currently, this script only supports Certification Declaration update, +but it could be modified to support all factory data fields. +''' + +import argparse +import logging +import os +import sys + +import ota_image_tool +from chip.tlv import TLVWriter +from custom import CertDeclaration, DacCert, DacPKey, PaiCert +from default import InputArgument +from generate import set_logger + +sys.path.insert(0, os.path.join( + os.path.dirname(__file__), '../factory_data_generator')) +sys.path.insert(0, os.path.join( + os.path.dirname(__file__), '../../../../src/controller/python')) +sys.path.insert(0, os.path.join( + os.path.dirname(__file__), '../../../../src/app/')) + + +OTA_FACTORY_TLV_TEMP = os.path.join(os.path.dirname(__file__), "ota_factory_tlv_temp.bin") +OTA_APP_TLV_TEMP = os.path.join(os.path.dirname(__file__), "ota_app_tlv_temp.bin") + + +class TAG: + APPLICATION = 1 + BOOTLOADER = 2 + FACTORY_DATA = 3 + + +def generate_header(tag: int, length: int): + header = bytearray(tag.to_bytes(4, "little")) + header += bytearray(length.to_bytes(4, "little")) + return header + + +def generate_factory_data(args: object): + """ + Generate custom OTA payload from InputArgument derived objects. The payload is + written in a temporary file that will be appended to args.input_files. + """ + fields = dict() + + if args.dac_key is not None: + args.dac_key.generate_private_key(args.dac_key_password) + + data = [obj for key, obj in vars(args).items() if isinstance(obj, InputArgument)] + for arg in sorted(data, key=lambda x: x.key()): + fields.update({arg.key(): arg.encode()}) + + if fields: + writer = TLVWriter() + writer.put(None, fields) + payload = generate_header(TAG.FACTORY_DATA, len(writer.encoding)) + payload += writer.encoding + + with open(OTA_FACTORY_TLV_TEMP, "wb") as _handle: + _handle.write(payload) + + logging.info(f"Factory data payload size: {len(payload)}") + + +def generate_app(args: object): + version = args.app_version.to_bytes(4, "little") + versionStr = bytearray(args.app_version_str, "ascii") + bytearray(64 - len(args.app_version_str)) + buildDate = bytearray(args.app_build_date, "ascii") + bytearray(64 - len(args.app_build_date)) + descriptor = version + versionStr + buildDate + file_size = os.path.getsize(args.app_input_file) + payload = generate_header(TAG.APPLICATION, len(descriptor) + file_size) + descriptor + + with open(OTA_APP_TLV_TEMP, "wb") as _handle: + _handle.write(payload) + + logging.info(f"Application payload size: {len(payload)}") + + +def generate_bootloader(args: object): + # TODO + pass + + +def show_payload(args: object): + """ + Parse and present OTA custom payload in human-readable form. + """ + # TODO: implement to show current TLVs + pass + + +def create_image(args: object): + ota_image_tool.validate_header_attributes(args) + input_files = list() + + if args.factory_data: + generate_factory_data(args) + input_files += [OTA_FACTORY_TLV_TEMP] + + if args.app_input_file is not None: + generate_app(args) + input_files += [OTA_APP_TLV_TEMP, args.app_input_file] + print(input_files) + args.input_files = input_files + ota_image_tool.generate_image(args) + + if args.factory_data: + os.remove(OTA_FACTORY_TLV_TEMP) + if args.app_input_file is not None: + os.remove(OTA_APP_TLV_TEMP) + + +def main(): + """ + This function is a modified version of ota_image_tool.py main function. + + The wrapper version defines a new set of args, which are used to generate + TLV data that will be embedded in the final OTA payload. + """ + + def any_base_int(s): return int(s, 0) + + set_logger() + parser = argparse.ArgumentParser( + description='Matter OTA (Over-the-air update) image utility', fromfile_prefix_chars='@') + subcommands = parser.add_subparsers( + dest='subcommand', title='valid subcommands', required=True) + + create_parser = subcommands.add_parser('create', help='Create OTA image') + create_parser.add_argument('-v', '--vendor-id', type=any_base_int, + required=True, help='Vendor ID') + create_parser.add_argument('-p', '--product-id', type=any_base_int, + required=True, help='Product ID') + create_parser.add_argument('-vn', '--version', type=any_base_int, + required=True, help='Software version (numeric)') + create_parser.add_argument('-vs', '--version-str', required=True, + help='Software version (string)') + create_parser.add_argument('-da', '--digest-algorithm', choices=ota_image_tool.DIGEST_ALL_ALGORITHMS, + required=True, help='Digest algorithm') + create_parser.add_argument('-mi', '--min-version', type=any_base_int, + help='Minimum software version that can be updated to this image') + create_parser.add_argument('-ma', '--max-version', type=any_base_int, + help='Maximum software version that can be updated to this image') + create_parser.add_argument( + '-rn', '--release-notes', help='Release note URL') + create_parser.add_argument('input_files', nargs='*', + help='Path to input image payload file') + create_parser.add_argument('output_file', help='Path to output image file') + + create_parser.add_argument('-app', '--app-input-file', + help='Path to input application image payload file') + create_parser.add_argument('--app-version', type=any_base_int, + help='Application Software version (numeric)') + create_parser.add_argument('--app-version-str', type=str, + help='Application Software version (string)') + create_parser.add_argument('--app-build-date', type=str, + help='Application build date (string)') + + create_parser.add_argument('-bl', '--bootloader-input-file', + help='Path to input bootloader image payload file') + create_parser.add_argument('--bl-version', type=any_base_int, + help='Bootloader Software version (numeric)') + create_parser.add_argument('--bl-version-str', type=str, + help='Bootloader Software version (string)') + create_parser.add_argument('--bl-build-date', type=str, + help='Bootloader build date (string)') + create_parser.add_argument('--bl-load-addr', type=any_base_int, + help='Bootloader load address (numeric)') + + # Factory data specific arguments. Will be used to generate the TLV payload. + create_parser.add_argument('-fd', '--factory-data', action='store_true', + help='If found, enable factory data payload generation.') + create_parser.add_argument("--cert_declaration", type=CertDeclaration, + help="[path] Path to Certification Declaration in DER format") + create_parser.add_argument("--dac_cert", type=DacCert, + help="[path] Path to DAC certificate in DER format") + create_parser.add_argument("--dac_key", type=DacPKey, + help="[path] Path to DAC key in DER format") + create_parser.add_argument("--dac_key_password", type=str, + help="[path] Password to decode DAC Key if available") + create_parser.add_argument("--pai_cert", type=PaiCert, + help="[path] Path to PAI certificate in DER format") + + show_parser = subcommands.add_parser('show', help='Show OTA image info') + show_parser.add_argument('image_file', help='Path to OTA image file') + + extract_tool = subcommands.add_parser('extract', help='Remove the OTA header from an image file') + extract_tool.add_argument('image_file', help='Path to OTA image file with header') + extract_tool.add_argument('output_file', help='Path to put the output file (no header)') + + change_tool = subcommands.add_parser('change_header', help='Change the specified values in the header') + change_tool.add_argument('-v', '--vendor-id', type=any_base_int, + help='Vendor ID') + change_tool.add_argument('-p', '--product-id', type=any_base_int, + help='Product ID') + change_tool.add_argument('-vn', '--version', type=any_base_int, + help='Software version (numeric)') + change_tool.add_argument('-vs', '--version-str', + help='Software version (string)') + change_tool.add_argument('-da', '--digest-algorithm', choices=ota_image_tool.DIGEST_ALL_ALGORITHMS, + help='Digest algorithm') + change_tool.add_argument('-mi', '--min-version', type=any_base_int, + help='Minimum software version that can be updated to this image') + change_tool.add_argument('-ma', '--max-version', type=any_base_int, + help='Maximum software version that can be updated to this image') + change_tool.add_argument( + '-rn', '--release-notes', help='Release note URL') + change_tool.add_argument('image_file', + help='Path to input OTA file') + change_tool.add_argument('output_file', help='Path to output OTA file') + + args = parser.parse_args() + + if args.subcommand == 'create': + create_image(args) + elif args.subcommand == 'show': + ota_image_tool.show_header(args) + show_payload(args) + elif args.subcommand == 'extract': + ota_image_tool.remove_header(args) + elif args.subcommand == 'change_header': + ota_image_tool.update_header_args(args) + + +if __name__ == "__main__": + main() diff --git a/src/platform/nxp/k32w/common/CHIPDevicePlatformRamStorageConfig.h b/src/platform/nxp/k32w/common/CHIPDevicePlatformRamStorageConfig.h index 62d2490fa20092..0d466e3913e160 100644 --- a/src/platform/nxp/k32w/common/CHIPDevicePlatformRamStorageConfig.h +++ b/src/platform/nxp/k32w/common/CHIPDevicePlatformRamStorageConfig.h @@ -121,3 +121,14 @@ #ifndef kNvmId_OTConfigData #define kNvmId_OTConfigData (uint16_t) 0x4F00 #endif + +#if CONFIG_CHIP_K32W0_REAL_FACTORY_DATA +/** + * @def kNvmId_FactoryDataBackup + * + * PDM ID used for factory data backup in K32W0FactoryDataProvider. + */ +#ifndef kNvmId_FactoryDataBackup +#define kNvmId_FactoryDataBackup (uint16_t) 0x7000 +#endif +#endif // CONFIG_CHIP_K32W0_REAL_FACTORY_DATA diff --git a/src/platform/nxp/k32w/k32w0/OTAImageProcessorImpl.cpp b/src/platform/nxp/k32w/common/OTAImageProcessorImpl.cpp similarity index 53% rename from src/platform/nxp/k32w/k32w0/OTAImageProcessorImpl.cpp rename to src/platform/nxp/k32w/common/OTAImageProcessorImpl.cpp index a64098a3df6c2c..4efb456e4a8f23 100644 --- a/src/platform/nxp/k32w/k32w0/OTAImageProcessorImpl.cpp +++ b/src/platform/nxp/k32w/common/OTAImageProcessorImpl.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2021 Project CHIP Authors + * Copyright (c) 2021-2023 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,22 +16,39 @@ * limitations under the License. */ +#include #include -#include #include #include -#include "OTAImageProcessorImpl.h" -#include "OtaSupport.h" -#include "OtaUtils.h" - -extern "C" void ResetMCU(void); +#include using namespace chip::DeviceLayer; using namespace ::chip::DeviceLayer::Internal; namespace chip { +CHIP_ERROR OTAImageProcessorImpl::Init(OTADownloader * downloader) +{ + ReturnErrorCodeIf(downloader == nullptr, CHIP_ERROR_INVALID_ARGUMENT); + mDownloader = downloader; + + OtaHookInit(); + + return CHIP_NO_ERROR; +} + +void OTAImageProcessorImpl::Clear() +{ + mHeaderParser.Clear(); + mAccumulator.Clear(); + mParams.totalFileBytes = 0; + mParams.downloadedBytes = 0; + mCurrentProcessor = nullptr; + + ReleaseBlock(); +} + CHIP_ERROR OTAImageProcessorImpl::PrepareDownload() { DeviceLayer::PlatformMgr().ScheduleWork(HandlePrepareDownload, reinterpret_cast(this)); @@ -52,12 +69,6 @@ CHIP_ERROR OTAImageProcessorImpl::Apply() CHIP_ERROR OTAImageProcessorImpl::Abort() { - if (mImageFile == nullptr) - { - ChipLogError(SoftwareUpdate, "Invalid output image file supplied"); - return CHIP_ERROR_INTERNAL; - } - DeviceLayer::PlatformMgr().ScheduleWork(HandleAbort, reinterpret_cast(this)); return CHIP_NO_ERROR; } @@ -94,107 +105,153 @@ void OTAImageProcessorImpl::HandlePrepareDownload(intptr_t context) return; } - if (gOtaSuccess_c == OTA_ClientInit()) - { - imageProcessor->mHeaderParser.Init(); - imageProcessor->mDownloader->OnPreparedForDownload(CHIP_NO_ERROR); - } + imageProcessor->mHeaderParser.Init(); + imageProcessor->mAccumulator.Init(sizeof(OTATlvHeader)); + imageProcessor->mDownloader->OnPreparedForDownload(CHIP_NO_ERROR); } CHIP_ERROR OTAImageProcessorImpl::ProcessHeader(ByteSpan & block) { OTAImageHeader header; - CHIP_ERROR error = mHeaderParser.AccumulateAndDecode(block, header); + ReturnErrorOnFailure(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(); + ChipLogError(SoftwareUpdate, "Processed header successfully"); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAImageProcessorImpl::ProcessPayload(ByteSpan & block) +{ + CHIP_ERROR status = CHIP_NO_ERROR; + + while (true) + { + if (!mCurrentProcessor) + { + ReturnErrorOnFailure(mAccumulator.Accumulate(block)); + ByteSpan tlvHeader{ mAccumulator.data(), sizeof(OTATlvHeader) }; + ReturnErrorOnFailure(SelectProcessor(tlvHeader)); + mCurrentProcessor->Init(); + } + + status = mCurrentProcessor->Process(block); + if (status == CHIP_OTA_CHANGE_PROCESSOR) + { + mAccumulator.Clear(); + mAccumulator.Init(sizeof(OTATlvHeader)); + mCurrentProcessor = nullptr; + } + else + { + break; + } + } + + return status; +} + +CHIP_ERROR OTAImageProcessorImpl::SelectProcessor(ByteSpan & block) +{ + OTATlvHeader header; + Encoding::LittleEndian::Reader reader(block.data(), sizeof(header)); + + ReturnErrorOnFailure(reader.Read32(&header.tag).StatusCode()); + ReturnErrorOnFailure(reader.Read32(&header.length).StatusCode()); + + auto pair = mProcessorMap.find(header.tag); + if (pair == mProcessorMap.end()) + { + ChipLogError(SoftwareUpdate, "There is no registered processor for tag: %" PRIu32, header.tag); + return CHIP_OTA_PROCESSOR_NOT_REGISTERED; + } + + mCurrentProcessor = pair->second; + mCurrentProcessor->SetLength(header.length); + mCurrentProcessor->SetWasSelected(true); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAImageProcessorImpl::RegisterProcessor(uint32_t tag, OTATlvProcessor * processor) +{ + auto pair = mProcessorMap.find(tag); + if (pair != mProcessorMap.end()) + { + ChipLogError(SoftwareUpdate, "A processor for tag %" PRIu32 " is already registered.", tag); + return CHIP_OTA_PROCESSOR_ALREADY_REGISTERED; + } + + mProcessorMap.insert({ tag, processor }); return CHIP_NO_ERROR; } void OTAImageProcessorImpl::HandleAbort(intptr_t context) { + ChipLogError(SoftwareUpdate, "OTA was aborted"); auto * imageProcessor = reinterpret_cast(context); - if (imageProcessor == nullptr) + if (imageProcessor != nullptr) { - return; + for (auto const & pair : imageProcessor->mProcessorMap) + { + if (pair.second->WasSelected()) + { + pair.second->AbortAction(); + pair.second->Clear(); + } + } } - - remove(imageProcessor->mImageFile); - imageProcessor->ReleaseBlock(); + imageProcessor->Clear(); } void OTAImageProcessorImpl::HandleProcessBlock(intptr_t context) { - CHIP_ERROR error = CHIP_NO_ERROR; - auto * imageProcessor = reinterpret_cast(context); if (imageProcessor == nullptr) { ChipLogError(SoftwareUpdate, "ImageProcessor context is null"); return; } - else if (imageProcessor->mDownloader == nullptr) + + if (imageProcessor->mDownloader == nullptr) { ChipLogError(SoftwareUpdate, "mDownloader is null"); return; } - /* process OTA header if not already did */ + CHIP_ERROR status; + auto block = ByteSpan(imageProcessor->mBlock.data(), imageProcessor->mBlock.size()); + if (imageProcessor->mHeaderParser.IsInitialized()) { - ByteSpan block = ByteSpan(imageProcessor->mBlock.data(), imageProcessor->mBlock.size()); - - error = imageProcessor->ProcessHeader(block); - if (error == CHIP_NO_ERROR) + status = imageProcessor->ProcessHeader(block); + if (status != CHIP_NO_ERROR) { - if (gOtaSuccess_c == OTA_StartImage(imageProcessor->mParams.totalFileBytes)) - { - uint8_t * ptr = static_cast(chip::Platform::MemoryAlloc(block.size())); - - if (ptr != nullptr) - { - MutableByteSpan mutableBlock = MutableByteSpan(ptr, block.size()); - error = CopySpanToMutableSpan(block, mutableBlock); - - if (error == CHIP_NO_ERROR) - { - imageProcessor->ReleaseBlock(); - imageProcessor->mBlock = MutableByteSpan(mutableBlock.data(), mutableBlock.size()); - } - } - else - { - error = CHIP_ERROR_NO_MEMORY; - } - } - else - { - error = CHIP_ERROR_INTERNAL; - } + imageProcessor->HandleStatus(status); } } - if (error != CHIP_NO_ERROR) + status = imageProcessor->ProcessPayload(block); + imageProcessor->HandleStatus(status); +} + +void OTAImageProcessorImpl::HandleStatus(CHIP_ERROR status) +{ + if (status == CHIP_NO_ERROR || status == CHIP_ERROR_BUFFER_TOO_SMALL) { - ChipLogError(SoftwareUpdate, "Failed to process OTA image header: cancel image update."); - GetRequestorInstance()->CancelImageUpdate(); - return; + mParams.downloadedBytes += mBlock.size(); + FetchNextData(reinterpret_cast(this)); } - - uint32_t param = reinterpret_cast(imageProcessor); - /* Will start an erase of 4K if necessary */ - if (gOtaSuccess_c == OTA_MakeHeadRoomForNextBlock(imageProcessor->mBlock.size(), HandleBlockEraseComplete, param)) + else if (status == CHIP_OTA_FETCH_ALREADY_SCHEDULED) { - if (gOtaSuccess_c == - OTA_PushImageChunk(imageProcessor->mBlock.data(), (uint16_t) imageProcessor->mBlock.size(), NULL, NULL)) - { - imageProcessor->mParams.downloadedBytes += imageProcessor->mBlock.size(); - return; - } + mParams.downloadedBytes += mBlock.size(); + } + else + { + ChipLogError(SoftwareUpdate, "Image update canceled. Failed to process OTA block: %s", ErrorStr(status)); + GetRequestorInstance()->CancelImageUpdate(); } } @@ -211,14 +268,13 @@ bool OTAImageProcessorImpl::IsFirstImageRun() CHIP_ERROR OTAImageProcessorImpl::ConfirmCurrentImage() { + uint32_t currentVersion; + uint32_t targetVersion; + OTARequestorInterface * requestor = chip::GetRequestorInstance(); - if (requestor == nullptr) - { - return CHIP_ERROR_INTERNAL; - } + ReturnErrorCodeIf(requestor == nullptr, CHIP_ERROR_INTERNAL); - uint32_t currentVersion; - uint32_t targetVersion = requestor->GetTargetVersion(); + targetVersion = requestor->GetTargetVersion(); ReturnErrorOnFailure(DeviceLayer::ConfigurationMgr().GetSoftwareVersion(currentVersion)); if (currentVersion != targetVersion) { @@ -236,6 +292,7 @@ CHIP_ERROR OTAImageProcessorImpl::SetBlock(ByteSpan & block) { return CHIP_NO_ERROR; } + if (mBlock.size() < block.size()) { if (!mBlock.empty()) @@ -249,6 +306,7 @@ CHIP_ERROR OTAImageProcessorImpl::SetBlock(ByteSpan & block) } mBlock = MutableByteSpan(mBlock_ptr, block.size()); } + CHIP_ERROR err = CopySpanToMutableSpan(block, mBlock); if (err != CHIP_NO_ERROR) { @@ -271,30 +329,38 @@ void OTAImageProcessorImpl::HandleFinalize(intptr_t context) void OTAImageProcessorImpl::HandleApply(intptr_t context) { + CHIP_ERROR error = CHIP_NO_ERROR; auto * imageProcessor = reinterpret_cast(context); - bool firstRun = true; - if (imageProcessor == nullptr) { return; } - OTA_CommitImage(NULL); - if (OTA_ImageAuthenticate() == gOtaImageAuthPass_c) + for (auto const & pair : imageProcessor->mProcessorMap) { - ChipLogProgress(SoftwareUpdate, "OTA image authentication success. Device will reboot with the new image!"); - // Set the necessary information to inform the SSBL that a new image is available - // and trigger the actual device reboot after some time, to take into account - // queued actions, e.g. sending events to a subscription - SystemLayer().StartTimer( - chip::System::Clock::Milliseconds32(CHIP_DEVICE_LAYER_OTA_REBOOT_DELAY), - [](chip::System::Layer *, void *) { OTA_SetNewImageFlag(); }, nullptr); - } - else - { - ChipLogError(SoftwareUpdate, "Image authentication error: cancel image update."); - GetRequestorInstance()->CancelImageUpdate(); + if (pair.second->WasSelected()) + { + error = pair.second->ApplyAction(); + if (error != CHIP_NO_ERROR) + { + ChipLogError(SoftwareUpdate, "Apply action for tag %d processor failed.", (uint8_t) pair.first); + imageProcessor->Clear(); + GetRequestorInstance()->Reset(); + return; + } + pair.second->Clear(); + pair.second->SetWasSelected(false); + } } + + imageProcessor->mAccumulator.Clear(); + + // Set the necessary information to inform the SSBL that a new image is available + // and trigger the actual device reboot after some time, to take into account + // queued actions, e.g. sending events to a subscription + SystemLayer().StartTimer( + chip::System::Clock::Milliseconds32(CHIP_DEVICE_LAYER_OTA_REBOOT_DELAY), + [](chip::System::Layer *, void *) { OtaHookReset(); }, nullptr); } CHIP_ERROR OTAImageProcessorImpl::ReleaseBlock() @@ -308,10 +374,10 @@ CHIP_ERROR OTAImageProcessorImpl::ReleaseBlock() return CHIP_NO_ERROR; } -void OTAImageProcessorImpl::HandleBlockEraseComplete(uint32_t context) +void OTAImageProcessorImpl::FetchNextData(uint32_t context) { CHIP_ERROR error = CHIP_NO_ERROR; - auto * imageProcessor = reinterpret_cast(context); + auto * imageProcessor = &OTAImageProcessorImpl::GetDefaultInstance(); SystemLayer().ScheduleLambda([imageProcessor] { if (imageProcessor->mDownloader) { @@ -320,4 +386,10 @@ void OTAImageProcessorImpl::HandleBlockEraseComplete(uint32_t context) }); } +OTAImageProcessorImpl & OTAImageProcessorImpl::GetDefaultInstance() +{ + static OTAImageProcessorImpl imageProcessor; + return imageProcessor; +} + } // namespace chip diff --git a/src/platform/nxp/k32w/k32w0/OTAImageProcessorImpl.h b/src/platform/nxp/k32w/common/OTAImageProcessorImpl.h similarity index 70% rename from src/platform/nxp/k32w/k32w0/OTAImageProcessorImpl.h rename to src/platform/nxp/k32w/common/OTAImageProcessorImpl.h index 4462169189a3c6..4c545c2a218f13 100644 --- a/src/platform/nxp/k32w/k32w0/OTAImageProcessorImpl.h +++ b/src/platform/nxp/k32w/common/OTAImageProcessorImpl.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2021 Project CHIP Authors + * Copyright (c) 2021-2023 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,15 +19,28 @@ #pragma once #include +#include +#include #include #include #include +/* + * OTA hooks that can be overwritten by application. + * Default behavior is implemented as WEAK symbols in + * platform OtaHooks.cpp. + */ +extern "C" CHIP_ERROR OtaHookInit(); +extern "C" void OtaHookReset(); + namespace chip { class OTAImageProcessorImpl : public OTAImageProcessorInterface { public: + CHIP_ERROR Init(OTADownloader * downloader); + void Clear(); + //////////// OTAImageProcessorInterface Implementation /////////////// CHIP_ERROR PrepareDownload() override; CHIP_ERROR Finalize() override; @@ -36,18 +49,24 @@ class OTAImageProcessorImpl : public OTAImageProcessorInterface CHIP_ERROR ProcessBlock(ByteSpan & block) override; bool IsFirstImageRun() override; CHIP_ERROR ConfirmCurrentImage() override; - void SetOTADownloader(OTADownloader * downloader) { mDownloader = downloader; } - void SetOTAImageFile(const char * imageFile) { mImageFile = imageFile; } + + CHIP_ERROR ProcessHeader(ByteSpan & block); + CHIP_ERROR ProcessPayload(ByteSpan & block); + CHIP_ERROR SelectProcessor(ByteSpan & block); + CHIP_ERROR RegisterProcessor(uint32_t tag, OTATlvProcessor * processor); + + static void FetchNextData(uint32_t context); + static OTAImageProcessorImpl & GetDefaultInstance(); private: //////////// Actual handlers for the OTAImageProcessorInterface /////////////// static void HandlePrepareDownload(intptr_t context); - CHIP_ERROR ProcessHeader(ByteSpan & block); static void HandleFinalize(intptr_t context); static void HandleApply(intptr_t context); static void HandleAbort(intptr_t context); static void HandleProcessBlock(intptr_t context); - static void HandleBlockEraseComplete(uint32_t context); + + void HandleStatus(CHIP_ERROR status); /** * Called to allocate memory for mBlock if necessary and set it to block @@ -59,10 +78,12 @@ class OTAImageProcessorImpl : public OTAImageProcessorInterface */ CHIP_ERROR ReleaseBlock(); + MutableByteSpan mBlock; OTADownloader * mDownloader; OTAImageHeaderParser mHeaderParser; - MutableByteSpan mBlock; - const char * mImageFile = nullptr; + OTATlvProcessor * mCurrentProcessor = nullptr; + OTADataAccumulator mAccumulator; + std::map mProcessorMap; }; } // namespace chip diff --git a/src/platform/nxp/k32w/common/OTATlvProcessor.cpp b/src/platform/nxp/k32w/common/OTATlvProcessor.cpp new file mode 100644 index 00000000000000..819f539263f572 --- /dev/null +++ b/src/platform/nxp/k32w/common/OTATlvProcessor.cpp @@ -0,0 +1,85 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * 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 +#include +#include + +#include +#include + +namespace chip { + +CHIP_ERROR OTATlvProcessor::Process(ByteSpan & block) +{ + CHIP_ERROR status = CHIP_NO_ERROR; + uint32_t bytes = chip::min(mLength - mProcessedLength, static_cast(block.size())); + ByteSpan relevantData = block.SubSpan(0, bytes); + + status = ProcessInternal(relevantData); + if (!IsError(status)) + { + mProcessedLength += bytes; + block = block.SubSpan(bytes); + if (mProcessedLength == mLength && block.size() > 0) + { + // If current block was processed fully and the block still contains data, it + // means that the block contains another TLV's data and the current processor + // should be changed by OTAImageProcessorImpl. + return CHIP_OTA_CHANGE_PROCESSOR; + } + } + + return status; +} + +bool OTATlvProcessor::IsError(CHIP_ERROR & status) +{ + return status != CHIP_NO_ERROR && status != CHIP_ERROR_BUFFER_TOO_SMALL && status != CHIP_OTA_FETCH_ALREADY_SCHEDULED; +} + +void OTADataAccumulator::Init(uint32_t threshold) +{ + mThreshold = threshold; + mBufferOffset = 0; + mBuffer.Alloc(mThreshold); +} + +void OTADataAccumulator::Clear() +{ + mThreshold = 0; + mBufferOffset = 0; + mBuffer.Free(); +} + +CHIP_ERROR OTADataAccumulator::Accumulate(ByteSpan & block) +{ + uint32_t numBytes = chip::min(mThreshold - mBufferOffset, static_cast(block.size())); + memcpy(&mBuffer[mBufferOffset], block.data(), numBytes); + mBufferOffset += numBytes; + block = block.SubSpan(numBytes); + + if (mBufferOffset < mThreshold) + { + return CHIP_ERROR_BUFFER_TOO_SMALL; + } + + return CHIP_NO_ERROR; +} + +} // namespace chip diff --git a/src/platform/nxp/k32w/common/OTATlvProcessor.h b/src/platform/nxp/k32w/common/OTATlvProcessor.h new file mode 100644 index 00000000000000..0f3f00ffaf9688 --- /dev/null +++ b/src/platform/nxp/k32w/common/OTATlvProcessor.h @@ -0,0 +1,134 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * 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. + */ + +#pragma once + +#include +#include +#include + +namespace chip { + +#define CHIP_ERROR_TLV_PROCESSOR(e) \ + ChipError(ChipError::Range::kLastRange, ((uint8_t) ChipError::Range::kLastRange << 3) | e, __FILE__, __LINE__) + +#define CHIP_OTA_TLV_CONTINUE_PROCESSING CHIP_ERROR_TLV_PROCESSOR(0x01) +#define CHIP_OTA_CHANGE_PROCESSOR CHIP_ERROR_TLV_PROCESSOR(0x02) +#define CHIP_OTA_PROCESSOR_NOT_REGISTERED CHIP_ERROR_TLV_PROCESSOR(0x03) +#define CHIP_OTA_PROCESSOR_ALREADY_REGISTERED CHIP_ERROR_TLV_PROCESSOR(0x04) +#define CHIP_OTA_PROCESSOR_CLIENT_INIT CHIP_ERROR_TLV_PROCESSOR(0x05) +#define CHIP_OTA_PROCESSOR_MAKE_ROOM CHIP_ERROR_TLV_PROCESSOR(0x06) +#define CHIP_OTA_PROCESSOR_PUSH_CHUNK CHIP_ERROR_TLV_PROCESSOR(0x07) +#define CHIP_OTA_PROCESSOR_IMG_AUTH CHIP_ERROR_TLV_PROCESSOR(0x08) +#define CHIP_OTA_FETCH_ALREADY_SCHEDULED CHIP_ERROR_TLV_PROCESSOR(0x09) +#define CHIP_OTA_PROCESSOR_IMG_COMMIT CHIP_ERROR_TLV_PROCESSOR(0x0a) + +// Descriptor constants +constexpr size_t kVersionStringSize = 64; +constexpr size_t kBuildDateSize = 64; + +constexpr uint16_t requestedOtaMaxBlockSize = 1024; + +struct OTATlvHeader +{ + uint32_t tag; + uint32_t length; +}; + +/** + * This class defines an interface for a Matter TLV processor. + * Instances of derived classes can be registered as processors + * in OTAImageProcessorImpl. Based on the TLV type, a certain + * processor is used to process subsequent blocks until the number + * of bytes found in the metadata is processed. In case a block contains + * data from two different TLVs, the processor should ensure the remaining + * data is returned in the block passed as input. + * The default processors: application, SSBL and factory data are registered + * in OTAImageProcessorImpl::Init. + * Applications should use OTAImageProcessorImpl::RegisterProcessor + * to register additional processors. + */ +class OTATlvProcessor +{ +public: + virtual ~OTATlvProcessor() {} + + virtual CHIP_ERROR Init() = 0; + virtual CHIP_ERROR Clear() = 0; + virtual CHIP_ERROR ApplyAction() = 0; + virtual CHIP_ERROR AbortAction() = 0; + + CHIP_ERROR Process(ByteSpan & block); + void SetLength(uint32_t length) { mLength = length; } + void SetWasSelected(bool selected) { mWasSelected = selected; } + bool WasSelected() { return mWasSelected; } + +protected: + /** + * @brief Process custom TLV payload + * + * The method takes subsequent chunks of the Matter OTA image file and processes them. + * If more image chunks are needed, CHIP_ERROR_BUFFER_TOO_SMALL error is returned. + * Other error codes indicate that an error occurred during processing. Fetching + * next data is scheduled automatically by OTAImageProcessorImpl if the return value + * is neither an error code, nor CHIP_OTA_FETCH_ALREADY_SCHEDULED (which implies the + * scheduling is done inside ProcessInternal or will be done in the future, through a + * callback). + * + * @param block Byte span containing a subsequent Matter OTA image chunk. When the method + * returns CHIP_NO_ERROR, the byte span is used to return a remaining part + * of the chunk, not used by current TLV processor. + * + * @retval CHIP_NO_ERROR Block was processed successfully. + * @retval CHIP_ERROR_BUFFER_TOO_SMALL Provided buffers are insufficient to decode some + * metadata (e.g. a descriptor). + * @retval CHIP_OTA_FETCH_ALREADY_SCHEDULED Should be returned if ProcessInternal schedules + * fetching next data (e.g. through a callback). + * @retval Error code Something went wrong. Current OTA process will be + * canceled. + */ + virtual CHIP_ERROR ProcessInternal(ByteSpan & block) = 0; + + bool IsError(CHIP_ERROR & status); + + uint32_t mLength = 0; + uint32_t mProcessedLength = 0; + bool mWasSelected = false; +}; + +/** + * This class can be used to accumulate data until a given threshold. + * Should be used by OTATlvProcessor derived classes if they need + * metadata accumulation (e.g. for custom header decoding). + */ +class OTADataAccumulator +{ +public: + void Init(uint32_t threshold); + void Clear(); + CHIP_ERROR Accumulate(ByteSpan & block); + + inline uint8_t * data() { return mBuffer.Get(); } + +private: + uint32_t mThreshold; + uint32_t mBufferOffset; + Platform::ScopedMemoryBuffer mBuffer; +}; + +} // namespace chip diff --git a/src/platform/nxp/k32w/k32w0/BUILD.gn b/src/platform/nxp/k32w/k32w0/BUILD.gn index e2f031e25214e3..98118b4f902c37 100644 --- a/src/platform/nxp/k32w/k32w0/BUILD.gn +++ b/src/platform/nxp/k32w/k32w0/BUILD.gn @@ -28,9 +28,7 @@ static_library("k32w0") { sources = [ "../../../SingletonConfigurationManager.cpp", "../common/RamStorage.cpp", - "../common/RamStorage.h", "../common/RamStorageKey.cpp", - "../common/RamStorageKey.h", "BLEManagerImpl.cpp", "BLEManagerImpl.h", "CHIPDevicePlatformConfig.h", @@ -61,6 +59,8 @@ static_library("k32w0") { "${chip_root}/src/credentials/examples/DeviceAttestationCredsExample.h", "${chip_root}/src/credentials/examples/ExampleDACs.h", "${chip_root}/src/credentials/examples/ExamplePAI.h", + "../common/RamStorage.h", + "../common/RamStorageKey.h", ] if (chip_with_factory_data == 1) { @@ -79,10 +79,31 @@ static_library("k32w0") { } if (chip_enable_ota_requestor) { + public += [ "../common/OTAImageProcessorImpl.h" ] + sources += [ - "OTAImageProcessorImpl.cpp", - "OTAImageProcessorImpl.h", + "../common/OTAImageProcessorImpl.cpp", + "../common/OTAImageProcessorImpl.h", + "../common/OTATlvProcessor.cpp", + "../common/OTATlvProcessor.h", ] + + if (chip_enable_ota_default_processors == 1) { + sources += [ + "OTAApplicationProcessor.cpp", + "OTAApplicationProcessor.h", + "OTABootloaderProcessor.cpp", + "OTABootloaderProcessor.h", + "OTAHooks.cpp", + ] + + if (chip_with_factory_data == 1) { + sources += [ + "OTAFactoryDataProcessor.cpp", + "OTAFactoryDataProcessor.h", + ] + } + } } deps = [] diff --git a/src/platform/nxp/k32w/k32w0/CHIPDevicePlatformConfig.h b/src/platform/nxp/k32w/k32w0/CHIPDevicePlatformConfig.h index 40a345c6249f2c..5bd6c3199d3c1d 100644 --- a/src/platform/nxp/k32w/k32w0/CHIPDevicePlatformConfig.h +++ b/src/platform/nxp/k32w/k32w0/CHIPDevicePlatformConfig.h @@ -103,6 +103,16 @@ #define CHIP_DEVICE_LAYER_OTA_REBOOT_DELAY 3000 #endif // CHIP_DEVICE_LAYER_OTA_REBOOT_DELAY +/** + * @def CONFIG_CHIP_K32W0_OTA_DEFAULT_PROCESSORS + * + * Enables default OTA TLV processors. + * Disabled by default. + */ +#ifndef CONFIG_CHIP_K32W0_OTA_DEFAULT_PROCESSORS +#define CONFIG_CHIP_K32W0_OTA_DEFAULT_PROCESSORS 0 +#endif // CONFIG_CHIP_K32W0_OTA_DEFAULT_PROCESSORS + /** * @def CHIP_DEVICE_LAYER_ENABLE_PDM_LOGS * diff --git a/src/platform/nxp/k32w/k32w0/K32W0FactoryDataProvider.cpp b/src/platform/nxp/k32w/k32w0/K32W0FactoryDataProvider.cpp index a7ed496e0da2c7..9bde35a7dcebc0 100644 --- a/src/platform/nxp/k32w/k32w0/K32W0FactoryDataProvider.cpp +++ b/src/platform/nxp/k32w/k32w0/K32W0FactoryDataProvider.cpp @@ -21,9 +21,6 @@ #include #endif -#include "OtaUtils.h" -#include "SecLib.h" -#include "fsl_flash.h" #include #include #include @@ -34,13 +31,12 @@ #include #include "K32W0FactoryDataProvider.h" +extern "C" { +#include "Flash_Adapter.h" +} #include -/* Grab symbol for the base address from the linker file. */ -extern uint32_t __FACTORY_DATA_START[]; -extern uint32_t __FACTORY_DATA_SIZE[]; - namespace chip { namespace DeviceLayer { @@ -48,16 +44,14 @@ static constexpr size_t kSpake2pSerializedVerifier_MaxBase64Len = BASE64_ENCODED_LEN(chip::Crypto::kSpake2p_VerifierSerialized_Length) + 1; static constexpr size_t kSpake2pSalt_MaxBase64Len = BASE64_ENCODED_LEN(chip::Crypto::kSpake2p_Max_PBKDF_Salt_Length) + 1; static constexpr size_t kMaxKeyLen = 32; -static constexpr size_t kHashLen = 4; -static constexpr size_t kDataSizeLen = 4; -static constexpr size_t kHashId = 0xCE47BA5E; -static uint32_t factoryDataActualSize = 0; -static uint32_t factoryDataStartAddress = 0; +OtaUtils_EEPROM_ReadData pFunctionEepromRead = (OtaUtils_EEPROM_ReadData) K32W0FactoryDataProvider::ReadDataMemcpy; -typedef otaUtilsResult_t (*OtaUtils_EEPROM_ReadData)(uint16_t nbBytes, uint32_t address, uint8_t * pInbuf); +uint32_t K32W0FactoryDataProvider::kFactoryDataStart = (uint32_t) __FACTORY_DATA_START; +uint32_t K32W0FactoryDataProvider::kFactoryDataSize = (uint32_t) __FACTORY_DATA_SIZE; +uint32_t K32W0FactoryDataProvider::kFactoryDataPayloadStart = kFactoryDataStart + sizeof(K32W0FactoryDataProvider::Header); -static uint8_t ReadDataMemCpy(uint16_t num, uint32_t src, uint8_t * dst) +uint8_t K32W0FactoryDataProvider::ReadDataMemcpy(uint16_t num, uint32_t src, uint8_t * dst) { memcpy(dst, (void *) (src), num); return 0; @@ -89,17 +83,15 @@ K32W0FactoryDataProvider::K32W0FactoryDataProvider() maxLengths[FactoryDataId::kHardwareVersionId] = sizeof(uint16_t); maxLengths[FactoryDataId::kHardwareVersionStrId] = ConfigurationManager::kMaxHardwareVersionStringLength; maxLengths[FactoryDataId::kUniqueId] = ConfigurationManager::kMaxUniqueIDLength; + maxLengths[FactoryDataId::kPartNumber] = ConfigurationManager::kMaxPartNumberLength; + maxLengths[FactoryDataId::kProductURL] = ConfigurationManager::kMaxProductURLLength; + maxLengths[FactoryDataId::kProductLabel] = ConfigurationManager::kMaxProductLabelLength; } CHIP_ERROR K32W0FactoryDataProvider::Init() { - uint8_t sha256Output[SHA256_HASH_SIZE] = { 0 }; - uint32_t start = (uint32_t) __FACTORY_DATA_START; - uint32_t sum = 0; - uint32_t size; - uint32_t hashId; - uint8_t hashReadFromFlash[kHashLen]; - OtaUtils_EEPROM_ReadData pFunctionEepromRead = (OtaUtils_EEPROM_ReadData) ReadDataMemCpy; + CHIP_ERROR error = CHIP_NO_ERROR; + uint32_t sum = 0; ReturnErrorOnFailure(SetCustomIds()); @@ -108,60 +100,91 @@ CHIP_ERROR K32W0FactoryDataProvider::Init() sum += maxLengths[i]; } - if (sum > (uint32_t) __FACTORY_DATA_SIZE) + if (sum > kFactoryDataSize) { ChipLogError(DeviceLayer, "Max size of factory data: %" PRIu32 " is bigger than reserved factory data size: %" PRIu32, sum, - (uint32_t) __FACTORY_DATA_SIZE); + kFactoryDataSize); } - /* Before trying to verify the hash, make sure there is a hash there by checking its tag */ - if (gOtaUtilsSuccess_c != - OtaUtils_ReadFromInternalFlash((uint16_t) sizeof(hashId), start, (uint8_t *) &hashId, NULL, pFunctionEepromRead)) + error = Validate(); + if (error != CHIP_NO_ERROR) { - return CHIP_ERROR_INTERNAL; + ChipLogError(DeviceLayer, "Factory data validation failed with error: %s", ErrorStr(error)); + return error; } - if (hashId != kHashId) - { - return CHIP_ERROR_NOT_FOUND; - } + return CHIP_NO_ERROR; +} - /* Read length of factory data from internal flash and 4 byte from computed SHA256 hash over factory data */ - start += sizeof(kHashId); +CHIP_ERROR K32W0FactoryDataProvider::Validate() +{ + uint8_t sha256Output[SHA256_HASH_SIZE] = { 0 }; - if (gOtaUtilsSuccess_c != - OtaUtils_ReadFromInternalFlash((uint16_t) sizeof(size), start, (uint8_t *) &size, NULL, pFunctionEepromRead) || - gOtaUtilsSuccess_c != - OtaUtils_ReadFromInternalFlash((uint16_t) sizeof(hashReadFromFlash), start + kHashLen, &hashReadFromFlash[0], NULL, - pFunctionEepromRead)) - { - return CHIP_ERROR_INTERNAL; - } + ReturnErrorOnFailure(Restore()); - start += (kHashLen + kDataSizeLen); + auto status = OtaUtils_ReadFromInternalFlash((uint16_t) sizeof(Header), kFactoryDataStart, (uint8_t *) &mHeader, NULL, + pFunctionEepromRead); + ReturnErrorCodeIf(gOtaUtilsSuccess_c != status, CHIP_FACTORY_DATA_HEADER_READ); + ReturnErrorCodeIf(mHeader.hashId != kHashId, CHIP_FACTORY_DATA_HASH_ID); - /* Calculate SHA256 on the factory data and compare with stored value */ - SHA256_Hash((uint8_t *) start, size, sha256Output); + SHA256_Hash((uint8_t *) kFactoryDataPayloadStart, mHeader.size, sha256Output); + ReturnErrorCodeIf(memcmp(sha256Output, mHeader.hash, kHashLen) != 0, CHIP_FACTORY_DATA_SHA_CHECK); - if (memcmp(&sha256Output[0], &hashReadFromFlash[0], kHashLen) != 0) + return CHIP_NO_ERROR; +} + +CHIP_ERROR K32W0FactoryDataProvider::Restore() +{ + CHIP_ERROR error = CHIP_NO_ERROR; + uint16_t backupLength = 0; + + // Check if PDM id related to factory data backup exists. + // If it does, it means an external event (such as a power loss) + // interrupted the factory data update process and the section + // from internal flash is most likely erased and should be restored. + if (PDM_bDoesDataExist(kNvmId_FactoryDataBackup, &backupLength)) { - return CHIP_ERROR_NOT_FOUND; + chip::Platform::ScopedMemoryBuffer buffer; + buffer.Calloc(kFactoryDataSize); + ReturnErrorCodeIf(buffer.Get() == nullptr, CHIP_ERROR_NO_MEMORY); + + auto status = PDM_eReadDataFromRecord(kNvmId_FactoryDataBackup, (void *) buffer.Get(), kFactoryDataSize, &backupLength); + ReturnErrorCodeIf(PDM_E_STATUS_OK != status, CHIP_FACTORY_DATA_PDM_RESTORE); + + error = UpdateData(buffer.Get()); + if (CHIP_NO_ERROR == error) + { + PDM_vDeleteDataRecord(kNvmId_FactoryDataBackup); + } } - /* Set factory data start address after hash id, hash and hash size */ - factoryDataStartAddress = start; + // TODO: add hook to enable restore customization + + return error; +} + +CHIP_ERROR K32W0FactoryDataProvider::UpdateData(uint8_t * pBuf) +{ + NV_Init(); + + auto status = NV_FlashEraseSector(kFactoryDataStart, kFactoryDataSize); + ReturnErrorCodeIf(status != kStatus_FLASH_Success, CHIP_FACTORY_DATA_FLASH_ERASE); + + Header * header = (Header *) pBuf; + status = NV_FlashProgramUnaligned(kFactoryDataStart, sizeof(Header) + header->size, pBuf); + ReturnErrorCodeIf(status != kStatus_FLASH_Success, CHIP_FACTORY_DATA_FLASH_PROGRAM); return CHIP_NO_ERROR; } -CHIP_ERROR K32W0FactoryDataProvider::SearchForId(uint8_t searchedType, uint8_t * pBuf, size_t bufLength, uint16_t & length) +CHIP_ERROR K32W0FactoryDataProvider::SearchForId(uint8_t searchedType, uint8_t * pBuf, size_t bufLength, uint16_t & length, + uint32_t * offset) { - CHIP_ERROR err = CHIP_ERROR_NOT_FOUND; - uint32_t addr = factoryDataStartAddress; - OtaUtils_EEPROM_ReadData pFunctionEepromRead = (OtaUtils_EEPROM_ReadData) ReadDataMemCpy; - uint8_t type = 0; + CHIP_ERROR err = CHIP_ERROR_NOT_FOUND; + uint32_t addr = kFactoryDataPayloadStart; + uint8_t type = 0; - while (addr < (factoryDataStartAddress + (uint32_t) __FACTORY_DATA_SIZE)) + while (addr < (kFactoryDataPayloadStart + mHeader.size)) { if (gOtaUtilsSuccess_c != OtaUtils_ReadFromInternalFlash((uint16_t) sizeof(type), addr, &type, NULL, pFunctionEepromRead) || gOtaUtilsSuccess_c != @@ -182,9 +205,12 @@ CHIP_ERROR K32W0FactoryDataProvider::SearchForId(uint8_t searchedType, uint8_t * } else { - if (gOtaUtilsSuccess_c != OtaUtils_ReadFromInternalFlash(length, addr + 3, pBuf, NULL, pFunctionEepromRead)) + if (gOtaUtilsSuccess_c != + OtaUtils_ReadFromInternalFlash(length, addr + kValueOffset, pBuf, NULL, pFunctionEepromRead)) break; + if (offset) + *offset = (addr - kFactoryDataPayloadStart); err = CHIP_NO_ERROR; } break; @@ -192,7 +218,7 @@ CHIP_ERROR K32W0FactoryDataProvider::SearchForId(uint8_t searchedType, uint8_t * else { /* Jump past 2 bytes of length and then use length to jump to next data */ - addr = addr + 3 + length; + addr = addr + kValueOffset + length; } } @@ -364,17 +390,29 @@ CHIP_ERROR K32W0FactoryDataProvider::GetProductId(uint16_t & productId) CHIP_ERROR K32W0FactoryDataProvider::GetPartNumber(char * buf, size_t bufSize) { - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + uint16_t length = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kPartNumber, (uint8_t *) buf, bufSize, length)); + buf[length] = '\0'; + + return CHIP_NO_ERROR; } CHIP_ERROR K32W0FactoryDataProvider::GetProductURL(char * buf, size_t bufSize) { - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + uint16_t length = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kProductURL, (uint8_t *) buf, bufSize, length)); + buf[length] = '\0'; + + return CHIP_NO_ERROR; } CHIP_ERROR K32W0FactoryDataProvider::GetProductLabel(char * buf, size_t bufSize) { - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + uint16_t length = 0; + ReturnErrorOnFailure(SearchForId(FactoryDataId::kProductLabel, (uint8_t *) buf, bufSize, length)); + buf[length] = '\0'; + + return CHIP_NO_ERROR; } CHIP_ERROR K32W0FactoryDataProvider::GetSerialNumber(char * buf, size_t bufSize) diff --git a/src/platform/nxp/k32w/k32w0/K32W0FactoryDataProvider.h b/src/platform/nxp/k32w/k32w0/K32W0FactoryDataProvider.h index 14b5d2d806b58e..742a750b2fcc45 100644 --- a/src/platform/nxp/k32w/k32w0/K32W0FactoryDataProvider.h +++ b/src/platform/nxp/k32w/k32w0/K32W0FactoryDataProvider.h @@ -23,10 +23,32 @@ #include "CHIPPlatformConfig.h" #include "K32W0Config.h" #include +#include + +#include "OtaUtils.h" +#include "SecLib.h" + +/* Grab symbol for the base address from the linker file. */ +extern uint32_t __FACTORY_DATA_START[]; +extern uint32_t __FACTORY_DATA_SIZE[]; namespace chip { namespace DeviceLayer { +#define CHIP_FACTORY_DATA_ERROR(e) \ + ChipError(ChipError::Range::kLastRange, ((uint8_t) ChipError::Range::kLastRange << 2) | e, __FILE__, __LINE__) + +#define CHIP_FACTORY_DATA_SHA_CHECK CHIP_FACTORY_DATA_ERROR(0x01) +#define CHIP_FACTORY_DATA_HEADER_READ CHIP_FACTORY_DATA_ERROR(0x02) +#define CHIP_FACTORY_DATA_HASH_ID CHIP_FACTORY_DATA_ERROR(0x03) +#define CHIP_FACTORY_DATA_PDM_RESTORE CHIP_FACTORY_DATA_ERROR(0x04) +#define CHIP_FACTORY_DATA_NULL CHIP_FACTORY_DATA_ERROR(0x05) +#define CHIP_FACTORY_DATA_FLASH_ERASE CHIP_FACTORY_DATA_ERROR(0x06) +#define CHIP_FACTORY_DATA_FLASH_PROGRAM CHIP_FACTORY_DATA_ERROR(0x07) +#define CHIP_FACTORY_DATA_INTERNAL_FLASH_READ CHIP_FACTORY_DATA_ERROR(0x08) +#define CHIP_FACTORY_DATA_PDM_SAVE_RECORD CHIP_FACTORY_DATA_ERROR(0x09) +#define CHIP_FACTORY_DATA_PDM_READ_RECORD CHIP_FACTORY_DATA_ERROR(0x0A) + /** * @brief This class provides Commissionable data, Device Attestation Credentials, * and Device Instance Info. @@ -37,6 +59,13 @@ class K32W0FactoryDataProvider : public DeviceInstanceInfoProvider, public Credentials::DeviceAttestationCredentialsProvider { public: + struct Header + { + uint32_t hashId; + uint32_t size; + uint8_t hash[4]; + }; + // Default factory data IDs enum FactoryDataId { @@ -58,6 +87,9 @@ class K32W0FactoryDataProvider : public DeviceInstanceInfoProvider, kHardwareVersionId, kHardwareVersionStrId, kUniqueId, + kPartNumber, + kProductURL, + kProductLabel, kMaxId }; @@ -69,13 +101,26 @@ class K32W0FactoryDataProvider : public DeviceInstanceInfoProvider, #else static constexpr uint16_t kNumberOfIds = FactoryDataId::kMaxId; #endif + static uint32_t kFactoryDataStart; + static uint32_t kFactoryDataSize; + static uint32_t kFactoryDataPayloadStart; + static constexpr uint32_t kLengthOffset = 1; + static constexpr uint32_t kValueOffset = 3; + static constexpr uint32_t kHashLen = 4; + static constexpr size_t kHashId = 0xCE47BA5E; + + typedef otaUtilsResult_t (*OtaUtils_EEPROM_ReadData)(uint16_t nbBytes, uint32_t address, uint8_t * pInbuf); + static uint8_t ReadDataMemcpy(uint16_t num, uint32_t src, uint8_t * dst); static K32W0FactoryDataProvider & GetDefaultInstance(); K32W0FactoryDataProvider(); CHIP_ERROR Init(); - CHIP_ERROR SearchForId(uint8_t searchedType, uint8_t * pBuf, size_t bufLength, uint16_t & length); + CHIP_ERROR Validate(); + CHIP_ERROR Restore(); + CHIP_ERROR UpdateData(uint8_t * pBuf); + CHIP_ERROR SearchForId(uint8_t searchedType, uint8_t * pBuf, size_t bufLength, uint16_t & length, uint32_t * offset = nullptr); // Custom factory data providers must implement this method in order to define // their own custom IDs. @@ -113,6 +158,7 @@ class K32W0FactoryDataProvider : public DeviceInstanceInfoProvider, protected: uint16_t maxLengths[kNumberOfIds]; + Header mHeader; }; } // namespace DeviceLayer diff --git a/src/platform/nxp/k32w/k32w0/OTAApplicationProcessor.cpp b/src/platform/nxp/k32w/k32w0/OTAApplicationProcessor.cpp new file mode 100644 index 00000000000000..9c78b705f0d852 --- /dev/null +++ b/src/platform/nxp/k32w/k32w0/OTAApplicationProcessor.cpp @@ -0,0 +1,111 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * 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 +#include +#include +#include + +#include "OtaSupport.h" +#include "OtaUtils.h" + +namespace chip { + +CHIP_ERROR OTAApplicationProcessor::Init() +{ + mAccumulator.Init(sizeof(AppDescriptor)); + + ReturnErrorCodeIf(gOtaSuccess_c != OTA_ClientInit(), CHIP_OTA_PROCESSOR_CLIENT_INIT); + ReturnErrorCodeIf(gOtaSuccess_c != OTA_StartImage(mLength - sizeof(AppDescriptor)), CHIP_ERROR_INTERNAL); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAApplicationProcessor::Clear() +{ + mAccumulator.Clear(); + mLength = 0; + mProcessedLength = 0; + mWasSelected = false; + mDescriptorProcessed = false; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAApplicationProcessor::ProcessInternal(ByteSpan & block) +{ + if (!mDescriptorProcessed) + { + ReturnErrorOnFailure(ProcessDescriptor(block)); + } + + auto status = OTA_MakeHeadRoomForNextBlock(requestedOtaMaxBlockSize, OTAImageProcessorImpl::FetchNextData, 0); + if (gOtaSuccess_c != status) + { + ChipLogError(SoftwareUpdate, "Failed to make room for next block. Status: %d", status); + return CHIP_OTA_PROCESSOR_MAKE_ROOM; + } + + status = OTA_PushImageChunk((uint8_t *) block.data(), (uint16_t) block.size(), NULL, NULL); + if (gOtaSuccess_c != status) + { + ChipLogError(SoftwareUpdate, "Failed to write image block. Status: %d", status); + return CHIP_OTA_PROCESSOR_PUSH_CHUNK; + } + + return CHIP_OTA_FETCH_ALREADY_SCHEDULED; +} + +CHIP_ERROR OTAApplicationProcessor::ProcessDescriptor(ByteSpan & block) +{ + ReturnErrorOnFailure(mAccumulator.Accumulate(block)); + + // TODO: use accumulator data in some way. What should be done with AppDescriptor data? + + mDescriptorProcessed = true; + mAccumulator.Clear(); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAApplicationProcessor::ApplyAction() +{ + if (OTA_CommitImage(NULL) != gOtaSuccess_c) + { + ChipLogError(SoftwareUpdate, "Failed to commit application image."); + return CHIP_OTA_PROCESSOR_IMG_COMMIT; + } + + if (OTA_ImageAuthenticate() != gOtaImageAuthPass_c) + { + ChipLogError(SoftwareUpdate, "Failed to authenticate application image."); + return CHIP_OTA_PROCESSOR_IMG_AUTH; + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAApplicationProcessor::AbortAction() +{ + OTA_CancelImage(); + Clear(); + + return CHIP_NO_ERROR; +} + +} // namespace chip diff --git a/src/platform/nxp/k32w/k32w0/OTAApplicationProcessor.h b/src/platform/nxp/k32w/k32w0/OTAApplicationProcessor.h new file mode 100644 index 00000000000000..c500eccff8c247 --- /dev/null +++ b/src/platform/nxp/k32w/k32w0/OTAApplicationProcessor.h @@ -0,0 +1,49 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * 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. + */ + +#pragma once + +#include +#include + +namespace chip { + +struct AppDescriptor +{ + uint32_t version; + char versionString[kVersionStringSize]; + char buildDate[kBuildDateSize]; +}; + +class OTAApplicationProcessor : public OTATlvProcessor +{ +public: + CHIP_ERROR Init() override; + CHIP_ERROR Clear() override; + CHIP_ERROR ApplyAction() override; + CHIP_ERROR AbortAction() override; + +private: + CHIP_ERROR ProcessInternal(ByteSpan & block) override; + CHIP_ERROR ProcessDescriptor(ByteSpan & block); + + OTADataAccumulator mAccumulator; + bool mDescriptorProcessed = false; +}; + +} // namespace chip diff --git a/src/platform/nxp/k32w/k32w0/OTABootloaderProcessor.cpp b/src/platform/nxp/k32w/k32w0/OTABootloaderProcessor.cpp new file mode 100644 index 00000000000000..eafb57bba9bd40 --- /dev/null +++ b/src/platform/nxp/k32w/k32w0/OTABootloaderProcessor.cpp @@ -0,0 +1,55 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * 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 +#include +#include +#include +#include + +#include "OtaSupport.h" +#include "OtaUtils.h" + +namespace chip { + +CHIP_ERROR OTABootloaderProcessor::Init() +{ + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTABootloaderProcessor::Clear() +{ + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTABootloaderProcessor::ProcessInternal(ByteSpan & block) +{ + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTABootloaderProcessor::ApplyAction() +{ + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTABootloaderProcessor::AbortAction() +{ + return CHIP_NO_ERROR; +} + +} // namespace chip diff --git a/src/platform/nxp/k32w/k32w0/OTABootloaderProcessor.h b/src/platform/nxp/k32w/k32w0/OTABootloaderProcessor.h new file mode 100644 index 00000000000000..e4e428114c8110 --- /dev/null +++ b/src/platform/nxp/k32w/k32w0/OTABootloaderProcessor.h @@ -0,0 +1,46 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * 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. + */ + +#pragma once + +#include +#include + +namespace chip { + +struct BootLoaderDescriptor +{ + uint32_t version; + char versionString[kVersionStringSize]; + char buildDate[kBuildDateSize]; + uint32_t loadAddress; +}; + +class OTABootloaderProcessor : public OTATlvProcessor +{ +public: + CHIP_ERROR Init() override; + CHIP_ERROR Clear() override; + CHIP_ERROR ApplyAction() override; + CHIP_ERROR AbortAction() override; + +private: + CHIP_ERROR ProcessInternal(ByteSpan & block) override; +}; + +} // namespace chip diff --git a/src/platform/nxp/k32w/k32w0/OTAFactoryDataProcessor.cpp b/src/platform/nxp/k32w/k32w0/OTAFactoryDataProcessor.cpp new file mode 100644 index 00000000000000..367d11d4ce2bf9 --- /dev/null +++ b/src/platform/nxp/k32w/k32w0/OTAFactoryDataProcessor.cpp @@ -0,0 +1,262 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * 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 +#include +#include +#include +#include + +#include "PDM.h" +#include "fsl_flash.h" + +namespace chip { + +static OtaUtils_EEPROM_ReadData pFunctionEepromRead = (OtaUtils_EEPROM_ReadData) FactoryProvider::ReadDataMemcpy; + +CHIP_ERROR OTAFactoryDataProcessor::Init() +{ + mAccumulator.Init(mLength); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAFactoryDataProcessor::Clear() +{ + mAccumulator.Clear(); + mPayload.Clear(); + ClearBuffer(); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAFactoryDataProcessor::ProcessInternal(ByteSpan & block) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + + ReturnErrorOnFailure(mAccumulator.Accumulate(block)); + error = DecodeTlv(); + + if (error != CHIP_NO_ERROR) + { + // The factory data payload can contain a variable number of fields + // to be updated. CHIP_END_OF_TLV is returned if no more fields are + // found. + if (error == CHIP_END_OF_TLV) + { + return CHIP_NO_ERROR; + } + + Clear(); + } + + return error; +} + +CHIP_ERROR OTAFactoryDataProcessor::ApplyAction() +{ + CHIP_ERROR error = CHIP_NO_ERROR; + + ReturnErrorOnFailure(Read()); + ReturnErrorOnFailure(Backup()); + + SuccessOrExit(error = Update((uint8_t) Tags::kDacPrivateKeyId, mPayload.mCertDacKey)); + SuccessOrExit(error = Update((uint8_t) Tags::kDacCertificateId, mPayload.mCertDac)); + SuccessOrExit(error = Update((uint8_t) Tags::kPaiCertificateId, mPayload.mCertPai)); + SuccessOrExit(error = Update((uint8_t) Tags::kCertDeclarationId, mPayload.mCertDeclaration)); + + error = FactoryProvider::GetDefaultInstance().UpdateData(mFactoryData); + +exit: + if (error != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "Failed to update factory data. Error: %s", ErrorStr(error)); + error = Restore(); + if (error == CHIP_NO_ERROR) + { + error = FactoryProvider::GetDefaultInstance().UpdateData(mFactoryData); + } + } + else + { + ChipLogProgress(DeviceLayer, "Factory data update finished."); + } + + ClearBuffer(); + + return error; +} + +CHIP_ERROR OTAFactoryDataProcessor::AbortAction() +{ + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAFactoryDataProcessor::DecodeTlv() +{ + TLV::TLVReader tlvReader; + tlvReader.Init(mAccumulator.data(), mLength); + ReturnErrorOnFailure(tlvReader.Next(TLV::TLVType::kTLVType_Structure, TLV::AnonymousTag())); + + TLV::TLVType outerType; + ReturnErrorOnFailure(tlvReader.EnterContainer(outerType)); + ReturnErrorOnFailure(tlvReader.Next()); + + if (tlvReader.GetTag() == TLV::ContextTag((uint8_t) Tags::kDacPrivateKeyId)) + { + ReturnErrorOnFailure(tlvReader.Get(mPayload.mCertDacKey.Emplace())); + ReturnErrorOnFailure(tlvReader.Next()); + } + + if (tlvReader.GetTag() == TLV::ContextTag((uint8_t) Tags::kDacCertificateId)) + { + ReturnErrorOnFailure(tlvReader.Get(mPayload.mCertDac.Emplace())); + ReturnErrorOnFailure(tlvReader.Next()); + } + + if (tlvReader.GetTag() == TLV::ContextTag((uint8_t) Tags::kPaiCertificateId)) + { + ReturnErrorOnFailure(tlvReader.Get(mPayload.mCertPai.Emplace())); + ReturnErrorOnFailure(tlvReader.Next()); + } + + if (tlvReader.GetTag() == TLV::ContextTag((uint8_t) Tags::kCertDeclarationId)) + { + ReturnErrorOnFailure(tlvReader.Get(mPayload.mCertDeclaration.Emplace())); + } + + ReturnErrorOnFailure(tlvReader.ExitContainer(outerType)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAFactoryDataProcessor::Update(uint8_t tag, Optional & optional) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + if (optional.HasValue()) + { + error = UpdateValue(tag, optional.Value()); + } + + return error; +} + +CHIP_ERROR OTAFactoryDataProcessor::Read() +{ + FactoryProvider::Header header; + auto status = OtaUtils_ReadFromInternalFlash(sizeof(FactoryProvider::Header), FactoryProvider::kFactoryDataStart, + (uint8_t *) &header, NULL, pFunctionEepromRead); + + mFactoryData = static_cast(chip::Platform::MemoryAlloc(FactoryProvider::kFactoryDataSize)); + ReturnErrorCodeIf(mFactoryData == nullptr, CHIP_FACTORY_DATA_NULL); + memset(mFactoryData, 0, FactoryProvider::kFactoryDataSize); + + status = OtaUtils_ReadFromInternalFlash(sizeof(FactoryProvider::Header) + header.size, FactoryProvider::kFactoryDataStart, + mFactoryData, NULL, pFunctionEepromRead); + if (status != gOtaUtilsSuccess_c) + { + ClearBuffer(); + return CHIP_FACTORY_DATA_INTERNAL_FLASH_READ; + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAFactoryDataProcessor::Backup() +{ + ReturnErrorCodeIf(mFactoryData == nullptr, CHIP_FACTORY_DATA_NULL); + + auto status = PDM_eSaveRecordData(kNvmId_FactoryDataBackup, (void *) mFactoryData, FactoryProvider::kFactoryDataSize); + ReturnErrorCodeIf(status != PDM_E_STATUS_OK, CHIP_FACTORY_DATA_PDM_SAVE_RECORD); + // PDM save will do an encryption in place, so a restore is neeeded in order + // to have the decrypted data back in the mFactoryData buffer. + ReturnErrorOnFailure(Restore()); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAFactoryDataProcessor::Restore() +{ + ReturnErrorCodeIf(mFactoryData == nullptr, CHIP_FACTORY_DATA_NULL); + + uint16_t bytesRead = 0; + + auto status = + PDM_eReadDataFromRecord(kNvmId_FactoryDataBackup, (void *) mFactoryData, FactoryProvider::kFactoryDataSize, &bytesRead); + ReturnErrorCodeIf(status != PDM_E_STATUS_OK, CHIP_FACTORY_DATA_PDM_READ_RECORD); + + return CHIP_NO_ERROR; +} + +void OTAFactoryDataProcessor::ClearBuffer() +{ + if (mFactoryData) + { + chip::Platform::MemoryFree(mFactoryData); + mFactoryData = nullptr; + } + + PDM_vDeleteDataRecord(kNvmId_FactoryDataBackup); +} + +CHIP_ERROR OTAFactoryDataProcessor::UpdateValue(uint8_t tag, ByteSpan & newValue) +{ + uint16_t oldLength = 0; + uint16_t newLength = newValue.size(); + uint32_t offset = 0; + FactoryProvider::Header * header = (FactoryProvider::Header *) mFactoryData; + uint8_t * data = mFactoryData + sizeof(FactoryProvider::Header); + + while (offset < header->size) + { + memcpy(&oldLength, &data[offset + FactoryProvider::kLengthOffset], sizeof(oldLength)); + + if (tag != data[offset]) + { + offset += FactoryProvider::kValueOffset + oldLength; + continue; + } + + if (oldLength == newLength) + { + memcpy(&data[offset + FactoryProvider::kValueOffset], newValue.data(), newLength); + } + else + { + uint32_t oldEndOffset = offset + FactoryProvider::kValueOffset + oldLength; + + memcpy(&data[offset + FactoryProvider::kLengthOffset], &newLength, sizeof(newLength)); + memmove(&data[offset + FactoryProvider::kValueOffset + newLength], &data[oldEndOffset], header->size - oldEndOffset); + memcpy(&data[offset + FactoryProvider::kValueOffset], newValue.data(), newLength); + } + + header->size = header->size - oldLength + newLength; + + uint8_t sha256Output[SHA256_HASH_SIZE] = { 0 }; + SHA256_Hash(data, header->size, sha256Output); + memcpy(header->hash, sha256Output, sizeof(header->hash)); + + ChipLogProgress(DeviceLayer, "Value at tag %d updated successfully.", tag); + return CHIP_NO_ERROR; + } + + ChipLogError(DeviceLayer, "Failed to find tag %d.", tag); + return CHIP_ERROR_NOT_FOUND; +} + +} // namespace chip diff --git a/src/platform/nxp/k32w/k32w0/OTAFactoryDataProcessor.h b/src/platform/nxp/k32w/k32w0/OTAFactoryDataProcessor.h new file mode 100644 index 00000000000000..e40e81f199c1ac --- /dev/null +++ b/src/platform/nxp/k32w/k32w0/OTAFactoryDataProcessor.h @@ -0,0 +1,80 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace chip { + +using FactoryProvider = DeviceLayer::K32W0FactoryDataProvider; +using Tags = FactoryProvider::FactoryDataId; + +/** + * OTA custom payload that uses Matter TLVs. + * The custom payload is used when factory data needs updating. + * Factory data will be encoded using Matter TLV format to make + * use of the ChipTlv reader. A payload contains metadata (size of + * TLVs) and the TLVs themselves contained in a structure. + * If no factory data need to be updated, the metadata will be 0 + */ +struct OTAFactoryPayload +{ + Optional mCertDacKey; + Optional mCertDac; + Optional mCertPai; + Optional mCertDeclaration; + + void Clear() + { + mCertDacKey.ClearValue(); + mCertDac.ClearValue(); + mCertPai.ClearValue(); + mCertDeclaration.ClearValue(); + } +}; + +class OTAFactoryDataProcessor : public OTATlvProcessor +{ +public: + CHIP_ERROR Init() override; + CHIP_ERROR Clear() override; + CHIP_ERROR ApplyAction() override; + CHIP_ERROR AbortAction() override; + +private: + CHIP_ERROR ProcessInternal(ByteSpan & block) override; + CHIP_ERROR DecodeTlv(); + CHIP_ERROR Update(uint8_t tag, Optional & optional); + + CHIP_ERROR Read(); + CHIP_ERROR Backup(); + CHIP_ERROR Restore(); + void ClearBuffer(); + CHIP_ERROR UpdateValue(uint8_t tag, ByteSpan & newValue); + + OTAFactoryPayload mPayload; + OTADataAccumulator mAccumulator; + uint8_t * mFactoryData = nullptr; +}; + +} // namespace chip diff --git a/src/platform/nxp/k32w/k32w0/OTAHooks.cpp b/src/platform/nxp/k32w/k32w0/OTAHooks.cpp new file mode 100644 index 00000000000000..28b8f764aac59e --- /dev/null +++ b/src/platform/nxp/k32w/k32w0/OTAHooks.cpp @@ -0,0 +1,55 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * 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 +#include + +#include +#include +#include +#if CONFIG_CHIP_K32W0_REAL_FACTORY_DATA +#include +#endif // CONFIG_CHIP_K32W0_REAL_FACTORY_DATA + +#include "OtaSupport.h" + +extern "C" void ResetMCU(void); + +static chip::OTAApplicationProcessor sApplicationProcessor; +static chip::OTABootloaderProcessor sBootloaderProcessor; +#if CONFIG_CHIP_K32W0_REAL_FACTORY_DATA +static chip::OTAFactoryDataProcessor sFactoryDataProcessor; +#endif // CONFIG_CHIP_K32W0_REAL_FACTORY_DATA + +extern "C" WEAK CHIP_ERROR OtaHookInit() +{ + auto & imageProcessor = chip::OTAImageProcessorImpl::GetDefaultInstance(); + ReturnErrorOnFailure(imageProcessor.RegisterProcessor(1, &sApplicationProcessor)); + ReturnErrorOnFailure(imageProcessor.RegisterProcessor(2, &sBootloaderProcessor)); +#if CONFIG_CHIP_K32W0_REAL_FACTORY_DATA + ReturnErrorOnFailure(imageProcessor.RegisterProcessor(3, &sFactoryDataProcessor)); +#endif // CONFIG_CHIP_K32W0_REAL_FACTORY_DATA + + return CHIP_NO_ERROR; +} + +extern "C" WEAK void OtaHookReset() +{ + OTA_SetNewImageFlag(); + ResetMCU(); +} diff --git a/third_party/nxp/k32w0_sdk/k32w0_sdk.gni b/third_party/nxp/k32w0_sdk/k32w0_sdk.gni index 88c0769a9d47ed..c42bfd85baa780 100644 --- a/third_party/nxp/k32w0_sdk/k32w0_sdk.gni +++ b/third_party/nxp/k32w0_sdk/k32w0_sdk.gni @@ -43,6 +43,7 @@ declare_args() { use_custom_factory_provider = 0 chip_crypto_flavor = "NXP-Ultrafast-P256" chip_reduce_ssbl_size = false + chip_enable_ota_default_processors = 1 } assert(k32w0_sdk_root != "", "k32w0_sdk_root must be specified") @@ -76,14 +77,6 @@ template("k32w0_sdk") { chip_with_ot_cli == 0 && chip_with_se05x == 0), "Please disable low power if expansion board, openthread CLI or SE is needed!") - assert(chip_with_low_power == 0 || - (chip_logging == false && - (invoker.chip_detail_logging == false && - invoker.chip_progress_logging == false && - invoker.chip_automation_logging == false && - invoker.chip_error_logging == false)), - "Please disable all logs when low power is enabled!") - assert( use_custom_factory_provider == 0 || chip_with_factory_data == 1, "Please set chip_with_factory_data=1 if using custom factory provider.") @@ -100,7 +93,6 @@ template("k32w0_sdk") { device = "K32W061" board = "k32w061dk6" chip_with_ntag = 0 - chip_with_high_power = 0 } if (build_for_k32w041am == 1) { assert(build_for_k32w041am == 1 && build_for_k32w061 == 0 && @@ -108,7 +100,6 @@ template("k32w0_sdk") { "Please build for only one platform") device = "K32W041AM" board = "k32w041amdk6" - chip_with_high_power = 1 chip_with_ntag = 0 } if (build_for_k32w041a == 1) { @@ -117,7 +108,6 @@ template("k32w0_sdk") { "Please build for only one platform") device = "K32W041A" board = "k32w041adk6" - chip_with_high_power = 1 chip_with_ntag = 0 } if (build_for_k32w041 == 1) { @@ -127,7 +117,6 @@ template("k32w0_sdk") { device = "K32W041" board = "k32w041dk6" chip_with_ntag = 0 - chip_with_high_power = 0 } print("device:", device) @@ -136,6 +125,13 @@ template("k32w0_sdk") { print("increased TX power:", chip_with_high_power) print("FRO32k: ", use_fro_32k) print("low power: ", chip_with_low_power) + print("OTA default processors: ", chip_enable_ota_default_processors) + + if (chip_with_low_power == 1 && chip_logging == true) { + print( + "WARNING: enabling logs in low power might break the LP timings. Use at your own risk!") + print("WARNING: set chip_logging=false to disable logging.") + } if (chip_crypto == "platform") { print("ECC crypto lib: ", chip_crypto_flavor) @@ -306,6 +302,7 @@ template("k32w0_sdk") { "gLogRingPlacementOffset_c=0xF000", "gSecLibUseSha256Alt_d=1", "gOTA_UseSecLibAes=1", + "gResetSystemReset_d=1", ] if (chip_crypto == "platform" && @@ -378,9 +375,19 @@ template("k32w0_sdk") { "gPWR_FreqScalingWFI=0", "mAppUseTickLessMode_c=1", "cPWR_DiscardRunningTimerForPowerDown=1", - "K32W_LOG_ENABLED=0", - "gUartDebugConsole_d=0", ] + + if (chip_logging == false) { + defines += [ + "K32W_LOG_ENABLED=0", + "gUartDebugConsole_d=0", + ] + } else { + defines += [ + "K32W_LOG_ENABLED=1", + "gUartDebugConsole_d=1", + ] + } } else { defines += [ "cPWR_UsePowerDownMode=0", @@ -422,6 +429,10 @@ template("k32w0_sdk") { defines += [ "CONFIG_CHIP_K32W0_REAL_FACTORY_DATA=1" ] } + if (chip_enable_ota_default_processors == 1) { + defines += [ "CONFIG_CHIP_K32W0_OTA_DEFAULT_PROCESSORS=1" ] + } + if (defined(invoker.defines)) { defines += invoker.defines }