ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
ota_rollback.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file ota_rollback.h
6 * @brief OTA rollback protection / soft-brick safeguard (PROTOCORE_ENABLE_OTA_ROLLBACK).
7 *
8 * After an OTA update the new image boots in the PENDING_VERIFY state. This service
9 * decides, each tick, whether to commit it (a self-test passed), roll back to the
10 * previous image (self-test failed, or the confirm window elapsed without success),
11 * or keep waiting - so a bad update self-heals instead of soft-bricking. The
12 * decision is a pure function (host-tested); the commit / rollback reach the platform
13 * seam. Needs the bootloader's app-rollback support.
14 *
15 * @author Douglas Quigg (dstroy0)
16 * @date 2026
17 */
18
19#ifndef PROTOCORE_OTA_ROLLBACK_H
20#define PROTOCORE_OTA_ROLLBACK_H
21
22#include "protocore_config.h" // the entry point: protocore_types.h for the widths
23
24#if PROTOCORE_ENABLE_OTA_ROLLBACK
25
27
28/** @brief OTA image states, mirroring PROTOCORE_PLATFORM_IMG_* so the core is host-pure. These arrive
29 * from the platform seam as a uint8_t and are compared, so integer constants - cast-free. */
30#define PROTOCORE_OTA_IMG_NEW 0
31#define PROTOCORE_OTA_IMG_PENDING_VERIFY 1
32#define PROTOCORE_OTA_IMG_VALID 2
33#define PROTOCORE_OTA_IMG_INVALID 3
34#define PROTOCORE_OTA_IMG_ABORTED 4
35#define PROTOCORE_OTA_IMG_UNDEFINED 0xFF
36
37/** @brief What the rollback tick should do. */
38typedef enum PROTO_ENUM_PACKED
39{
40 PROTOCORE_OTA_WAIT = 0, ///< still pending, within the window: keep waiting.
41 PROTOCORE_OTA_COMMIT = 1, ///< self-test passed: mark the image valid.
42 PROTOCORE_OTA_ROLLBACK = 2, ///< self-test failed or window elapsed: roll back + reboot.
43} protocore_ota_action;
44
45// ---------------------------------------------------------------------------
46// Host-testable decision core
47// ---------------------------------------------------------------------------
48
49/** @brief What the pure decision reads. */
50typedef struct
51{
52 uint8_t img_state; ///< the running image's state (PROTOCORE_OTA_IMG_*)
53 proto_bool self_test_ok; ///< the application has confirmed itself healthy
54 uint32_t ms_since_boot; ///< how long this image has been running
55 uint32_t window_ms; ///< how long it has to confirm before the rollback self-heals
56} OtaDecideArgs;
57
58/**
59 * @brief The OTA confirm-or-roll-back policy.
60 *
61 * A caller sets the members a call takes, invokes it through ::OtaRollback, and reads the outcome
62 * off the same handle. The decision is pure; the commit and the rollback reach the platform seam.
63 *
64 * @var OtaRollbackNs::decide_args what the pure decision reads
65 * @var OtaRollbackNs::self_test_ok what a tick reports about the application's own health
66 * @var OtaRollbackNs::action the action a decide or a tick chose
67 * @var OtaRollbackNs::img_state the running image's state a lookup reports
68 * @var OtaRollbackNs::decide choose an action, reading nothing outside decide_args
69 * @var OtaRollbackNs::state the running image's state, from the platform seam
70 * @var OtaRollbackNs::commit mark the running image valid and cancel the pending rollback
71 * @var OtaRollbackNs::rollback mark it invalid and reboot into the previous one
72 * @var OtaRollbackNs::tick decide against the clock, then carry the decision out
73 *
74 * No storage member: the policy holds nothing between calls; the image state lives in the part.
75 */
76typedef struct
77{
78 OtaDecideArgs decide_args;
79 proto_bool self_test_ok;
80 protocore_ota_action action;
81 uint8_t img_state;
82} OtaRollbackVars;
83
84/** @brief The operands and the outcome. */
85extern OtaRollbackVars OtaRollbackV;
86
87/** @brief The entries. */
88typedef struct
89{
90 void (*const decide)(uint8_t *work);
91 void (*const state)(uint8_t *work);
92 void (*const commit)(uint8_t *work);
93 void (*const rollback)(uint8_t *work);
94 void (*const tick)(uint8_t *work);
95} OtaRollbackNs;
96
97// What the table binds, defined once in the .c and taking one parameter each: everything
98// else an entry needs is an operand in OtaRollbackV or a region of the borrow at a fixed offset.
99void protocore_ota_rollback_decide(uint8_t *work);
100void protocore_ota_rollback_state(uint8_t *work);
101void protocore_ota_rollback_commit(uint8_t *work);
102void protocore_ota_rollback_rollback(uint8_t *work);
103void protocore_ota_rollback_tick(uint8_t *work);
104
105// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
106// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
107// `OtaRollback.decide(work)` resolves to a named function and becomes a DIRECT call. An extern table
108// leaves the call indirect and the symbol live at every level, -O2 -flto included.
109static const OtaRollbackNs OtaRollback __attribute__((unused)) = {
110 .decide = protocore_ota_rollback_decide,
111 .state = protocore_ota_rollback_state,
112 .commit = protocore_ota_rollback_commit,
113 .rollback = protocore_ota_rollback_rollback,
114 .tick = protocore_ota_rollback_tick,
115};
116
118
119#endif // PROTOCORE_ENABLE_OTA_ROLLBACK
120
121#endif // PROTOCORE_OTA_ROLLBACK_H
PROTO_ENUM_PACKED
Application protocol spoken on a listener port or connection slot.
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97