ffd462ef3c
卡 5(protect_task.c 第⑤闭锁源,「数据可信」§4.3 决策 1=闭锁): - UA/UN/IA 的 adc_cal VALID 未全置位 → prot_elm_init + 本拍不 eval;每拍现查 flags、无自有状态(卡 12 标定写入后自动解锁);判据链优先级 = 采样可信在前、 标定可用在后(数据都是旧的谈不上标定) - 边沿日志用 DBG_INFO 而非 [ERR]:未标定是出厂常态、非故障,且 @test 禁用 [ERR](「默认构建保护不动作 = 期望」的台架判据同步项已在 MR 报 lead); SOE 分型 SOE_T_CAL_BLOCK/RESUME(soe_rec.h 枚举尾追加,值 6/7——reader 侧 显示标签映射归道B reader,MR 已报) - 台架旁路 = 编译期宏 DTU_BENCH(§4.3:不进 cfg 不进 104;本仓所有提交目标 都不定义它 = 产品恒强制闭锁) 卡 6(下游三常数按工程量重定,依据同源 docs/任务规划/数据可信.md §2.3): - FREQ_AMP_MIN(freq.h):语义从拟定码值域重定为二次值 V——50 V = 0.5×Un (标称 100 V),断保险/PT 空载/浮空必低于之;数值沿用 50.0f 属巧合并在 注释明写;eng=0 过渡期本门恒失效 → OF/UF 按必不启动填充,与卡 5 同向安全 - k_yc[].deadband(iec104_pts.c):9 点按「额定 0.5 %」规则重定:电压类 0.5 %×100 V=0.5、电流类 0.5 %×1 A=0.005、频率维持 0.5 Hz 绝对值(非额定 比,卡 10 运行参数化);标称基准与 prot_set 头部同源,防两处真相 - g_prot_set_meta[](prot_set.c):数值/范围复核结论=不动(0~4×标称自洽), 文件头补量纲口径块:A/V = 二次值、标称 100 V/1 A、OV120=1.2Un/UV20=.2Un/ 3U0 15=.15Un、OC1/2/3=10/5/2×Ir、OL=1.0×Ir;PS_ID_COUNT/blob VER 未动 - iec104 套件 TC-008 死区用例同步重定标(100.4 带内 / 101.25 越带),97/97 卡 7(test/host/prot_dim 新套件,真实链 adc_cal→ring→phasor→prot_elm): - 用例从 ADC 码值出发、显式写出 code↔二次值互逆换算(dim_code vs apply), 折算点被旁路或量纲再错位断言立刻翻转(M4 判据⑥「不该动作时不动作」) - 合成 ROM 覆写(#undef K_Q16 宏)模拟卡 12 标定后板级口径,走 adc_cal_set 正规路径置 VALID;b=-30 码全链对消进 rms 断言 - 38/38:基线静默(含 OL 低定值陷阱)、反错配对照(raw 码值灌环复现历史事故 必假动作 vs 经折算必静默)、OC1/2/3+OL+OV+UV+3U0 七类逐一:越限定值→ 受控启动拍→dly-2 不动作/+5 动作→返回系数带内保持→跌破释放、双件聚合位图 独立性(撤 IA 只回 OC1) 测试与注册:prot-dim-unit-test 钩子 + TC-HOST-PROTDIM 族(游标 010,B 层口径 同 ADCHEALTH/ADCCAL 先例);无新 MCU 源文件(uvprojx/SIM_EXEMPT 零改动) gate:@build 0 错 0 警(Code=215640 RO=21388 RW=1220 ZI=131980 vs 基线 215440/19780/1220/131980 → Flash +1808 B、RAM 零增量);protectTask 静态最坏 链 872 B/margin 1176 B(57%)PASS,adcTask 不变;staged 门禁除 mem-numbers/ mem-budget(台账漂移,回填归 lead)外全绿: gate: only mem-budget/mem-numbers pending lead ledger backfill BOM/CR/U+FFFD/字面量 ASCII 已逐文件自查(本轮未触碰带 BOM 文件)
457 lines
18 KiB
C
457 lines
18 KiB
C
/*
|
||
* 模块名称:保护定值模型(P1,L5-1 载体)——实现
|
||
* 模块功能:见 prot_set.h。纯逻辑,零平台依赖(仅 stdint.h + 自身头)。
|
||
*
|
||
* ⚠ 默认值/范围为拟定值(参考 EEPROM 出厂默认表不在本仓库,规划 §10.3 待补);
|
||
* 通讯地址逐项对照参考 Comm/Pub/Const_CommSetPara.c g_tCommSetParaTab。
|
||
*
|
||
* 量纲口径(「数据可信」卡 6 复核,2026-10-25):A/V 项 = **二次值**工程量,
|
||
* 标称基准 100 V / 1 A(UA/UB/UC 相电压、3U0、OV/UV 同域——OV 120 = 1.2×Un、
|
||
* UV 20 = 0.2×Un、3U0 15 = 0.15×Un;OC1/2/3 = 10/5/2 ×Ir、OL = 1.0×Ir,
|
||
* 三段递升与过负荷低于速断均自洽)。ADC 帧仅在 adc_cal VALID 置位后才处
|
||
* 于此域(未标定 = 引脚电压域,由卡 5 闭锁停走判据,见 protect_task.c);
|
||
* min/max 范围覆盖 0~4×标称,无需随本卡改动数值——换算依据与三处下游
|
||
* 常数同源,见 docs/任务规划/数据可信.md §2.3。
|
||
*
|
||
* 创建日期:2026-10-11
|
||
*/
|
||
#include "prot_set.h"
|
||
|
||
#include <string.h> /* memcpy(set_sealed 影子区拷贝,B7) */
|
||
|
||
/* 元数据表:顺序必须与 prot_set_id_t 严格一致(逐行带地址注释,同 phasor
|
||
* k_inst 的朴素行式风格) */
|
||
const prot_set_meta_t g_prot_set_meta[PS_ID_COUNT] = {
|
||
/* addr ctl default min max 说明 */
|
||
{ 0x8856, 1u, PROT_SET_Q10(1), 0, PROT_SET_Q10(1) }, /* OC1 投退 */
|
||
{ 0x8857, 1u, PROT_SET_Q10(1), 0, PROT_SET_Q10(1) }, /* OC1 跳闸投退 */
|
||
{ 0x8858, 0u, PROT_SET_Q10(10.0), 0, PROT_SET_Q10(100.0) }, /* OC1 电流 A */
|
||
{ 0x8859, 0u, PROT_SET_Q10(0.0), 0, PROT_SET_Q10(60000) }, /* OC1 延时 ms */
|
||
{ 0x885A, 1u, PROT_SET_Q10(1), 0, PROT_SET_Q10(1) },
|
||
{ 0x885B, 1u, PROT_SET_Q10(1), 0, PROT_SET_Q10(1) },
|
||
{ 0x885C, 0u, PROT_SET_Q10(5.0), 0, PROT_SET_Q10(100.0) },
|
||
{ 0x885D, 0u, PROT_SET_Q10(500.0), 0, PROT_SET_Q10(60000) },
|
||
{ 0x885E, 1u, PROT_SET_Q10(1), 0, PROT_SET_Q10(1) },
|
||
{ 0x885F, 1u, PROT_SET_Q10(0), 0, PROT_SET_Q10(1) }, /* OC3 默认告警 */
|
||
{ 0x8860, 0u, PROT_SET_Q10(2.0), 0, PROT_SET_Q10(100.0) },
|
||
{ 0x8861, 0u, PROT_SET_Q10(3000.0), 0, PROT_SET_Q10(60000) },
|
||
{ 0x8871, 1u, PROT_SET_Q10(1), 0, PROT_SET_Q10(1) }, /* 过负荷投退 */
|
||
{ 0x8872, 1u, PROT_SET_Q10(0), 0, PROT_SET_Q10(1) }, /* 默认告警 */
|
||
{ 0x8873, 0u, PROT_SET_Q10(1.0), 0, PROT_SET_Q10(100.0) },
|
||
{ 0x8874, 0u, PROT_SET_Q10(10000.0),0, PROT_SET_Q10(60000) },
|
||
{ 0x0000, 1u, PROT_SET_Q10(1), 0, PROT_SET_Q10(1) }, /* OV 投退(本地) */
|
||
{ 0x8029, 0u, PROT_SET_Q10(120.0), 0, PROT_SET_Q10(200.0) }, /* 过压 V */
|
||
{ 0x802A, 0u, PROT_SET_Q10(10000.0),0, PROT_SET_Q10(60000) },
|
||
{ 0x0000, 1u, PROT_SET_Q10(1), 0, PROT_SET_Q10(1) }, /* UV 投退(本地) */
|
||
{ 0x8027, 0u, PROT_SET_Q10(20.0), 0, PROT_SET_Q10(200.0) }, /* 欠压 V */
|
||
{ 0x8028, 0u, PROT_SET_Q10(10000.0),0, PROT_SET_Q10(60000) },
|
||
{ 0x888E, 1u, PROT_SET_Q10(1), 0, PROT_SET_Q10(1) }, /* 3U0 投退 */
|
||
{ 0x888F, 0u, PROT_SET_Q10(15.0), 0, PROT_SET_Q10(200.0) }, /* 3U0 V */
|
||
{ 0x8890, 0u, PROT_SET_Q10(5000.0), 0, PROT_SET_Q10(60000) },
|
||
{ 0x0000, 0u, PROT_SET_Q10(0.95), PROT_SET_Q10(0.5), PROT_SET_Q10(1.0) }, /* 过量返回系数 */
|
||
{ 0x0000, 0u, PROT_SET_Q10(1.05), PROT_SET_Q10(1.0), PROT_SET_Q10(1.5) }, /* 欠量返回系数 */
|
||
{ 0x8820, 0u, PROT_SET_Q10(10000.0),PROT_SET_Q10(100.0), PROT_SET_Q10(500000.0) },
|
||
{ 0x8821, 0u, PROT_SET_Q10(100.0), PROT_SET_Q10(1.0), PROT_SET_Q10(200.0) },
|
||
{ 0x8825, 0u, PROT_SET_Q10(100.0), PROT_SET_Q10(1.0), PROT_SET_Q10(10000.0) },
|
||
{ 0x8826, 0u, PROT_SET_Q10(5.0), PROT_SET_Q10(1.0), PROT_SET_Q10(10.0) },
|
||
{ 0x0000, 1u, PROT_SET_Q10(1), 0, PROT_SET_Q10(1) }, /* 全局软压板投退(YK12,B7) */
|
||
/* --- 过频 / 低频(L3-5,B8):地址全 0 = 本地项(参考逐项地址不在
|
||
* 本仓库,规划 §5.1 仅示"8820~8882 含低压/低频"类目);
|
||
* 默认值/范围**拟定**(DTU 馈线典型量级),待对照参考出厂表修订 --- */
|
||
{ 0x0000, 1u, PROT_SET_Q10(1), 0, PROT_SET_Q10(1) }, /* OF 投退(本地) */
|
||
{ 0x0000, 0u, PROT_SET_Q10(51.0), PROT_SET_Q10(50.0), PROT_SET_Q10(55.0) }, /* OF 动作频率 Hz */
|
||
{ 0x0000, 0u, PROT_SET_Q10(1000.0), 0, PROT_SET_Q10(60000) }, /* OF 延时 ms */
|
||
{ 0x0000, 1u, PROT_SET_Q10(1), 0, PROT_SET_Q10(1) }, /* UF 投退(本地) */
|
||
{ 0x0000, 0u, PROT_SET_Q10(49.0), PROT_SET_Q10(45.0), PROT_SET_Q10(50.0) }, /* UF 动作频率 Hz */
|
||
{ 0x0000, 0u, PROT_SET_Q10(1000.0), 0, PROT_SET_Q10(60000) }, /* UF 延时 ms */
|
||
{ 0x0000, 0u, PROT_SET_Q10(0.99), PROT_SET_Q10(0.9), PROT_SET_Q10(1.0) }, /* 过频返回系数 */
|
||
{ 0x0000, 0u, PROT_SET_Q10(1.01), PROT_SET_Q10(1.0), PROT_SET_Q10(1.1) }, /* 低频返回系数 */
|
||
};
|
||
|
||
static uint32_t s_val[2][PS_ID_COUNT]; /* 双缓冲值区(uint32 承载 int32,校验和按字覆盖)
|
||
* B7:写者改影子区后单字交换 s_act,
|
||
* 读侧(protectTask selfcheck/get)永远看到一致快照 */
|
||
static volatile uint8_t s_act; /* 活跃区下标(0/1);交换 = 单字存储 */
|
||
static uint32_t s_sum; /* 封存的正码 */
|
||
static uint32_t s_sumn; /* 封存的反码 */
|
||
static uint64_t s_bad_mask; /* load_raw/parse 越界位图(B8:40 项 → 64 位) */
|
||
static uint8_t s_locked; /* 定值非法 → 闭锁(L5-1) */
|
||
static uint8_t s_dirty; /* 未落盘定值改动(B7 持久化脏标志) */
|
||
|
||
/*
|
||
* 函数功能:sum32 覆盖指定值区(参考 Fun_CalSum32 同款:32 位字累加自然回绕)
|
||
* 入口参数:vals - PS_ID_COUNT 个字的值区
|
||
* 返 回 值:校验和正码
|
||
*/
|
||
static uint32_t prot_sum_buf(const uint32_t *vals) {
|
||
uint32_t sum = 0u;
|
||
uint32_t i;
|
||
|
||
for (i = 0u; i < (uint32_t)PS_ID_COUNT; i++) {
|
||
sum += vals[i];
|
||
}
|
||
return sum;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:把元数据表默认值刷进 RAM 值区
|
||
* 入口参数:无
|
||
* 返 回 值:无
|
||
*/
|
||
static void prot_load_defaults(void) {
|
||
uint32_t i;
|
||
|
||
for (i = 0u; i < (uint32_t)PS_ID_COUNT; i++) {
|
||
s_val[s_act][i] = (uint32_t)g_prot_set_meta[i].def;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 函数功能:装载出厂默认值并封存校验和(幂等)
|
||
* 入口参数:无
|
||
* 返 回 值:无
|
||
*/
|
||
void prot_set_init(void) {
|
||
prot_load_defaults();
|
||
s_bad_mask = 0u;
|
||
s_locked = 0u;
|
||
s_dirty = 0u;
|
||
prot_set_seal();
|
||
}
|
||
|
||
/*
|
||
* 函数功能:取最近一次 load_raw 的越界位图
|
||
* 入口参数:无
|
||
* 返 回 值:bit i = 第 i 项越界(load_raw 失败后有效)
|
||
*/
|
||
uint64_t prot_set_bad_mask(void) {
|
||
return s_bad_mask;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:整表装载外部值(P4 起由 cfg_file 定值段调用),逐项范围校验
|
||
* 入口参数:vals - PS_ID_COUNT 个 Q10 值
|
||
* 返 回 值:0 全部合法(已装载并封存)/ -1 存在越界项或空指针(整表不装载)
|
||
* 函数说明:all-or-nothing——宁可全旧不可半新(定值一致性优先);
|
||
* 越界项经 prot_set_bad_mask() 指认。
|
||
*/
|
||
int prot_set_load_raw(const int32_t *vals) {
|
||
uint64_t mask = 0u;
|
||
uint32_t cur_act;
|
||
uint32_t *shadow;
|
||
uint32_t i;
|
||
|
||
if (vals == 0) {
|
||
return -1;
|
||
}
|
||
for (i = 0u; i < (uint32_t)PS_ID_COUNT; i++) {
|
||
if ((vals[i] < g_prot_set_meta[i].min) || (vals[i] > g_prot_set_meta[i].max)) {
|
||
mask |= (1uLL << i);
|
||
}
|
||
}
|
||
s_bad_mask = mask;
|
||
if (mask != 0u) {
|
||
return -1;
|
||
}
|
||
/* 原子装载(B7):写影子区 → 封存 → 单字交换;读侧无中间态 */
|
||
cur_act = s_act;
|
||
shadow = s_val[cur_act ^ 1u];
|
||
for (i = 0u; i < (uint32_t)PS_ID_COUNT; i++) {
|
||
shadow[i] = (uint32_t)vals[i];
|
||
}
|
||
s_sum = prot_sum_buf(shadow);
|
||
s_sumn = ~s_sum;
|
||
s_locked = 0u;
|
||
s_act = (uint8_t)(cur_act ^ 1u);
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:单项写入(范围校验)
|
||
* 入口参数:id - 定值项编号;q10 - Q10 定点值
|
||
* 返 回 值:0 / -1 越界或非法编号拒写(原值不动)
|
||
* 函数说明:**不自动重封校验和**——批量改完必须显式 prot_set_seal(),
|
||
* 否则 selfcheck 判"定值被非法改写"(参考系统改定值走重写整表)。
|
||
*/
|
||
int prot_set_set(prot_set_id_t id, int32_t q10) {
|
||
if (id >= PS_ID_COUNT) {
|
||
return -1;
|
||
}
|
||
if ((q10 < g_prot_set_meta[id].min) || (q10 > g_prot_set_meta[id].max)) {
|
||
return -1;
|
||
}
|
||
s_val[s_act][(uint32_t)id] = (uint32_t)q10;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:单项**原子**写入+封存(B7,收口 B5 竞态窗口)
|
||
* 入口参数:id - 定值项编号;q10 - Q10 定点值
|
||
* 返 回 值:0 / -1 越界或非法编号拒写(原值不动)
|
||
* 函数说明:影子区拷贝 → 改影子 → 按影子重封校验和 → 单字交换活跃区。
|
||
* selfcheck 任何时刻看到的都是"值区与封存一致"的完整快照
|
||
* (区别于 prot_set_set+seal 批处理路径;两路径勿在一次批处理中交错)。
|
||
* 置脏标志 → 104 任务通知 defaultTask(配置引擎属主)落盘。
|
||
*/
|
||
int prot_set_set_sealed(prot_set_id_t id, int32_t q10) {
|
||
uint32_t cur_act;
|
||
uint32_t *shadow;
|
||
|
||
if (id >= PS_ID_COUNT) {
|
||
return -1;
|
||
}
|
||
if ((q10 < g_prot_set_meta[id].min) || (q10 > g_prot_set_meta[id].max)) {
|
||
return -1;
|
||
}
|
||
cur_act = s_act;
|
||
shadow = s_val[cur_act ^ 1u];
|
||
(void)memcpy(shadow, s_val[cur_act], sizeof(s_val[0]));
|
||
shadow[(uint32_t)id] = (uint32_t)q10;
|
||
s_sum = prot_sum_buf(shadow);
|
||
s_sumn = ~s_sum;
|
||
s_locked = 0u;
|
||
s_act = (uint8_t)(cur_act ^ 1u); /* 单字交换:读侧原子切换 */
|
||
s_dirty = 1u;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:脏标志三件套(B7 持久化握手)
|
||
* 入口参数:无
|
||
* 返 回 值:peek/take = 1 存在未落盘改动 / 0 干净;mark 无返回
|
||
* 函数说明:peek 非消费(iec104Task 每轮通知,直到落盘成功);
|
||
* take 消费(defaultTask 落盘前);mark 写盘失败回置(等下轮重试)。
|
||
* 单字节标志,多读者安全;写者只有 set_sealed(置)与属主任务(清/置)。
|
||
*/
|
||
uint8_t prot_set_dirty_peek(void) {
|
||
return s_dirty;
|
||
}
|
||
|
||
uint8_t prot_set_dirty_take(void) {
|
||
uint8_t d = s_dirty;
|
||
|
||
s_dirty = 0u;
|
||
return d;
|
||
}
|
||
|
||
void prot_set_dirty_mark(void) {
|
||
s_dirty = 1u;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读定值 Q10 原值
|
||
* 入口参数:id - 定值项编号
|
||
* 返 回 值:Q10 值;非法编号返回 0
|
||
*/
|
||
int32_t prot_set_get(prot_set_id_t id) {
|
||
if (id >= PS_ID_COUNT) {
|
||
return 0;
|
||
}
|
||
return (int32_t)s_val[s_act][(uint32_t)id];
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读定值工程量浮点值
|
||
* 入口参数:id - 定值项编号
|
||
* 返 回 值:Q10 → float;非法编号返回 0.0
|
||
*/
|
||
float prot_set_get_f(prot_set_id_t id) {
|
||
return PROT_SET_FROM_Q10(prot_set_get(id));
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读投退/枚举项
|
||
* 入口参数:id - 定值项编号
|
||
* 返 回 值:!=0 视为投入(1/0)
|
||
*/
|
||
uint8_t prot_set_get_ctl(prot_set_id_t id) {
|
||
return (prot_set_get(id) != 0) ? 1u : 0u;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:实时计算校验和(不读封存值)
|
||
* 入口参数:无
|
||
* 返 回 值:sum32 正码
|
||
*/
|
||
uint32_t prot_set_checksum(void) {
|
||
return prot_sum_buf(s_val[s_act]);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:封存当前值区的正/反码对(合法改定值闭环的最后一步)
|
||
* 入口参数:无
|
||
* 返 回 值:无
|
||
* 函数说明:封存同时解除闭锁——selfcheck 失配后的唯一合法解锁路径。
|
||
* ⚠ 批处理路径(prot_set_set 多次 + 本函数收尾)在 set 与 seal
|
||
* 之间存在"已改值未封存"窗口,selfcheck 会如实判失配(防篡改语义);
|
||
* 板端单项写一律走 prot_set_set_sealed(原子,无此窗口)。
|
||
*/
|
||
void prot_set_seal(void) {
|
||
s_sum = prot_sum_buf(s_val[s_act]);
|
||
s_sumn = ~s_sum;
|
||
s_locked = 0u;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:定值自检(L5-1)——重算 sum32 并与封存值比对
|
||
* 入口参数:无
|
||
* 返 回 值:0 通过 / -1 失配(已置闭锁)
|
||
* 函数说明:失配 → s_locked 置位并保持,直到下一次 seal;
|
||
* 元件层每拍读 prot_set_locked() 决定整体复位(联动闭锁)。
|
||
* B7 双缓冲 + seqlock 式复核:先读封存和、再读值区、再读封存和,
|
||
* 两次不同 = 检查期间发生了原子提交 → 本轮不作数(下拍再查),
|
||
* 杜绝"提交瞬间被巡检撞见"的误锁(收口 B5 竞态窗口)。
|
||
*/
|
||
int prot_set_selfcheck(void) {
|
||
uint32_t sum_a = s_sum;
|
||
uint32_t calc = prot_sum_buf(s_val[s_act]);
|
||
uint32_t sum_b = s_sum;
|
||
|
||
if (sum_a != sum_b) {
|
||
/* 检查期间有 set_sealed/load_raw 提交:快照不一致,本轮跳过 */
|
||
return (s_locked != 0u) ? -1 : 0;
|
||
}
|
||
if ((calc != sum_a) || (sum_a != ~s_sumn)) {
|
||
s_locked = 1u; /* 值区与封存不符:定值被非法改写 → 闭锁 */
|
||
}
|
||
return (s_locked != 0u) ? -1 : 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读闭锁状态
|
||
* 入口参数:无
|
||
* 返 回 值:1 = 定值非法(保护应整体闭锁)/ 0 = 正常
|
||
*/
|
||
uint8_t prot_set_locked(void) {
|
||
return s_locked;
|
||
}
|
||
|
||
/* ==================== 持久化 blob(B7,纯逻辑) ========================= */
|
||
|
||
/* 小端编解码(blob 跨端一致;主机单测与 MCU 同一份字节口径) */
|
||
|
||
/*
|
||
* 函数功能:16 位值按小端写入缓冲
|
||
* 入口参数:p - 写位置(≥2 字节余量);v - 值
|
||
* 返 回 值:无
|
||
*/
|
||
static void s_put16(uint8_t *p, uint16_t v) {
|
||
p[0] = (uint8_t)(v & 0xFFu);
|
||
p[1] = (uint8_t)(v >> 8);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:32 位值按小端写入缓冲
|
||
* 入口参数:p - 写位置(≥4 字节余量);v - 值
|
||
* 返 回 值:无
|
||
*/
|
||
static void s_put32(uint8_t *p, uint32_t v) {
|
||
p[0] = (uint8_t)(v & 0xFFu);
|
||
p[1] = (uint8_t)((v >> 8) & 0xFFu);
|
||
p[2] = (uint8_t)((v >> 16) & 0xFFu);
|
||
p[3] = (uint8_t)(v >> 24);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:从小端缓冲读 16 位值
|
||
* 入口参数:p - 读位置
|
||
* 返 回 值:值 uint16_t
|
||
*/
|
||
static uint16_t s_get16(const uint8_t *p) {
|
||
return (uint16_t)((uint32_t)p[0] | ((uint32_t)p[1] << 8));
|
||
}
|
||
|
||
/*
|
||
* 函数功能:从小端缓冲读 32 位值
|
||
* 入口参数:p - 读位置
|
||
* 返 回 值:值 uint32_t
|
||
*/
|
||
static uint32_t s_get32(const uint8_t *p) {
|
||
return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16)
|
||
| ((uint32_t)p[3] << 24);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:把当前值区序列化为 blob(校验和按值区现算,不依赖封存值)
|
||
* 入口参数:p_buf - 输出缓冲;cap - 容量;p_len - 实际长度输出
|
||
* 返 回 值:0 / -1 参数非法或容量不足
|
||
*/
|
||
int prot_set_store_serialize(uint8_t *p_buf, uint32_t cap, uint32_t *p_len) {
|
||
const uint32_t *vals = s_val[s_act];
|
||
uint32_t sum = 0u;
|
||
uint32_t i;
|
||
|
||
if ((p_buf == 0) || (p_len == 0) || (cap < PROT_SET_BLOB_SZ)) {
|
||
return -1;
|
||
}
|
||
s_put32(p_buf, PROT_SET_BLOB_MAGIC);
|
||
s_put16(p_buf + 4u, (uint16_t)PROT_SET_BLOB_VER);
|
||
s_put16(p_buf + 6u, (uint16_t)PS_ID_COUNT);
|
||
for (i = 0u; i < (uint32_t)PS_ID_COUNT; i++) {
|
||
s_put32(p_buf + PROT_SET_BLOB_HDR_SZ + 4u * i, vals[i]);
|
||
sum += vals[i];
|
||
}
|
||
s_put32(p_buf + PROT_SET_BLOB_SZ - 8u, sum);
|
||
s_put32(p_buf + PROT_SET_BLOB_SZ - 4u, ~sum);
|
||
*p_len = PROT_SET_BLOB_SZ;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:解析 blob 并**整表原子**装载(all-or-nothing)
|
||
* 入口参数:p_buf/len - blob 字节流
|
||
* 返 回 值:0 已装载并封存 / -1 拒绝(magic/版本/长度/校验对不符或任一项
|
||
* 越界 → 活跃值区一字不动;越界项经 prot_set_bad_mask() 指认)
|
||
* 函数说明:解码/校验全在影子区进行,只有全部通过才交换活跃区——
|
||
* 损坏 blob 不可能污染在用的定值(掉电/写坏/版本换代同一路径)。
|
||
*/
|
||
int prot_set_store_parse(const uint8_t *p_buf, uint32_t len) {
|
||
uint32_t cur_act;
|
||
uint32_t *shadow;
|
||
uint32_t sum = 0u;
|
||
uint64_t mask = 0u;
|
||
uint32_t i;
|
||
|
||
if ((p_buf == 0) || (len != PROT_SET_BLOB_SZ)) {
|
||
return -1;
|
||
}
|
||
if (s_get32(p_buf) != PROT_SET_BLOB_MAGIC) {
|
||
return -1;
|
||
}
|
||
if (s_get16(p_buf + 4u) != (uint16_t)PROT_SET_BLOB_VER) {
|
||
return -1;
|
||
}
|
||
if (s_get16(p_buf + 6u) != (uint16_t)PS_ID_COUNT) {
|
||
return -1;
|
||
}
|
||
cur_act = s_act;
|
||
shadow = s_val[cur_act ^ 1u];
|
||
for (i = 0u; i < (uint32_t)PS_ID_COUNT; i++) {
|
||
uint32_t v = s_get32(p_buf + PROT_SET_BLOB_HDR_SZ + 4u * i);
|
||
|
||
shadow[i] = v;
|
||
sum += v;
|
||
}
|
||
if (sum != s_get32(p_buf + PROT_SET_BLOB_SZ - 8u)) {
|
||
return -1; /* 校验和正码不符(影子区未交换前不可见,无害) */
|
||
}
|
||
if (~sum != s_get32(p_buf + PROT_SET_BLOB_SZ - 4u)) {
|
||
return -1; /* 反码对不符 */
|
||
}
|
||
for (i = 0u; i < (uint32_t)PS_ID_COUNT; i++) {
|
||
if (((int32_t)shadow[i] < g_prot_set_meta[i].min)
|
||
|| ((int32_t)shadow[i] > g_prot_set_meta[i].max)) {
|
||
mask |= (1uLL << i);
|
||
}
|
||
}
|
||
s_bad_mask = mask;
|
||
if (mask != 0u) {
|
||
return -1; /* 校验对一致但含越界项:同样整表拒绝 */
|
||
}
|
||
s_sum = sum;
|
||
s_sumn = ~sum;
|
||
s_locked = 0u;
|
||
s_act = (uint8_t)(cur_act ^ 1u);
|
||
return 0;
|
||
}
|