| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639 |
- /*
- * Copyright (c) 2026, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2026-03-09 user first version
- */
- #define DBG_TAG "BAK"
- #define DBG_LVL DBG_LOG
- #include <rtdbg.h>
- #include <rthw.h>
- #include <fal.h>
- #include <string.h>
- #include <easyflash.h>
- #include "parabak.h"
- #include "ecode.h"
- #include "main.h"
- /* 引入需要备份的外部结构体 */
- #include "ym310_protecl.h" // 提供 sys_info, dev_info, limit 的定义
- /* 外部时间变量 */
- extern struct rtc_time time_now;
- /*=============================================================================
- * 常量定义
- *============================================================================*/
- #define PARABAK_MAGIC 0x50415241 /* "PARA" */
- #define PARABAK_VERSION 1
- #define PARABAK_PART_SIZE 8192 /* 8KB */
- #define HEADER_SECTOR_OFFSET 0 /* 头部扇区偏移 0 */
- #define DATA_SECTOR_OFFSET 4096 /* 数据扇区偏移 4096 */
- #define SECTOR_SIZE 4096
- /*=============================================================================
- * 备份项表(内置,静态)
- *============================================================================*/
- static const parabak_item_t parabak_items[PARABAK_ID_MAX] = {
- {PARABAK_ID_SYS_INFO, &sys_info, sizeof(SYS_INFO), "para_sys", EF_SYS_INFO},
- {PARABAK_ID_DEV_INFO, &dev_info, sizeof(DEV_INFO), "para_dev", EF_DEV_INFO},
- {PARABAK_ID_LIMIT, &limit, sizeof(LIMIT), "para_lim", EF_LIMIT},
- };
- /*=============================================================================
- * 静态变量(互斥锁、初始化标志)
- *============================================================================*/
- static rt_mutex_t parabak_lock = RT_NULL; /* 保护数据操作的互斥锁 */
- static rt_mutex_t init_mutex = RT_NULL; /* 保护初始化的互斥锁 */
- static rt_bool_t initialized = RT_FALSE; /* 是否已初始化 */
- /*=============================================================================
- * CRC32 计算(标准算法)
- *============================================================================*/
- static uint32_t crc32_update(uint32_t crc, uint8_t data)
- {
- crc ^= data;
- for (int i = 0; i < 8; i++)
- {
- if (crc & 1)
- crc = (crc >> 1) ^ 0xEDB88320;
- else
- crc >>= 1;
- }
- return crc;
- }
- static uint32_t calc_crc32(const uint8_t *data, uint32_t len)
- {
- uint32_t crc = 0xFFFFFFFF;
- for (uint32_t i = 0; i < len; i++)
- crc = crc32_update(crc, data[i]);
- return crc ^ 0xFFFFFFFF;
- }
- /*=============================================================================
- * 时间戳转换(rtc_time -> parabak_tm)
- *============================================================================*/
- static void rtc_to_parabak_tm(const struct rtc_time *src, struct parabak_tm *dst)
- {
- dst->tm_year = src->year - 2000;
- dst->tm_mon = src->month;
- dst->tm_mday = src->date;
- dst->tm_hour = src->hour;
- dst->tm_min = src->minute;
- dst->tm_sec = src->second;
- }
- static void get_timestamp(struct parabak_tm *ts)
- {
- rtc_to_parabak_tm(&time_now, ts);
- }
- /*=============================================================================
- * 分区操作辅助函数
- *============================================================================*/
- static const struct fal_partition *get_partition_by_id(parabak_id_t id)
- {
- if (id >= PARABAK_ID_MAX)
- return RT_NULL;
- const char *part_name = parabak_items[id].partition_name;
- return fal_partition_find(part_name);
- }
- static int erase_sector(const struct fal_partition *part, uint32_t offset)
- {
- if (fal_partition_erase(part, offset, SECTOR_SIZE) < 0)
- {
- LOG_E("erase sector at 0x%x failed", offset);
- return -RT_ERROR;
- }
- return RT_EOK;
- }
- static int write_header(const struct fal_partition *part, const parabak_header_t *hdr)
- {
- if (erase_sector(part, HEADER_SECTOR_OFFSET) != RT_EOK)
- return -RT_ERROR;
- if (fal_partition_write(part, HEADER_SECTOR_OFFSET, (uint8_t *)hdr, sizeof(parabak_header_t)) < 0)
- {
- LOG_E("write header failed");
- return -RT_ERROR;
- }
- return RT_EOK;
- }
- static int read_header(const struct fal_partition *part, parabak_header_t *hdr)
- {
- if (fal_partition_read(part, HEADER_SECTOR_OFFSET, (uint8_t *)hdr, sizeof(parabak_header_t)) < 0)
- {
- LOG_E("read header failed");
- return -RT_ERROR;
- }
- return RT_EOK;
- }
- static int write_data(const struct fal_partition *part, const uint8_t *data, uint32_t len)
- {
- if (len > SECTOR_SIZE)
- {
- LOG_E("data too large for sector");
- return -RT_ERROR;
- }
- if (erase_sector(part, DATA_SECTOR_OFFSET) != RT_EOK)
- return -RT_ERROR;
- if (fal_partition_write(part, DATA_SECTOR_OFFSET, data, len) < 0)
- {
- LOG_E("write data failed");
- return -RT_ERROR;
- }
- return RT_EOK;
- }
- static int read_data(const struct fal_partition *part, uint8_t *buf, uint32_t len)
- {
- if (len > SECTOR_SIZE)
- return -RT_ERROR;
- if (fal_partition_read(part, DATA_SECTOR_OFFSET, buf, len) < 0)
- {
- LOG_E("read data failed");
- return -RT_ERROR;
- }
- return RT_EOK;
- }
- /*=============================================================================
- * 内部初始化核心函数(仅执行一次)
- *============================================================================*/
- static int parabak_init_core(void)
- {
- parabak_lock = rt_mutex_create("parabak", RT_IPC_FLAG_FIFO);
- if (parabak_lock == RT_NULL)
- {
- LOG_E("create data mutex failed");
- return -RT_ERROR;
- }
- LOG_I("parabak core initialized");
- return RT_EOK;
- }
- /* 确保模块已初始化(线程安全,只执行一次) */
- static int ensure_initialized(void)
- {
- if (initialized)
- return RT_EOK;
- if (init_mutex == RT_NULL)
- {
- init_mutex = rt_mutex_create("parabak_init", RT_IPC_FLAG_FIFO);
- if (init_mutex == RT_NULL)
- {
- LOG_E("create init mutex failed");
- return -RT_ERROR;
- }
- }
- rt_mutex_take(init_mutex, RT_WAITING_FOREVER);
- if (!initialized)
- {
- int ret = parabak_init_core();
- if (ret == RT_EOK)
- initialized = RT_TRUE;
- else
- LOG_E("parabak_init_core failed");
- }
- rt_mutex_release(init_mutex);
- return initialized ? RT_EOK : -RT_ERROR;
- }
- /*=============================================================================
- * 公共 API(全部自动调用 ensure_initialized)
- *============================================================================*/
- int parabak_backup(parabak_id_t id)
- {
- const parabak_item_t *item;
- const struct fal_partition *part;
- parabak_header_t hdr;
- uint32_t crc;
- int ret;
- if (ensure_initialized() != RT_EOK)
- {
- LOG_E("parabak not initialized");
- return -RT_ERROR;
- }
- if (id >= PARABAK_ID_MAX)
- return -RT_ERROR;
- item = ¶bak_items[id];
- part = get_partition_by_id(id);
- if (part == RT_NULL)
- {
- LOG_E("partition not found for id %d", id);
- return -RT_ERROR;
- }
- rt_mutex_take(parabak_lock, RT_WAITING_FOREVER);
- crc = calc_crc32((uint8_t *)item->data_ptr, item->data_size);
- hdr.magic = PARABAK_MAGIC;
- hdr.version = PARABAK_VERSION;
- hdr.data_len = item->data_size;
- hdr.data_crc = crc;
- get_timestamp(&hdr.time_stamp);
- hdr.state = 1; /* 已备份 */
- memset(hdr.reserved, 0, sizeof(hdr.reserved));
- if (write_header(part, &hdr) != RT_EOK)
- {
- LOG_E("backup: write header failed");
- ret = -RT_ERROR;
- goto out;
- }
- if (write_data(part, (uint8_t *)item->data_ptr, item->data_size) != RT_EOK)
- {
- LOG_E("backup: write data failed");
- ret = -RT_ERROR;
- goto out;
- }
- LOG_I("✅ Backup id %d (%s) SUCC, CRC=0x%08X, TIME=%02d/%02d/%02d %02d:%02d:%02d",
- id, item->partition_name, crc,
- hdr.time_stamp.tm_year, hdr.time_stamp.tm_mon, hdr.time_stamp.tm_mday,
- hdr.time_stamp.tm_hour, hdr.time_stamp.tm_min, hdr.time_stamp.tm_sec);
- Recode_e_code(e_bak_backup, id, "执行备份分区");
- ret = RT_EOK;
- out:
- rt_mutex_release(parabak_lock);
- return ret;
- }
- int parabak_self_check(parabak_id_t id, parabak_header_t *hdr)
- {
- const struct fal_partition *part;
- parabak_header_t tmp_hdr;
- uint8_t *data_buf = RT_NULL;
- uint32_t crc;
- int ret = -1;
- if (ensure_initialized() != RT_EOK)
- {
- LOG_E("parabak not initialized");
- return -1;
- }
- if (id >= PARABAK_ID_MAX)
- return -1;
- part = get_partition_by_id(id);
- if (part == RT_NULL)
- return -1;
- rt_mutex_take(parabak_lock, RT_WAITING_FOREVER);
- if (read_header(part, &tmp_hdr) != RT_EOK)
- {
- LOG_E("self_check: read header failed");
- goto out;
- }
- if (hdr)
- *hdr = tmp_hdr;
- if (tmp_hdr.magic != PARABAK_MAGIC || tmp_hdr.version != PARABAK_VERSION)
- {
- LOG_D("self_check: invalid magic/version");
- ret = 0;
- goto out;
- }
- if (tmp_hdr.data_len == 0 || tmp_hdr.data_len > SECTOR_SIZE)
- {
- LOG_W("self_check: invalid data len %d", tmp_hdr.data_len);
- ret = 0;
- goto out;
- }
- data_buf = rt_malloc(tmp_hdr.data_len);
- if (data_buf == RT_NULL)
- {
- LOG_E("self_check: out of memory");
- goto out;
- }
- if (read_data(part, data_buf, tmp_hdr.data_len) != RT_EOK)
- {
- LOG_E("self_check: read data failed");
- goto out;
- }
- crc = calc_crc32(data_buf, tmp_hdr.data_len);
- if (crc != tmp_hdr.data_crc)
- {
- LOG_W("self_check: CRC mismatch (stored=0x%08x, calc=0x%08x)", tmp_hdr.data_crc, crc);
- ret = 0;
- }
- else
- {
- ret = tmp_hdr.state;
- }
- out:
- if (data_buf)
- rt_free(data_buf);
- rt_mutex_release(parabak_lock);
- return ret;
- }
- int parabak_verify(parabak_id_t id)
- {
- const parabak_item_t *item;
- parabak_header_t hdr;
- int state;
- if (ensure_initialized() != RT_EOK)
- {
- LOG_E("parabak not initialized");
- return -RT_ERROR;
- }
- if (id >= PARABAK_ID_MAX)
- return -RT_ERROR;
- item = ¶bak_items[id];
- state = parabak_self_check(id, &hdr);
- if (state < 0)
- return -RT_ERROR;
- if (state == 0)
- {
- LOG_W("verify id %d: no valid backup", id);
- return -RT_ERROR;
- }
- uint32_t mem_crc = calc_crc32((uint8_t *)item->data_ptr, item->data_size);
- if (mem_crc != hdr.data_crc)
- {
- LOG_W("verify id %d: mem CRC mismatch (mem=0x%08x, backup=0x%08x)", id, mem_crc, hdr.data_crc);
- return -RT_ERROR;
- }
- LOG_I("✅ Verify OK: id=%d, %s=%d, CRC=0x%08X, TIME=%02d/%02d/%02d %02d:%02d:%02d",
- id, item->partition_name, hdr.state, hdr.data_crc,
- hdr.time_stamp.tm_year, hdr.time_stamp.tm_mon, hdr.time_stamp.tm_mday,
- hdr.time_stamp.tm_hour, hdr.time_stamp.tm_min, hdr.time_stamp.tm_sec);
- return RT_EOK;
- }
- int parabak_restore(parabak_id_t id)
- {
- const parabak_item_t *item;
- parabak_header_t hdr;
- uint8_t *data_buf;
- int state;
- int ret = -RT_ERROR;
- if (ensure_initialized() != RT_EOK)
- {
- LOG_E("parabak not initialized");
- return -RT_ERROR;
- }
- if (id >= PARABAK_ID_MAX)
- return -RT_ERROR;
- item = ¶bak_items[id];
- state = parabak_self_check(id, &hdr);
- if (state < 0)
- {
- LOG_E("restore id %d: self check failed", id);
- return -RT_ERROR;
- }
- if (state == 0)
- {
- LOG_W("restore id %d: no valid backup", id);
- return -RT_ERROR;
- }
- if (state == 2)
- {
- LOG_I("restore id %d: already restored once, skip", id);
- return RT_EOK;
- }
- data_buf = rt_malloc(hdr.data_len);
- if (data_buf == RT_NULL)
- {
- LOG_E("restore id %d: out of memory", id);
- return -RT_ERROR;
- }
- rt_mutex_take(parabak_lock, RT_WAITING_FOREVER);
- if (read_data(get_partition_by_id(id), data_buf, hdr.data_len) != RT_EOK)
- {
- LOG_E("restore id %d: read data failed", id);
- goto out;
- }
- uint32_t crc = calc_crc32(data_buf, hdr.data_len);
- if (crc != hdr.data_crc)
- {
- LOG_E("restore id %d: CRC mismatch after read", id);
- goto out;
- }
- /* 1. 恢复到内存 */
- rt_memcpy(item->data_ptr, data_buf, hdr.data_len);
- /* 2. 写入 EasyFlash (env) */
- int env_ret = ef_set_env_blob(item->env_key, item->data_ptr, item->data_size);
- if (env_ret != EF_NO_ERR)
- {
- LOG_E("restore id %d: write to env failed, ret=%d", id, env_ret);
- goto out; // 失败则退出,不更新头部状态
- }
- LOG_I("restore id %d: env updated", id);
- /* 3. 更新头部状态为已还原 (2) */
- hdr.state = 2;
-
- if (write_header(get_partition_by_id(id), &hdr) != RT_EOK)
- {
- LOG_W("restore id %d: update header state failed", id);
- // 即使头部更新失败,env已写入,内存已恢复,仍算成功,但状态未变下次可能再次还原
- }
- LOG_I("restore id %d: memory and env restored", id);
- Recode_e_code(e_bak_restore, id, "执行还原分区");
- ret = RT_EOK;
- out:
- rt_mutex_release(parabak_lock);
- rt_free(data_buf);
- return ret;
- }
- int parabak_get_header(parabak_id_t id, parabak_header_t *hdr)
- {
- const struct fal_partition *part;
- if (ensure_initialized() != RT_EOK)
- {
- LOG_E("parabak not initialized");
- return -RT_ERROR;
- }
- if (id >= PARABAK_ID_MAX || hdr == RT_NULL)
- return -RT_ERROR;
- part = get_partition_by_id(id);
- if (part == RT_NULL)
- return -RT_ERROR;
- rt_mutex_take(parabak_lock, RT_WAITING_FOREVER);
- int ret = read_header(part, hdr);
- rt_mutex_release(parabak_lock);
- return ret;
- }
- /*=============================================================================
- * 自动校验功能(新增自动备份)
- *============================================================================*/
- int parabak_auto_check(void)
- {
- int restored_id = -1;
- parabak_id_t id;
- int state;
- if (ensure_initialized() != RT_EOK)
- {
- LOG_E("auto_check: parabak not initialized");
- return -1;
- }
- for (id = 0; id < PARABAK_ID_MAX; id++)
- {
- parabak_header_t hdr;
- state = parabak_self_check(id, &hdr);
- if (state < 0)
- {
- LOG_E("auto_check: self check failed for id %d", id);
- return -1;
- }
- if (state == 0)
- {
- // 未备份,执行一次备份
- LOG_I("auto_check: no backup for id %d, performing initial backup", id);
- if (parabak_backup(id) != RT_EOK)
- {
- LOG_W("auto_check: initial backup failed for id %d", id);
- // 继续处理其他项
- }
- }
- else if (state == 1)
- { // 已备份状态
- if (parabak_verify(id) != RT_EOK)
- {
- LOG_W("auto_check: verify failed for id %d, try restore", id);
- if (parabak_restore(id) == RT_EOK)
- {
- if (restored_id == -1)
- {
- restored_id = id; // 记录第一个被还原的ID
- }
- }
- else
- {
- LOG_E("auto_check: restore failed for id %d", id);
- return -1;
- }
- }
- }
- else if (state == 2)
- {
- // 已还原状态,检查当前内存是否与备份一致
- if (parabak_verify(id) == RT_EOK)
- {
- // 一致,说明系统稳定,将备份区状态置为已备份
- LOG_I("auto_check: id %d already restored and consistent, set state to backup", id);
- const struct fal_partition *part = get_partition_by_id(id);
- if (part != RT_NULL)
- {
- rt_mutex_take(parabak_lock, RT_WAITING_FOREVER);
- parabak_header_t hdr_mod;
- if (read_header(part, &hdr_mod) == RT_EOK && hdr_mod.state == 2)
- {
- hdr_mod.state = 1;
- if (write_header(part, &hdr_mod) == RT_EOK)
- {
- LOG_I("auto_check: id %d state changed from 2 to 1", id);
- }
- else
- {
- LOG_W("auto_check: id %d state change failed", id);
- }
- }
- rt_mutex_release(parabak_lock);
- }
- }
- }
- }
- if (restored_id >= 0)
- {
- LOG_I("auto_check: some partitions restored, first id=%d", restored_id);
- return restored_id;
- }
- LOG_I("auto_check: all partitions OK");
- return 0;
- }
- /**
- * @brief 测试指令
- *
- */
- /* 擦除所有备份分区(仅测试用) */
- static void parabak_erase_all(int argc, char **argv)
- {
- parabak_id_t id;
- int count = 0;
- int ret;
- if (ensure_initialized() != RT_EOK)
- {
- LOG_E("parabak not initialized");
- return;
- }
- LOG_W("!!! Erasing all backup partitions !!!");
- for (id = 0; id < PARABAK_ID_MAX; id++)
- {
- const struct fal_partition *part = get_partition_by_id(id);
- if (part == RT_NULL)
- {
- LOG_E("partition not found for id %d", id);
- continue;
- }
- rt_mutex_take(parabak_lock, RT_WAITING_FOREVER);
- ret = fal_partition_erase(part, 0, PARABAK_PART_SIZE);
- rt_mutex_release(parabak_lock);
- if (ret < 0)
- {
- LOG_E("erase partition %s failed", part->name);
- }
- else
- {
- LOG_I("erased partition %s", part->name);
- count++;
- }
- }
- LOG_I("Erased %d partitions", count);
- }
- MSH_CMD_EXPORT(parabak_erase_all, Erase all backup partitions (for test only));
|