Files
STM32F4-Base/App/algo/prot_set.c
T
wandering1 0264b49489 feat(algo): B8 频率测量层 + 过频/低频保护(L3-5)
新增 App/algo/freq.c/h:由 L2 基波相量导出系统频率。相邻两拍共轭
乘积 P=Z(m)·conj(Z(m-1)) 取角 = 每拍相位增量(免显式解缠绕),再按
自适应窗长 K_eff=round(8·Fs/(2·f_hat))(45~55 Hz 段 73~89 拍)做滑动
平均。定长窗受 20 点矩形窗负序泄漏纹波限制(实测扫频最坏 0.317 Hz;
60 拍 Kay 复数和 0.197 Hz,交叉项和角不按 1/K^2 衰减),自适应窗使
窗长恰为纹波周期 fs/(2f) 整数倍 -> 最坏 0.0101 Hz。推导与两轮弯路
写在 freq.c 文件头。

假值钳制带取 35~65 Hz,刻意宽于系统合理带 40.5~60.5:单拍相位差本身
带 ±5 Hz 泄漏纹波,贴边钳制会非对称丢弃纹波半周 -> 均值偏置(实测
45 Hz 点 +0.16 Hz)。系统合理带由 OF/UF 定值范围在消费侧把关。
幅值门 FREQ_AMP_MIN=50 码(拟定,待 L1-5 标定复核):低于门判无有效
波形,停更新、valid 清零、读数保持 -> 浮空/PT 断线不发散。源优先级
UA(C02)->IA(C01),按名前缀解析、每拍动态回退,切换即废相位基准。

prot_set:尾追加 8 项(PS_ID_COUNT 32->40,编号不回收)
OF_EN/OF_HZ/OF_DLY/UF_EN/UF_HZ/UF_DLY/RET_OF/RET_UF。OF/UF 逐项通讯
地址不在本仓库 -> 全部 comm_addr=0 本地项(104 C_SE 不可寻址,待对照
参考表补)。频率返回系数独立(0.99/1.01),勿与电气量 0.95/1.05 共用。
bad_mask 随项数扩为 uint64_t。PROT_SET_BLOB_VER 1->2。

prot_set_store:换代与损坏分流。load 返回 2 = 版本/项数换代(本文件
打 INFO "prot set blob version change, defaults in use"),-1 = 真损坏
(defaultTask 按 CORRUPT 报 ERROR + 自检留痕)。B7 旧档(ver=1/32 项)
升级首启属预期路径,不得报成损坏。语义分流收在属主文件内,
default_task.c 不动。

prot_elm:实例表加 src 字段(0=phasor rms,1=freq),PE_OF/PE_UF 尾
追加(PE_COUNT 7->9)。freq 无有效读数按方向填"必不启动"值(过量 0 /
欠量 1e30 -> 滞回比较恒假 -> 元件复位),不整拍跳过——频率源缺失不得
饿死电流/电压类元件(B5 行为不回归)。OF/UF 暂未映射独立 104 遥信点,
动作只经 YX15 保护总聚合可见(送检点表 = B10)。

板端接线:adcTask 在 phasor_run() 之后同拍调 freq_run();心跳/首拍日志
追加 freq(<tag>): f=%ld valid=%u (x0.01)(MicroLIB 无 %f)。app_main
内核前 freq_init()。

主机单测:freq 46 用例(J1 扫频 45~55 Hz <=0.02 Hz 实测最坏 0.0101;
J2 噪声 1% <=0.10 实测 0.0160;J3 启动收敛 <=40 ms 实测 20;J4 阶跃
50->51 <=120 ms 实测 98;J5 浮空 valid=0 不发散;J6 跳变 60 ms 内回
±0.05;另直流 8000 码 / 3-5 次谐波 20%/10% / UA->IA 回退)。
prot_set 90(+TC-PS-011 OF/UF 行 + 64 位 bad_mask bit39 + 旧档拒绝)、
prot_elm 47(+OF/UF 滞回与方向隔离 + run() 测频接线)。回归:phasor
57、iec104 94 全绿。

J4=120 ms 是"精度优先于响应"的显式取舍(Lead 批准):4 周波窗换
0.317->0.0101 Hz,测频响应 98 ms << OF/UF 定时限默认 1000 ms(占
总动作时间 <10%),暂态另有 J6 <=60 ms 兜底。

注册:freq.c 入 uvprojx algo 组 + check_registration.py SIM_EXEMPT
(纯算法、模拟器无消费路径,照 prot_set.c 先例)。

@build 0 错误 0 警告(Code=207468 RO=18304 RW=1068 ZI=153084)。
RAM:RW_IRAM1 96096->96728 B(+632 B,73.3%->73.8%)、RW_CCM 不变
57376 B(87.9%)、Flash 224596->226840 B(+2244 B,21.4%->21.6%);
台账随合并由 Lead 回填。stack_report:adcTask 静态最坏链 1320 B /
余量 1240 B(48%)改前后完全不变(freq_run 帧未进最坏链,无需抬栈);
protectTask 链 872 B / 余 1176 B(57%)同样不变。

gate: only mem-budget/mem-numbers pending lead ledger backfill
--------------------------------------------------------------
门禁实跑(pre-commit run --all-files,Lead 批准 --no-verify 提交):
28 Passed / 2 Failed,失败仅 mem-numbers 与 mem-budget 两条——均为
docs/内存管理 受管快照与本 .map 不一致(台账由 Lead 每次合并后
--write 回填,避免多分支同改受管表冲突)。其余含 doc-links /
coding-standard / no-mojibake / cppcheck / registration-check /
13 条主机单测钩子全 Passed(gitlint 属 commit-msg 阶段,不在该次
--all-files 内)。ci_local L0.5 全绿(freq 46/46 等)。
字节卫生自查(--no-verify 跳过卫生钩子,故手工核):17 个暂存文件
全部 BOM=无 / CR=0(纯 LF)/ U+FFFD=0;自研 .c/.h 去注释后字符串
字面量 100% ASCII(唯一非 ASCII 字面量在 test/host/iec104/iec104_test.c
的既有用例名,非本次改动行;test/ 不在 coding-standard 钩子范围)。
2026-10-12 19:16:37 +08:00

449 lines
17 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 模块名称:保护定值模型(P1,L5-1 载体)——实现
* 模块功能:见 prot_set.h。纯逻辑,零平台依赖(仅 stdint.h + 自身头)。
*
* ⚠ 默认值/范围为拟定值(参考 EEPROM 出厂默认表不在本仓库,规划 §10.3 待补);
* 通讯地址逐项对照参考 Comm/Pub/Const_CommSetPara.c g_tCommSetParaTab。
*
* 创建日期: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;
}