Files
STM32F4-Base/App/algo/prot_set.c
T
wandering1 94b0d79026 feat(ui)+chore(docs): UI 补齐清单清空;数据可信销项与「卡」号清退
本笔含两组收尾工作(同一工作区内交错在 6 个共享文档/门禁配置里,
拆开会产生"文档声称的页数与树里黄金数不一致"的中间提交,故合为一笔):

一、UI 补齐(清单清空)
- 新建 6 个内容页模块:ui_page_accident / oper / alarm / start / yk
  复用 ui_w_records 共享组件(分页、空态"无记录"、按实例读盘缓存),
  记录层与五个写入点已由道F task-10 / 道G task-12 落地,本批只补页面;
  ui_page_runreport 为 13 行 2 页只读统计,四个数逐个挂范围标签
  (自电池周期 / 本次开机),boot_ready=0 显 "--" 不显 0。
- 数据面:ui_data_info_t 尾部追加 9 字段,ui_data_feed 用
  boot_stats_get() + iec104_serv_get_stats() 纯搬运;模拟器填确定值假数据
  (差异登记 docs/测试说明/模拟器差距清单.md §2.3 两行)。
- 接线:菜单树 6 叶绑 #page=,绑定叶 22→28、UI_PAGE_MAP_COUNT=28;
  字库 185→218 字 / 280→313 字形;双端注册 uvprojx +30、CMakeLists +6。
- 回归:内容页黄金 53→60(含 runreport_p2 真拍第 2 页),文本断言
  425 正向 / 36 反向;存量 53 张逐字节零漂移;记录 5 页各用独立空目录
  (模拟器把操作留痕写进 cwd 的 sim_oper.log,共用 tmp 会让空态拍不到)。

二、数据可信销项 + 「数据可信」任务卡号整族清退
- 删除已闭环的"已知电压注入验证"行与闭环结论块(证据在
  docs/硬件平台/ADC通道映射说明.md §8.1 与
  docs/测试说明/ADC前端注入验证流程.md §5~§7),§5 标识列改语义短语。
- 卡 0/0b/1~12 共 14 个号全仓 288 站点 / 56 文件清退成语义短语,
  代码注释、测试、门禁配置与文档一并改;换算表入 docs/编号规范.md §9.3。
  这一族是中文前缀,不进 check_ids 语料,刻意不入 id_aliases.json。

验证(实测)
- @build 0 错 0 警:Code=236456 RO=28124 RW=1876 ZI=137084;
  RAM +464 B(RW +304 / ZI +160),RW_IRAM1 62.2% / RW_CCM 87.9%,
  体积同码凭证与 RAM 台账已回填。
- check_menu_rows 8 状态 rc=0(池 5368/5448 常量未动);
  check_content_pages 60 页 rc=0;check_font_charset / registration /
  coding-standard / bom-drift / mojibake / doc-links --all / layer-deps /
  nested-comment 全 rc=0;check_ids --strict 无错误;主机单测
  ui_data 295 / boot_stats 21 / sys_log_mcu 141 / soe 32 / soe_store 27。

未做(不是遗漏)
- 六页真机判据(真记录翻页、掉电保持、清除后归零、boot_count 自增、
  拔 RTC 电池落 RESTARTED、IWDG 复位落 rst_cause、主站连一次 conn_up+1)
  已登记 docs/任务规划/登记簿.md §7;模拟器结构上照不到。
- docs/产品需求/产品需求设计文档.md 属另一条在飞改动,本笔不含;
  其中我已同步 6 处现状数字(28 叶 / 60 页 / 218 字),随该笔入库。
2026-10-28 07:23:16 +08:00

458 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,定值校验和 载体)——实现
*(保护功能清单见 docs/算法/继电保护功能复现规划.md)
* 模块功能:见 prot_set.h。纯逻辑,零平台依赖(仅 stdint.h + 自身头)。
*
* ⚠ 默认值/范围为拟定值(参考 EEPROM 出厂默认表不在本仓库,规划 §10.3 待补);
* 通讯地址逐项对照参考 Comm/Pub/Const_CommSetPara.c g_tCommSetParaTab。
*
* 量纲口径(「数据可信」下游常数按工程量重定一项复核,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 置位后才处
* 于此域(未标定 = 引脚电压域,由未标定即闭锁停走判据,见 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) */
/* --- 过频 / 低频(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; /* 定值非法 → 闭锁(定值校验和) */
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;
}
/*
* 函数功能:定值自检(定值校验和)——重算 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;
}