From 07e309815eb07e17156748d4a0407237f3f81ac2 Mon Sep 17 00:00:00 2001 From: Kamil Kasperczyk <66371704+kkasperczyk-no@users.noreply.github.com> Date: Mon, 6 May 2024 12:45:19 +0200 Subject: [PATCH] [nrfconnect] Introduced several improvements for the factory data management (#33195) * [nrfconnect] Added FactoryDataBase to facilitate dynamic assignments of FactoryDataProvider concrete types to to the generic pointer. Signed-off-by: Marcin Kajor * [nrfconnect] Added non-PM based factory data support Added support for platforms not built using PM. This commit includes fixes only firmware implementation and does not include cmake changes for factory data generation. Signed-off-by: Kamil Kasperczyk * [nrfconnect] Improved generation of the factory data Allow to merge the factory data .hex file with the firmware's .hex file when partition manager is not available. * [nrfconnect] Disable External FD if QSPI NOR is not available We need to have support for factory data when the partition manager is enabled but there is no support for QSPI external flash. --------- Signed-off-by: Marcin Kajor Signed-off-by: Kamil Kasperczyk Co-authored-by: Marcin Kajor Co-authored-by: Arkadiusz Balys --- config/nrfconnect/chip-module/Kconfig | 3 +- .../chip-module/generate_factory_data.cmake | 31 +++-- .../nrfconnect/FactoryDataProvider.cpp | 3 + src/platform/nrfconnect/FactoryDataProvider.h | 118 ++++++++++++------ 4 files changed, 108 insertions(+), 47 deletions(-) diff --git a/config/nrfconnect/chip-module/Kconfig b/config/nrfconnect/chip-module/Kconfig index e3c5a86bf96c1d..d4ccccd983ed3f 100644 --- a/config/nrfconnect/chip-module/Kconfig +++ b/config/nrfconnect/chip-module/Kconfig @@ -106,7 +106,6 @@ config CHIP_MALLOC_SYS_HEAP config CHIP_FACTORY_DATA bool "Factory data provider" select ZCBOR - imply FPROTECT help Enables the default nRF Connect factory data provider implementation that supports reading the factory data encoded in the CBOR format from the @@ -149,7 +148,7 @@ config CHIP_FACTORY_DATA_WRITE_PROTECT bool "Enable Factory Data write protection" select FPROTECT depends on CHIP_FACTORY_DATA - default y if CHIP_FACTORY_DATA + default y help Enables the write protection of the Factory Data partition in the flash memory. This is a recommended feature, but it requires the Settings partition size to be diff --git a/config/nrfconnect/chip-module/generate_factory_data.cmake b/config/nrfconnect/chip-module/generate_factory_data.cmake index 21f7360cd03f10..b1f4ad153c4fb9 100644 --- a/config/nrfconnect/chip-module/generate_factory_data.cmake +++ b/config/nrfconnect/chip-module/generate_factory_data.cmake @@ -129,9 +129,21 @@ set(factory_data_output_path ${output_path}/${factory_data_target}) string(APPEND script_args "-o \"${factory_data_output_path}\"\n") string(APPEND script_args "-s \"${schema_path}\"\n") -# Add optional offset and size arguments to generate both .hex and .json files. -string(APPEND script_args "--offset $\n") -string(APPEND script_args "--size $\n") +# Add optional offset and size arguments to generate .hex file as well as .json. +if(CONFIG_PARTITION_MANAGER_ENABLED) + string(APPEND script_args "--offset $\n") + string(APPEND script_args "--size $\n") +else() + dt_alias(factory_data_alias PROPERTY "factory-data") + dt_node_exists(factory_data_exists PATH "${factory_data_alias}") + if(NOT ${factory_data_exists}) + message(FATAL_ERROR "factory-data alias does not exist in DTS") + endif() + dt_reg_addr(factory_data_addr PATH ${factory_data_alias}) + dt_reg_size(factory_data_size PATH ${factory_data_alias}) + string(APPEND script_args "--offset ${factory_data_addr}\n") + string(APPEND script_args "--size ${factory_data_size}\n") +endif() # execute first script to create a JSON file separate_arguments(separated_script_args NATIVE_COMMAND ${script_args}) @@ -175,10 +187,15 @@ nrfconnect_create_factory_data(factory_data ${FACTORY_DATA_SCHEMA_PATH} ${OUTPUT_FILE_PATH}) -if(CONFIG_CHIP_FACTORY_DATA_MERGE_WITH_FIRMWARE) - # set custom target for merging factory_data hex file - set_property(GLOBAL PROPERTY factory_data_PM_HEX_FILE ${OUTPUT_FILE_PATH}/factory_data.hex) - set_property(GLOBAL PROPERTY factory_data_PM_TARGET factory_data) +if(CONFIG_CHIP_FACTORY_DATA_MERGE_WITH_FIRMWARE) + if(CONFIG_PARTITION_MANAGER_ENABLED) + # set custom target for merging factory_data hex file + set_property(GLOBAL PROPERTY factory_data_PM_HEX_FILE ${OUTPUT_FILE_PATH}/factory_data.hex) + set_property(GLOBAL PROPERTY factory_data_PM_TARGET factory_data) + else() + set_property(GLOBAL APPEND PROPERTY HEX_FILES_TO_MERGE ${OUTPUT_FILE_PATH}/factory_data.hex ${OUTPUT_FILE_PATH}/zephyr.hex) + set_property(TARGET runners_yaml_props_target PROPERTY hex_file ${OUTPUT_FILE_PATH}/merged.hex) + endif() endif() diff --git a/src/platform/nrfconnect/FactoryDataProvider.cpp b/src/platform/nrfconnect/FactoryDataProvider.cpp index 11995faccd8272..aaa6f4b93e12bb 100644 --- a/src/platform/nrfconnect/FactoryDataProvider.cpp +++ b/src/platform/nrfconnect/FactoryDataProvider.cpp @@ -432,7 +432,10 @@ CHIP_ERROR FactoryDataProvider::GetUserKey(const char * userKe // Fully instantiate the template class in whatever compilation unit includes this file. template class FactoryDataProvider; +#if defined(USE_PARTITION_MANAGER) && USE_PARTITION_MANAGER == 1 && (defined(CONFIG_CHIP_QSPI_NOR) || defined(CONFIG_CHIP_SPI_NOR)) template class FactoryDataProvider; +#endif // if defined(USE_PARTITION_MANAGER) && USE_PARTITION_MANAGER == 1 (defined(CONFIG_CHIP_QSPI_NOR) || + // defined(CONFIG_CHIP_SPI_NOR)) } // namespace DeviceLayer } // namespace chip diff --git a/src/platform/nrfconnect/FactoryDataProvider.h b/src/platform/nrfconnect/FactoryDataProvider.h index bc1ef16ee6d161..ef6c8ffa66185f 100644 --- a/src/platform/nrfconnect/FactoryDataProvider.h +++ b/src/platform/nrfconnect/FactoryDataProvider.h @@ -25,8 +25,20 @@ #include #endif +#ifdef CONFIG_FPROTECT #include +#endif // if CONFIG_FPROTECT + +#if defined(USE_PARTITION_MANAGER) && USE_PARTITION_MANAGER == 1 #include +#define FACTORY_DATA_ADDRESS PM_FACTORY_DATA_ADDRESS +#define FACTORY_DATA_SIZE PM_FACTORY_DATA_SIZE +#else +#include +#define FACTORY_DATA_SIZE DT_REG_SIZE(DT_ALIAS(factory_data)) +#define FACTORY_DATA_ADDRESS DT_REG_ADDR(DT_ALIAS(factory_data)) +#endif // if defined(USE_PARTITION_MANAGER) && USE_PARTITION_MANAGER == 1 + #include #include @@ -39,8 +51,8 @@ struct InternalFlashFactoryData { CHIP_ERROR GetFactoryDataPartition(uint8_t *& data, size_t & dataSize) { - data = reinterpret_cast(PM_FACTORY_DATA_ADDRESS); - dataSize = PM_FACTORY_DATA_SIZE; + data = reinterpret_cast(FACTORY_DATA_ADDRESS); + dataSize = FACTORY_DATA_SIZE; return CHIP_NO_ERROR; } @@ -54,8 +66,8 @@ struct InternalFlashFactoryData // the application code at runtime anyway. constexpr size_t FactoryDataBlockBegin() { - // calculate the nearest multiple of CONFIG_FPROTECT_BLOCK_SIZE smaller than PM_FACTORY_DATA_ADDRESS - return PM_FACTORY_DATA_ADDRESS & (-CONFIG_FPROTECT_BLOCK_SIZE); + // calculate the nearest multiple of CONFIG_FPROTECT_BLOCK_SIZE smaller than FACTORY_DATA_ADDRESS + return FACTORY_DATA_ADDRESS & (-CONFIG_FPROTECT_BLOCK_SIZE); } constexpr size_t FactoryDataBlockSize() @@ -63,7 +75,7 @@ struct InternalFlashFactoryData // calculate the factory data end address rounded up to the nearest multiple of CONFIG_FPROTECT_BLOCK_SIZE // and make sure we do not overlap with settings partition constexpr size_t kFactoryDataBlockEnd = - (PM_FACTORY_DATA_ADDRESS + PM_FACTORY_DATA_SIZE + CONFIG_FPROTECT_BLOCK_SIZE - 1) & (-CONFIG_FPROTECT_BLOCK_SIZE); + (FACTORY_DATA_ADDRESS + FACTORY_DATA_SIZE + CONFIG_FPROTECT_BLOCK_SIZE - 1) & (-CONFIG_FPROTECT_BLOCK_SIZE); static_assert(kFactoryDataBlockEnd <= PM_SETTINGS_STORAGE_ADDRESS, "FPROTECT memory block, which contains factory data" "partition overlaps with the settings partition." @@ -75,19 +87,24 @@ struct InternalFlashFactoryData #undef TO_STR_IMPL CHIP_ERROR ProtectFactoryDataPartitionAgainstWrite() { +#ifdef CONFIG_FPROTECT int ret = fprotect_area(FactoryDataBlockBegin(), FactoryDataBlockSize()); return System::MapErrorZephyr(ret); +#else + return CHIP_ERROR_NOT_IMPLEMENTED; +#endif // if CONFIG_FPROTECT } #else CHIP_ERROR ProtectFactoryDataPartitionAgainstWrite() { return CHIP_ERROR_NOT_IMPLEMENTED; } #endif }; +#if defined(USE_PARTITION_MANAGER) && USE_PARTITION_MANAGER == 1 && (defined(CONFIG_CHIP_QSPI_NOR) || defined(CONFIG_CHIP_SPI_NOR)) struct ExternalFlashFactoryData { CHIP_ERROR GetFactoryDataPartition(uint8_t *& data, size_t & dataSize) { - int ret = flash_read(mFlashDevice, PM_FACTORY_DATA_ADDRESS, mFactoryDataBuffer, PM_FACTORY_DATA_SIZE); + int ret = flash_read(mFlashDevice, FACTORY_DATA_ADDRESS, mFactoryDataBuffer, FACTORY_DATA_SIZE); if (ret != 0) { @@ -95,24 +112,69 @@ struct ExternalFlashFactoryData } data = mFactoryDataBuffer; - dataSize = PM_FACTORY_DATA_SIZE; + dataSize = FACTORY_DATA_SIZE; return CHIP_NO_ERROR; } CHIP_ERROR ProtectFactoryDataPartitionAgainstWrite() { return CHIP_ERROR_NOT_IMPLEMENTED; } - const struct device * mFlashDevice = DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller)); - uint8_t mFactoryDataBuffer[PM_FACTORY_DATA_SIZE]; + const struct device * mFlashDevice = DEVICE_DT_GET(DT_CHOSEN(nordic_pm_ext_flash)); + uint8_t mFactoryDataBuffer[FACTORY_DATA_SIZE]; +}; +#endif // if defined(USE_PARTITION_MANAGER) && USE_PARTITION_MANAGER == 1 && (defined(CONFIG_CHIP_QSPI_NOR) || + // defined(CONFIG_CHIP_SPI_NOR)) + +class FactoryDataProviderBase : public chip::Credentials::DeviceAttestationCredentialsProvider, + public CommissionableDataProvider, + public DeviceInstanceInfoProvider +{ +public: + /** + * @brief Perform all operations needed to initialize factory data provider. + * + * @returns CHIP_NO_ERROR in case of a success, specific error code otherwise + */ + virtual CHIP_ERROR Init() = 0; + + /** + * @brief Get the EnableKey as MutableByteSpan + * + * @param enableKey MutableByteSpan object to obtain EnableKey + * @returns + * CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND if factory data does not contain enable_key field, or the value cannot be read + * out. CHIP_ERROR_BUFFER_TOO_SMALL if provided MutableByteSpan is too small + */ + virtual CHIP_ERROR GetEnableKey(MutableByteSpan & enableKey) = 0; + + /** + * @brief Get the user data in CBOR format as MutableByteSpan + * + * @param userData MutableByteSpan object to obtain all user data in CBOR format + * @returns + * CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND if factory data does not contain user field, or the value cannot be read out. + * CHIP_ERROR_BUFFER_TOO_SMALL if provided MutableByteSpan is too small + */ + virtual CHIP_ERROR GetUserData(MutableByteSpan & userData) = 0; + + /** + * @brief Try to find user data key and return its value + * + * @param userKey A key name to be found + * @param buf Buffer to store value of found key + * @param len Length of the buffer. This value will be updated to the actual value if the key is read. + * @returns + * CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND if factory data does not contain user key field, or the value cannot be read + * out. CHIP_ERROR_BUFFER_TOO_SMALL if provided buffer length is too small + */ + virtual CHIP_ERROR GetUserKey(const char * userKey, void * buf, size_t & len) = 0; }; template -class FactoryDataProvider : public chip::Credentials::DeviceAttestationCredentialsProvider, - public CommissionableDataProvider, - public DeviceInstanceInfoProvider +class FactoryDataProvider : public FactoryDataProviderBase { public: - CHIP_ERROR Init(); + CHIP_ERROR Init() override; // ===== Members functions that implement the DeviceAttestationCredentialsProvider CHIP_ERROR GetCertificationDeclaration(MutableByteSpan & outBuffer) override; @@ -147,33 +209,13 @@ class FactoryDataProvider : public chip::Credentials::DeviceAttestationCredentia CHIP_ERROR GetProductPrimaryColor(app::Clusters::BasicInformation::ColorEnum * primaryColor) override; // ===== Members functions that are platform-specific - CHIP_ERROR GetEnableKey(MutableByteSpan & enableKey); - - /** - * @brief Get the user data in CBOR format as MutableByteSpan - * - * @param userData MutableByteSpan object to obtain all user data in CBOR format - * @returns - * CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND if factory data does not contain user field, or the value cannot be read out. - * CHIP_ERROR_BUFFER_TOO_SMALL if provided MutableByteSpan is too small - */ - CHIP_ERROR GetUserData(MutableByteSpan & userData); - - /** - * @brief Try to find user data key and return its value - * - * @param userKey A key name to be found - * @param buf Buffer to store value of found key - * @param len Length of the buffer. This value will be updated to the actual value if the key is read. - * @returns - * CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND if factory data does not contain user field, or the value cannot be read out. - * CHIP_ERROR_BUFFER_TOO_SMALL if provided buffer length is too small - */ - CHIP_ERROR GetUserKey(const char * userKey, void * buf, size_t & len); + CHIP_ERROR GetEnableKey(MutableByteSpan & enableKey) override; + CHIP_ERROR GetUserData(MutableByteSpan & userData) override; + CHIP_ERROR GetUserKey(const char * userKey, void * buf, size_t & len) override; private: - static constexpr uint16_t kFactoryDataPartitionSize = PM_FACTORY_DATA_SIZE; - static constexpr uint32_t kFactoryDataPartitionAddress = PM_FACTORY_DATA_ADDRESS; + static constexpr uint16_t kFactoryDataPartitionSize = FACTORY_DATA_SIZE; + static constexpr uint32_t kFactoryDataPartitionAddress = FACTORY_DATA_ADDRESS; static constexpr uint8_t kDACPrivateKeyLength = 32; static constexpr uint8_t kDACPublicKeyLength = 65;