Skip to content

Commit

Permalink
fw_update: refactor fw_update_validate to fw_update_check_candidate
Browse files Browse the repository at this point in the history
Update fw_update_validate to fw_update_check_candidate and pass an image
hash and size such that image validateion is not coupled to any state
maintained in ports. Ports should be able to validate an image in a
candidate slot without needing to have downloaded since last reboot.

Make a corresponding update in fw_update thread to check whether the
requested firmware update image is already present in candidate slot. If
so, download is skipped and we attempt to reboot into existing candidate
slot image.

Note that the call to fw_update_validate is now removed from the post
download flow and not replaced in the same call site by
fw_update_check_image. fw_update_validate was previously a no-op for most
platform ports, and for those that did implement checks (esp_idf), the
functionality was moved to fw_update_post_download, which is still called
following an image download.

Signed-off-by: Daniel Mangum <[email protected]>
  • Loading branch information
hasheddan committed Feb 11, 2025
1 parent 2764e8c commit b0de623
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 49 deletions.
12 changes: 7 additions & 5 deletions include/golioth/fw_update.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,17 @@ enum golioth_status fw_update_handle_block(const uint8_t *block,
/// Can be used by backend ports to do any final work, if needed.
enum golioth_status fw_update_post_download(void);

/// Validate new image after downloading
/// Check if candidate image matches target.
///
/// Called by golioth_fw_update.c after downloading the full image.
/// Can be used by backend ports to validate the image before
/// attempting to boot into it.
/// Called by golioth_fw_update.c prior to downloading a new image
/// to check whether it is already present in candidate slot.
///
/// @param hash Image SHA256 hash of size GOLIOTH_OTA_COMPONENT_BIN_HASH_LEN
/// @param img_size Image size, in bytes
///
/// @return GOLIOTH_OK - image validated
/// @return Otherwise - error in validation, abort firmware update
enum golioth_status fw_update_validate(void);
enum golioth_status fw_update_check_candidate(const uint8_t *hash, size_t img_size);

/// Switch to the new boot image. This will cause the new image
/// to be booted next time.
Expand Down
5 changes: 3 additions & 2 deletions port/esp_idf/fw_update_esp_idf.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,10 @@ enum golioth_status fw_update_post_download(void)
return GOLIOTH_OK;
}

enum golioth_status fw_update_validate(void)
enum golioth_status fw_update_check_candidate(const uint8_t *hash, size_t img_size)
{
return GOLIOTH_OK;
// TODO(hasheddan): support checking candidate firmware image.
return GOLIOTH_ERR_NOT_IMPLEMENTED;
}

enum golioth_status fw_update_change_boot_image(void)
Expand Down
5 changes: 3 additions & 2 deletions port/linux/fw_update_linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,10 @@ enum golioth_status fw_update_post_download(void)
return GOLIOTH_OK;
}

enum golioth_status fw_update_validate(void)
enum golioth_status fw_update_check_candidate(const uint8_t *hash, size_t img_size)
{
return GOLIOTH_OK;
// TODO(hasheddan): support checking candidate firmware image.
return GOLIOTH_ERR_NOT_IMPLEMENTED;
}

enum golioth_status fw_update_change_boot_image(void)
Expand Down
6 changes: 3 additions & 3 deletions port/modus_toolbox/fw_update_mcuboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,10 @@ enum golioth_status fw_update_post_download(void)
return GOLIOTH_OK;
}

enum golioth_status fw_update_validate(void)
enum golioth_status fw_update_check_candidate(const uint8_t *hash, size_t img_size)
{
// Nothing to do
return GOLIOTH_OK;
// TODO(hasheddan): support checking candidate firmware image.
return GOLIOTH_ERR_NOT_IMPLEMENTED;
}

enum golioth_status fw_update_change_boot_image(void)
Expand Down
9 changes: 7 additions & 2 deletions port/zephyr/golioth_fw_zephyr.c
Original file line number Diff line number Diff line change
Expand Up @@ -240,9 +240,14 @@ enum golioth_status fw_update_post_download(void)
return GOLIOTH_OK;
}

