Files
STM32F4-Base/App/algo/prot_set.c
T
wandering1 ffd462ef3c feat(保护标定): 卡5 未标定闭锁 + 卡6 下游三常数重定 + 卡7 量纲回归单测
卡 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 文件)
2026-10-25 05:22:17 +08:00

457 lines
18 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。
*
* 量纲口径(「数据可信」卡 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;
}