25#include <zephyr/kernel.h>
26#include <zephyr/sys/util.h>
27#include <zephyr/toolchain.h>
38#if !defined(CONFIG_SAFE_DATA_REDUNDANT)
39#define CONFIG_SAFE_DATA_REDUNDANT 0
46#if !defined(CONFIG_SAFE_DATA_LOCKING) && !defined(CONFIG_SAFE_DATA)
47#define CONFIG_SAFE_DATA_LOCKING 1
53#if !defined(CONFIG_SAFE_DATA)
54#if !defined(CONFIG_SAFE_DATA_LOCK_TIMEOUT_MS)
55#define CONFIG_SAFE_DATA_LOCK_TIMEOUT_MS 0
57#if !defined(CONFIG_SAFE_DATA_GNU_EXTENSIONS)
58#define CONFIG_SAFE_DATA_GNU_EXTENSIONS 1
60#if !defined(CONFIG_SAFE_DATA_ALLOW_UNCHECKED_COMMIT)
61#define CONFIG_SAFE_DATA_ALLOW_UNCHECKED_COMMIT 1
83#define SAFE_DATA_RECOVERED (-EAGAIN)
212#if defined(CONFIG_SAFE_DATA_SELFTEST) || defined(__DOXYGEN__)
267 uint32_t *crc,
void *shadow);
338 uint32_t *crc,
void *shadow,
void *out);
364 uint32_t *crc,
void *shadow,
const void *in);
394 uint32_t *crc,
void *shadow,
397#if defined(CONFIG_SAFE_DATA_ALLOW_UNCHECKED_COMMIT) || defined(__DOXYGEN__)
424 uint32_t *crc,
void *shadow);
430void z_safe_data_seal(
void *
payload,
size_t len, uint32_t *crc,
void *shadow);
431void z_safe_data_lock_failed(
void);
434#if CONFIG_SAFE_DATA_LOCK_TIMEOUT_MS > 0
435#define Z_SAFE_LOCK_TIMEOUT K_MSEC(CONFIG_SAFE_DATA_LOCK_TIMEOUT_MS)
437#define Z_SAFE_LOCK_TIMEOUT K_FOREVER
446static inline int z_safe_lock(
struct k_mutex *lock)
449 __ASSERT(!k_is_in_isr(),
450 "safe_data: locking API is not ISR-safe");
451 if (k_mutex_lock(lock, Z_SAFE_LOCK_TIMEOUT) != 0) {
461static inline void z_safe_unlock(
struct k_mutex *lock)
464 k_mutex_unlock(lock);
487#if defined(CONFIG_SAFE_DATA_CRC8_CCITT)
488#define Z_SAFE_DATA_MAX_PAYLOAD 16
489#elif defined(CONFIG_SAFE_DATA_CRC16_CCITT)
490#define Z_SAFE_DATA_MAX_PAYLOAD 256
492#define Z_SAFE_DATA_MAX_PAYLOAD SIZE_MAX
495#define Z_SAFE_CONTAINER_BODY(_payload_type) \
496 IF_ENABLED(CONFIG_SAFE_DATA_LOCKING, (struct k_mutex _lock;)) \
498 IF_ENABLED(CONFIG_SAFE_DATA_REDUNDANT, \
499 (uint8_t _shadow[sizeof(_payload_type)];)) \
500 _payload_type payload;
523#define SAFE_CONTAINER_DEFINE(_container_name, _payload_type) \
524 struct _container_name { \
525 BUILD_ASSERT(sizeof(_payload_type) > 0, \
526 "safe_data: zero-size payload"); \
527 BUILD_ASSERT(sizeof(_payload_type) <= Z_SAFE_DATA_MAX_PAYLOAD, \
528 "safe_data: payload too large for the configured" \
529 " integrity tag (see Z_SAFE_DATA_MAX_PAYLOAD)"); \
530 Z_SAFE_CONTAINER_BODY(_payload_type) \
540#define SAFE_CONTAINER_DEFINE_UNCHECKED(_container_name, _payload_type) \
541 struct _container_name { \
542 BUILD_ASSERT(sizeof(_payload_type) > 0, \
543 "safe_data: zero-size payload"); \
544 Z_SAFE_CONTAINER_BODY(_payload_type) \
548#define _SAFE_SHADOW(_c) \
549 COND_CODE_1(CONFIG_SAFE_DATA_REDUNDANT, ((_c)->_shadow), (NULL))
554#define _SAFE_LOCK(_c) \
555 COND_CODE_1(CONFIG_SAFE_DATA_LOCKING, (&(_c)->_lock), (NULL))
562#define SAFE_INIT(_c) \
563 safe_data_init(_SAFE_LOCK(_c), &(_c)->payload, sizeof((_c)->payload), \
564 &(_c)->_crc, _SAFE_SHADOW(_c))
572#define SAFE_VERIFY(_c) \
573 safe_data_verify(&(_c)->payload, sizeof((_c)->payload), (_c)->_crc, \
582#define SAFE_READ(_c, _out_ptr) \
583 safe_data_read(_SAFE_LOCK(_c), &(_c)->payload, sizeof((_c)->payload), \
584 &(_c)->_crc, _SAFE_SHADOW(_c), (_out_ptr))
592#define SAFE_WRITE(_c, _in_ptr) \
593 safe_data_write(_SAFE_LOCK(_c), &(_c)->payload, sizeof((_c)->payload), \
594 &(_c)->_crc, _SAFE_SHADOW(_c), (_in_ptr))
603#define SAFE_UPDATE(_c, _fn, _user) \
604 safe_data_update(_SAFE_LOCK(_c), &(_c)->payload, sizeof((_c)->payload),\
605 &(_c)->_crc, _SAFE_SHADOW(_c), (_fn), (_user))
607#if defined(CONFIG_SAFE_DATA_ALLOW_UNCHECKED_COMMIT) || defined(__DOXYGEN__)
616#define SAFE_COMMIT(_c) \
617 safe_data_commit(_SAFE_LOCK(_c), &(_c)->payload, \
618 sizeof((_c)->payload), &(_c)->_crc, _SAFE_SHADOW(_c))
662#if defined(CONFIG_SAFE_DATA_GNU_EXTENSIONS) || defined(__DOXYGEN__)
663#define SAFE_SECTION(_c, _pvar) \
664 for (int _sd_st = ((z_safe_lock(_SAFE_LOCK(_c)) == 0) \
665 ? safe_data_verify_repair(&(_c)->payload, \
666 sizeof((_c)->payload), (_c)->_crc, \
668 : (z_safe_data_lock_failed(), -ETIMEDOUT)); \
670 ({ bool _ok = (_sd_st == 0 || _sd_st == SAFE_DATA_RECOVERED); \
671 if (!_ok && _sd_st != -ETIMEDOUT) { \
672 z_safe_unlock(_SAFE_LOCK(_c)); \
675 z_safe_data_seal(&(_c)->payload, sizeof((_c)->payload), \
676 &(_c)->_crc, _SAFE_SHADOW(_c)), \
677 z_safe_unlock(_SAFE_LOCK(_c)), _sd_st = 1) \
678 for (__typeof__((_c)->payload) *_pvar = &(_c)->payload; \
679 _pvar != NULL; _pvar = NULL)
int safe_data_verify_repair(void *payload, size_t len, uint32_t crc, void *shadow)
Verify payload integrity and repair a recoverable fault in place.
int safe_data_update(struct k_mutex *lock, void *payload, size_t len, uint32_t *crc, void *shadow, safe_data_mutator_t fn, void *user)
Atomic read-modify-write: lock, verify, run fn, reseal on success.
int safe_data_read(struct k_mutex *lock, void *payload, size_t len, uint32_t *crc, void *shadow, void *out)
Verify under lock and copy the payload out to out.
int(* safe_data_mutator_t)(void *payload, void *user)
Mutator callback used by safe_data_update() / SAFE_UPDATE().
int safe_data_verify(const void *payload, size_t len, uint32_t crc, const void *shadow)
Verify payload integrity. Pure: read-only and lock-free; it NEVER modifies the payload,...
int safe_data_write(struct k_mutex *lock, void *payload, size_t len, uint32_t *crc, void *shadow, const void *in)
Copy in into the payload under lock and reseal the integrity tag.
int safe_data_init(struct k_mutex *lock, void *payload, size_t len, uint32_t *crc, void *shadow)
Initialise the lock and seal the current payload contents.
int safe_data_commit(struct k_mutex *lock, void *payload, size_t len, uint32_t *crc, void *shadow)
Reseal after an explicit external modification (advanced use). Lock, recompute the integrity tag,...
void(* safe_data_fault_cb_t)(const struct safe_data_fault_info *info)
Fault/diagnostic event callback.
safe_data_fault_kind
Kind of integrity event reported to the fault handler / statistics.
int safe_data_selftest_result(void)
Result of the boot-time self-test (CONFIG_SAFE_DATA_SELFTEST_BOOT).
void safe_data_stats_reset(void)
Reset all statistics counters to zero.
int safe_data_stats_get(struct safe_data_stats *out)
Snapshot the global statistics.
void safe_data_fault_handler(const void *payload, size_t len)
Legacy user-overridable (weak) fault handler.
int safe_data_selftest(void)
Self-test of the integrity mechanism itself.
int safe_data_fault_handler_register(safe_data_fault_cb_t cb)
Register the fault handler callback.
@ SAFE_DATA_FAULT_KIND_UNRECOVERABLE
@ SAFE_DATA_FAULT_KIND_LOCK_TIMEOUT
@ SAFE_DATA_FAULT_KIND_RECOVERED_PAYLOAD
@ SAFE_DATA_FAULT_KIND_MUTATOR_CLAMPED
@ SAFE_DATA_FAULT_KIND_RECOVERED_TAG
@ SAFE_DATA_FAULT_KIND_OVERWRITTEN
Event descriptor passed to the registered fault handler.
enum safe_data_fault_kind kind
Global fault/recovery statistics (CONFIG_SAFE_DATA_STATS).
uint32_t recovered_payload