|
ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
|
Phase-5 OTA firmware-update orchestration for the RA8D2. More...
Go to the source code of this file.
Data Structures | |
| struct | ra8_ota_manifest_t |
| Decoded representation of the server manifest. More... | |
| struct | ra8_ota_progress_t |
| Snapshot delivered to the caller's progress callback. More... | |
| struct | ra8_ota_net_iface_t |
| Injected HTTPS-download interface (Dependency Inversion). More... | |
| struct | ra8_ota_crypto_iface_t |
| Injected hash + ECDSA verification interface. More... | |
| struct | ra8_ota_flash_iface_t |
| Injected flash backend (so tests do not need real MRAM). More... | |
| struct | ra8_ota_cfg_t |
| Initialisation descriptor for ra8_ota_init. More... | |
Typedefs | |
| typedef void(* | ra8_ota_progress_cb_t) (const ra8_ota_progress_t *p) |
| Caller progress callback type. | |
Enumerations | |
| enum | ra8_ota_constants_t : uint32_t { k_ra8_ota_chunk_bytes = 4096U , k_ra8_ota_manifest_max_bytes = 2048U , k_ra8_ota_sha256_bytes = 32U , k_ra8_ota_signature_max_bytes = 96U , k_ra8_ota_url_max_bytes = 256U , k_ra8_ota_version_str_bytes = 32U , k_ra8_ota_max_image_bytes = 0x80000U , k_ra8_ota_thread_stack_bytes = 4096U } |
| Compile-time bounds for the OTA module. More... | |
| enum | ra8_ota_state_t : uint8_t { k_ra8_ota_state_idle = 0U , k_ra8_ota_state_checking = 1U , k_ra8_ota_state_downloading = 2U , k_ra8_ota_state_verifying = 3U , k_ra8_ota_state_committing = 4U , k_ra8_ota_state_done = 5U , k_ra8_ota_state_error = 6U , k_ra8_ota_state_count = 7U } |
| Cooperative state-machine states. More... | |
Functions | |
| ra8_err_t | ra8_ota_init (const ra8_ota_cfg_t *cfg) |
| Initialise the OTA module. | |
| ra8_err_t | ra8_ota_deinit (void) |
| Tear the OTA module down (mostly for tests / re-init). | |
| ra8_err_t | ra8_ota_check_for_update (ra8_ota_manifest_t *out_manifest) |
| Fetch and decode the manifest JSON over HTTPS. | |
| ra8_err_t | ra8_ota_download_to_inactive_bank (const ra8_ota_manifest_t *manifest) |
| Stream the firmware blob into the inactive MRAM bank. | |
| ra8_err_t | ra8_ota_verify_signature (const ra8_ota_manifest_t *manifest) |
| Verify SHA-256 + ECDSA over the freshly programmed bank. | |
| ra8_err_t | ra8_ota_commit_and_reboot (void) |
| Persist the bank swap and reboot. | |
| ra8_ota_state_t | ra8_ota_get_state (void) |
| Return the current state-machine value. | |
| ra8_err_t | ra8_ota_run_step (void) |
| Drive the OTA state machine one step forward. | |
| ra8_err_t | ra8_ota_run_full_update (void) |
| Kick off an end-to-end update from idle. | |
| void | ra8_ota_system_reset_hook (void) |
| Test/host hook for commit_and_reboot. | |
Phase-5 OTA firmware-update orchestration for the RA8D2.
The RA8D2 has 1 MiB of code-MRAM split into two equal-size banks (HUM Ch 7.2 "BTFLG Boot-Area Swap" p 282..286 + HUM Ch 59.5 "MSUACR / MSUASMON" p 3582..3586). At any moment the part is executing out of one bank ("active"); the other bank ("inactive") is free to be erased and re-programmed. ra8_ota orchestrates the safe-update flow on top of the existing ra8_flash driver:
Each public entry point is one transition arrow. ra8_ota_run_step drives the machine cooperatively from a caller's main loop; cfg.run_as_thread = true instead spawns a single static ThreadX thread that calls ra8_ota_run_step until DONE / ERROR.
The HTTPS download and ECDSA verification are exposed through function-pointer interfaces (ra8_ota_net_iface_t, ra8_ota_crypto_iface_t) so the module compiles independently of ra8_tls / mbedtls / tf-psa-crypto and can be unit-tested with mocks. This is the Dependency-Inversion deviation called out in CLAUDE.md "NASA Rule 9 -- INTENTIONAL DEVIATION".
Every buffer is a file-static in ra8_ota.c. There is zero dynamic allocation anywhere in this module (NASA Rule 3).
Definition in file ra8_ota.h.
| typedef void(* ra8_ota_progress_cb_t) (const ra8_ota_progress_t *p) |
Caller progress callback type.
| [in] | p | Snapshot of OTA progress (state, bytes, last err). |
| enum ra8_ota_constants_t : uint32_t |
Compile-time bounds for the OTA module.
Centralises every numeric limit so callers and unit tests can reference them by name. All values are typed (NASA Rule 8 + CLAUDE.md "C23 typed enums").
| enum ra8_ota_state_t : uint8_t |
Cooperative state-machine states.
Returned by ra8_ota_get_state. Values are stable across calls so callers may compare with == or use them as table indices.
|
nodiscard |
Fetch and decode the manifest JSON over HTTPS.
Drives cfg.net.open / read / close against the manifest URL, runs a small JSON parser to extract version, image URL, image size, expected SHA-256 and ECDSA signature, and returns the decoded struct in out_manifest. The bytes of the JSON itself are dropped after decoding – only the fixed-size struct is kept.
| [out] | out_manifest | Non-NULL destination. |
| k_ra8_ok | Manifest decoded. |
| k_ra8_err_null_ptr | out_manifest was NULL. |
| k_ra8_err_not_initialized | ra8_ota_init not called. |
| k_ra8_err_invalid_state | Module not in idle. |
| k_ra8_err_hw_error | Network backend reported an error. |
| k_ra8_err_invalid_size | Manifest exceeded k_ra8_ota_manifest_max_bytes. |
| k_ra8_err_invalid_arg | JSON malformed / missing field. |
Fetch and decode the manifest JSON over HTTPS.
Transitions the state machine idle -> checking -> idle (success) or idle -> checking -> error (failure). On success the manifest is mirrored both into *out_manifest and into the module-private s_manifest so subsequent steps can refer to it.
| [out] | out_manifest | Caller-owned manifest buffer. |
| k_ra8_ok | Manifest cached and returned. |
| k_ra8_err_not_initialized | Module not initialized. |
| k_ra8_err_null_ptr | out_manifest was NULL. |
| k_ra8_err_invalid_state | Module not in idle. |
| other | Network or decode error. |
Definition at line 338 of file ra8_ota.c.
References g_ra8_ota_buf, g_ra8_ota_initialized, g_ra8_ota_state, internal_fetch_manifest_payload(), k_ra8_err_invalid_state, k_ra8_err_not_initialized, k_ra8_ok, k_ra8_ota_state_checking, k_ra8_ota_state_error, k_ra8_ota_state_idle, memcpy(), priv_ota_manifest_decode(), priv_ota_set_state(), RA8_CHECK_NULL_PTR, s_manifest, s_manifest_valid, and s_tag.
Referenced by internal_step_dispatch().
|
nodiscard |
Persist the bank swap and reboot.
Calls cfg.flash.set_startup with the inactive-bank index and persistent = true (so BTFLG sticks across reset), sets the state to k_ra8_ota_state_done, and – in production – triggers NVIC_SystemReset. In the host test build the system-reset call is forwarded to a weak ra8_ota_system_reset_hook symbol so the test process does not actually exit.
| k_ra8_ok | Boot bank swapped (host build only). |
| k_ra8_err_not_initialized | Module not initialized. |
| k_ra8_err_invalid_state | Verification has not passed. |
| k_ra8_err_hw_error | set_startup failed. |
Persist the bank swap and reboot.
Calls g_ra8_ota_cfg.flash.set_startup to mark the inactive bank as the boot bank, then invokes ra8_ota_system_reset_hook (which on hardware overrides to NVIC_SystemReset and on host is a no-op for testability).
| k_ra8_ok | Bank latched (the call normally doesn't return on hardware). |
| k_ra8_err_not_initialized | Module not initialized. |
| k_ra8_err_invalid_state | Module not in committing. |
| other | Backend error from set_startup. |
Definition at line 603 of file ra8_ota.c.
References g_ra8_ota_cfg, g_ra8_ota_initialized, g_ra8_ota_state, k_ra8_err_invalid_state, k_ra8_err_not_initialized, k_ra8_ok, k_ra8_ota_state_committing, k_ra8_ota_state_done, k_ra8_ota_state_error, priv_ota_set_state(), and ra8_ota_system_reset_hook().
Referenced by app_run_attempt(), and internal_step_dispatch().
|
nodiscard |
Tear the OTA module down (mostly for tests / re-init).
Aborts an in-progress download, signals the worker thread to exit, and clears the module-static state. Safe to call from idle.
| k_ra8_ok | Module deinitialized. |
Tear the OTA module down (mostly for tests / re-init).
Clears the cached configuration, manifest, byte-counter and last error so a future ra8_ota_init starts from a clean slate.
| k_ra8_ok | Always succeeds. |
Definition at line 224 of file ra8_ota.c.
References g_ra8_ota_cfg, g_ra8_ota_initialized, g_ra8_ota_state, k_ra8_ok, k_ra8_ota_state_idle, memset(), s_bytes_done, s_last_err, and s_manifest_valid.
Referenced by app_run_attempt().
|
nodiscard |
Stream the firmware blob into the inactive MRAM bank.
Walks the image URL one k_ra8_ota_chunk_bytes chunk at a time. Each chunk is:
Writes are page-aligned (32-byte MRAM page). The function tracks a high-water byte offset so it can resume cleanly if the caller re-invokes it after a partial download.
| [in] | manifest | Non-NULL, validated manifest. |
| k_ra8_ok | All bytes programmed. |
| k_ra8_err_null_ptr | manifest was NULL. |
| k_ra8_err_not_initialized | Module not initialized. |
| k_ra8_err_invalid_state | Wrong state for download. |
| k_ra8_err_invalid_size | image_size > bank size. |
| k_ra8_err_hw_error | Flash or network backend failed. |
Stream the firmware blob into the inactive MRAM bank.
On a fresh start (s_bytes_done == 0) erases the bank and primes the SHA accumulator via internal_prepare_bank. Then opens the image URL and runs internal_download_loop. On success the state machine lands in verifying; on failure it lands in error.
| [in] | manifest | Manifest describing the image to fetch. |
| k_ra8_ok | Download complete; ready to verify. |
| k_ra8_err_not_initialized | Module not initialized. |
| k_ra8_err_null_ptr | manifest was NULL. |
| k_ra8_err_invalid_state | Module not in idle or downloading. |
| k_ra8_err_invalid_size | Image larger than the configured bank. |
| other | Network / crypto / flash error. |
Definition at line 534 of file ra8_ota.c.
References g_ra8_ota_cfg, g_ra8_ota_initialized, g_ra8_ota_state, ra8_ota_manifest_t::image_size_bytes, ra8_ota_manifest_t::image_url, internal_download_loop(), internal_prepare_bank(), k_ra8_err_invalid_size, k_ra8_err_invalid_state, k_ra8_err_not_initialized, k_ra8_ok, k_ra8_ota_state_downloading, k_ra8_ota_state_error, k_ra8_ota_state_idle, k_ra8_ota_state_verifying, priv_ota_download_state_invalid(), priv_ota_set_state(), RA8_CHECK_NULL_PTR, s_bytes_done, and s_tag.
Referenced by app_run_attempt(), and internal_step_dispatch().
| ra8_ota_state_t ra8_ota_get_state | ( | void | ) |
Return the current state-machine value.
Direct read of the latched state byte. s_state is a single uint8_t so a torn read is impossible on the target.
| k_ra8_ota_state_idle | Uninitialized, idle, or already done. |
| k_ra8_ota_state_checking | Manifest fetch in progress. |
| k_ra8_ota_state_downloading | Image download in progress. |
| k_ra8_ota_state_verifying | Verifying signature. |
| k_ra8_ota_state_committing | Committing bank swap. |
| k_ra8_ota_state_done | Update complete. |
| k_ra8_ota_state_error | Last step failed. |
Return the current state-machine value.
Reads the latched g_ra8_ota_state directly. g_ra8_ota_state is a single byte, so a torn read is impossible on the target.
| k_ra8_ota_state_idle | Module not initialized, or genuinely idle. |
| other | Whatever state the worker last latched. |
Definition at line 257 of file ra8_ota.c.
References g_ra8_ota_state.
Referenced by app_run_attempt().
|
nodiscard |
Initialise the OTA module.
Validates every field of cfg, copies it into module-static storage, optionally spawns the ThreadX worker, and parks the state machine in k_ra8_ota_state_idle.
| [in] | cfg | Non-NULL configuration descriptor. |
| k_ra8_ok | Module ready. |
| k_ra8_err_null_ptr | cfg was NULL or any required function pointer was NULL. |
| k_ra8_err_invalid_arg | URL string was empty or not NUL-terminated; bank size 0; etc. |
| k_ra8_err_invalid_state | Already initialized. |
Initialise the OTA module.
Verifies the module is in the un-initialized state, runs the full priv_ota_validate_cfg check on cfg, then captures the descriptor by-value into g_ra8_ota_cfg and resets the state machine to k_ra8_ota_state_idle.
| [in] | cfg | Configuration descriptor (function pointers + URLs). |
| k_ra8_ok | Module initialized. |
| k_ra8_err_invalid_state | Module already initialized. |
| k_ra8_err_null_ptr | cfg (or sub-pointer) was NULL. |
| k_ra8_err_invalid_arg | Configuration field out of range. |
Definition at line 182 of file ra8_ota.c.
References g_ra8_ota_cfg, g_ra8_ota_initialized, g_ra8_ota_state, k_ra8_err_invalid_state, k_ra8_ok, k_ra8_ota_state_idle, memcpy(), priv_ota_validate_cfg(), s_bytes_done, s_last_err, and s_manifest_valid.
Referenced by app_run_attempt().
|
nodiscard |
Kick off an end-to-end update from idle.
Sequences check_for_update -> download_to_inactive_bank -> verify_signature -> commit_and_reboot against the cached manifest. Used by the ThreadX worker thread; equally available to single-threaded callers that prefer one call over driving the state machine themselves.
Kick off an end-to-end update from idle.
Loops calling ra8_ota_run_step for at most k_ra8_ota_state_count iterations (NASA Rule 2 bound: idle -> checking -> downloading -> verifying -> committing -> done). Stops early on done or error.
| k_ra8_ok | Update completed (or already done). |
| k_ra8_err_not_initialized | Module not initialized. |
| other | Whatever the failing step returned. |
Definition at line 740 of file ra8_ota.c.
References g_ra8_ota_initialized, g_ra8_ota_state, k_ra8_err_not_initialized, k_ra8_ok, k_ra8_ota_state_count, k_ra8_ota_state_done, k_ra8_ota_state_error, ra8_ota_run_step(), and s_last_err.
|
nodiscard |
Drive the OTA state machine one step forward.
For callers that opted out of cfg.run_as_thread: invoke this from a main loop. Each call advances at most one transition. The caller may inspect ra8_ota_get_state between invocations.
| k_ra8_ok | Step completed. |
| k_ra8_err_not_initialized | Module not initialized. |
Thin wrapper over internal_step_dispatch that gates on g_ra8_ota_initialized. Intended for callers that opted out of running the OTA worker as a background thread.
| k_ra8_ok | Step completed. |
| k_ra8_err_not_initialized | Module not initialized. |
| other | Step-specific error. |
Definition at line 707 of file ra8_ota.c.
References g_ra8_ota_initialized, internal_step_dispatch(), and k_ra8_err_not_initialized.
Referenced by ra8_ota_run_full_update().
| void ra8_ota_system_reset_hook | ( | void | ) |
Test/host hook for commit_and_reboot.
Called instead of NVIC_SystemReset when the implementation detects it is running outside the ARM target (RA8_OFF_TARGET defined). Default implementation is a no-op. Tests override to count invocations.
Test/host hook for commit_and_reboot.
Called from ra8_ota_commit_and_reboot after the bank-swap is latched. The hardware build overrides this with a definition that calls NVIC_SystemReset. The host (unit-test) build keeps the weak no-op default so tests can observe post-commit state without actually exiting the process.
Definition at line 791 of file ra8_ota.c.
Referenced by ra8_ota_commit_and_reboot().
|
nodiscard |
Verify SHA-256 + ECDSA over the freshly programmed bank.
Re-reads the inactive bank chunk-by-chunk, computes the SHA-256, and compares it against manifest->image_sha256. On a match it binds the manifest metadata (version / image_url / image_size_bytes) into the signed material – the ECDSA verify runs over SHA-256 of the version, URL, size and image digest concatenated, not over the bare image digest – so a MITM that alters the declared version (an anti-rollback bypass), redirects the URL, or changes the size cannot present a valid signature (T5-05). Then it dispatches the crypto interface's ecdsa_verify over that bound digest and manifest->signature. All checks must pass.
| [in] | manifest | Non-NULL, validated manifest. |
| k_ra8_ok | Verification passed. |
| k_ra8_err_null_ptr | manifest was NULL. |
| k_ra8_err_not_initialized | Module not initialized. |
| k_ra8_err_invalid_state | Wrong state for verification. |
| k_ra8_err_crc_mismatch | SHA-256 digest mismatch. |
| k_ra8_err_hw_error | ECDSA verification rejected the signature (bad sig or tampered metadata). |
Verify SHA-256 + ECDSA over the freshly programmed bank.
Re-hashes the inactive bank via internal_rehash_bank and compares the digest against manifest->image_sha256. On a match it binds the manifest metadata (version / URL / size) into the signed material via internal_bind_manifest_material and invokes the configured ECDSA verifier over that metadata-bound digest, so a MITM that alters the declared version (defeating anti-rollback), redirects the URL, or changes the size cannot ride a signature made over the bare image digest (T5-05). On success the state machine lands in committing.
| [in] | manifest | Manifest used for the download. |
| k_ra8_ok | Image authenticated. |
| k_ra8_err_not_initialized | Module not initialized. |
| k_ra8_err_null_ptr | manifest was NULL. |
| k_ra8_err_invalid_state | Module not in verifying. |
| k_ra8_err_crc_mismatch | SHA-256 mismatch (image corrupt). |
| k_ra8_err_hw_error | ECDSA verify rejected the signature. |
| other | Crypto / flash backend error. |
Definition at line 227 of file ra8_ota_verify.c.
References g_ra8_ota_cfg, g_ra8_ota_initialized, g_ra8_ota_state, ra8_ota_manifest_t::image_sha256, internal_bind_manifest_material(), internal_rehash_bank(), k_ra8_err_crc_mismatch, k_ra8_err_hw_error, k_ra8_err_invalid_state, k_ra8_err_not_initialized, k_ra8_ok, k_ra8_ota_sha256_bytes, k_ra8_ota_state_committing, k_ra8_ota_state_error, k_ra8_ota_state_verifying, priv_ota_set_state(), RA8_CHECK_NULL_PTR, ra8_ct_equal(), s_tag, ra8_ota_manifest_t::signature, and ra8_ota_manifest_t::signature_len.
Referenced by app_run_attempt(), and internal_step_dispatch().