12#include <zephyr/kernel.h>
13#include <zephyr/init.h>
14#include <zephyr/sys/crc.h>
15#include <zephyr/logging/log.h>
25#if defined(CONFIG_SAFE_DATA_CRC16_CCITT)
26#define SAFE_TAG_SEED 0xFFFFu
27static inline uint32_t
safe_tag_update(uint32_t c,
const uint8_t *p,
size_t len)
29 return crc16_ccitt((uint16_t)c, p, len);
31#elif defined(CONFIG_SAFE_DATA_CRC8_CCITT)
32#define SAFE_TAG_SEED 0xFFu
33static inline uint32_t
safe_tag_update(uint32_t c,
const uint8_t *p,
size_t len)
35 return crc8_ccitt((uint8_t)c, p, len);
38#define SAFE_TAG_SEED 0x0u
41 return crc32_ieee_update(c, p, len);
45static inline uint32_t
safe_tag(
const void *p,
size_t len)
55 const uint8_t *sh = shadow;
60 size_t n = MIN(len,
sizeof(tmp));
62 for (
size_t i = 0; i < n; i++) {
63 tmp[i] = (uint8_t)~sh[i];
78 const uint8_t *sh = shadow;
80 for (
size_t i = 0; i < len; i++) {
81 if (pl[i] != (uint8_t)~sh[i]) {
92 LOG_ERR(
"integrity fault: payload %p, %zu bytes",
payload, len);
107#define SAFE_DATA_KIND_COUNT (SAFE_DATA_FAULT_KIND_MUTATOR_CLAMPED + 1)
109#if defined(CONFIG_SAFE_DATA_STATS)
175#if defined(CONFIG_SAFE_DATA_STATS)
190 if (IS_ENABLED(CONFIG_SAFE_DATA_FAULT_PANIC)) {
191 LOG_ERR(
"integrity fault -> k_panic()");
193 }
else if (IS_ENABLED(CONFIG_SAFE_DATA_FAULT_HANDLER) &&
202void z_safe_data_lock_failed(
void)
208void z_safe_data_seal(
void *
payload,
size_t len, uint32_t *crc,
void *shadow)
212 if (IS_ENABLED(CONFIG_SAFE_DATA_REDUNDANT) && shadow != NULL) {
214 uint8_t *dst = shadow;
216 for (
size_t i = 0; i <
len; i++) {
217 dst[i] = (uint8_t)~src[i];
225 uint32_t *crc,
void *shadow)
227 if (
payload == NULL || crc == NULL ||
len == 0) {
257 if (IS_ENABLED(CONFIG_SAFE_DATA_REDUNDANT) && shadow != NULL) {
282 if (ret == -EILSEQ) {
283 LOG_ERR(
"corruption detected; unrecoverable");
295 if (ret == -EILSEQ) {
296 LOG_ERR(
"payload and shadow both corrupt; unrecoverable");
310 LOG_WRN(
"integrity tag stale; data consistent, will reseal");
313 const uint8_t *sh = shadow;
316 for (
size_t i = 0; i <
len; i++) {
317 pl[i] = (uint8_t)~sh[i];
319 LOG_WRN(
"payload corrupt; recovered from shadow copy");
327 uint32_t *crc,
void *shadow,
void *out)
331 if (
payload == NULL || crc == NULL || out == NULL ||
len == 0) {
335 ret = z_safe_lock(lock);
355 uint32_t *crc,
void *shadow,
const void *in)
359 if (
payload == NULL || crc == NULL || in == NULL ||
len == 0) {
363 ret = z_safe_lock(lock);
373 if (IS_ENABLED(CONFIG_SAFE_DATA_WRITE_CHECKS_OLD) &&
375 LOG_WRN(
"overwriting corrupt contents");
387 uint32_t *crc,
void *shadow,
392 if (
payload == NULL || crc == NULL || fn == NULL ||
len == 0) {
396 ret = z_safe_lock(lock);
417 LOG_WRN(
"mutator returned %d; clamped to -EINVAL", mret);
435 if (IS_ENABLED(CONFIG_SAFE_DATA_REDUNDANT) &&
437 const uint8_t *sh = shadow;
440 for (
size_t i = 0; i <
len; i++) {
441 pl[i] = (uint8_t)~sh[i];
453#if defined(CONFIG_SAFE_DATA_ALLOW_UNCHECKED_COMMIT)
455 uint32_t *crc,
void *shadow)
459 if (
payload == NULL || crc == NULL ||
len == 0) {
463 ret = z_safe_lock(lock);
476#if defined(CONFIG_SAFE_DATA_SELFTEST)
482#if defined(CONFIG_SAFE_DATA_CRC16_CCITT)
483#define SAFE_DATA_KAT_EXPECTED 0x6F91u
484#elif defined(CONFIG_SAFE_DATA_CRC8_CCITT)
485#define SAFE_DATA_KAT_EXPECTED 0xFBu
487#define SAFE_DATA_KAT_EXPECTED 0xCBF43926u
492 static const uint8_t kat[] = {
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9'};
493 uint8_t buf[8] = {0xA5, 0x5A, 0x01, 0xFE, 0x10, 0x20, 0x40, 0x80};
494 uint8_t shadow[
sizeof(buf)];
495 void *sh = IS_ENABLED(CONFIG_SAFE_DATA_REDUNDANT) ? shadow : NULL;
513 z_safe_data_seal(buf,
sizeof(buf), &crc, sh);
522 if (
safe_tag(buf,
sizeof(buf)) == crc) {
530 if (ret == 0 && IS_ENABLED(CONFIG_SAFE_DATA_REDUNDANT)) {
542 LOG_ERR(
"integrity-mechanism self-test FAILED");
548#if defined(CONFIG_SAFE_DATA_SELFTEST_BOOT)
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.
#define SAFE_DATA_RECOVERED
Return code for a fault that the redundant shadow copy covers.
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
static uint32_t safe_tag_update(uint32_t c, const uint8_t *p, size_t len)
static int safe_data_boot_selftest(void)
#define SAFE_DATA_KAT_EXPECTED
static bool payload_matches_shadow(const void *payload, const void *shadow, size_t len)
static safe_data_fault_cb_t fault_cb
#define SAFE_DATA_KIND_COUNT
static uint32_t safe_tag_inverted(const void *shadow, size_t len)
static int verify_quiet(const void *payload, size_t len, uint32_t crc, const void *shadow)
static uint32_t safe_tag(const void *p, size_t len)
static int boot_selftest_result
LOG_MODULE_REGISTER(safe_data, CONFIG_SAFE_DATA_LOG_LEVEL)
static volatile bool event_mute
static void safe_event(enum safe_data_fault_kind kind, const void *payload, size_t len)
static atomic_t stat_counters[(SAFE_DATA_FAULT_KIND_MUTATOR_CLAMPED+1)]
SYS_INIT(safe_data_boot_selftest, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT)
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