enum golioth_status fw_update_validate(void)
/*
* @note This is similar to Zephyr's flash_img_check() but uses the Golioth
* port's sha256 API.
*/
enum golioth_status fw_update_check_candidate(const uint8_t *hash, size_t img_size)
{
return GOLIOTH_OK;
// TODO(hasheddan): support checking candidate firmware image.
return GOLIOTH_ERR_NOT_IMPLEMENTED;
}

enum golioth_status fw_update_change_boot_image(void)
Expand Down
80 changes: 45 additions & 35 deletions src/fw_update.c
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,35 @@ static void fw_download_failed(enum golioth_ota_reason reason)
| FW_REPORT_TARGET_VERSION);
}

static enum golioth_status fw_change_image_and_reboot()
{
GLTH_LOGI(TAG, "State = Updating");
golioth_fw_update_report_state_sync(&_component_ctx,
GOLIOTH_OTA_STATE_UPDATING,
GOLIOTH_OTA_REASON_READY,
FW_REPORT_COMPONENT_NAME | FW_REPORT_CURRENT_VERSION
| FW_REPORT_TARGET_VERSION);
enum golioth_status status = fw_update_change_boot_image();
if (status != GOLIOTH_OK)
{
GLTH_LOGE(TAG, "Firmware change boot image failed");
return status;
}

int countdown = 5;
while (countdown > 0)
{
GLTH_LOGI(TAG, "Rebooting into new image in %d seconds", countdown);
golioth_sys_msleep(1000);
countdown--;
}
fw_update_reboot();

// Should never be reached.
return GOLIOTH_OK;
}


static void fw_update_thread(void *arg)
{
// If it's the first time booting a new OTA image,
Expand Down Expand Up @@ -420,6 +449,19 @@ static void fw_update_thread(void *arg)
continue;
}

// clang-format off
if (fw_update_check_candidate(_component_ctx.target_component.hash,
_component_ctx.target_component.size) == GOLIOTH_OK)
// clang-format on
{
GLTH_LOGI(TAG, "Target component already downloaded. Attempting to update.");
if (fw_change_image_and_reboot() != GOLIOTH_OK)
{
GLTH_LOGE(TAG,
"Failed to reboot into new image. Attempting to download again.");
}
}

break;
}

Expand Down Expand Up @@ -561,53 +603,21 @@ static void fw_update_thread(void *arg)
download_ctx.bytes_downloaded,
golioth_sys_now_ms() - start_time_ms);

if (fw_update_validate() != GOLIOTH_OK)
{
GLTH_LOGE(TAG, "Firmware validate failed");
fw_update_end();

GLTH_LOGI(TAG, "State = Idle");
golioth_fw_update_report_state_sync(&_component_ctx,
GOLIOTH_OTA_STATE_IDLE,
GOLIOTH_OTA_REASON_INTEGRITY_CHECK_FAILURE,
FW_REPORT_COMPONENT_NAME | FW_REPORT_CURRENT_VERSION
| FW_REPORT_TARGET_VERSION);

continue;
}

GLTH_LOGI(TAG, "State = Downloaded");
golioth_fw_update_report_state_sync(&_component_ctx,
GOLIOTH_OTA_STATE_DOWNLOADED,
GOLIOTH_OTA_REASON_READY,
FW_REPORT_COMPONENT_NAME | FW_REPORT_CURRENT_VERSION
| FW_REPORT_TARGET_VERSION);

GLTH_LOGI(TAG, "State = Updating");
golioth_fw_update_report_state_sync(&_component_ctx,
GOLIOTH_OTA_STATE_UPDATING,
GOLIOTH_OTA_REASON_READY,
FW_REPORT_COMPONENT_NAME | FW_REPORT_CURRENT_VERSION
| FW_REPORT_TARGET_VERSION);

if (fw_update_change_boot_image() != GOLIOTH_OK)
{
GLTH_LOGE(TAG, "Firmware change boot image failed");
fw_update_end();
continue;
}

/* Download successful. Reset backoff */
backoff_reset(&_component_ctx);

int countdown = 5;
while (countdown > 0)
if (fw_change_image_and_reboot() != GOLIOTH_OK)
{
GLTH_LOGI(TAG, "Rebooting into new image in %d seconds", countdown);
golioth_sys_msleep(1000);
countdown--;
GLTH_LOGE(TAG, "Failed to reboot into new image.");
fw_update_end();
}
fw_update_reboot();
}
}

Expand Down

0 comments on commit b0de623

Please sign in to comment.