From 599e25565369ded77d2530e7e0775ae155f083e7 Mon Sep 17 00:00:00 2001 From: coffee Date: Thu, 26 Mar 2026 13:49:08 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E6=8F=90=E5=8D=87Flash=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/HDLog.h | 5 + include/HFlashMem.h | 17 +- include/HFlashServer.h | 42 ++- src/HDLog.c | 11 +- src/HFlashMem.c | 595 +++++++++++++++++++++++++---------------- src/HFlashServer.c | 479 ++++++++++++++++++++++++++++++--- 6 files changed, 863 insertions(+), 286 deletions(-) diff --git a/include/HDLog.h b/include/HDLog.h index 2b0b7da..18f2fe4 100644 --- a/include/HDLog.h +++ b/include/HDLog.h @@ -33,6 +33,11 @@ #define USE_RTOS_LOG_LOCK (0) #endif +// 是否使用HShell +#ifndef HDLOG_USE_HSHELL +#define HDLOG_USE_HSHELL (1) +#endif + // 标准日志输出 #define _LogDExtLevel(level, format, ...) HDLogOut(1, level, __FILE__, __func__, __LINE__, format, ##__VA_ARGS__) #define _LogDLevel(level, format, ...) HDLogOut(0, level, __FILE__, __func__, __LINE__, format, ##__VA_ARGS__) diff --git a/include/HFlashMem.h b/include/HFlashMem.h index 554e51b..e64f230 100644 --- a/include/HFlashMem.h +++ b/include/HFlashMem.h @@ -11,6 +11,11 @@ #include +// 是否使用 hshell +#ifndef HFLASH_USE_HSHELL +#define HFLASH_USE_HSHELL 1 +#endif + // 使用定时器任务ID #ifndef HFLASH_TIMER_ID #define HFLASH_TIMER_ID 0 @@ -25,13 +30,17 @@ enum eHFlashMemIO }; ///< 内存缓存页, 配置需和 (memSize / 4096) 大小一致 +///< 这里不再使用 heat 衰减, 改成 referenced + probation 的二次机会策略 +///< 旧方案需要周期性降温, 小缓存且写入频繁时维护成本高, 也更容易把一次性读页长期留在缓存里 typedef struct HFlashMemCache { uint32_t addr; ///< 地址 uint16_t offset; ///< 内存偏移 uint16_t eraseStatus : 2; ///< 擦除状态 uint16_t isDirty : 1; ///< 是否脏页 - uint16_t heat : 5; ///< 热度优先级 + uint16_t referenced : 1; ///< 最近是否访问过, 仅在命中/装载时置1, 淘汰扫描时清0给第二次机会 + uint16_t probation : 1; ///< 新进入缓存的试用页, 只有再次命中后才转正式页, 这样能优先淘汰一次性访问 + uint16_t reserved : 11; } HFlashMemCache; ///< 内存操作, 回调返回0表示失败 @@ -42,14 +51,16 @@ typedef struct HFlashMemOpts uint8_t (*ioctl)(uint32_t addr, uint32_t cmd, void *arg); ///< 控制Flash, 需要外部实现以上eHFlashMemIO指令 uint8_t *mem; ///< Flash映射内存, 需要4k倍数 HFlashMemCache *cache; ///< 内存缓存 - uint16_t memSize; ///< Flash映射内存大小 - uint16_t cacheSize; ///< 内存缓存大小 + uint32_t memSize; ///< Flash映射内存大小, 改成32位后可避免映射区超过64KB时页数计算溢出 + uint32_t cacheSize; ///< 内存缓存页数量, 需要始终等于 memSize / 4096, 否则 offset 映射会错位 uint32_t flashSize; ///< Flash大小 } HFlashMemOpts; /** * @brief 初始化Flash内存映射 * @param opts 内存操作信息 + * mem/cache 内存由外部分配并长期持有, 初始化不会复制这两块内存 + * cacheSize 必须和 memSize / 4096 完全一致, 因为每个 cache 结构固定对应一个4K映射页 */ void HFlashMemInit(HFlashMemOpts *opts); diff --git a/include/HFlashServer.h b/include/HFlashServer.h index 9bf60e7..967ccc2 100644 --- a/include/HFlashServer.h +++ b/include/HFlashServer.h @@ -12,6 +12,11 @@ #include +// 使用HShell +#ifndef HFLASH_USE_HSHELL +#define HFLASH_USE_HSHELL 1 +#endif + ///< 使用Flash内存映射功能 #ifndef HFLASH_NOT_USE_FLASH_MEM #define HFLASH_USE_FLASH_MEM @@ -85,6 +90,12 @@ typedef union HFlashPageInfo { }; } HFlashPageInfo; +typedef struct HFlashProtectInfo { + HFlashAddr_t addr; ///< 保护区地址 + uint32_t size; ///< 保护区大小 + HFlashAddr_t backupAddr; ///< 保护区备份地址 +} HFlashProtectInfo; + typedef struct HFlashCacheInfo { HFlashPageInfo info; ///< 页表信息 uint32_t pos : 19; ///< 存储页表信息的位置 @@ -149,6 +160,14 @@ uint16_t HFlashGetVersion(HFlashAddr_t addr, HFlashVersionInfo *info); */ void HFlashSetPageCache(HFlashCacheInfo *pageInfo, uint16_t size); +/** + * @brief HFlashSetProtectCache 设置保护区描述缓存, 内部不构建内存, 由外部分配 + * 需要在设置保护地址前调用 + * @param protectInfo 保护区描述缓存 + * @param size 大小 + */ +void HFlashSetProtectCache(HFlashProtectInfo *protectInfo, uint16_t size); + /** * @brief HFlashSetPageAddr 设置页表地址 * @param addr 地址 @@ -158,24 +177,31 @@ void HFlashSetPageAddr(HFlashAddr_t addr, uint32_t size); /** * @brief HFlashSetPageBackupAddr 设置页表备份地址, 需要先设置页表再设置备份 + * 页表备份地址独立存储, 大小必须和页表地址大小相同 * @param addr 地址 - * @param size 大小, 必须和页表地址大小相同 + * @param size 大小, 需要4K对齐 */ void HFlashSetPageBackupAddr(HFlashAddr_t addr, uint32_t size); /** - * @brief HFlashSetProtectAddr 设置保护地址, 内部会使用双备份区域 - * @param addr 地址 - * @param size 大小 + * @brief HFlashClearProtectAddr 清空保护地址配置和对应备份映射 */ -void HFlashSetProtectAddr(HFlashAddr_t addr, uint32_t size); +void HFlashClearProtectAddr(); /** - * @brief HFlashSetProtectBackupAddr 设置保护备份地址, 需要先设置保护地址再设置备份 + * @brief HFlashAddProtectAddr 添加保护地址, 可多次调用追加多个保护区域 + * 保护区描述内存由 HFlashSetProtectCache 外部提供 * @param addr 地址 - * @param size 大小, 必须和保护地址大小相同 + * @param size 大小 + * @param backupAddr 备份地址, 会按保护区大小自动建立一条连续备份映射 */ -void HFlashSetProtectBackupAddr(HFlashAddr_t addr, uint32_t size); +void HFlashAddProtectAddr(HFlashAddr_t addr, uint32_t size, HFlashAddr_t backupAddr); + +/** + * @brief HFlashDelProtectAddr 删除保护地址和对应备份映射 + * @param addr 地址 + */ +void HFlashDelProtectAddr(HFlashAddr_t addr); /** * @brief HFlashInitCheck 在HFlashRegister前需要初始化检查, 用于检查页表是否可用, 不可用则需要初始化 diff --git a/src/HDLog.c b/src/HDLog.c index a9e5ff8..8a684bc 100644 --- a/src/HDLog.c +++ b/src/HDLog.c @@ -2,12 +2,15 @@ #include "HDLog.h" #include "HBit.h" -#include "HShellLex.h" #include #include #include #include +#if HDLOG_USE_HSHELL +#include "HShellLex.h" +#endif + #if USE_RTOS_LOG_LOCK #include "FreeRTOS.h" #include "semphr.h" @@ -39,6 +42,7 @@ typedef struct HDLogFpgaCheckInfo #define CHECK_FPGA_INFO_ID (100) #endif +#if HDLOG_USE_HSHELL static HShellMatch sLogMatch[] = { HSHELL_MATCH_ITEM(kLogLevelSwitch, "log"), HSHELL_MATCH_ITEM(kLogLevelColor, "logColor"), @@ -54,6 +58,7 @@ static HShellMatch sLogMatch[] = { HSHELL_MATCH_ITEM(CHECK_FPGA_INFO_ID + 1, "clearCheckFpga"), #endif }; +#endif ///< 日志级别 static HBIT_DEFINE(sLogItem, kLogLevelMax); @@ -184,6 +189,7 @@ static void LogHex(const uint8_t *data, int len, uint8_t checkPrint, uint8_t log } } +#if HDLOG_USE_HSHELL static void LogShell(HSHELL_FUNC_ARGS) { uint32_t arg1 = 0; @@ -211,6 +217,7 @@ static void LogShell(HSHELL_FUNC_ARGS) sFlashCall(1, key, arg1); } } +#endif void HDLogSetOptFlashCall(HDLogFlashOpt_t call) { @@ -234,7 +241,9 @@ void HDLogOptFlashInit() void HDLogInit(uint32_t (*getTime)()) { sGetTime = getTime; +#if HDLOG_USE_HSHELL HSHellRegister(sLogMatch, sizeof(sLogMatch) / sizeof(HShellMatch), LogShell, 1); +#endif if (sFlashCall == NULL) { HBitSet(sLogItem, kLogLevelSwitch, 0); diff --git a/src/HFlashMem.c b/src/HFlashMem.c index 709563d..8a75fe8 100644 --- a/src/HFlashMem.c +++ b/src/HFlashMem.c @@ -3,34 +3,31 @@ #include "HDLog.h" #include "HTimer.h" +#ifndef USE_STD_MEM +#if HFLASH_USE_HSHELL +#include "HShellLex.h" +#endif +#endif #ifdef USE_STD_MEM #define FATAL_ERROR(format, ...) LogE(format, ##__VA_ARGS__); -#else -#include "HShellLex.h" +#elif HFLASH_USE_HSHELL #define FATAL_ERROR(format, ...) while (1) { LogE(format, ##__VA_ARGS__); HShellRun(); }; +#else +#define FATAL_ERROR(format, ...) while (1) { LogE(format, ##__VA_ARGS__); }; #endif // flash块大小 #define HFLASH_BLOCK_SIZE (4096) -// 调度热度时间 -#define HEAT_TIME_MS (1000) - // 是否对齐页 #define IS_4K(size) ((size & (HFLASH_BLOCK_SIZE - 1)) == 0) -// 最大热度 -#define MAX_HEAT ((1 << 5) - 1) - -// 写转读增加热度 -#define WRITE_CONV_READ_ADD_HEAT (2) - -// 读最小热度 -#define READ_MIN_HEAT (0) - -// 快速最小值 -#define FAST_MIN(type, a, b) ((b) ^ (((a) ^ (b)) & (-(type)((a) < (b))))) +// 这里允许少量读 miss 直接绕过缓存 +// 当缓存已被脏页占满时, 如果每次只读 miss 都强制抢占缓存, 会触发同步刷写, 小缓存场景抖动很大 +// 因此只有同一页连续被直读多次, 才认为它值得挤进缓存 +// 全脏页下, 同一页连续直读达到该次数后才提升进缓存 +#define BYPASS_PROMOTE_THRESHOLD (3) enum eErase { @@ -41,9 +38,13 @@ enum eErase struct Info { - uint32_t currHeatTime; ///< 当前热度时间, 用于计算下一次热度下降 + uint32_t bypassAddr; ///< 全脏页读 miss 时, 最近一次直读页地址 uint16_t useNum; ///< 缓存已使用数量 + uint16_t readClockHand; ///< 读页淘汰时的时钟指针 + uint16_t dirtyClockHand; ///< 脏页回收时的时钟指针 + uint8_t bypassCount; ///< 当前直读页连续命中次数 uint16_t needWaitErase : 1; ///< 用于判断是否在擦除 + uint16_t bypassValid : 1; ///< 最近一次直读页地址是否有效 }; // 内存操作 @@ -55,6 +56,9 @@ static struct Info sInfo; // 检查定时器 static HTimer_t sCheckTimer = HTIMER_INVALID; +static void WatiErase(); +static void CheckSyncCache(); + // 对齐页地址, 向下取整 static inline uint32_t Align4K(uint32_t addr) { return addr & ~(HFLASH_BLOCK_SIZE - 1); @@ -65,6 +69,110 @@ static inline uint8_t *GetMMap(uint16_t offset) { return (uint8_t *)sOpts->mem + offset * HFLASH_BLOCK_SIZE; } +static inline uint8_t IsReusableCleanCache(const HFlashMemCache *cache) +{ + return cache->isDirty == 0 && cache->eraseStatus == kEraseWait; +} + +static void TouchCache(HFlashMemCache *cache) +{ + // 这里同时清 probation + // 新页首次装入时先标记为“试用页”, 只有再次命中才认为它真的是热点, 避免一次性读页长期占坑 + cache->referenced = 1; + cache->probation = 0; +} + +static void InitCache(HFlashMemCache *cache, uint16_t index, uint32_t addr, uint8_t probation) +{ + memset(cache, 0, sizeof(HFlashMemCache)); + cache->addr = addr; + cache->offset = index; + cache->referenced = 1; + cache->probation = probation; +} + +static void MarkCleanCache(HFlashMemCache *cache) +{ + // 脏页刷回后会被重新当作试用页 + // 刚完成回写只代表它“被写过”, 不代表后续仍然高频访问, 立即降为 probation 能避免旧脏页长期压制新页 + cache->isDirty = 0; + cache->eraseStatus = kEraseWait; + cache->referenced = 0; + cache->probation = 1; +} + +static void ClearBypassAddr(uint32_t addr) +{ + if (sInfo.bypassValid && sInfo.bypassAddr == addr) { + sInfo.bypassValid = 0; + sInfo.bypassCount = 0; + } +} + +static void RecordBypassAddr(uint32_t addr) +{ + if (sInfo.bypassValid && sInfo.bypassAddr == addr) { + if (sInfo.bypassCount < BYPASS_PROMOTE_THRESHOLD) { + ++sInfo.bypassCount; + } + return; + } + + sInfo.bypassAddr = addr; + sInfo.bypassValid = 1; + sInfo.bypassCount = 1; +} + +static uint8_t HasDirtyCache() +{ + for (uint16_t i = 0; i < sInfo.useNum; ++i) { + if (sOpts->cache[i].isDirty) { + return 1; + } + } + + return 0; +} + +static HFlashMemCache *FindCacheByAddr(uint32_t addr) +{ + for (uint16_t i = 0; i < sInfo.useNum; ++i) { + if (addr == sOpts->cache[i].addr) { + return sOpts->cache + i; + } + } + + return NULL; +} + +static uint8_t LoadCachePage(HFlashMemCache *cache, uint32_t addr) +{ + WatiErase(); + if (sOpts->read(addr, GetMMap(cache->offset), HFLASH_BLOCK_SIZE) == 0) { + LogE("addr[0x%08x], read faild", addr); + return 0; + } + + return 1; +} + +static uint8_t SyncDirtyCache(HFlashMemCache *cache) +{ + if (cache == NULL || cache->isDirty == 0) { + return cache != NULL; + } + + WatiErase(); + sOpts->ioctl(cache->addr, kHFlashMemIOSyncErase, NULL); + if (sOpts->write(cache->addr, GetMMap(cache->offset), HFLASH_BLOCK_SIZE) == 0) { + LogE("addr[0x%08x], write faild", cache->addr); + return 0; + } + + MarkCleanCache(cache); + return 1; +} + static void WatiErase() { // 如果存在异步等待擦除的, 先等擦除完成 @@ -90,66 +198,145 @@ static void SyncCache() } LogD("Sync[%d], addr[0x%08x], offset[%d]", i, sOpts->cache[i].addr, sOpts->cache[i].offset); - sOpts->ioctl(sOpts->cache[i].addr, kHFlashMemIOSyncErase, NULL); - if (sOpts->write(sOpts->cache[i].addr, GetMMap(i), HFLASH_BLOCK_SIZE) == 0) { - LogD("Sync[%d], addr[0x%08x], offset[%d] faild", i, sOpts->cache[i].addr, sOpts->cache[i].offset); - --i; - continue; + if (SyncDirtyCache(sOpts->cache + i) == 0) { + LogE("Sync[%d], addr[0x%08x], offset[%d] faild", i, sOpts->cache[i].addr, sOpts->cache[i].offset); + } + } +} + +static HFlashMemCache *FindCleanVictim() +{ + HFlashMemCache *fallback = NULL; + if (sInfo.useNum == 0) { + return NULL; + } + + // 两轮时钟扫描: + // 1. referenced=1 的页先清位, 给第二次机会, 不立刻淘汰 + // 2. 优先淘汰 probation 页, 因为它们还没被二次命中过, 更可能只是一次性访问 + // 3. 如果没有 probation 页, 再回退到普通 clean 页 + for (uint8_t round = 0; round < 2; ++round) { + for (uint16_t count = 0; count < sInfo.useNum; ++count) { + const uint16_t index = sInfo.readClockHand; + HFlashMemCache *cache = sOpts->cache + index; + sInfo.readClockHand = (index + 1) % sInfo.useNum; + if (IsReusableCleanCache(cache) == 0) { + continue; + } + + if (cache->referenced) { + cache->referenced = 0; + continue; + } + + if (cache->probation) { + return cache; + } + + fallback = cache; } - sOpts->cache[i].isDirty = 0; - sOpts->cache[i].eraseStatus = kEraseWait; - - // 写入页转换空闲页, 热度持续增加 - sOpts->cache[i].heat = FAST_MIN(uint8_t, sOpts->cache[i].heat + WRITE_CONV_READ_ADD_HEAT, MAX_HEAT); + if (fallback) { + return fallback; + } } + + for (uint16_t i = 0; i < sInfo.useNum; ++i) { + if (IsReusableCleanCache(sOpts->cache + i)) { + return sOpts->cache + i; + } + } + + return NULL; +} + +static HFlashMemCache *FindDirtyVictim() +{ + HFlashMemCache *fallback = NULL; + if (sInfo.useNum == 0) { + return NULL; + } + + // 脏页选择也走 clock, 不再依赖 heat + // 这里只挑 eraseStatus=kEraseWait 的脏页, 避免和正在擦除或等待写回的页并发冲突 + // 当前底层 ioctl 的擦除状态是全局查询模型, 同一时刻只维护一个异步擦除流程更安全 + for (uint8_t round = 0; round < 2; ++round) { + HFlashMemCache *candidate = NULL; + for (uint16_t count = 0; count < sInfo.useNum; ++count) { + const uint16_t index = sInfo.dirtyClockHand; + HFlashMemCache *cache = sOpts->cache + index; + sInfo.dirtyClockHand = (index + 1) % sInfo.useNum; + if (cache->isDirty == 0 || cache->eraseStatus != kEraseWait) { + continue; + } + + fallback = cache; + + if (cache->referenced) { + cache->referenced = 0; + continue; + } + + candidate = cache; + } + + if (candidate) { + return candidate; + } + } + + return fallback; } static void NotifyASyncErase() { - for (uint16_t i = 0; i < sInfo.useNum; ++i) { - if (sOpts->cache[i].isDirty == 0) { - continue; - } - + HFlashMemCache *cache = FindDirtyVictim(); + if (cache) { + // 这里只发起一个异步擦除请求 + // CheckSyncCache 依赖 needWaitErase + eraseStatus 顺序推进状态机, 多个并发擦除会让状态归属变复杂 sInfo.needWaitErase = 1; - sOpts->cache[i].eraseStatus = kEraseStart; - sOpts->ioctl(sOpts->cache[i].addr, kHFlashMemIOErase, NULL); - break; + cache->eraseStatus = kEraseStart; + sOpts->ioctl(cache->addr, kHFlashMemIOErase, NULL); } } -static void FastSyncCache() +static void StartSyncCache() { - WatiErase(); - for (uint16_t i = 0; i < sInfo.useNum; ++i) { - // 存在空闲页, 可以立刻解除 - if (sOpts->cache[i].isDirty == 0) { - break; - } - - LogD("FastSync[%d], addr[0x%08x], offset[%d]", i, sOpts->cache[i].addr, sOpts->cache[i].offset); - sOpts->ioctl(sOpts->cache[i].addr, kHFlashMemIOSyncErase, NULL); - if (sOpts->write(sOpts->cache[i].addr, GetMMap(i), HFLASH_BLOCK_SIZE) == 0) { - LogD("FastSync[%d], addr[0x%08x], offset[%d] faild", i, sOpts->cache[i].addr, sOpts->cache[i].offset); - --i; - continue; - } - - sOpts->cache[i].isDirty = 0; - sOpts->cache[i].eraseStatus = kEraseWait; - - // 写入页转换空闲页, 热度持续增加 - sOpts->cache[i].heat = FAST_MIN(uint8_t, sOpts->cache[i].heat + WRITE_CONV_READ_ADD_HEAT, MAX_HEAT); - break; + if (HasDirtyCache() == 0) { + return; } - NotifyASyncErase(); + if (sCheckTimer != HTIMER_INVALID) { + return; + } + + sCheckTimer = HTimerAdd(HFLASH_TIMER_ID, 0, CheckSyncCache, kHTimerLoop); +} + +static HFlashMemCache *ReclaimOneCleanCache() +{ + HFlashMemCache *cache = FindCleanVictim(); + if (cache) { + return cache; + } + + cache = FindDirtyVictim(); + if (cache == NULL) { + return NULL; + } + + // 这里退化成同步回收一个脏页 + // 写路径必须拿到可用 cache, 如果此时全是脏页, 只能牺牲一次阻塞把一个页刷回, 否则无法继续分配 + LogD("Reclaim addr[0x%08x], offset[%d]", cache->addr, cache->offset); + if (SyncDirtyCache(cache) == 0) { + return NULL; + } + + return cache; } static void CheckSyncCache() { - // 检查擦除是否完成 uint8_t status = 0; if (sInfo.needWaitErase) { sOpts->ioctl(0, kHFlashMemIOCheckErase, &status); @@ -158,63 +345,44 @@ static void CheckSyncCache() } sInfo.needWaitErase = 0; + for (uint16_t i = 0; i < sInfo.useNum; ++i) { + if (sOpts->cache[i].eraseStatus == kEraseStart) { + sOpts->cache[i].eraseStatus = kWaitWrite; + break; + } + } } - uint16_t needErase = sInfo.useNum; - uint8_t heat = MAX_HEAT; - uint8_t waitWrite = 0; + // 这里先处理 kWaitWrite, 再决定是否继续提交新的擦除 + // 写回完成后才能把页重新放回 clean 集合, 否则 read/write 侧会一直看不到可回收页 for (uint16_t i = 0; i < sInfo.useNum; ++i) { - // 检查哪个页的异步擦除, 现在擦除完成 - if (sOpts->cache[i].eraseStatus == kEraseStart) { - sOpts->cache[i].eraseStatus = kWaitWrite; - } - - waitWrite |= sOpts->cache[i].isDirty; - // 寻找需要擦除的页 - if (sOpts->cache[i].isDirty && sOpts->cache[i].eraseStatus == kEraseWait) { - // 查找最低热度的优先擦除, 方便低热度页转换空闲 - if (sOpts->cache[i].heat <= heat) { - heat = sOpts->cache[i].heat; - needErase = i; - } - } - - // 如果不是脏页就跳过 - if (sOpts->cache[i].isDirty == 0) { + HFlashMemCache *cache = sOpts->cache + i; + if (cache->isDirty == 0 || cache->eraseStatus != kWaitWrite) { continue; } - // 检查Flash是否已经擦除完成 - if (sOpts->cache[i].eraseStatus != kWaitWrite) { + if (sOpts->write(cache->addr, GetMMap(cache->offset), HFLASH_BLOCK_SIZE) == 0) { + LogE("addr[0x%08x], write faild", cache->addr); + cache->eraseStatus = kEraseWait; continue; } - // 如果写入失败, 则需要再次擦除重新写入 - if (sOpts->write(sOpts->cache[i].addr, GetMMap(i), HFLASH_BLOCK_SIZE) == 0) { - LogE("addr[0x%08x], write faild", sOpts->cache[i].addr); - sInfo.needWaitErase = 1; - sOpts->cache[i].eraseStatus = kEraseWait; - if (needErase == sInfo.useNum) { - heat = sOpts->cache[i].heat; - needErase = i; - } - continue; - } - - sOpts->cache[i].isDirty = 0; - sOpts->cache[i].eraseStatus = kEraseWait; - - // 写入页转换空闲页, 热度持续增加 - sOpts->cache[i].heat = FAST_MIN(uint8_t, sOpts->cache[i].heat + WRITE_CONV_READ_ADD_HEAT, MAX_HEAT); + MarkCleanCache(cache); } - if (needErase != sInfo.useNum) { - sInfo.needWaitErase = 1; - sOpts->cache[needErase].eraseStatus = kEraseStart; - sOpts->ioctl(sOpts->cache[needErase].addr, kHFlashMemIOErase, NULL); + uint8_t hasPendingWrite = 0; + for (uint16_t i = 0; i < sInfo.useNum; ++i) { + if (sOpts->cache[i].isDirty && sOpts->cache[i].eraseStatus == kWaitWrite) { + hasPendingWrite = 1; + break; + } } - if (sInfo.needWaitErase == 0 && waitWrite == 0) { + if (sInfo.needWaitErase == 0 && hasPendingWrite == 0 && HasDirtyCache()) { + NotifyASyncErase(); + } + + if (sInfo.needWaitErase == 0 && hasPendingWrite == 0 && HasDirtyCache() == 0) { if (sCheckTimer != HTIMER_INVALID) { HTimerRemove(sCheckTimer); sCheckTimer = HTIMER_INVALID; @@ -222,67 +390,7 @@ static void CheckSyncCache() } } -static void StartSyncCache() -{ - if (sCheckTimer != HTIMER_INVALID) { - HTimerRemove(sCheckTimer); - sCheckTimer = HTIMER_INVALID; - } - - sCheckTimer = HTimerAdd(HFLASH_TIMER_ID, 0, CheckSyncCache, kHTimerLoop); -} - -static void ScheduleHeat() -{ - if (HDLogGetTime() - sInfo.currHeatTime < HEAT_TIME_MS) { - return; - } - - for (uint16_t i = 0; i < sInfo.useNum; ++i) { - if (sOpts->cache[i].eraseStatus || sOpts->cache[i].isDirty) { - continue; - } - - // 仅对空闲页降温 - sOpts->cache[i].heat >>= 1; - } - - sInfo.currHeatTime = HDLogGetTime(); -} - -static HFlashMemCache *FindMinHeatCache() -{ - int16_t index = -1; - uint8_t minHeat = MAX_HEAT; - for (uint16_t i = 0; i < sInfo.useNum; ++i) { - if (sOpts->cache[i].eraseStatus || sOpts->cache[i].isDirty) { - continue; - } - - // 寻找最小热度 - const uint8_t currHeat = sOpts->cache[i].heat; - if (index == -1) { - index = i; - minHeat = currHeat; - continue; - } - - if (currHeat > minHeat) { - continue; - } - - index = i; - minHeat = currHeat; - } - - if (index == -1) { - return NULL; - } - - return sOpts->cache + index; -} - -static HFlashMemCache *FindReadCache(uint32_t addr, uint8_t needRead) +static HFlashMemCache *FindReadCache(uint32_t addr, uint8_t needRead, uint8_t allowBypass) { addr = Align4K(addr); if (addr + HFLASH_BLOCK_SIZE > sOpts->flashSize) { @@ -290,54 +398,56 @@ static HFlashMemCache *FindReadCache(uint32_t addr, uint8_t needRead) return NULL; } - for (uint16_t i = 0; i < sInfo.useNum; ++i) { - if (addr != sOpts->cache[i].addr) { - continue; - } - - sOpts->cache[i].heat = FAST_MIN(uint8_t, sOpts->cache[i].heat + 1, MAX_HEAT); - return sOpts->cache + i; + HFlashMemCache *cache = FindCacheByAddr(addr); + if (cache) { + ClearBypassAddr(addr); + TouchCache(cache); + return cache; } // 如果缓存没有满, 直接增加 if (sInfo.useNum < sOpts->cacheSize) { const uint16_t index = sInfo.useNum; - ++sInfo.useNum; + cache = sOpts->cache + index; + InitCache(cache, index, addr, 1); if (needRead) { - WatiErase(); - if (sOpts->read(addr, GetMMap(index), HFLASH_BLOCK_SIZE) == 0) { - LogE("addr[0x%08x], read faild", addr); + if (LoadCachePage(cache, addr) == 0) { return NULL; } } - memset(sOpts->cache + index, 0, sizeof(HFlashMemCache)); - sOpts->cache[index].addr = addr; - sOpts->cache[index].offset = index; - sOpts->cache[index].heat = FAST_MIN(uint8_t, sOpts->cache[index].heat + 1, MAX_HEAT); - return sOpts->cache + index; + ClearBypassAddr(addr); + ++sInfo.useNum; + return cache; } - // 查找空闲的最低热度页 - HFlashMemCache *cache = FindMinHeatCache(); - if (cache == NULL || cache->heat > READ_MIN_HEAT) { - return NULL; - } - - const uint16_t index = cache->offset; - memset(cache, 0, sizeof(HFlashMemCache)); - cache->addr = addr; - cache->offset = index; - cache->heat = READ_MIN_HEAT; - - if (needRead) { - WatiErase(); - if (sOpts->read(addr, GetMMap(index), HFLASH_BLOCK_SIZE) == 0) { - LogE("addr[0x%08x], read faild", addr); + cache = FindCleanVictim(); + if (cache == NULL && allowBypass && needRead) { + // bypass 只允许真正的读 miss + // 普通读接口可以接受本次直接读 flash, 但像 mmap/callback 这种需要稳定页指针的场景必须强制拿到 cache + RecordBypassAddr(addr); + if (sInfo.bypassCount < BYPASS_PROMOTE_THRESHOLD) { return NULL; } } + if (cache == NULL) { + cache = ReclaimOneCleanCache(); + } + if (cache == NULL) { + return NULL; + } + + const uint16_t index = cache->offset; + InitCache(cache, index, addr, 1); + + if (needRead) { + if (LoadCachePage(cache, addr) == 0) { + return NULL; + } + } + + ClearBypassAddr(addr); return cache; } @@ -349,38 +459,47 @@ static HFlashMemCache *FindWriteCache(uint32_t addr, uint8_t needRead) return NULL; } - HFlashMemCache *cache = FindReadCache(addr, needRead); + HFlashMemCache *cache = FindCacheByAddr(addr); if (cache) { + ClearBypassAddr(addr); + TouchCache(cache); return cache; } - for (;;) { - // 写入模式下, 需要抢占缓存页 - cache = FindMinHeatCache(); - if (cache == NULL) { - // 如果在创建模式下, 没有一个空闲的缓存, 就需要先同步脏页, 再获取一个 - FastSyncCache(); - continue; - } - - const uint16_t index = cache->offset; - memset(cache, 0, sizeof(HFlashMemCache)); - cache->addr = addr; - cache->offset = index; - cache->heat = READ_MIN_HEAT; + if (sInfo.useNum < sOpts->cacheSize) { + const uint16_t index = sInfo.useNum; + cache = sOpts->cache + index; + InitCache(cache, index, addr, 0); if (needRead) { - WatiErase(); - // 找到一个空闲且最低温度的 - if (sOpts->read(addr, GetMMap(index), HFLASH_BLOCK_SIZE) == 0) { - LogE("addr[0x%08x], read faild", addr); + if (LoadCachePage(cache, addr) == 0) { return NULL; } } - + ClearBypassAddr(addr); + ++sInfo.useNum; return cache; } - return NULL; + cache = FindCleanVictim(); + if (cache == NULL) { + cache = ReclaimOneCleanCache(); + } + if (cache == NULL) { + return NULL; + } + + { + const uint16_t index = cache->offset; + InitCache(cache, index, addr, 0); + if (needRead) { + if (LoadCachePage(cache, addr) == 0) { + return NULL; + } + } + ClearBypassAddr(addr); + } + + return cache; } @@ -393,9 +512,6 @@ static uint8_t WriteData(uint32_t addr, const void *buf, uint32_t len, void (*cp return 0; } - // 开启逐步同步 - StartSyncCache(); - // 找到映射表, 先处理首页 const uint8_t *bufPtr = (const uint8_t *)buf; const uint32_t offset = addr - cache->addr; @@ -430,6 +546,8 @@ static uint8_t WriteData(uint32_t addr, const void *buf, uint32_t len, void (*cp cpCall(GetMMap(cache->offset), bufPtr, len, userData); } + // 写路径只负责标脏, 后台异步回收逐步推进, 避免小缓存下同步冲刷 + StartSyncCache(); return 1; } @@ -461,7 +579,7 @@ static uint8_t FindReadPageCopy(uint32_t addr, void *buf, uint32_t len) return 0; } - HFlashMemCache *cache = FindReadCache(pageAddr, 1); + HFlashMemCache *cache = FindReadCache(pageAddr, 1, 1); if (cache) { memcpy(p, GetMMap(cache->offset) + offset, copyLen); } else { @@ -501,6 +619,14 @@ void HFlashMemInit(HFlashMemOpts *opts) FATAL_ERROR("flash Size not 4k align"); return ; } + + // 重新初始化时会把运行态和定时器一并清掉 + // 这些状态记录的是上一次 cache 使用轨迹和异步擦除进度, 沿用会把新配置带进旧状态机 + memset(&sInfo, 0, sizeof(sInfo)); + if (sCheckTimer != HTIMER_INVALID) { + HTimerRemove(sCheckTimer); + sCheckTimer = HTIMER_INVALID; + } } const HFlashMemOpts *HFlashMemGetOpt() @@ -518,7 +644,6 @@ uint8_t HFlashMemRead(uint32_t addr, void *buf, uint32_t len) return 1; } - ScheduleHeat(); uint8_t *p = (uint8_t *)buf; while (len > 0) { const uint32_t pageAddr = Align4K(addr); @@ -530,7 +655,7 @@ uint8_t HFlashMemRead(uint32_t addr, void *buf, uint32_t len) return 0; } - HFlashMemCache *cache = FindReadCache(pageAddr, 1); + HFlashMemCache *cache = FindReadCache(pageAddr, 1, 1); if (cache) { memcpy(p, GetMMap(cache->offset) + offset, copyLen); } else { @@ -565,7 +690,6 @@ uint8_t HFlashMemWrite(uint32_t addr, const void *buf, uint32_t len) return 1; } - ScheduleHeat(); return WriteData(addr, buf, len, _MemCpyHelper, NULL); } @@ -581,7 +705,6 @@ uint8_t HFlashMemSetValue(uint32_t addr, uint8_t value, uint32_t len) return 0; } - ScheduleHeat(); return WriteData(addr, NULL, len, _MemSetValueHelper, &value); } @@ -597,7 +720,6 @@ uint8_t HFlashMemAddrCopy(uint32_t srcAddr, uint32_t destAddr, uint32_t len) return 0; } - ScheduleHeat(); return WriteData(destAddr, NULL, len, _MemAddrCopy, &srcAddr); } @@ -607,15 +729,15 @@ uint8_t HFlashMemMMapCall(uint32_t addr, uint8_t(*call)(uint32_t addr, uint32_t return 0; } - ScheduleHeat(); uint32_t offset = 0; - HFlashMemCache *cache = NULL; uint16_t len = 0; uint8_t result = 0; // 先处理未对齐的部分 if (addr != Align4K(addr)) { - cache = FindWriteCache(addr + offset, 1); + // 这里显式禁止 bypass + // 回调拿到的是 cache 内存指针, 如果退化成直读 flash, 本接口就无法提供连续且可复用的页视图 + HFlashMemCache *cache = FindReadCache(addr + offset, 1, 0); if (cache == NULL) { LogE("addr[0x%08x], find cache faild", addr); return 0; @@ -632,7 +754,7 @@ uint8_t HFlashMemMMapCall(uint32_t addr, uint8_t(*call)(uint32_t addr, uint32_t // 剩余处理对齐部分 while (result == 0) { - cache = FindWriteCache(addr + offset, 1); + HFlashMemCache *cache = FindReadCache(addr + offset, 1, 0); if (cache == NULL) { LogE("addr[0x%08x], find cache faild", addr + offset); return 0; @@ -651,14 +773,19 @@ uint8_t HFlashMemMMapCall(uint32_t addr, uint8_t(*call)(uint32_t addr, uint32_t void HFlashMemSync() { - ScheduleHeat(); + if (sCheckTimer != HTIMER_INVALID) { + HTimerRemove(sCheckTimer); + sCheckTimer = HTIMER_INVALID; + } SyncCache(); } void HFlashMemFreeCache() { HFlashMemSync(); - sInfo.useNum = 0; - sInfo.currHeatTime = HDLogGetTime(); + if (sCheckTimer != HTIMER_INVALID) { + HTimerRemove(sCheckTimer); + sCheckTimer = HTIMER_INVALID; + } + memset(&sInfo, 0, sizeof(sInfo)); } - diff --git a/src/HFlashServer.c b/src/HFlashServer.c index c80697c..fa26d7e 100644 --- a/src/HFlashServer.c +++ b/src/HFlashServer.c @@ -4,7 +4,9 @@ #include "HDLog.h" #include "HVector.h" #include "HTimer.h" +#if HFLASH_USE_HSHELL #include "HShellLex.h" +#endif #include #ifdef HFLASH_USE_FLASH_MEM @@ -49,7 +51,9 @@ __attribute__((weak)) void HDMainLoopCallOverride(uint8_t event) { (void)(event); +#if HFLASH_USE_HSHELL HShellRun(); +#endif } #define FATAL_ERROR(format, ...) while (1) { LogE(format, ##__VA_ARGS__); HDMainLoopCallOverride(0); }; #endif @@ -60,7 +64,9 @@ void HDMainLoopCallOverride(uint8_t event) // 检查是不是4对齐 #define IS_NOT_4(size) ((size & (4 - 1)) != 0) #define IS_NOT_4K(size) ((size & (HFLASH_BLOCK_SIZE - 1)) != 0) +#define ALIGN_4K(size) (((size) + HFLASH_BLOCK_SIZE - 1) & ~(HFLASH_BLOCK_SIZE - 1)) +#if HFLASH_USE_HSHELL enum eShell { kShellPageInfo, ///< 查看页表信息 @@ -84,6 +90,7 @@ static HShellMatch sShellMatch[] = { HSHELL_MATCH_ITEM(kShellFlashMemInfo, "flashMemInfo"), #endif }; +#endif union PageInfo { @@ -104,9 +111,9 @@ struct Info { HFlashAddr_t pageAddr; ///< 页表地址 HFlashAddr_t pageBackupAddr; ///< 页表备份地址 uint32_t pageSize; ///< 页大小 - HFlashAddr_t protectAddr; ///< 保护区地址 - HFlashAddr_t protectBackupAddr; ///< 保护区备份地址 - uint32_t protectSize; ///< 保护区大小 + HFlashProtectInfo *protectInfo; ///< 保护区描述缓存 + uint16_t protectCacheSize; ///< 保护区描述缓存大小 + uint16_t protectUseNum; ///< 保护区使用数量 HFlashCacheInfo *pageCache; ///< 页表缓存 uint16_t pageCacheSize; ///< 页表缓存大小 uint16_t pageCacheUseNum; ///< 页表缓存使用页 @@ -130,30 +137,193 @@ static HTimer_t sSyncCacheTimer = HTIMER_INVALID; static HVECTOR_DEFINE32(sNeedBackupOffset, 10); static uint8_t RestoreBackup(HFlashAddr_t srcAddr, HFlashAddr_t dstAddr, uint32_t size); +static uint8_t RestoreBackupRange(HFlashAddr_t addr, uint32_t size); +static uint8_t SyncBackupRange(HFlashAddr_t addr, uint32_t size); static void StartSyncBackupTimer(); +static uint8_t ReadFlashCall(HFlashAddr_t addr, void *data, uint32_t size); +static int16_t FindProtectIndex(HFlashAddr_t addr, uint32_t size); +static int16_t FindProtectBackupIndex(HFlashAddr_t addr, uint32_t size); +static int16_t FindProtectAddrIndex(HFlashAddr_t addr); static HFlashCacheInfo *FindCache(HFlashAddr_t addr); static void WriteCachePage(HFlashCacheInfo *info); static uint32_t GetFlashCrc32(HFlashAddr_t addr, uint32_t size); +static uint8_t ReadBackup(HFlashAddr_t addr, void *data, uint32_t size); +static uint32_t GetBackupCrc32(HFlashAddr_t addr, uint32_t size); +static uint8_t GetBackupAddr(HFlashAddr_t addr, uint32_t size, HFlashAddr_t *backupAddr); +static uint8_t GetProtectBackupRange(HFlashAddr_t addr, uint32_t size, HFlashAddr_t *backupStartAddr, uint32_t *backupSize); +static inline uint32_t AdjustProtectBackupSize(HFlashAddr_t addr, uint32_t size); + +static void ResetRuntimeState() +{ + if (sBackupTimer != HTIMER_INVALID) { + HTimerRemove(sBackupTimer); + sBackupTimer = HTIMER_INVALID; + } + +#ifndef HFLASH_USE_FLASH_MEM + if (sSyncPageTimer != HTIMER_INVALID) { + HTimerRemove(sSyncPageTimer); + sSyncPageTimer = HTIMER_INVALID; + } +#endif + + if (sSyncCacheTimer != HTIMER_INVALID) { + HTimerRemove(sSyncCacheTimer); + sSyncCacheTimer = HTIMER_INVALID; + } + + HVectorClear(sNeedBackupOffset); +} ///< 检查地址是否在双备份保护区内 static uint8_t IsProtect(HFlashAddr_t addr, uint32_t size) +{ + return FindProtectIndex(addr, size) >= 0; +} + +static uint8_t GetProtectBackupRange(HFlashAddr_t addr, uint32_t size, HFlashAddr_t *backupStartAddr, uint32_t *backupSize) +{ + const uint64_t startAddr = addr & ~((uint64_t)HFLASH_BLOCK_SIZE - 1u); + const uint64_t endAddr = (uint64_t)addr + size; + const uint64_t alignMask = (uint64_t)HFLASH_BLOCK_SIZE - 1u; + uint64_t backupEnd = endAddr; + + if (size == 0) { + *backupStartAddr = (HFlashAddr_t)startAddr; + *backupSize = 0; + return 1; + } + + if (endAddr > UINT32_MAX) { + FATAL_ERROR("protect addr[0x%08x] size[%d] overflow", addr, size); + return 0; + } + + if ((backupEnd & alignMask) != 0) { + backupEnd = (backupEnd + alignMask) & ~alignMask; + } + + if (backupEnd > UINT32_MAX) { + FATAL_ERROR("protect addr[0x%08x] size[%d] align overflow", addr, size); + return 0; + } + + *backupStartAddr = (HFlashAddr_t)startAddr; + *backupSize = (uint32_t)(backupEnd - startAddr); + return 1; +} + +static int16_t FindProtectIndexByRange(HFlashAddr_t addr, uint32_t size, uint8_t useBackupRange) { const HFlashAddr_t addrEnd = addr + size; if (addrEnd < addr) { FATAL_ERROR("addr[0x%08x] size[%d] overflow", addr, size); + return -1; + } + + for (uint16_t i = 0; i < sInfo.protectUseNum; ++i) { + HFlashAddr_t protectAddr = sInfo.protectInfo[i].addr; + uint32_t protectSize = sInfo.protectInfo[i].size; + + if (useBackupRange && GetProtectBackupRange(protectAddr, protectSize, &protectAddr, &protectSize) == 0) { + return -1; + } + + const HFlashAddr_t protectEnd = protectAddr + protectSize; + if (protectEnd < protectAddr) { + FATAL_ERROR("protect addr[0x%08x] size[%d] overflow", protectAddr, protectSize); + return -1; + } + + if (addr < protectAddr || addr >= protectEnd) { + continue; + } + + if (addrEnd > protectEnd) { + FATAL_ERROR("addr[0x%08x][0x%08x] size[%d][%d] overflow protect%s", + addr, + protectAddr, + size, + protectSize, + useBackupRange ? " backup" : ""); + return -1; + } + + return (int16_t)i; + } + + return -1; +} + +static int16_t FindProtectIndex(HFlashAddr_t addr, uint32_t size) +{ + return FindProtectIndexByRange(addr, size, 0); +} + +static int16_t FindProtectBackupIndex(HFlashAddr_t addr, uint32_t size) +{ + return FindProtectIndexByRange(addr, size, 1); +} + +static int16_t FindProtectAddrIndex(HFlashAddr_t addr) +{ + for (uint16_t i = 0; i < sInfo.protectUseNum; ++i) { + if (sInfo.protectInfo[i].addr == addr) { + return (int16_t)i; + } + } + + return -1; +} + +static uint8_t GetBackupAddr(HFlashAddr_t addr, uint32_t size, HFlashAddr_t *backupAddr) +{ + const HFlashAddr_t addrEnd = addr + size; + if (addrEnd < addr) { + FATAL_ERROR("backup addr[0x%08x] size[%d] overflow", addr, size); return 0; } - if (addr < sInfo.protectAddr || addr >= sInfo.protectAddr + sInfo.protectSize) { + const HFlashAddr_t pageEnd = sInfo.pageAddr + sInfo.pageSize; + if (pageEnd >= sInfo.pageAddr && addr >= sInfo.pageAddr && addrEnd <= pageEnd) { + *backupAddr = sInfo.pageBackupAddr + (addr - sInfo.pageAddr); + return 1; + } + + const int16_t index = FindProtectBackupIndex(addr, size); + if (index >= 0) { + HFlashProtectInfo *info = &sInfo.protectInfo[index]; + HFlashAddr_t protectAddr = 0; + uint32_t protectSize = 0; + if (GetProtectBackupRange(info->addr, info->size, &protectAddr, &protectSize) == 0) { + return 0; + } + + (void)protectSize; + *backupAddr = info->backupAddr + (addr - protectAddr); + return 1; + } + + return 0; +} + +static uint8_t ReadBackup(HFlashAddr_t addr, void *data, uint32_t size) +{ + if (size == 0) { + return 1; + } + + if (data == NULL) { return 0; } - if (addrEnd > sInfo.protectAddr + sInfo.protectSize) { - FATAL_ERROR("addr[0x%08x][0x%08x] size[%d][%d] overflow protect", addr, sInfo.protectAddr, size, sInfo.protectSize); + HFlashAddr_t backupAddr = 0; + if (GetBackupAddr(addr, size, &backupAddr) == 0) { + LogE("backup addr[0x%08x] not config", addr); return 0; } - return 1; + return ReadFlashCall(backupAddr, data, size); } static uint8_t ReadFlashCall(HFlashAddr_t addr, void *data, uint32_t size) @@ -330,6 +500,19 @@ static inline uint32_t AdjustPageByte(uint32_t index) return index * sizeof(HFlashPageInfo); } +static inline uint32_t AdjustProtectBackupSize(HFlashAddr_t addr, uint32_t size) +{ + HFlashAddr_t backupStartAddr = 0; + uint32_t backupSize = 0; + + if (GetProtectBackupRange(addr, size, &backupStartAddr, &backupSize) == 0) { + return 0; + } + + (void)backupStartAddr; + return backupSize; +} + static void SyncBackup() { LogD("Start sync Backup"); @@ -340,8 +523,7 @@ static void SyncBackup() for (uint16_t i = 0; i < HVectorGetUseLen(sNeedBackupOffset); ++i) { const uint32_t addr = HVectorGetData(sNeedBackupOffset, i); - const uint32_t destAddr = sInfo.protectBackupAddr + (addr - sInfo.protectAddr); - if (RestoreBackup(addr, destAddr, HFLASH_BLOCK_SIZE) == 0) { + if (SyncBackupRange(addr, HFLASH_BLOCK_SIZE) == 0) { LogE("RestorePage faild, addr[0x%08x]", addr); StartSyncBackupTimer(); return ; @@ -351,9 +533,9 @@ static void SyncBackup() HVectorClear(sNeedBackupOffset); // 备份页表数据 - const uint32_t crcValue = GetFlashCrc32(AdjustPageAddr(sInfo.pageBackupAddr), AdjustPageByte(sPageInfo.useNum)); + const uint32_t crcValue = GetBackupCrc32(AdjustPageAddr(sInfo.pageAddr), AdjustPageByte(sPageInfo.useNum)); if (crcValue != sPageInfo.crc32) { - RestoreBackup(sInfo.pageAddr, sInfo.pageBackupAddr, AdjustPageByte(sPageInfo.useNum + 1)); + SyncBackupRange(sInfo.pageAddr, AdjustPageByte(sPageInfo.useNum + 1)); } LogD("Sync Backup End"); } @@ -466,6 +648,21 @@ static uint32_t GetFlashCrc32(HFlashAddr_t addr, uint32_t size) return HSCrc32Get(); } +static uint32_t GetBackupCrc32(HFlashAddr_t addr, uint32_t size) +{ + if (size == 0) { + return 0xFFFFFFFF; + } + + HFlashAddr_t backupAddr = 0; + if (GetBackupAddr(addr, size, &backupAddr) == 0) { + LogE("backup addr[0x%08x] not config", addr); + return 0xFFFFFFFF; + } + + return GetFlashCrc32(backupAddr, size); +} + static uint8_t RestoreBackup(HFlashAddr_t srcAddr, HFlashAddr_t dstAddr, uint32_t copySize) { if (copySize == 0) { @@ -541,6 +738,31 @@ _FlashError: #endif } +static uint8_t CopyBackupRange(HFlashAddr_t addr, uint32_t size, uint8_t isRestore) +{ + HFlashAddr_t backupAddr = 0; + if (GetBackupAddr(addr, size, &backupAddr) == 0) { + LogE("backup addr[0x%08x] not config", addr); + return 0; + } + + if (isRestore) { + return RestoreBackup(backupAddr, addr, size); + } + + return RestoreBackup(addr, backupAddr, size); +} + +static uint8_t RestoreBackupRange(HFlashAddr_t addr, uint32_t size) +{ + return CopyBackupRange(addr, size, 1); +} + +static uint8_t SyncBackupRange(HFlashAddr_t addr, uint32_t size) +{ + return CopyBackupRange(addr, size, 0); +} + ///< 恢复指定页表 static uint8_t RestorePage(uint32_t index) { @@ -549,9 +771,7 @@ static uint8_t RestorePage(uint32_t index) return 0; } - const HFlashAddr_t srcAddr = AdjustPageAddrOffset(sInfo.pageBackupAddr, index); - const HFlashAddr_t dstAddr = AdjustPageAddrOffset(sInfo.pageAddr, index); - return RestoreBackup(srcAddr, dstAddr, sizeof(HFlashPageInfo)); + return RestoreBackupRange(AdjustPageAddrOffset(sInfo.pageAddr, index), sizeof(HFlashPageInfo)); } static void SyncPageInfo() @@ -944,6 +1164,18 @@ void HFlashSetPageCache(HFlashCacheInfo *pageInfo, uint16_t size) sInfo.pageCacheSize = size; } +void HFlashSetProtectCache(HFlashProtectInfo *protectInfo, uint16_t size) +{ + ResetRuntimeState(); + sInfo.protectInfo = protectInfo; + sInfo.protectCacheSize = size; + sInfo.protectUseNum = 0; + + if (protectInfo != NULL && size) { + memset(protectInfo, 0, size * sizeof(HFlashProtectInfo)); + } +} + void HFlashSetPageAddr(HFlashAddr_t addr, uint32_t size) { if (IS_NOT_4K(addr) || IS_NOT_4K(size)) { @@ -956,42 +1188,200 @@ void HFlashSetPageAddr(HFlashAddr_t addr, uint32_t size) void HFlashSetPageBackupAddr(HFlashAddr_t addr, uint32_t size) { - if (IS_NOT_4K(addr) || IS_NOT_4K(size)) { + if (IS_NOT_4K(addr) || IS_NOT_4K(size) || size != sInfo.pageSize) { FATAL_ERROR("not align page size[0x%08x], size[%x]", addr, size); + return ; } - if (sInfo.pageSize != size) { - FATAL_ERROR("page size not match[%d][%d]", sInfo.pageSize, size); + if (IsOverLap(sInfo.pageAddr, sInfo.pageSize, addr, size)) { + FATAL_ERROR("page backup overlap src/dst[0x%08x][0x%08x]", sInfo.pageAddr, addr); return ; } + for (uint16_t i = 0; i < sInfo.protectUseNum; ++i) { + HFlashProtectInfo *info = &sInfo.protectInfo[i]; + if (IsOverLap(addr, size, info->backupAddr, AdjustProtectBackupSize(info->addr, info->size))) { + FATAL_ERROR("page backup overlap protect backup[0x%08x][0x%08x]", addr, info->backupAddr); + return ; + } + } + sInfo.pageBackupAddr = addr; - sInfo.pageSize = size; } -void HFlashSetProtectAddr(HFlashAddr_t addr, uint32_t size) +static uint8_t CheckProtectConflict(HFlashAddr_t addr, uint32_t size, HFlashAddr_t backupAddr) { - if (IS_NOT_4K(addr) || IS_NOT_4K(size)) { - FATAL_ERROR("not align page size[0x%08x], size[%x]", addr, size); + HFlashAddr_t adjustAddr = 0; + uint32_t backupSize = 0; + if (GetProtectBackupRange(addr, size, &adjustAddr, &backupSize) == 0) { + return 0; } - sInfo.protectAddr = addr; - sInfo.protectSize = size; + if (IsOverLap(adjustAddr, backupSize, sInfo.pageAddr, sInfo.pageSize)) { + FATAL_ERROR("protect area overlap page[0x%08x][0x%08x]", addr, sInfo.pageAddr); + return 0; + } + + if (sInfo.pageBackupAddr && IsOverLap(backupAddr, backupSize, sInfo.pageBackupAddr, sInfo.pageSize)) { + FATAL_ERROR("protect backup overlap page backup[0x%08x][0x%08x]", backupAddr, sInfo.pageBackupAddr); + return 0; + } + + if (IsOverLap(adjustAddr, backupSize, backupAddr, backupSize)) { + FATAL_ERROR("protect backup overlap src/dst[0x%08x][0x%08x]", addr, backupAddr); + return 0; + } + + for (uint16_t i = 0; i < sInfo.protectUseNum; ++i) { + HFlashProtectInfo *info = &sInfo.protectInfo[i]; + HFlashAddr_t infoAdjustAddr = 0; + uint32_t infoBackupSize = 0; + + if (GetProtectBackupRange(info->addr, info->size, &infoAdjustAddr, &infoBackupSize) == 0) { + return 0; + } + + if (info->addr == addr) { + FATAL_ERROR("protect addr[0x%08x] duplicate", addr); + return 0; + } + + if (IsOverLap(infoAdjustAddr, infoBackupSize, adjustAddr, backupSize)) { + FATAL_ERROR("protect area overlap[0x%08x][0x%08x]", info->addr, addr); + return 0; + } + + if (info->backupAddr == backupAddr) { + FATAL_ERROR("protect backup addr[0x%08x] duplicate", backupAddr); + return 0; + } + + if (IsOverLap(info->backupAddr, infoBackupSize, backupAddr, backupSize)) { + FATAL_ERROR("protect backup overlap[0x%08x][0x%08x]", info->backupAddr, backupAddr); + return 0; + } + } + + return 1; } -void HFlashSetProtectBackupAddr(HFlashAddr_t addr, uint32_t size) +void HFlashClearProtectAddr() { - if (IS_NOT_4K(addr) || IS_NOT_4K(size)) { - FATAL_ERROR("not align page size[0x%08x], size[%x]", addr, size); + ResetRuntimeState(); + sInfo.protectUseNum = 0; + if (sInfo.protectInfo != NULL && sInfo.protectCacheSize) { + memset(sInfo.protectInfo, 0, sInfo.protectCacheSize * sizeof(HFlashProtectInfo)); } +} - if (sInfo.protectSize != size) { - FATAL_ERROR("protect size not match[%d][%d]", sInfo.protectSize, size); +void HFlashAddProtectAddr(HFlashAddr_t addr, uint32_t size, HFlashAddr_t backupAddr) +{ + if (size == 0) { + FATAL_ERROR("protect size is 0 addr[0x%08x]", addr); return ; } - sInfo.protectBackupAddr = addr; - sInfo.protectSize = size; + if (IS_NOT_4K(backupAddr)) { + FATAL_ERROR("not align backup addr[0x%08x]", backupAddr); + return ; + } + + if (sInfo.protectInfo == NULL || sInfo.protectCacheSize == 0) { + FATAL_ERROR("protectCache is null"); + return ; + } + + if (sInfo.protectUseNum >= sInfo.protectCacheSize) { + FATAL_ERROR("protect is full"); + return ; + } + + if (CheckProtectConflict(addr, size, backupAddr) == 0) { + LogE("protect addr[0x%08x] conflict", addr); + return ; + } + + HFlashProtectInfo *info = &sInfo.protectInfo[sInfo.protectUseNum]; + info->addr = addr; + info->size = size; + info->backupAddr = backupAddr; + ++sInfo.protectUseNum; +} + +void HFlashDelProtectAddr(HFlashAddr_t addr) +{ + const int16_t index = FindProtectAddrIndex(addr); + if (index < 0) { + FATAL_ERROR("protect addr[0x%08x] not exist", addr); + return ; + } + + ResetRuntimeState(); + for (uint16_t i = (uint16_t)index; i + 1 < sInfo.protectUseNum; ++i) { + sInfo.protectInfo[i] = sInfo.protectInfo[i + 1]; + } + + --sInfo.protectUseNum; + memset(&sInfo.protectInfo[sInfo.protectUseNum], 0, sizeof(HFlashProtectInfo)); +} + +static uint8_t CheckBackupArea() +{ + if (sInfo.pageBackupAddr == 0) { + FATAL_ERROR("page backup addr is null"); + return 0; + } + + if (IsOverLap(sInfo.pageAddr, sInfo.pageSize, sInfo.pageBackupAddr, sInfo.pageSize)) { + FATAL_ERROR("page backup overlap src/dst[0x%08x][0x%08x]", sInfo.pageAddr, sInfo.pageBackupAddr); + return 0; + } + + for (uint16_t i = 0; i < sInfo.protectUseNum; ++i) { + HFlashProtectInfo *info = &sInfo.protectInfo[i]; + HFlashAddr_t adjustAddr = 0; + uint32_t backupSize = 0; + + if (GetProtectBackupRange(info->addr, info->size, &adjustAddr, &backupSize) == 0) { + return 0; + } + + if (IsOverLap(adjustAddr, backupSize, info->backupAddr, backupSize)) { + FATAL_ERROR("protect backup overlap src/dst[0x%08x][0x%08x]", info->addr, info->backupAddr); + return 0; + } + + if (IsOverLap(info->backupAddr, backupSize, sInfo.pageBackupAddr, sInfo.pageSize)) { + FATAL_ERROR("protect backup overlap page backup[0x%08x][0x%08x]", info->backupAddr, sInfo.pageBackupAddr); + return 0; + } + } + + for (uint16_t i = 0; i < sInfo.protectUseNum; ++i) { + for (uint16_t j = i + 1; j < sInfo.protectUseNum; ++j) { + HFlashAddr_t adjustAddrI = 0; + HFlashAddr_t adjustAddrJ = 0; + uint32_t backupSizeI = 0; + uint32_t backupSizeJ = 0; + + if (GetProtectBackupRange(sInfo.protectInfo[i].addr, sInfo.protectInfo[i].size, &adjustAddrI, &backupSizeI) == 0 || + GetProtectBackupRange(sInfo.protectInfo[j].addr, sInfo.protectInfo[j].size, &adjustAddrJ, &backupSizeJ) == 0) { + return 0; + } + + if (IsOverLap(adjustAddrI, backupSizeI, adjustAddrJ, backupSizeJ)) { + FATAL_ERROR("protect area overlap[0x%08x][0x%08x]", sInfo.protectInfo[i].addr, sInfo.protectInfo[j].addr); + return 0; + } + + if (IsOverLap(sInfo.protectInfo[i].backupAddr, backupSizeI, sInfo.protectInfo[j].backupAddr, backupSizeJ)) { + FATAL_ERROR("protect backup overlap[0x%08x][0x%08x]", sInfo.protectInfo[i].backupAddr, sInfo.protectInfo[j].backupAddr); + return 0; + } + } + } + + return 1; } static uint8_t _Print(uint32_t index, HFlashPageInfo *info, void *userData) @@ -1010,6 +1400,7 @@ static uint8_t _Print(uint32_t index, HFlashPageInfo *info, void *userData) return 0; } +#if HFLASH_USE_HSHELL static void Shell(HSHELL_FUNC_ARGS) { uint32_t arg1 = 0; @@ -1048,7 +1439,7 @@ static void Shell(HSHELL_FUNC_ARGS) HFlashResetPage(); } break; case kShellRestorePage: { - RestoreBackup(sInfo.pageAddr, sInfo.pageBackupAddr, AdjustPageByte(sPageInfo.pageNum + 1)); + RestoreBackupRange(sInfo.pageAddr, AdjustPageByte(sPageInfo.pageNum + 1)); } break; #ifdef HFLASH_USE_FLASH_MEM case kShellFlashMemInfo: { @@ -1058,7 +1449,13 @@ static void Shell(HSHELL_FUNC_ARGS) } for (uint8_t i = 0; i < opt->cacheSize; ++i) { - HSHELL_PRINTFL("addr[0x%08x], offset[%d], eraseStatus[%d], isDirty[%d], heat[%d]", opt->cache[i].addr, opt->cache[i].offset, opt->cache[i].eraseStatus, opt->cache[i].isDirty, opt->cache[i].heat); + HSHELL_PRINTFL("addr[0x%08x], offset[%d], eraseStatus[%d], isDirty[%d], referenced[%d], probation[%d]", + opt->cache[i].addr, + opt->cache[i].offset, + opt->cache[i].eraseStatus, + opt->cache[i].isDirty, + opt->cache[i].referenced, + opt->cache[i].probation); } } break; #endif @@ -1066,18 +1463,20 @@ static void Shell(HSHELL_FUNC_ARGS) break; } } +#endif void HFlashInitCheck() { +#if HFLASH_USE_HSHELL HSHellRegister(sShellMatch, sizeof(sShellMatch) / sizeof(HShellMatch), Shell, 0); +#endif if (sInfo.pageCache == NULL || sInfo.pageCacheSize == 0) { FATAL_ERROR("pageCache is null"); return ; } - // 检查页表地址有没有和备份地址重叠问题 - if (IsOverLap(sInfo.pageAddr, sInfo.pageSize, sInfo.pageBackupAddr, sInfo.pageSize)) { - FATAL_ERROR("page table and backup table is overlap"); + if (CheckBackupArea() == 0) { + LogE("Backup area check faild"); return ; } @@ -1101,15 +1500,15 @@ void HFlashInitCheck() LogD("page table is invalid, check backup table"); // 备份校验 - if (ReadFlashCall(sInfo.pageBackupAddr, sPageInfo.reverse, sizeof(sPageInfo)) == 0) { - LogE("Read backup page table[0x%08x] faild", sInfo.pageBackupAddr); + if (ReadBackup(sInfo.pageAddr, sPageInfo.reverse, sizeof(sPageInfo)) == 0) { + LogE("Read backup page table[0x%08x] faild", sInfo.pageAddr); break; } invalid = sPageInfo.pageNum > num; invalid = invalid || sPageInfo.useNum > num; invalid = invalid || sPageInfo.useNum > sPageInfo.pageNum; - invalid = invalid || GetFlashCrc32(AdjustPageAddr(sInfo.pageBackupAddr), AdjustPageByte(sPageInfo.useNum)) != sPageInfo.crc32; + invalid = invalid || GetBackupCrc32(AdjustPageAddr(sInfo.pageAddr), AdjustPageByte(sPageInfo.useNum)) != sPageInfo.crc32; if (invalid) { // 备份也无效, 首次创建, 去初始化数据 LogD("backup page table is invalid, init data"); @@ -1117,7 +1516,7 @@ void HFlashInitCheck() } // 备份校验通过, 恢复全备份数据 - RestoreBackup(sInfo.pageBackupAddr, sInfo.pageAddr, sInfo.pageSize); + RestoreBackupRange(sInfo.pageAddr, sInfo.pageSize); return ; } while (0); @@ -1191,7 +1590,7 @@ void HFlashRegister(HFlashAddr_t addr, uint32_t size) // 先恢复内容区域 LogD("addr[0x%08x] size[%d] crc32 not match, start restore", addr, size); - RestoreBackup(sInfo.protectBackupAddr + (cache->info.addr - sInfo.protectAddr), cache->info.addr, cache->info.size); + RestoreBackupRange(cache->info.addr, cache->info.size); // 如果恢复后还校验失败, 那就恢复对应页表 const uint32_t contentCrc = GetFlashCrc32(cache->info.addr, cache->info.useSize);