ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_dfu_antirollback.c
Go to the documentation of this file.
1
24
26
27#ifdef RA8_ENABLE_ROOT_OF_TRUST
28
29#include <string.h>
30
31#include "ra8_attributes.h"
32#include "ra8_check.h"
33#include "ra8_flash_core.h"
34#include "ra8_flash_regs.h"
35
55typedef enum : uint32_t {
56 k_ra8_rot_ar_erased = 0xFFFFFFFFU,
57} ra8_rot_ar_nv_t;
58
59#ifdef RA8_OFF_TARGET
72static uint32_t s_fake_ar_counter = (uint32_t)k_ra8_rot_ar_erased;
73#endif
74
84static const char* s_tag = "ROLLBACK";
85
86ra8_err_t ra8_rot_antirollback_check(uint32_t image_version, uint32_t stored_min_version)
87{
88 /* Downgrade: a strictly older image carries since-patched defects -- deny. */
89 if (image_version < stored_min_version) {
90 ra8_log_error(s_tag, "anti-rollback: image version below stored minimum");
92 }
93 /* Newer or equal: not a downgrade -- accept. */
94 return k_ra8_ok;
95}
96
97#ifndef RA8_OFF_TARGET
107volatile bool g_ra8_rot_ar_probing = false;
108
115volatile bool g_ra8_rot_ar_faulted = false;
116
121typedef enum : uint32_t {
122 k_ra8_ar_cfsr_addr = 0xE000ED28U,
123 k_ra8_ar_thumb_hw_msk = 0x0000F800U,
124 k_ra8_ar_thumb32_min = 0x0000E800U,
125} ra8_ar_probe_reg_t;
126
127bool ra8_rot_antirollback_on_probe_fault(uint32_t* exc_frame)
128{
129 if (exc_frame == nullptr || !g_ra8_rot_ar_probing) {
130 return false;
131 }
132 g_ra8_rot_ar_probing = false;
133 g_ra8_rot_ar_faulted = true;
134 /* Advance the stacked PC past the faulting load so the exception return resumes
135 * at the next instruction. The basic exception frame is
136 * [R0 R1 R2 R3 R12 LR PC xPSR]; index 6 is the stacked PC. A Thumb instruction
137 * is 32-bit iff its first halfword's bits [15:11] are >= 0b11101. */
138 const uint16_t instr = *(const volatile uint16_t*)(uintptr_t)exc_frame[6];
139 exc_frame[6] +=
140 ((instr & (uint16_t)k_ra8_ar_thumb_hw_msk) >= (uint16_t)k_ra8_ar_thumb32_min) ? 4U : 2U;
141 /* W1C-clear the sticky Configurable Fault Status so this deliberate fault does
142 * not shadow a later real one. Reading the live value and writing it back is
143 * a real read-modify-write on the device: every bit that reads as 1 is
144 * written as 1, which clears it (write-1-to-clear semantics). */
145 volatile uint32_t* const cfsr = (volatile uint32_t*)(uintptr_t)k_ra8_ar_cfsr_addr;
146 const uint32_t cfsr_sticky = *cfsr;
147 *cfsr = cfsr_sticky;
148 __asm__ volatile("dsb 0xF\n isb 0xF\n" ::: "memory");
149 return true;
150}
151
179RA8_INTERNAL static uint32_t internal_probe_counter(bool* out_blank)
180{
181 g_ra8_rot_ar_faulted = false;
182 g_ra8_rot_ar_probing = true;
183 __asm__ volatile("dsb 0xF\n isb 0xF\n" ::: "memory");
184 const volatile uint32_t raw = *(const volatile uint32_t*)(uintptr_t)k_ra8_flash_extra_start;
185 __asm__ volatile("dsb 0xF\n isb 0xF\n" ::: "memory");
186 g_ra8_rot_ar_probing = false;
187 *out_blank = g_ra8_rot_ar_faulted;
188 return g_ra8_rot_ar_faulted ? (uint32_t)k_ra8_rot_ar_erased : raw;
189}
190#endif /* !RA8_OFF_TARGET */
191
220RA8_INTERNAL static ra8_err_t internal_default_store_read(uint32_t* out_min_version)
221{
222 RA8_CHECK_NULL_PTR(out_min_version, s_tag, "out_min_version");
223#ifdef RA8_OFF_TARGET
224 const uint32_t raw = s_fake_ar_counter;
225#else
226 bool blank = false;
227 const uint32_t raw = internal_probe_counter(&blank);
228#endif
229 *out_min_version = (raw == (uint32_t)k_ra8_rot_ar_erased) ? 0U : raw;
230 return k_ra8_ok;
231}
232
260RA8_INTERNAL static ra8_err_t internal_default_store_commit(uint32_t new_version)
261{
262#ifdef RA8_OFF_TARGET
263 const uint32_t raw = s_fake_ar_counter;
264 const bool blank = false;
265#else
266 bool blank = false;
267 const uint32_t raw = internal_probe_counter(&blank);
268#endif
269 const uint32_t stored = (raw == (uint32_t)k_ra8_rot_ar_erased) ? 0U : raw;
270 if (new_version <= stored) {
271 return k_ra8_ok; /* already at or above the floor -- nothing to persist */
272 }
273#ifdef RA8_OFF_TARGET
274 (void)blank;
275 s_fake_ar_counter = new_version;
276 return k_ra8_ok;
277#else
278 uint8_t le[sizeof(uint32_t)] = {};
279 (void)memcpy(le, &new_version, sizeof(le)); /* both toolchains little-endian */
280 const ra8_err_t wr =
281 ra8_flash_extra_mram_write((uint32_t)k_ra8_flash_extra_start, le, (uint32_t)sizeof(le));
282 /* #315 bench-proved on silicon that a virgin extra-MRAM page CAN be programmed at
283 * runtime write-first (no prior read) and reads back byte-exact with valid ECC, so
284 * the commit programs the counter directly and returns the write result. The
285 * earlier "#194: the read leaves the controller unable to program a fresh word"
286 * claim was an artifact of the phantom 0x27000000 window (corrected by #397). The
287 * ``blank`` leg is retained defensively -- it is not expected on the corrected
288 * window (a virgin read returns 0xFFFFFFFF without faulting, so ``blank`` stays
289 * false) -- and lets a signature-authenticated fresh device launch its first
290 * authentic image without a persisted floor. On a real write error the commit
291 * default-denies the launch. */
292 return blank ? k_ra8_ok : wr;
293#endif
294}
295
308 .read = internal_default_store_read,
309 .commit = internal_default_store_commit,
310};
311
313 uint32_t image_version)
314{
315 RA8_CHECK_NULL_PTR(store, s_tag, "store");
316 RA8_CHECK_NULL_PTR(store->read, s_tag, "store->read");
317 RA8_CHECK_NULL_PTR(store->commit, s_tag, "store->commit");
318
319 /* Read the stored highest-accepted version. A read fault is DEFAULT-DENY. */
320 uint32_t stored_min = 0U;
321 const ra8_err_t read_err = store->read(&stored_min);
322 RA8_RETURN_ON_ERROR(read_err, s_tag, "anti-rollback: stored-version read failed");
323
324 /* Apply the pure downgrade policy. A downgrade returns validation_failed. */
325 const ra8_err_t policy_err = ra8_rot_antirollback_check(image_version, stored_min);
326 RA8_RETURN_ON_ERROR(policy_err, s_tag, "anti-rollback: downgrade rejected");
327
328 /* Accepted: advance the durable counter. A commit fault is DEFAULT-DENY. */
329 const ra8_err_t commit_err = store->commit(image_version);
330 RA8_RETURN_ON_ERROR(commit_err, s_tag, "anti-rollback: counter commit failed");
331
332 return k_ra8_ok;
333}
334
336{
337 return &s_default_store;
338}
339
340#endif /* RA8_ENABLE_ROOT_OF_TRUST */
static const char * s_tag
Logging / check tag.
Definition ra8_app.c:17
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Validation and Error-Checking Macros for ra8-firmware.
#define RA8_RETURN_ON_ERROR(err, tag, message)
Early return on error, propagating the code upward.
Definition ra8_check.h:184
#define RA8_CHECK_NULL_PTR(ptr, tag, message)
Reject nullptr pointer, returning k_ra8_err_null_ptr.
Definition ra8_check.h:243
static const ra8_devcfg_store_t s_default_store
The process-lifetime extra-MRAM-backed store.
DFU anti-rollback (downgrade protection) policy + storage seam.
const ra8_rot_antirollback_store_t * ra8_rot_antirollback_default_store(void)
Return the non-faking default store (reports "not provisioned").
ra8_err_t ra8_rot_antirollback_verify(const ra8_rot_antirollback_store_t *store, uint32_t image_version)
Read the stored minimum, apply the policy, and commit on accept.
bool ra8_rot_antirollback_on_probe_fault(uint32_t *exc_frame)
Recover a fault-tolerant counter probe from the app fault handler.
ra8_err_t ra8_rot_antirollback_check(uint32_t image_version, uint32_t stored_min_version)
Pure downgrade policy: accept iff the image is not older than stored.
@ k_ra8_err_validation_failed
Validation rule failed (caller-supplied invariant not satisfied).
Definition ra8_err.h:459
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
Code MRAM + Extra MRAM + Option-Setting driver – core API.
ra8_err_t ra8_flash_extra_mram_write(uint32_t mram_addr, const uint8_t *src, uint32_t len)
Program 1..32 contiguous bytes into the general-purpose extra-MRAM window.
Flash / MRAM controller register layout for the Renesas RA8D2.
@ k_ra8_flash_extra_start
First legal Program target (FSBL setting).
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
#define ra8_log_error(tag, message)
RA8 log error.
Definition ra8_log.h:335
Dependency-injection vtable for the non-volatile version counter.
ra8_rot_antirollback_read_fn_t read
Read stored highest-accepted version.
ra8_rot_antirollback_commit_fn_t commit
Persist the newly-accepted version.