diff --git a/hal/inc/hal_dynalib_ota.h b/hal/inc/hal_dynalib_ota.h index f5b6c61c55..bcb97e2602 100644 --- a/hal/inc/hal_dynalib_ota.h +++ b/hal/inc/hal_dynalib_ota.h @@ -51,6 +51,7 @@ DYNALIB_FN(6, hal_ota, HAL_FLASH_Update, int(const uint8_t*, uint32_t, uint32_t, DYNALIB_FN(7, hal_ota, HAL_FLASH_End, hal_update_complete_t(hal_module_t*)) DYNALIB_FN(8, hal_ota, HAL_FLASH_OTA_Validate, int(hal_module_t*, bool, module_validation_flags_t, void*)) DYNALIB_FN(9, hal_ota, HAL_OTA_Add_System_Info, void(hal_system_info_t* info, bool create, void* reserved)) +DYNALIB_FN(10, hal_ota, HAL_FLASH_ApplyPendingUpdate, hal_update_complete_t(hal_module_t*, bool, void*)) DYNALIB_END(hal_ota) #endif /* HAL_DYNALIB_OTA_H */ diff --git a/hal/inc/ota_flash_hal.h b/hal/inc/ota_flash_hal.h index cccee5ceb8..1a26c1c221 100644 --- a/hal/inc/ota_flash_hal.h +++ b/hal/inc/ota_flash_hal.h @@ -143,6 +143,13 @@ typedef enum { hal_update_complete_t HAL_FLASH_End(hal_module_t* module); +/** + * @param module Optional pointer to a module that receives the module definition of the firmware that was flashed. + * @param dryRun when true, only test that the system has a pending update in memory. When false, the test is performed and the module + * applied if valid (or the bootloader instructed to apply it.) + */ +hal_update_complete_t HAL_FLASH_ApplyPendingUpdate(hal_module_t* module, bool dryRun, void* reserved); + uint32_t HAL_FLASH_ModuleAddress(uint32_t address); uint32_t HAL_FLASH_ModuleLength(uint32_t address); bool HAL_FLASH_VerifyCRC32(uint32_t address, uint32_t length); diff --git a/hal/src/core/ota_flash_hal.c b/hal/src/core/ota_flash_hal.c index cc7cc465e9..6bc02fc5af 100644 --- a/hal/src/core/ota_flash_hal.c +++ b/hal/src/core/ota_flash_hal.c @@ -69,7 +69,7 @@ int HAL_FLASH_OTA_Validate(hal_module_t* mod, bool userDepsOptional, module_vali return 0; } -hal_update_complete_t HAL_FLASH_End(hal_module_t* reserved) +hal_update_complete_t HAL_FLASH_ApplyPendingUpdate(hal_module_t* module, bool dryRun, void* reserved) { FLASH_End(); return HAL_UPDATE_APPLIED_PENDING_RESTART; diff --git a/hal/src/gcc/ota_flash_hal.cpp b/hal/src/gcc/ota_flash_hal.cpp index 88ed0f57b9..829d675351 100644 --- a/hal/src/gcc/ota_flash_hal.cpp +++ b/hal/src/gcc/ota_flash_hal.cpp @@ -58,7 +58,11 @@ int HAL_FLASH_OTA_Validate(hal_module_t* mod, bool userDepsOptional, module_vali return HAL_UPDATE_APPLIED; } - +hal_update_complete_t HAL_FLASH_ApplyPendingUpdate(hal_module_t* module, bool dryRun, void* reserved) +{ + HAL_FLASH_End(module); + return HAL_UPDATE_APPLIED_PENDING_RESTART; +} /** * Set the claim code for this device. diff --git a/hal/src/nRF52840/ota_flash_hal.cpp b/hal/src/nRF52840/ota_flash_hal.cpp index 5d4f5ea0b2..2e6ddca70f 100644 --- a/hal/src/nRF52840/ota_flash_hal.cpp +++ b/hal/src/nRF52840/ota_flash_hal.cpp @@ -450,6 +450,31 @@ hal_update_complete_t HAL_FLASH_End(hal_module_t* mod) return result; } +// Todo this code and much of the code here is duplicated between Gen2 and Gen3 +// This should be factored out into directory shared by both platforms. +hal_update_complete_t HAL_FLASH_ApplyPendingUpdate(hal_module_t* module, bool dryRun, void* reserved) +{ + uint8_t otaUpdateFlag = DCT_OTA_UPDATE_FLAG_CLEAR; + STATIC_ASSERT(sizeof(otaUpdateFlag)==DCT_OTA_UPDATE_FLAG_SIZE, "expected ota update flag size to be 1"); + dct_read_app_data_copy(DCT_OTA_UPDATE_FLAG_OFFSET, &otaUpdateFlag, DCT_OTA_UPDATE_FLAG_SIZE); + hal_update_complete_t result = HAL_UPDATE_ERROR; + if (otaUpdateFlag==DCT_OTA_UPDATE_FLAG_SET) { + if (dryRun) { + // todo - we should probably check the module integrity too + // ideally this parameter would be passed to HAL_FLASH_End to avoid duplication of logic here. + result = HAL_UPDATE_APPLIED; + } + else { + // clear the flag + otaUpdateFlag = DCT_OTA_UPDATE_FLAG_CLEAR; + dct_write_app_data(&otaUpdateFlag, DCT_OTA_UPDATE_FLAG_OFFSET, DCT_OTA_UPDATE_FLAG_SIZE); + result = HAL_FLASH_End(module); + } + } + return result; +} + + void HAL_FLASH_Read_ServerAddress(ServerAddress* server_addr) { bool udp = HAL_Feature_Get(FEATURE_CLOUD_UDP); diff --git a/hal/src/stm32f2xx/ota_flash_hal_stm32f2xx.cpp b/hal/src/stm32f2xx/ota_flash_hal_stm32f2xx.cpp index 8f8d4dd337..e83c043353 100644 --- a/hal/src/stm32f2xx/ota_flash_hal_stm32f2xx.cpp +++ b/hal/src/stm32f2xx/ota_flash_hal_stm32f2xx.cpp @@ -340,6 +340,31 @@ hal_update_complete_t HAL_FLASH_End(hal_module_t* mod) return result; } +// Todo this code and much of the code here is duplicated between Gen2 and Gen3 +// This should be factored out into directory shared by both platforms. +hal_update_complete_t HAL_FLASH_ApplyPendingUpdate(hal_module_t* module, bool dryRun, void* reserved) +{ + uint8_t otaUpdateFlag = DCT_OTA_UPDATE_FLAG_CLEAR; + STATIC_ASSERT(sizeof(otaUpdateFlag)==DCT_OTA_UPDATE_FLAG_SIZE, "expected ota update flag size to be 1"); + dct_read_app_data_copy(DCT_OTA_UPDATE_FLAG_OFFSET, &otaUpdateFlag, DCT_OTA_UPDATE_FLAG_SIZE); + hal_update_complete_t result = HAL_UPDATE_ERROR; + if (otaUpdateFlag==DCT_OTA_UPDATE_FLAG_SET) { + if (dryRun) { + // todo - we should probably check the module integrity too + // ideally this parameter would be passed to HAL_FLASH_End to avoid duplication of logic here. + result = HAL_UPDATE_APPLIED; + } + else { + // clear the flag + otaUpdateFlag = DCT_OTA_UPDATE_FLAG_CLEAR; + dct_write_app_data(&otaUpdateFlag, DCT_OTA_UPDATE_FLAG_OFFSET, DCT_OTA_UPDATE_FLAG_SIZE); + result = HAL_FLASH_End(module); + } + } + return result; +} + + void copy_dct(void* target, uint16_t offset, uint16_t length) { dct_read_app_data_copy(offset, target, length); } diff --git a/platform/MCU/STM32F2xx/SPARK_Firmware_Driver/inc/dct.h b/platform/MCU/STM32F2xx/SPARK_Firmware_Driver/inc/dct.h index d97184ab4a..69974494a4 100644 --- a/platform/MCU/STM32F2xx/SPARK_Firmware_Driver/inc/dct.h +++ b/platform/MCU/STM32F2xx/SPARK_Firmware_Driver/inc/dct.h @@ -74,7 +74,8 @@ typedef struct __attribute__((packed)) application_dct { uint8_t device_private_key[1216]; // sufficient for 2048 bits uint8_t device_public_key[384]; // sufficient for 2048 bits static_ip_config_t ip_config; - uint8_t unused[96]; + uint8_t unused[95]; // + uint8_t ota_update_flag; // should be 0xA5 to trigger an update from data stored in the update region uint32_t feature_flags[1]; // Configurable feature flags (see HAL_Feature_Set()). Default uninitialized value is 0xffffffff uint8_t country_code[4]; // WICED country code. Stored as bit-endian format: CH1/CH2/0/rev (max 255) uint8_t claim_code[63]; // claim code. no terminating null. @@ -111,6 +112,7 @@ typedef struct __attribute__((packed)) application_dct { #define DCT_SERVER_PUBLIC_KEY_OFFSET (offsetof(application_dct_t, server_public_key)) #define DCT_SERVER_ADDRESS_OFFSET ((DCT_SERVER_PUBLIC_KEY_OFFSET)+384) #define DCT_IP_CONFIG_OFFSET (offsetof(application_dct_t, ip_config)) +#define DCT_OTA_UPDATE_FLAG_OFFSET (offsetof(application_dct_t, ota_update_flag)) #define DCT_FEATURE_FLAGS_OFFSET (offsetof(application_dct_t, feature_flags)) #define DCT_COUNTRY_CODE_OFFSET (offsetof(application_dct_t, country_code)) #define DCT_CLAIM_CODE_OFFSET (offsetof(application_dct_t, claim_code)) @@ -138,6 +140,7 @@ typedef struct __attribute__((packed)) application_dct { #define DCT_DEVICE_PUBLIC_KEY_SIZE (sizeof(application_dct_t::device_public_key)) #define DCT_SERVER_PUBLIC_KEY_SIZE (sizeof(application_dct_t::server_public_key)) #define DCT_IP_CONFIG_SIZE (sizeof(application_dct_t::ip_config)) +#define DCT_OTA_UPDATE_FLAG_SIZE (sizeof(application_dct_t::ota_update_flag)) #define DCT_FEATURE_FLAGS_SIZE (sizeof(application_dct_t::feature_flags)) #define DCT_COUNTRY_CODE_SIZE (sizeof(application_dct_t::country_code)) #define DCT_CLAIM_CODE_SIZE (sizeof(application_dct_t::claim_code)) @@ -171,6 +174,7 @@ STATIC_ASSERT_DCT_OFFSET(version, 32); STATIC_ASSERT_DCT_OFFSET(device_private_key, 34); STATIC_ASSERT_DCT_OFFSET(device_public_key, 1250 /*34+1216*/); STATIC_ASSERT_DCT_OFFSET(ip_config, 1634 /* 1250 + 384 */); +STATIC_ASSERT_DCT_OFFSET(ota_update_flag, 1753); STATIC_ASSERT_DCT_OFFSET(feature_flags, 1754 /* 1634 + 120 */); STATIC_ASSERT_DCT_OFFSET(country_code, 1758 /* 1754 + 4 */); STATIC_ASSERT_DCT_OFFSET(claim_code, 1762 /* 1758 + 4 */); @@ -212,6 +216,10 @@ STATIC_ASSERT_FLAGS_OFFSET(FeaturesEnabled_SysFlag, 19); STATIC_ASSERT_FLAGS_OFFSET(RCC_CSR_SysFlag, 20); STATIC_ASSERT_FLAGS_OFFSET(reserved, 24); +#define DCT_OTA_UPDATE_FLAG_SET (0xA5) +#define DCT_OTA_UPDATE_FLAG_CLEAR (0XFF) + + // Note: This function is deprecated, use dct_read_app_data_copy() or dct_read_app_data_lock() instead const void* dct_read_app_data(uint32_t offset); diff --git a/platform/MCU/nRF52840/inc/dct.h b/platform/MCU/nRF52840/inc/dct.h index 9badb49c45..78a537669f 100644 --- a/platform/MCU/nRF52840/inc/dct.h +++ b/platform/MCU/nRF52840/inc/dct.h @@ -77,7 +77,8 @@ typedef struct __attribute__((packed)) application_dct { uint8_t device_private_key[1216]; // sufficient for 2048 bits uint8_t device_public_key[384]; // sufficient for 2048 bits static_ip_config_t ip_config; - uint8_t unused[96]; + uint8_t unused[95]; + uint8_t ota_update_flag; // should be 0xA5 to trigger an update from data stored in the update region uint32_t feature_flags[1]; // Configurable feature flags (see HAL_Feature_Set()). Default uninitialized value is 0xffffffff uint8_t country_code[4]; // WICED country code. Stored as bit-endian format: CH1/CH2/0/rev (max 255) uint8_t claim_code[63]; // claim code. no terminating null. @@ -118,6 +119,7 @@ typedef struct __attribute__((packed)) application_dct { #define DCT_SERVER_PUBLIC_KEY_OFFSET (offsetof(application_dct_t, server_public_key)) #define DCT_SERVER_ADDRESS_OFFSET ((DCT_SERVER_PUBLIC_KEY_OFFSET)+384) #define DCT_IP_CONFIG_OFFSET (offsetof(application_dct_t, ip_config)) +#define DCT_OTA_UPDATE_FLAG_OFFSET (offsetof(application_dct_t, ota_update_flag)) #define DCT_FEATURE_FLAGS_OFFSET (offsetof(application_dct_t, feature_flags)) #define DCT_COUNTRY_CODE_OFFSET (offsetof(application_dct_t, country_code)) #define DCT_CLAIM_CODE_OFFSET (offsetof(application_dct_t, claim_code)) @@ -149,6 +151,7 @@ typedef struct __attribute__((packed)) application_dct { #define DCT_DEVICE_PUBLIC_KEY_SIZE (sizeof(application_dct_t::device_public_key)) #define DCT_SERVER_PUBLIC_KEY_SIZE (sizeof(application_dct_t::server_public_key)) #define DCT_IP_CONFIG_SIZE (sizeof(application_dct_t::ip_config)) +#define DCT_OTA_UPDATE_FLAG_SIZE (sizeof(application_dct_t::ota_update_flag)) #define DCT_FEATURE_FLAGS_SIZE (sizeof(application_dct_t::feature_flags)) #define DCT_COUNTRY_CODE_SIZE (sizeof(application_dct_t::country_code)) #define DCT_CLAIM_CODE_SIZE (sizeof(application_dct_t::claim_code)) @@ -178,6 +181,9 @@ typedef struct __attribute__((packed)) application_dct { #define STATIC_ASSERT_DCT_OFFSET(field, expected) PARTICLE_STATIC_ASSERT( dct_##field, offsetof(application_dct_t, field)==expected) #define STATIC_ASSERT_FLAGS_OFFSET(field, expected) PARTICLE_STATIC_ASSERT( dct_sysflag_##field, offsetof(platform_system_flags_t, field)==expected) +#define DCT_OTA_UPDATE_FLAG_SET (0xA5) +#define DCT_OTA_UPDATE_FLAG_CLEAR (0XFF) + /** * Assert offsets. These ensure that the layout in flash isn't inadvertently changed. */ @@ -186,6 +192,7 @@ STATIC_ASSERT_DCT_OFFSET(version, 32); STATIC_ASSERT_DCT_OFFSET(device_private_key, 34); STATIC_ASSERT_DCT_OFFSET(device_public_key, 1250 /*34+1216*/); STATIC_ASSERT_DCT_OFFSET(ip_config, 1634 /* 1250 + 384 */); +STATIC_ASSERT_DCT_OFFSET(ota_update_flag, 1753); STATIC_ASSERT_DCT_OFFSET(feature_flags, 1754 /* 1634 + 120 */); STATIC_ASSERT_DCT_OFFSET(country_code, 1758 /* 1754 + 4 */); STATIC_ASSERT_DCT_OFFSET(claim_code, 1762 /* 1758 + 4 */); diff --git a/system/src/main.cpp b/system/src/main.cpp index a6382e4abe..d1d505947c 100644 --- a/system/src/main.cpp +++ b/system/src/main.cpp @@ -767,7 +767,13 @@ void app_setup_and_loop(void) bool threaded = system_thread_get_state(NULL) != spark::feature::DISABLED && (system_mode()!=SAFE_MODE); - Network_Setup(threaded); + if (HAL_FLASH_ApplyPendingUpdate(nullptr /*module*/, false /*dryRun*/, nullptr /*reserved*/)==HAL_UPDATE_APPLIED_PENDING_RESTART) { + // the regular OTA update delays 100 milliseconds so maintaining the same behavior. + HAL_Delay_Milliseconds(100); + HAL_Core_System_Reset_Ex(RESET_REASON_UPDATE, 0, nullptr); + } + + Network_Setup(threaded); // todo - why does this come before system thread initialization? #if PLATFORM_THREADING if (threaded) diff --git a/system/src/system_update.cpp b/system/src/system_update.cpp index cf41615ac5..63dc14f6aa 100644 --- a/system/src/system_update.cpp +++ b/system/src/system_update.cpp @@ -382,8 +382,8 @@ int Spark_Finish_Firmware_Update(FileTransfer::Descriptor& file, uint32_t flags, if (file.store==FileTransfer::Store::FIRMWARE) { hal_update_complete_t result = HAL_FLASH_End(module ? (hal_module_t*)module : &mod); - system_notify_event(firmware_update, result!=HAL_UPDATE_ERROR ? firmware_update_complete : firmware_update_failed, &file); - res = (result == HAL_UPDATE_ERROR); + system_notify_event(firmware_update, result<=HAL_UPDATE_ERROR ? firmware_update_complete : firmware_update_failed, &file); + res = (result <= HAL_UPDATE_ERROR); // always restart for now if ((true || result==HAL_UPDATE_APPLIED_PENDING_RESTART) && !(flags & UpdateFlag::DONT_RESET))