File syn_ota.c¶
File List > src > syntropic > system > syn_ota.c
Go to the documentation of this file
#if __has_include("syn_config.h")
#include "syn_config.h"
#endif
#if !defined(SYN_USE_OTA) || SYN_USE_OTA
#include "../util/syn_assert.h"
#include "syn_ota.h"
#include <string.h>
/* ── LwM2M Object 5 Forward Types (if bound) ─────────────────────────────── */
typedef enum {
LWM2M_FW_STATE_IDLE = 0,
LWM2M_FW_STATE_DOWNLOADING = 1,
LWM2M_FW_STATE_DOWNLOADED = 2,
LWM2M_FW_STATE_UPDATING = 3,
} LwM2M_FwState;
typedef enum {
LWM2M_FW_RES_DEFAULT = 0,
LWM2M_FW_RES_SUCCESS = 1,
LWM2M_FW_RES_NO_FLASH = 2,
LWM2M_FW_RES_OUT_OF_RAM = 3,
LWM2M_FW_RES_CONN_LOST = 4,
LWM2M_FW_RES_INTEGRITY_FAIL = 5,
LWM2M_FW_RES_BAD_PKG_TYPE = 6,
LWM2M_FW_RES_INVALID_URI = 7,
} LwM2M_FwResult;
typedef struct {
LwM2M_FwState state;
LwM2M_FwResult result;
char package_uri[128];
char pkg_name[32];
char pkg_version[32];
bool update_requested;
} LwM2M_FwCtx;
/* ── API Implementation ─────────────────────────────────────────────────── */
SYN_Status syn_ota_init(SYN_OTA_Manager *mgr, SYN_FwBootManager *boot_mgr, uint32_t slot_size,
uint8_t *page_buf, size_t page_buf_sz)
{
if (mgr == NULL || boot_mgr == NULL || page_buf == NULL || page_buf_sz < 16U ||
slot_size <= sizeof(SYN_FwImageHeader)) {
return SYN_INVALID_PARAM;
}
(void)memset(mgr, 0, sizeof(*mgr));
mgr->boot_mgr = boot_mgr;
mgr->slot_size = slot_size;
mgr->page_buf = page_buf;
mgr->page_buf_sz = page_buf_sz;
mgr->target_slot = SYN_OTA_SLOT_AUTO;
mgr->state = SYN_OTA_STATE_IDLE;
mgr->last_error = SYN_OTA_ERR_NONE;
mgr->crypto_mode = SYN_OTA_CRYPTO_NONE;
return SYN_OK;
}
SYN_Status syn_ota_set_target_slot(SYN_OTA_Manager *mgr, uint8_t slot_idx)
{
if (mgr == NULL) {
return SYN_INVALID_PARAM;
}
if (mgr->state != SYN_OTA_STATE_IDLE && mgr->state != SYN_OTA_STATE_ERROR) {
return SYN_ERROR;
}
if (slot_idx != SYN_FW_SLOT_A && slot_idx != SYN_FW_SLOT_B && slot_idx != SYN_OTA_SLOT_AUTO) {
return SYN_INVALID_PARAM;
}
mgr->target_slot = slot_idx;
return SYN_OK;
}
SYN_Status syn_ota_set_verification_key(SYN_OTA_Manager *mgr, SYN_OTA_CryptoMode mode,
const uint8_t *key, size_t key_len)
{
if (mgr == NULL) {
return SYN_INVALID_PARAM;
}
if (mgr->state != SYN_OTA_STATE_IDLE && mgr->state != SYN_OTA_STATE_ERROR) {
return SYN_ERROR;
}
switch (mode) {
case SYN_OTA_CRYPTO_NONE:
mgr->crypto_mode = SYN_OTA_CRYPTO_NONE;
return SYN_OK;
case SYN_OTA_CRYPTO_HMAC_SHA256:
#if defined(SYN_FW_USE_HMAC) && SYN_FW_USE_HMAC
if (key == NULL || key_len == 0U || key_len > sizeof(mgr->hmac_key)) {
return SYN_INVALID_PARAM;
}
(void)memcpy(mgr->hmac_key, key, key_len);
mgr->hmac_key_len = key_len;
mgr->hmac_key_set = true;
mgr->crypto_mode = SYN_OTA_CRYPTO_HMAC_SHA256;
return SYN_OK;
#else
mgr->last_error = SYN_OTA_ERR_UNSUPPORTED_CRYPTO;
return SYN_ERROR;
#endif
case SYN_OTA_CRYPTO_ED25519:
#if defined(SYN_FW_USE_ED25519) && SYN_FW_USE_ED25519
if (key == NULL || key_len != 32U) {
return SYN_INVALID_PARAM;
}
(void)memcpy(mgr->ed25519_pubkey, key, 32U);
mgr->ed25519_key_set = true;
mgr->crypto_mode = SYN_OTA_CRYPTO_ED25519;
return SYN_OK;
#else
mgr->last_error = SYN_OTA_ERR_UNSUPPORTED_CRYPTO;
return SYN_ERROR;
#endif
default:
return SYN_INVALID_PARAM;
}
}
SYN_Status syn_ota_set_aes_gcm_params(SYN_OTA_Manager *mgr, const uint8_t *key, size_t key_len,
const uint8_t *iv, size_t iv_len)
{
if (mgr == NULL || key == NULL || iv == NULL) {
return SYN_INVALID_PARAM;
}
if (mgr->state != SYN_OTA_STATE_IDLE && mgr->state != SYN_OTA_STATE_ERROR) {
return SYN_ERROR;
}
#if defined(SYN_FW_USE_AES_GCM) && SYN_FW_USE_AES_GCM
if ((key_len != 16U && key_len != 24U && key_len != 32U) || iv_len != 12U) {
return SYN_INVALID_PARAM;
}
(void)memcpy(mgr->gcm_key, key, key_len);
mgr->gcm_key_len = key_len;
(void)memcpy(mgr->gcm_iv, iv, 12U);
mgr->gcm_key_set = true;
mgr->crypto_mode = SYN_OTA_CRYPTO_AES_GCM;
return SYN_OK;
#else
mgr->last_error = SYN_OTA_ERR_UNSUPPORTED_CRYPTO;
return SYN_ERROR;
#endif
}
SYN_Status syn_ota_begin(SYN_OTA_Manager *mgr, uint32_t expected_total_sz, uint32_t target_version,
uint32_t expected_crc)
{
if (mgr == NULL || mgr->boot_mgr == NULL) {
return SYN_INVALID_PARAM;
}
if (mgr->state != SYN_OTA_STATE_IDLE && mgr->state != SYN_OTA_STATE_ERROR) {
return SYN_ERROR;
}
if (expected_total_sz == 0U ||
(expected_total_sz + sizeof(SYN_FwImageHeader)) > mgr->slot_size) {
mgr->last_error = SYN_OTA_ERR_OUT_OF_SPACE;
return SYN_INVALID_PARAM;
}
/* Resolve destination flash slot */
uint8_t slot = mgr->target_slot;
if (slot == SYN_OTA_SLOT_AUTO) {
uint8_t active = mgr->boot_mgr->active_slot;
slot = (active == SYN_FW_SLOT_A) ? (uint8_t)SYN_FW_SLOT_B : (uint8_t)SYN_FW_SLOT_A;
}
if (slot > 1U) {
mgr->last_error = SYN_OTA_ERR_NO_FLASH_SLOT;
mgr->state = SYN_OTA_STATE_ERROR;
syn_ota_sync_lwm2m(mgr);
return SYN_ERROR;
}
mgr->target_slot_addr = mgr->boot_mgr->slot_addr[slot];
mgr->expected_total_sz = expected_total_sz;
mgr->target_version = target_version;
mgr->expected_crc = expected_crc;
uint32_t max_img_sz = mgr->slot_size - (uint32_t)sizeof(SYN_FwImageHeader);
SYN_Status st = syn_fwupdate_begin(&mgr->fw_upd, mgr->target_slot_addr, max_img_sz,
mgr->page_buf, (uint16_t)mgr->page_buf_sz);
if (st != SYN_OK) {
mgr->last_error = SYN_OTA_ERR_FLASH_ERASE;
mgr->state = SYN_OTA_STATE_ERROR;
syn_ota_sync_lwm2m(mgr);
return st;
}
/* Configure cryptographic verification hooks */
#if defined(SYN_FW_USE_HMAC) && SYN_FW_USE_HMAC
if (mgr->crypto_mode == SYN_OTA_CRYPTO_HMAC_SHA256 && mgr->hmac_key_set) {
syn_fwupdate_set_key(&mgr->fw_upd, mgr->hmac_key, mgr->hmac_key_len);
}
#endif
#if defined(SYN_FW_USE_ED25519) && SYN_FW_USE_ED25519
if (mgr->crypto_mode == SYN_OTA_CRYPTO_ED25519 && mgr->ed25519_key_set) {
syn_fwupdate_set_public_key(&mgr->fw_upd, mgr->ed25519_pubkey);
}
#endif
#if defined(SYN_FW_USE_AES_GCM) && SYN_FW_USE_AES_GCM
if (mgr->crypto_mode == SYN_OTA_CRYPTO_AES_GCM && mgr->gcm_key_set) {
(void)syn_fwupdate_set_aes_gcm_key(&mgr->fw_upd, mgr->gcm_key, mgr->gcm_key_len,
mgr->gcm_iv, sizeof(mgr->gcm_iv));
}
#endif
mgr->state = SYN_OTA_STATE_DOWNLOADING;
mgr->last_error = SYN_OTA_ERR_NONE;
syn_ota_sync_lwm2m(mgr);
return SYN_OK;
}
static inline uint32_t syn_ota_bytes_ingested(const SYN_OTA_Manager *mgr)
{
return mgr->fw_upd.bytes_written + (uint32_t)mgr->fw_upd.page_buf_used;
}
SYN_Status syn_ota_write_chunk(SYN_OTA_Manager *mgr, const uint8_t *chunk, size_t chunk_sz)
{
if (mgr == NULL) {
return SYN_INVALID_PARAM;
}
if (chunk_sz == 0U) {
return SYN_OK;
}
if (mgr->state != SYN_OTA_STATE_DOWNLOADING) {
return SYN_ERROR;
}
if (chunk == NULL) {
return SYN_INVALID_PARAM;
}
if ((syn_ota_bytes_ingested(mgr) + chunk_sz) > mgr->expected_total_sz) {
syn_ota_abort(mgr, SYN_OTA_ERR_OUT_OF_SPACE);
return SYN_ERROR;
}
SYN_Status st = syn_fwupdate_write(&mgr->fw_upd, chunk, chunk_sz);
if (st != SYN_OK) {
syn_ota_abort(mgr, SYN_OTA_ERR_FLASH_WRITE);
return st;
}
if (syn_ota_bytes_ingested(mgr) == mgr->expected_total_sz) {
mgr->state = SYN_OTA_STATE_DOWNLOADED;
}
syn_ota_sync_lwm2m(mgr);
return SYN_OK;
}
SYN_Status syn_ota_finish(SYN_OTA_Manager *mgr, const uint8_t *expected_sig_or_tag, size_t sig_len)
{
if (mgr == NULL) {
return SYN_INVALID_PARAM;
}
if (mgr->state != SYN_OTA_STATE_DOWNLOADING && mgr->state != SYN_OTA_STATE_DOWNLOADED) {
return SYN_ERROR;
}
#if defined(SYN_FW_USE_AES_GCM) && SYN_FW_USE_AES_GCM
if (mgr->crypto_mode == SYN_OTA_CRYPTO_AES_GCM) {
if (expected_sig_or_tag == NULL || sig_len != 16U) {
syn_ota_abort(mgr, SYN_OTA_ERR_INVALID_PARAM);
return SYN_INVALID_PARAM;
}
}
#endif
if (syn_ota_bytes_ingested(mgr) != mgr->expected_total_sz) {
syn_ota_abort(mgr, SYN_OTA_ERR_INTEGRITY_CHECK);
return SYN_ERROR;
}
mgr->state = SYN_OTA_STATE_VERIFYING;
SYN_Status st = SYN_ERROR;
#if defined(SYN_FW_USE_AES_GCM) && SYN_FW_USE_AES_GCM
if (mgr->crypto_mode == SYN_OTA_CRYPTO_AES_GCM) {
st = syn_fwupdate_finish_gcm(&mgr->fw_upd, expected_sig_or_tag, mgr->target_version);
} else
#endif
{
(void)sig_len;
#if defined(SYN_FW_USE_HMAC) && SYN_FW_USE_HMAC
const uint8_t *hmac_ptr =
(mgr->crypto_mode == SYN_OTA_CRYPTO_HMAC_SHA256) ? expected_sig_or_tag : NULL;
#endif
#if defined(SYN_FW_USE_ED25519) && SYN_FW_USE_ED25519
const uint8_t *sig_ptr =
(mgr->crypto_mode == SYN_OTA_CRYPTO_ED25519) ? expected_sig_or_tag : NULL;
#endif
st = syn_fwupdate_finish(&mgr->fw_upd, mgr->expected_crc,
#if defined(SYN_FW_USE_HMAC) && SYN_FW_USE_HMAC
hmac_ptr,
#endif
#if defined(SYN_FW_USE_ED25519) && SYN_FW_USE_ED25519
sig_ptr,
#endif
mgr->target_version);
}
if (st != SYN_OK) {
syn_ota_abort(mgr, SYN_OTA_ERR_INTEGRITY_CHECK);
return st;
}
(void)syn_fwboot_refresh(mgr->boot_mgr);
mgr->state = SYN_OTA_STATE_READY_TO_APPLY;
mgr->last_error = SYN_OTA_ERR_NONE;
syn_ota_sync_lwm2m(mgr);
return SYN_OK;
}
SYN_Status syn_ota_apply(SYN_OTA_Manager *mgr)
{
if (mgr == NULL) {
return SYN_INVALID_PARAM;
}
if (mgr->state != SYN_OTA_STATE_READY_TO_APPLY) {
return SYN_ERROR;
}
mgr->state = SYN_OTA_STATE_APPLIED;
syn_ota_sync_lwm2m(mgr);
return SYN_OK;
}
void syn_ota_abort(SYN_OTA_Manager *mgr, SYN_OTA_ErrorCode err)
{
if (mgr == NULL) {
return;
}
if (mgr->fw_upd.active) {
syn_fwupdate_abort(&mgr->fw_upd);
}
mgr->state = SYN_OTA_STATE_ERROR;
mgr->last_error = err;
syn_ota_sync_lwm2m(mgr);
}
SYN_Status syn_ota_get_progress(const SYN_OTA_Manager *mgr, uint32_t *out_written,
uint32_t *out_total, uint8_t *out_percent)
{
if (mgr == NULL) {
return SYN_INVALID_PARAM;
}
uint32_t written = syn_ota_bytes_ingested(mgr);
uint32_t total = mgr->expected_total_sz;
if (out_written != NULL) {
*out_written = written;
}
if (out_total != NULL) {
*out_total = total;
}
if (out_percent != NULL) {
if (total == 0U) {
*out_percent = 0U;
} else {
uint64_t pct = ((uint64_t)written * 100ULL) / (uint64_t)total;
if (pct > 100ULL) {
pct = 100ULL;
}
*out_percent = (uint8_t)pct;
}
}
return SYN_OK;
}
SYN_OTA_State syn_ota_get_state(const SYN_OTA_Manager *mgr)
{
if (mgr == NULL) {
return SYN_OTA_STATE_ERROR;
}
return mgr->state;
}
SYN_OTA_ErrorCode syn_ota_get_last_error(const SYN_OTA_Manager *mgr)
{
if (mgr == NULL) {
return SYN_OTA_ERR_INVALID_PARAM;
}
return mgr->last_error;
}
SYN_Status syn_ota_bind_lwm2m(SYN_OTA_Manager *mgr, void *lwm2m_fw_ctx)
{
if (mgr == NULL) {
return SYN_INVALID_PARAM;
}
mgr->lwm2m_fw_ctx = lwm2m_fw_ctx;
syn_ota_sync_lwm2m(mgr);
return SYN_OK;
}
void syn_ota_sync_lwm2m(SYN_OTA_Manager *mgr)
{
if (mgr == NULL || mgr->lwm2m_fw_ctx == NULL) {
return;
}
LwM2M_FwCtx *ctx = (LwM2M_FwCtx *)mgr->lwm2m_fw_ctx;
switch (mgr->state) {
case SYN_OTA_STATE_IDLE:
ctx->state = LWM2M_FW_STATE_IDLE;
break;
case SYN_OTA_STATE_DOWNLOADING:
ctx->state = LWM2M_FW_STATE_DOWNLOADING;
break;
case SYN_OTA_STATE_DOWNLOADED:
case SYN_OTA_STATE_VERIFYING:
case SYN_OTA_STATE_READY_TO_APPLY:
ctx->state = LWM2M_FW_STATE_DOWNLOADED;
break;
case SYN_OTA_STATE_APPLIED:
ctx->state = LWM2M_FW_STATE_UPDATING;
ctx->result = LWM2M_FW_RES_SUCCESS;
break;
case SYN_OTA_STATE_ERROR:
default:
ctx->state = LWM2M_FW_STATE_IDLE;
break;
}
switch (mgr->last_error) {
case SYN_OTA_ERR_NONE:
if (mgr->state == SYN_OTA_STATE_APPLIED) {
ctx->result = LWM2M_FW_RES_SUCCESS;
} else {
ctx->result = LWM2M_FW_RES_DEFAULT;
}
break;
case SYN_OTA_ERR_OUT_OF_SPACE:
case SYN_OTA_ERR_FLASH_ERASE:
case SYN_OTA_ERR_FLASH_WRITE:
ctx->result = LWM2M_FW_RES_NO_FLASH;
break;
case SYN_OTA_ERR_INTEGRITY_CHECK:
ctx->result = LWM2M_FW_RES_INTEGRITY_FAIL;
break;
case SYN_OTA_ERR_UNSUPPORTED_CRYPTO:
case SYN_OTA_ERR_INVALID_PARAM:
ctx->result = LWM2M_FW_RES_BAD_PKG_TYPE;
break;
default:
ctx->result = LWM2M_FW_RES_DEFAULT;
break;
}
}
#else
typedef int syn_ota_dummy_t;
#endif /* SYN_USE_OTA */